summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am7
-rw-r--r--src/d_iwad.c3
-rw-r--r--src/d_mode.h1
-rw-r--r--src/deh_io.c141
-rw-r--r--src/deh_io.h1
-rw-r--r--src/deh_main.c49
-rw-r--r--src/deh_main.h2
-rw-r--r--src/doom/d_main.c191
-rw-r--r--src/doom/d_net.c770
-rw-r--r--src/doom/d_net.h1
-rw-r--r--src/doom/doomstat.h3
-rw-r--r--src/doom/dstrings.h9
-rw-r--r--src/doom/g_game.c33
-rw-r--r--src/doom/m_menu.c10
-rw-r--r--src/doom/p_map.c2
-rw-r--r--src/doom/p_setup.c12
-rw-r--r--src/doom/p_spec.c9
-rw-r--r--src/doom/r_data.c20
-rw-r--r--src/doomfeatures.h3
-rw-r--r--src/i_sdlsound.c41
-rw-r--r--src/i_system.c8
-rw-r--r--src/i_video.c235
-rw-r--r--src/i_video.h1
-rw-r--r--src/m_argv.c15
-rw-r--r--src/m_argv.h4
-rw-r--r--src/m_config.c28
-rw-r--r--src/net_client.c257
-rw-r--r--src/net_client.h6
-rw-r--r--src/net_dedicated.c2
-rw-r--r--src/net_defs.h33
-rw-r--r--src/net_gui.c18
-rw-r--r--src/net_gui.h2
-rw-r--r--src/net_loop.c22
-rw-r--r--src/net_query.c691
-rw-r--r--src/net_query.h15
-rw-r--r--src/net_sdl.c4
-rw-r--r--src/net_server.c128
-rw-r--r--src/net_server.h4
-rw-r--r--src/net_structrw.c22
-rw-r--r--src/net_structrw.h3
-rw-r--r--src/setup/display.c304
-rw-r--r--src/setup/execute.c55
-rw-r--r--src/setup/execute.h2
-rw-r--r--src/setup/joystick.c4
-rw-r--r--src/setup/mainmenu.c31
-rw-r--r--src/setup/multiplayer.c148
-rw-r--r--src/setup/multiplayer.h1
-rw-r--r--src/setup/txt_joybinput.c1
-rw-r--r--src/setup/txt_keyinput.c1
-rw-r--r--src/setup/txt_mouseinput.c1
-rw-r--r--src/strife/d_main.c105
-rw-r--r--src/strife/d_net.c27
-rw-r--r--src/strife/doomstat.h1
-rw-r--r--src/strife/dstrings.h9
-rw-r--r--src/strife/g_game.c19
-rw-r--r--src/strife/m_menu.c8
-rw-r--r--src/strife/p_map.c2
-rw-r--r--src/strife/p_spec.c7
-rw-r--r--src/strife/r_data.c20
-rw-r--r--src/w_main.c12
60 files changed, 2543 insertions, 1021 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 2ba6dbef..0b747f89 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,6 +34,7 @@ net_dedicated.c net_dedicated.h \
net_io.c net_io.h \
net_packet.c net_packet.h \
net_sdl.c net_sdl.h \
+net_query.c net_query.h \
net_server.c net_server.h \
net_structrw.c net_structrw.h \
z_native.c z_zone.h
@@ -142,9 +143,11 @@ EXTRA_LIBS = \
@SDLNET_LIBS@
if HAVE_WINDRES
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) resource.rc \
+ $(FEATURE_MULTIPLAYER_SOURCE_FILES)
else
-@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH)
+@PROGRAM_PREFIX@doom_SOURCES=$(SOURCE_FILES_WITH_DEH) \
+ $(FEATURE_MULTIPLAYER_SOURCE_FILES)
endif
@PROGRAM_PREFIX@doom_LDADD = doom/libdoom.a $(EXTRA_LIBS)
diff --git a/src/d_iwad.c b/src/d_iwad.c
index e7f25234..a6187c10 100644
--- a/src/d_iwad.c
+++ b/src/d_iwad.c
@@ -47,6 +47,7 @@ static iwad_t iwads[] =
{ "doom.wad", doom, retail, "Doom" },
{ "doom1.wad", doom, shareware, "Doom Shareware" },
{ "chex.wad", doom, shareware, "Chex Quest" },
+ { "hacx.wad", doom2, commercial, "Hacx" },
{ "heretic.wad", heretic, retail, "Heretic" },
{ "heretic1.wad", heretic, shareware, "Heretic Shareware" },
{ "hexen.wad", hexen, commercial, "Hexen" },
@@ -657,7 +658,7 @@ char *D_FindIWAD(int mask, GameMission_t *mission)
// @arg <file>
//
- iwadparm = M_CheckParm("-iwad");
+ iwadparm = M_CheckParmWithArgs("-iwad", 1);
if (iwadparm)
{
diff --git a/src/d_mode.h b/src/d_mode.h
index 8746b354..20cb89e2 100644
--- a/src/d_mode.h
+++ b/src/d_mode.h
@@ -63,6 +63,7 @@ typedef enum
typedef enum
{
exe_doom_1_9, // Doom 1.9: used for shareware, registered and commercial
+ exe_hacx, // Hacx
exe_ultimate, // Ultimate Doom (retail)
exe_final, // Final Doom
exe_chex, // Chex Quest executable (based on Final Doom)
diff --git a/src/deh_io.c b/src/deh_io.c
index 3386a6fa..92c81632 100644
--- a/src/deh_io.c
+++ b/src/deh_io.c
@@ -30,21 +30,63 @@
#include <string.h>
#include "i_system.h"
+#include "w_wad.h"
#include "z_zone.h"
#include "deh_defs.h"
#include "deh_io.h"
+typedef enum
+{
+ DEH_INPUT_FILE,
+ DEH_INPUT_LUMP
+} deh_input_type_t;
+
struct deh_context_s
{
- FILE *stream;
+ deh_input_type_t type;
char *filename;
+
+ // If the input comes from a memory buffer, pointer to the memory
+ // buffer.
+
+ unsigned char *input_buffer;
+ size_t input_buffer_len;
+ unsigned int input_buffer_pos;
+ int lumpnum;
+
+ // If the input comes from a file, the file stream for reading
+ // data.
+
+ FILE *stream;
+
+ // Current line number that we have reached:
+
int linenum;
+
+ // Used by DEH_ReadLine:
+
boolean last_was_newline;
char *readbuffer;
int readbuffer_size;
};
+static deh_context_t *DEH_NewContext(void)
+{
+ deh_context_t *context;
+
+ context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
+
+ // Initial read buffer size of 128 bytes
+
+ context->readbuffer_size = 128;
+ context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
+ context->linenum = 0;
+ context->last_was_newline = true;
+
+ return context;
+}
+
// Open a dehacked file for reading
// Returns NULL if open failed
@@ -52,22 +94,41 @@ deh_context_t *DEH_OpenFile(char *filename)
{
FILE *fstream;
deh_context_t *context;
-
+
fstream = fopen(filename, "r");
if (fstream == NULL)
return NULL;
- context = Z_Malloc(sizeof(*context), PU_STATIC, NULL);
+ context = DEH_NewContext();
+
+ context->type = DEH_INPUT_FILE;
context->stream = fstream;
-
- // Initial read buffer size of 128 bytes
+ context->filename = strdup(filename);
- context->readbuffer_size = 128;
- context->readbuffer = Z_Malloc(context->readbuffer_size, PU_STATIC, NULL);
- context->filename = filename;
- context->linenum = 0;
- context->last_was_newline = true;
+ return context;
+}
+
+// Open a WAD lump for reading.
+
+deh_context_t *DEH_OpenLump(int lumpnum)
+{
+ deh_context_t *context;
+ void *lump;
+
+ lump = W_CacheLumpNum(lumpnum, PU_STATIC);
+
+ context = DEH_NewContext();
+
+ context->type = DEH_INPUT_LUMP;
+ context->lumpnum = lumpnum;
+ context->input_buffer = lump;
+ context->input_buffer_len = W_LumpLength(lumpnum);
+ context->input_buffer_pos = 0;
+
+ context->filename = malloc(9);
+ strncpy(context->filename, lumpinfo[lumpnum].name, 8);
+ context->filename[8] = '\0';
return context;
}
@@ -76,33 +137,67 @@ deh_context_t *DEH_OpenFile(char *filename)
void DEH_CloseFile(deh_context_t *context)
{
- fclose(context->stream);
+ if (context->type == DEH_INPUT_FILE)
+ {
+ fclose(context->stream);
+ }
+ else if (context->type == DEH_INPUT_LUMP)
+ {
+ W_ReleaseLumpNum(context->lumpnum);
+ }
+
Z_Free(context->readbuffer);
Z_Free(context);
}
+int DEH_GetCharFile(deh_context_t *context)
+{
+ if (feof(context->stream))
+ {
+ // end of file
+
+ return -1;
+ }
+
+ return fgetc(context->stream);
+}
+
+int DEH_GetCharLump(deh_context_t *context)
+{
+ int result;
+
+ if (context->input_buffer_pos >= context->input_buffer_len)
+ {
+ return -1;
+ }
+
+ result = context->input_buffer[context->input_buffer_pos];
+ ++context->input_buffer_pos;
+
+ return result;
+}
+
// Reads a single character from a dehacked file
int DEH_GetChar(deh_context_t *context)
{
int result;
-
+
// Read characters, but ignore carriage returns
// Essentially this is a DOS->Unix conversion
- do
+ do
{
- if (feof(context->stream))
+ switch (context->type)
{
- // end of file
+ case DEH_INPUT_FILE:
+ result = DEH_GetCharFile(context);
+ break;
- result = -1;
+ case DEH_INPUT_LUMP:
+ result = DEH_GetCharLump(context);
+ break;
}
- else
- {
- result = fgetc(context->stream);
- }
-
} while (result == '\r');
// Track the current line number
@@ -111,9 +206,9 @@ int DEH_GetChar(deh_context_t *context)
{
++context->linenum;
}
-
+
context->last_was_newline = result == '\n';
-
+
return result;
}
diff --git a/src/deh_io.h b/src/deh_io.h
index 061a5a0e..9d22b360 100644
--- a/src/deh_io.h
+++ b/src/deh_io.h
@@ -30,6 +30,7 @@
#include "deh_defs.h"
deh_context_t *DEH_OpenFile(char *filename);
+deh_context_t *DEH_OpenLump(int lumpnum);
void DEH_CloseFile(deh_context_t *context);
int DEH_GetChar(deh_context_t *context);
char *DEH_ReadLine(deh_context_t *context);
diff --git a/src/deh_main.c b/src/deh_main.c
index 75934087..39d59e8c 100644
--- a/src/deh_main.c
+++ b/src/deh_main.c
@@ -32,6 +32,7 @@
#include "doomtype.h"
#include "d_iwad.h"
#include "m_argv.h"
+#include "w_wad.h"
#include "deh_defs.h"
#include "deh_io.h"
@@ -246,9 +247,6 @@ static void DEH_ParseContext(deh_context_t *context)
DEH_Error(context, "This is not a valid dehacked patch file!");
}
- deh_allow_long_strings = false;
- deh_allow_long_cheats = false;
-
// Read the file
for (;;)
@@ -260,7 +258,9 @@ static void DEH_ParseContext(deh_context_t *context)
// end of file?
if (line == NULL)
+ {
return;
+ }
while (line[0] != '\0' && isspace(line[0]))
++line;
@@ -347,6 +347,48 @@ int DEH_LoadFile(char *filename)
return 1;
}
+// Load dehacked file from WAD lump.
+
+int DEH_LoadLump(int lumpnum)
+{
+ deh_context_t *context;
+
+ // If it's in a lump, it's probably designed for a modern source port,
+ // so allow it to do long string and cheat replacements.
+
+ deh_allow_long_strings = true;
+ deh_allow_long_cheats = true;
+
+ context = DEH_OpenLump(lumpnum);
+
+ if (context == NULL)
+ {
+ fprintf(stderr, "DEH_LoadFile: Unable to open lump %i\n", lumpnum);
+ return 0;
+ }
+
+ DEH_ParseContext(context);
+
+ DEH_CloseFile(context);
+
+ return 1;
+}
+
+int DEH_LoadLumpByName(char *name)
+{
+ int lumpnum;
+
+ lumpnum = W_CheckNumForName(name);
+
+ if (lumpnum == -1)
+ {
+ fprintf(stderr, "DEH_LoadLumpByName: '%s' lump not found\n", name);
+ return 0;
+ }
+
+ return DEH_LoadLump(lumpnum);
+}
+
// Checks the command line for -deh argument
void DEH_Init(void)
@@ -387,4 +429,3 @@ void DEH_Init(void)
}
}
-
diff --git a/src/deh_main.h b/src/deh_main.h
index f9cb44ca..9ac2c6c7 100644
--- a/src/deh_main.h
+++ b/src/deh_main.h
@@ -41,6 +41,8 @@
void DEH_Init(void);
int DEH_LoadFile(char *filename);
+int DEH_LoadLump(int lumpnum);
+int DEH_LoadLumpByName(char *name);
boolean DEH_ParseAssignment(char *line, char **variable_name, char **value);
diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index f3a4b037..2f116f1e 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -122,8 +122,6 @@ int startmap;
boolean autostart;
int startloadgame;
-FILE* debugfile;
-
boolean advancedemo;
// Store demo, do not accept any inputs
@@ -424,14 +422,6 @@ void D_DoomLoop (void)
if (demorecording)
G_BeginRecording ();
- if (M_CheckParm ("-debugfile"))
- {
- char filename[20];
- sprintf (filename,"debug%i.txt",consoleplayer);
- printf ("debug output to: %s\n",filename);
- debugfile = fopen (filename,"w");
- }
-
TryRunTics();
I_SetWindowTitle(gamedescription);
@@ -457,9 +447,12 @@ void D_DoomLoop (void)
// process one or more tics
if (singletics)
{
+ static ticcmd_t cmds[MAXPLAYERS];
+
I_StartTic ();
D_ProcessEvents ();
- G_BuildTiccmd (&netcmds[consoleplayer][maketic%BACKUPTICS]);
+ netcmds = cmds;
+ G_BuildTiccmd(&cmds[consoleplayer]);
if (advancedemo)
D_DoAdvanceDemo ();
M_Ticker ();
@@ -533,7 +526,13 @@ void D_DoAdvanceDemo (void)
paused = false;
gameaction = ga_nothing;
- if (gamemode == retail && gameversion != exe_chex)
+ // The Ultimate Doom executable changed the demo sequence to add
+ // a DEMO4 demo. Final Doom was based on Ultimate, so also
+ // includes this change; however, the Final Doom IWADs do not
+ // include a DEMO4 lump, so the game bombs out with an error
+ // when it reaches this point in the demo sequence.
+
+ if (gameversion == exe_ultimate || gameversion == exe_final)
demosequence = (demosequence+1)%7;
else
demosequence = (demosequence+1)%6;
@@ -608,8 +607,12 @@ void D_StartTitle (void)
// These are from the original source: some of them are perhaps
// not used in any dehacked patches
-static char *banners[] =
+static char *banners[] =
{
+ // doom2.wad
+ " "
+ "DOOM 2: Hell on Earth v%i.%i"
+ " ",
// doom1.wad
" "
"DOOM Shareware Startup v%i.%i"
@@ -626,10 +629,6 @@ static char *banners[] =
" "
"The Ultimate DOOM Startup v%i.%i"
" ",
- // doom2.wad
- " "
- "DOOM 2: Hell on Earth v%i.%i"
- " ",
// tnt.wad
" "
"DOOM 2: TNT - Evilution v%i.%i"
@@ -830,6 +829,18 @@ static boolean CheckChex(char *iwadname)
chex_iwadname));
}
+// Check if the IWAD file is the Hacx IWAD.
+// Returns true if this is hacx.wad.
+
+static boolean CheckHacx(char *iwadname)
+{
+ char *hacx_iwadname = "hacx.wad";
+
+ return (strlen(iwadname) > strlen(hacx_iwadname)
+ && !strcasecmp(iwadname + strlen(iwadname) - strlen(hacx_iwadname),
+ hacx_iwadname));
+}
+
// print title for every printed line
char title[128];
@@ -900,6 +911,7 @@ static struct
GameVersion_t version;
} gameversions[] = {
{"Doom 1.9", "1.9", exe_doom_1_9},
+ {"Hacx", "hacx", exe_hacx},
{"Ultimate Doom", "ultimate", exe_ultimate},
{"Final Doom", "final", exe_final},
{"Chex Quest", "chex", exe_chex},
@@ -921,9 +933,9 @@ static void InitGameVersion(void)
// "ultimate" and "final".
//
- p = M_CheckParm("-gameversion");
+ p = M_CheckParmWithArgs("-gameversion", 1);
- if (p > 0)
+ if (p)
{
for (i=0; gameversions[i].description != NULL; ++i)
{
@@ -957,6 +969,12 @@ static void InitGameVersion(void)
gameversion = exe_chex;
}
+ else if (CheckHacx(iwadfile))
+ {
+ // hacx exe: identified by iwad filename
+
+ gameversion = exe_hacx;
+ }
else if (gamemode == shareware || gamemode == registered)
{
// original
@@ -1057,6 +1075,20 @@ static void D_Endoom(void)
I_Endoom(endoom);
}
+static void LoadHacxDeh(void)
+{
+ // If this is the HACX IWAD, we need to load the DEHACKED lump.
+
+ if (gameversion == exe_hacx)
+ {
+ if (!DEH_LoadLumpByName("DEHACKED"))
+ {
+ I_Error("DEHACKED lump not found. Please check that this is the "
+ "Hacx v1.2 IWAD.");
+ }
+ }
+}
+
//
// D_DoomMain
//
@@ -1094,6 +1126,21 @@ void D_DoomMain (void)
}
//!
+ // @category net
+ //
+ // Query the Internet master server for a global list of active
+ // servers.
+ //
+
+ if (M_CheckParm("-search"))
+ {
+ printf("\nSearching for servers on Internet ...\n");
+ p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
+ printf("\n%i server(s) found.\n", p);
+ exit(0);
+ }
+
+ //!
// @arg <address>
// @category net
//
@@ -1101,11 +1148,12 @@ void D_DoomMain (void)
// address.
//
- p = M_CheckParm("-query");
+ p = M_CheckParmWithArgs("-query", 1);
- if (p > 0)
+ if (p)
{
NET_QueryAddress(myargv[p+1]);
+ exit(0);
}
//!
@@ -1114,8 +1162,13 @@ void D_DoomMain (void)
// Search the local LAN for running servers.
//
- if (M_CheckParm("-search"))
- NET_LANQuery();
+ if (M_CheckParm("-localsearch"))
+ {
+ printf("\nSearching for servers on local LAN ...\n");
+ p = NET_LANQuery(NET_QueryPrintCallback, NULL);
+ printf("\n%i server(s) found.\n", p);
+ exit(0);
+ }
#endif
@@ -1265,40 +1318,32 @@ void D_DoomMain (void)
D_AddFile(iwadfile);
modifiedgame = W_ParseCommandLine();
- // add any files specified on the command line with -file wadfile
- // to the wad list
+ //!
+ // @arg <files>
+ // @vanilla
//
- // convenience hack to allow -wart e m to add a wad file
- // prepend a tilde to the filename so wadfile will be reloadable
- p = M_CheckParm ("-wart");
+ // Load the specified PWAD files.
+ //
+
+ p = M_CheckParmWithArgs("-file", 1);
if (p)
{
- myargv[p][4] = 'p'; // big hack, change to -warp
+ // the parms after p are wadfile/lump names,
+ // until end of parms or another - preceded parm
+ modifiedgame = true; // homebrew levels
+ while (++p != myargc && myargv[p][0] != '-')
+ {
+ char *filename;
- // Map name handling.
- switch (gamemode )
- {
- case shareware:
- case retail:
- case registered:
- sprintf (file,"~"DEVMAPS"E%cM%c.wad",
- myargv[p+1][0], myargv[p+2][0]);
- printf("Warping to Episode %s, Map %s.\n",
- myargv[p+1],myargv[p+2]);
- break;
-
- case commercial:
- default:
- p = atoi (myargv[p+1]);
- if (p<10)
- sprintf (file,"~"DEVMAPS"cdata/map0%i.wad", p);
- else
- sprintf (file,"~"DEVMAPS"cdata/map%i.wad", p);
- break;
- }
- D_AddFile (file);
+ filename = D_TryFindWADByName(myargv[p]);
+
+ D_AddFile(filename);
+ }
}
+ // Debug:
+// W_PrintDirectory();
+
//!
// @arg <demo>
// @category demo
@@ -1307,7 +1352,7 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp.
//
- p = M_CheckParm ("-playdemo");
+ p = M_CheckParmWithArgs ("-playdemo", 1);
if (!p)
{
@@ -1319,11 +1364,11 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp, determining the framerate
// of the screen.
//
- p = M_CheckParm ("-timedemo");
+ p = M_CheckParmWithArgs("-timedemo", 1);
}
- if (p && p < myargc-1)
+ if (p)
{
if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
{
@@ -1362,6 +1407,7 @@ void D_DoomMain (void)
D_IdentifyVersion();
InitGameVersion();
LoadChexDeh();
+ LoadHacxDeh();
D_SetGameDescription();
SetSaveGameDir(iwadfile);
@@ -1404,9 +1450,9 @@ void D_DoomMain (void)
// 0 disables all monsters.
//
- p = M_CheckParm ("-skill");
+ p = M_CheckParmWithArgs("-skill", 1);
- if (p && p < myargc-1)
+ if (p)
{
startskill = myargv[p+1][0]-'1';
autostart = true;
@@ -1419,9 +1465,9 @@ void D_DoomMain (void)
// Start playing on episode n (1-4)
//
- p = M_CheckParm ("-episode");
+ p = M_CheckParmWithArgs("-episode", 1);
- if (p && p < myargc-1)
+ if (p)
{
startepisode = myargv[p+1][0]-'0';
startmap = 1;
@@ -1438,12 +1484,11 @@ void D_DoomMain (void)
// For multiplayer games: exit each level after n minutes.
//
- p = M_CheckParm ("-timer");
+ p = M_CheckParmWithArgs("-timer", 1);
- if (p && p < myargc-1 && deathmatch)
+ if (p)
{
timelimit = atoi(myargv[p+1]);
- printf("timer: %i\n", timelimit);
}
//!
@@ -1455,10 +1500,8 @@ void D_DoomMain (void)
p = M_CheckParm ("-avg");
- if (p && p < myargc-1 && deathmatch)
+ if (p)
{
- DEH_printf("Austin Virtual Gaming: Levels will end "
- "after 20 minutes\n");
timelimit = 20;
}
@@ -1470,9 +1513,9 @@ void D_DoomMain (void)
// (Doom 2)
//
- p = M_CheckParm ("-warp");
+ p = M_CheckParmWithArgs("-warp", 1);
- if (p && p < myargc-1)
+ if (p)
{
if (gamemode == commercial)
startmap = atoi (myargv[p+1]);
@@ -1516,9 +1559,9 @@ void D_DoomMain (void)
// Load the game in slot s.
//
- p = M_CheckParm ("-loadgame");
+ p = M_CheckParmWithArgs("-loadgame", 1);
- if (p && p < myargc-1)
+ if (p)
{
startloadgame = atoi(myargv[p+1]);
}
@@ -1588,24 +1631,24 @@ void D_DoomMain (void)
// Record a demo named x.lmp.
//
- p = M_CheckParm ("-record");
+ p = M_CheckParmWithArgs("-record", 1);
- if (p && p < myargc-1)
+ if (p)
{
G_RecordDemo (myargv[p+1]);
autostart = true;
}
- p = M_CheckParm ("-playdemo");
- if (p && p < myargc-1)
+ p = M_CheckParmWithArgs("-playdemo", 1);
+ if (p)
{
singledemo = true; // quit after one demo
G_DeferedPlayDemo (demolumpname);
D_DoomLoop (); // never returns
}
- p = M_CheckParm ("-timedemo");
- if (p && p < myargc-1)
+ p = M_CheckParmWithArgs("-timedemo", 1);
+ if (p)
{
G_TimeDemo (demolumpname);
D_DoomLoop (); // never returns
diff --git a/src/doom/d_net.c b/src/doom/d_net.c
index 464922d4..dd1ec563 100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -25,7 +25,7 @@
//
//-----------------------------------------------------------------------------
-
+#include <stdlib.h>
#include "doomfeatures.h"
@@ -49,26 +49,34 @@
#include "net_sdl.h"
#include "net_loop.h"
+// The complete set of data for a particular tic.
+
+typedef struct
+{
+ ticcmd_t cmds[MAXPLAYERS];
+ boolean ingame[MAXPLAYERS];
+} ticcmd_set_t;
//
// 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
+// maketic is the tic that hasn't had control made for it yet
+// recvtic is the latest tic received from the server.
//
-// a gametic cannot be run until nettics[] > gametic for all players
+// a gametic cannot be run until ticcmds are received for it
+// from all players.
//
-ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
-int nettics[MAXPLAYERS];
+ticcmd_set_t ticdata[BACKUPTICS];
+ticcmd_t *netcmds;
int maketic;
+int recvtic;
// Used for original sync code.
-int lastnettic;
-int skiptics = 0;
+static int skiptics = 0;
// Reduce the bandwidth needed by sampling game input less and transmitting
// less. If ticdup is 2, sample half normal, 3 = one third normal, etc.
@@ -85,11 +93,7 @@ fixed_t offsetms;
// Use new client syncronisation code
-boolean net_cl_new_sync = true;
-
-// Connected but not participating in the game (observer)
-
-boolean drone = false;
+boolean new_sync = true;
// 35 fps clock adjusted by offsetms milliseconds
@@ -99,7 +103,7 @@ static int GetAdjustedTime(void)
time_ms = I_GetTimeMS();
- if (net_cl_new_sync)
+ if (new_sync)
{
// Use the adjustments from net_client.c only if we are
// using the new sync mode.
@@ -177,7 +181,7 @@ void NetUpdate (void)
continue;
}
- if (net_cl_new_sync)
+ if (new_sync)
{
// If playing single player, do not allow tics to buffer
// up very far
@@ -200,20 +204,95 @@ void NetUpdate (void)
G_BuildTiccmd(&cmd);
#ifdef FEATURE_MULTIPLAYER
-
- if (netgame && !demoplayback)
+
+ if (net_client_connected)
{
NET_CL_SendTiccmd(&cmd, maketic);
}
#endif
- netcmds[consoleplayer][maketic % BACKUPTICS] = cmd;
+ ticdata[maketic % BACKUPTICS].cmds[consoleplayer] = cmd;
+ ticdata[maketic % BACKUPTICS].ingame[consoleplayer] = true;
++maketic;
- nettics[consoleplayer] = maketic;
}
}
+// Called when a player leaves the game
+
+static void D_PlayerQuitGame(player_t *player)
+{
+ 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
+
+ strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
+ exitmsg[sizeof(exitmsg) - 1] = '\0';
+
+ exitmsg[7] += player_num;
+
+ playeringame[player_num] = false;
+ players[consoleplayer].message = exitmsg;
+
+ // TODO: check if it is sensible to do this:
+
+ if (demorecording)
+ {
+ G_CheckDemoStatus ();
+ }
+}
+
+static void D_Disconnected(void)
+{
+ // In drone mode, the game cannot continue once disconnected.
+
+ if (drone)
+ {
+ I_Error("Disconnected from server in drone mode.");
+ }
+
+ // disconnected from server
+
+ printf("Disconnected from server.\n");
+}
+
+//
+// Invoked by the network engine when a complete set of ticcmds is
+// available.
+//
+
+void D_ReceiveTic(ticcmd_t *ticcmds, boolean *players_mask)
+{
+ int i;
+
+ // Disconnected from server?
+
+ if (ticcmds == NULL && players_mask == NULL)
+ {
+ D_Disconnected();
+ return;
+ }
+
+ for (i = 0; i < MAXPLAYERS; ++i)
+ {
+ if (!drone && i == consoleplayer)
+ {
+ // This is us. Don't overwrite it.
+ }
+ else
+ {
+ ticdata[recvtic % BACKUPTICS].cmds[i] = ticcmds[i];
+ ticdata[recvtic % BACKUPTICS].ingame[i] = players_mask[i];
+ }
+ }
+
+ ++recvtic;
+}
+
//
// Start game loop
//
@@ -225,169 +304,340 @@ void D_StartGameLoop(void)
lasttime = GetAdjustedTime() / ticdup;
}
+// Load game settings from the specified structure and
+// set global variables.
-//
-// D_CheckNetGame
-// Works out player numbers among the net participants
-//
-extern int viewangleoffset;
+static void LoadGameSettings(net_gamesettings_t *settings)
+{
+ unsigned int i;
+
+ deathmatch = settings->deathmatch;
+ ticdup = settings->ticdup;
+ extratics = settings->extratics;
+ startepisode = settings->episode;
+ startmap = settings->map;
+ startskill = settings->skill;
+ startloadgame = settings->loadgame;
+ lowres_turn = settings->lowres_turn;
+ nomonsters = settings->nomonsters;
+ fastparm = settings->fast_monsters;
+ respawnparm = settings->respawn_monsters;
+ timelimit = settings->timelimit;
+
+ if (lowres_turn)
+ {
+ printf("NOTE: Turning resolution is reduced; this is probably "
+ "because there is a client recording a Vanilla demo.\n");
+ }
-void D_CheckNetGame (void)
+ new_sync = settings->new_sync;
+
+ if (new_sync == false)
+ {
+ printf("Syncing netgames like Vanilla Doom.\n");
+ }
+
+ if (!drone)
+ {
+ consoleplayer = settings->consoleplayer;
+ }
+ else
+ {
+ consoleplayer = 0;
+ }
+
+ for (i=0; i<MAXPLAYERS; ++i)
+ {
+ playeringame[i] = i < settings->num_players;
+ }
+}
+
+// Save the game settings from global variables to the specified
+// game settings structure.
+
+static void SaveGameSettings(net_gamesettings_t *settings,
+ net_connect_data_t *connect_data)
{
int i;
- int num_players;
- // Call D_QuitNetGame on exit
+ // Fill in game settings structure with appropriate parameters
+ // for the new game
+
+ settings->deathmatch = deathmatch;
+ settings->episode = startepisode;
+ settings->map = startmap;
+ settings->skill = startskill;
+ settings->loadgame = startloadgame;
+ settings->gameversion = gameversion;
+ settings->nomonsters = nomonsters;
+ settings->fast_monsters = fastparm;
+ settings->respawn_monsters = respawnparm;
+ settings->timelimit = timelimit;
+
+ settings->lowres_turn = M_CheckParm("-record") > 0
+ && M_CheckParm("-longtics") == 0;
+
+ //!
+ // @category net
+ //
+ // Use original game sync code.
+ //
+
+ if (M_CheckParm("-oldsync") > 0)
+ settings->new_sync = 0;
+ else
+ settings->new_sync = 1;
+
+ //!
+ // @category net
+ // @arg <n>
+ //
+ // Send n extra tics in every packet as insurance against dropped
+ // packets.
+ //
+
+ i = M_CheckParmWithArgs("-extratics", 1);
+
+ if (i > 0)
+ settings->extratics = atoi(myargv[i+1]);
+ else
+ settings->extratics = 1;
- I_AtExit(D_QuitNetGame, true);
+ //!
+ // @category net
+ // @arg <n>
+ //
+ // Reduce the resolution of the game by a factor of n, reducing
+ // the amount of network bandwidth needed.
+ //
+
+ i = M_CheckParmWithArgs("-dup", 1);
+
+ if (i > 0)
+ settings->ticdup = atoi(myargv[i+1]);
+ else
+ settings->ticdup = 1;
+
+ //
+ // Connect data
+ //
+
+ // Game type fields:
+
+ connect_data->gamemode = gamemode;
+ connect_data->gamemission = gamemission;
+ // Drone mode?
+
+ connect_data->drone = M_CheckParm("-drone") > 0;
+
+ // Are we recording a demo? Possibly set lowres turn mode
+
+ connect_data->lowres_turn = settings->lowres_turn;
+}
+
+void D_InitSinglePlayerGame(net_gamesettings_t *settings)
+{
// default values for single player
- consoleplayer = 0;
+ settings->consoleplayer = 0;
+ settings->num_players = 1;
+
netgame = false;
- ticdup = 1;
- extratics = 1;
- lowres_turn = false;
- offsetms = 0;
-
- for (i=0; i<MAXPLAYERS; 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)
{
- playeringame[i] = false;
- nettics[i] = 0;
+ netgame = true;
}
+}
+
+boolean D_InitNetGame(net_connect_data_t *connect_data,
+ net_gamesettings_t *settings)
+{
+ net_addr_t *addr = NULL;
+ int i;
- playeringame[0] = true;
#ifdef FEATURE_MULTIPLAYER
+ //!
+ // @category net
+ //
+ // Start a multiplayer server, listening for connections.
+ //
+
+ if (M_CheckParm("-server") > 0)
{
- net_addr_t *addr = NULL;
+ NET_SV_Init();
+ NET_SV_AddModule(&net_loop_server_module);
+ NET_SV_AddModule(&net_sdl_module);
+ NET_SV_RegisterWithMaster();
- //!
+ net_loop_client_module.InitClient();
+ addr = net_loop_client_module.ResolveAddress(NULL);
+ }
+ else
+ {
+ //!
// @category net
//
- // Start a multiplayer server, listening for connections.
+ // Automatically search the local LAN for a multiplayer
+ // server and join it.
//
- if (M_CheckParm("-server") > 0)
+ i = M_CheckParm("-autojoin");
+
+ if (i > 0)
{
- NET_SV_Init();
- NET_SV_AddModule(&net_loop_server_module);
- NET_SV_AddModule(&net_sdl_module);
+ addr = NET_FindLANServer();
- net_loop_client_module.InitClient();
- addr = net_loop_client_module.ResolveAddress(NULL);
+ if (addr == NULL)
+ {
+ I_Error("No server found on local LAN");
+ }
}
- else
- {
- //!
- // @category net
- //
- // Automatically search the local LAN for a multiplayer
- // server and join it.
- //
- i = M_CheckParm("-autojoin");
+ //!
+ // @arg <address>
+ // @category net
+ //
+ // Connect to a multiplayer server running on the given
+ // address.
+ //
+
+ i = M_CheckParmWithArgs("-connect", 1);
- if (i > 0)
- {
- addr = NET_FindLANServer();
+ if (i > 0)
+ {
+ net_sdl_module.InitClient();
+ addr = net_sdl_module.ResolveAddress(myargv[i+1]);
- if (addr == NULL)
- {
- I_Error("No server found on local LAN");
- }
+ if (addr == NULL)
+ {
+ I_Error("Unable to resolve '%s'\n", myargv[i+1]);
}
+ }
+ }
- //!
- // @arg <address>
- // @category net
- //
- // Connect to a multiplayer server running on the given
- // address.
- //
-
- i = M_CheckParm("-connect");
-
- if (i > 0)
- {
- net_sdl_module.InitClient();
- addr = net_sdl_module.ResolveAddress(myargv[i+1]);
+ if (addr != NULL)
+ {
+ if (M_CheckParm("-drone") > 0)
+ {
+ connect_data->drone = true;
+ }
- if (addr == NULL)
- {
- I_Error("Unable to resolve '%s'\n", myargv[i+1]);
- }
- }
+ //!
+ // @category net
+ //
+ // Run as the left screen in three screen mode.
+ //
+
+ if (M_CheckParm("-left") > 0)
+ {
+ viewangleoffset = ANG90;
+ connect_data->drone = true;
}
- if (addr != NULL)
+ //!
+ // @category net
+ //
+ // Run as the right screen in three screen mode.
+ //
+
+ if (M_CheckParm("-right") > 0)
{
- if (M_CheckParm("-drone") > 0)
- {
- drone = true;
- }
+ viewangleoffset = ANG270;
+ connect_data->drone = true;
+ }
- //!
- // @category net
- //
- // Run as the left screen in three screen mode.
- //
+ if (!NET_CL_Connect(addr, connect_data))
+ {
+ I_Error("D_CheckNetGame: Failed to connect to %s\n",
+ NET_AddrToString(addr));
+ }
- if (M_CheckParm("-left") > 0)
- {
- viewangleoffset = ANG90;
- drone = true;
- }
+ printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
- //!
- // @category net
- //
- // Run as the right screen in three screen mode.
- //
+ // Wait for game start message received from server.
- if (M_CheckParm("-right") > 0)
- {
- viewangleoffset = ANG270;
- drone = true;
- }
+ NET_WaitForStart(settings);
- if (!NET_CL_Connect(addr))
- {
- I_Error("D_CheckNetGame: Failed to connect to %s\n",
- NET_AddrToString(addr));
- }
+ // Read the game settings that were received.
- printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
+ NET_CL_GetSettings(settings);
- NET_WaitForStart();
- }
+ return true;
}
#endif
- num_players = 0;
+ return false;
+}
+
+//
+// D_CheckNetGame
+// Works out player numbers among the net participants
+//
+extern int viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+ net_connect_data_t connect_data;
+ net_gamesettings_t settings;
- for (i=0; i<MAXPLAYERS; ++i)
+ offsetms = 0;
+ recvtic = 0;
+
+ // Call D_QuitNetGame on exit
+
+ I_AtExit(D_QuitNetGame, true);
+
+ SaveGameSettings(&settings, &connect_data);
+
+ if (D_InitNetGame(&connect_data, &settings))
{
- if (playeringame[i])
- ++num_players;
+ netgame = true;
+ autostart = true;
}
+ else
+ {
+ D_InitSinglePlayerGame(&settings);
+ }
+
+ LoadGameSettings(&settings);
DEH_printf("startskill %i deathmatch: %i startmap: %i startepisode: %i\n",
startskill, deathmatch, startmap, startepisode);
DEH_printf("player %i of %i (%i nodes)\n",
- consoleplayer+1, num_players, num_players);
+ consoleplayer+1, settings.num_players, settings.num_players);
// Show players here; the server might have specified a time limit
- if (timelimit > 0)
+ if (timelimit > 0 && deathmatch)
{
- DEH_printf("Levels will end after %d minute", timelimit);
- if (timelimit > 1)
- printf("s");
- printf(".\n");
+ // Gross hack to work like Vanilla:
+
+ if (timelimit == 20 && M_CheckParm("-avg"))
+ {
+ DEH_printf("Austin Virtual Gaming: Levels will end "
+ "after 20 minutes\n");
+ }
+ else
+ {
+ DEH_printf("Levels will end after %d minute", timelimit);
+ if (timelimit > 1)
+ printf("s");
+ printf(".\n");
+ }
}
}
@@ -399,9 +649,6 @@ void D_CheckNetGame (void)
//
void D_QuitNetGame (void)
{
- if (debugfile)
- fclose (debugfile);
-
#ifdef FEATURE_MULTIPLAYER
NET_SV_Shutdown();
@@ -411,62 +658,172 @@ void D_QuitNetGame (void)
}
-// Returns true if there are currently any players in the game.
+static int GetLowTic(void)
+{
+ int lowtic;
-static boolean PlayersInGame(void)
+ lowtic = maketic;
+
+#ifdef FEATURE_MULTIPLAYER
+ if (net_client_connected)
+ {
+ if (drone || recvtic < lowtic)
+ {
+ lowtic = recvtic;
+ }
+ }
+#endif
+
+ return lowtic;
+}
+
+int frametics[4];
+int frameon;
+int frameskip[4];
+int oldnettics;
+
+static void OldNetSync(void)
{
- int i;
+ unsigned int i;
+ unsigned int keyplayer = -1;
- for (i=0; i<MAXPLAYERS; ++i)
+ frameon++;
+
+ // ideally maketic should be 1 - 3 tics above lowtic
+ // if we are consistantly slower, speed up time
+
+ for (i=0 ; i<MAXPLAYERS ; i++)
{
+ // TODO: playeringame should not be used here.
+
if (playeringame[i])
{
- return true;
+ keyplayer = i;
+ break;
}
}
- return false;
+ if (keyplayer < 0)
+ {
+ // If there are no players, we can never advance anyway
+
+ return;
+ }
+
+ if (consoleplayer == keyplayer)
+ {
+ // the key player does not adapt
+ }
+ else
+ {
+ if (maketic <= recvtic)
+ {
+ lasttime--;
+ // printf ("-");
+ }
+
+ frameskip[frameon & 3] = oldnettics > recvtic;
+ oldnettics = maketic;
+
+ if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
+ {
+ skiptics = 1;
+ // printf ("+");
+ }
+ }
}
-static int GetLowTic(void)
+// Returns true if there are players in the game:
+
+static boolean PlayersInGame(void)
{
- int lowtic;
+ boolean result = false;
+ unsigned int i;
+
+ // If we are connected to a server, check if there are any players
+ // in the game.
-#ifdef FEATURE_MULTIPLAYER
if (net_client_connected)
{
- int i;
-
- lowtic = INT_MAX;
-
- for (i=0; i<MAXPLAYERS; ++i)
+ for (i = 0; i < MAXPLAYERS; ++i)
{
- if (playeringame[i])
- {
- if (nettics[i] < lowtic)
- lowtic = nettics[i];
- }
+ result = result || playeringame[i];
}
}
- else
-#endif
+
+ // Whether single or multi-player, unless we are running as a drone,
+ // we are in the game.
+
+ if (!drone)
{
- lowtic = maketic;
+ result = true;
}
- return lowtic;
+ return result;
+}
+
+// When using ticdup, certain values must be cleared out when running
+// the duplicate ticcmds.
+
+static void TicdupSquash(ticcmd_set_t *set)
+{
+ ticcmd_t *cmd;
+ unsigned int i;
+
+ for (i = 0; i < MAXPLAYERS ; ++i)
+ {
+ cmd = &set->cmds[i];
+ cmd->chatchar = 0;
+ if (cmd->buttons & BT_SPECIAL)
+ cmd->buttons = 0;
+ }
+}
+
+static void D_RunTic(ticcmd_set_t *set)
+{
+ extern boolean advancedemo;
+ unsigned int i;
+
+ // Check for player quits.
+
+ for (i = 0; i < MAXPLAYERS; ++i)
+ {
+ if (playeringame[i] && !set->ingame[i])
+ {
+ D_PlayerQuitGame(&players[i]);
+ }
+ }
+
+ netcmds = set->cmds;
+
+ // check that there are players in the game. if not, we cannot
+ // run a tic.
+
+ if (advancedemo)
+ D_DoAdvanceDemo ();
+
+ G_Ticker ();
+}
+
+// When running in single player mode, clear all the ingame[] array
+// except the consoleplayer.
+
+static void SinglePlayerClear(ticcmd_set_t *set)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXPLAYERS; ++i)
+ {
+ if (i != consoleplayer)
+ {
+ set->ingame[i] = false;
+ }
+ }
}
//
// TryRunTics
//
-int oldnettics;
-int frametics[4];
-int frameon;
-int frameskip[4];
-int oldnettics;
-
-extern boolean advancedemo;
void TryRunTics (void)
{
@@ -492,7 +849,7 @@ void TryRunTics (void)
// decide how many tics to run
- if (net_cl_new_sync)
+ if (new_sync)
{
counts = availabletics;
}
@@ -509,52 +866,9 @@ void TryRunTics (void)
if (counts < 1)
counts = 1;
- frameon++;
-
- if (!demoplayback)
+ if (net_client_connected)
{
- int keyplayer = -1;
-
- // ideally maketic should be 1 - 3 tics above lowtic
- // if we are consistantly slower, speed up time
-
- for (i=0 ; i<MAXPLAYERS ; i++)
- {
- if (playeringame[i])
- {
- keyplayer = i;
- break;
- }
- }
-
- if (keyplayer < 0)
- {
- // If there are no players, we can never advance anyway
-
- return;
- }
-
- if (consoleplayer == keyplayer)
- {
- // the key player does not adapt
- }
- else
- {
- if (maketic <= nettics[keyplayer])
- {
- lasttime--;
- // printf ("-");
- }
-
- frameskip[frameon & 3] = (oldnettics > nettics[keyplayer]);
- oldnettics = maketic;
-
- if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
- {
- skiptics = 1;
- // printf ("+");
- }
- }
+ OldNetSync();
}
}
@@ -586,41 +900,33 @@ void TryRunTics (void)
// run the count * ticdup dics
while (counts--)
{
+ ticcmd_set_t *set;
+
+ if (!PlayersInGame())
+ {
+ return;
+ }
+
+ set = &ticdata[(gametic / ticdup) % BACKUPTICS];
+
+ if (!net_client_connected)
+ {
+ SinglePlayerClear(set);
+ }
+
for (i=0 ; i<ticdup ; i++)
{
- // check that there are players in the game. if not, we cannot
- // run a tic.
-
- if (!PlayersInGame())
- {
- return;
- }
-
- if (gametic/ticdup > lowtic)
- I_Error ("gametic>lowtic");
- if (advancedemo)
- D_DoAdvanceDemo ();
+ if (gametic/ticdup > lowtic)
+ I_Error ("gametic>lowtic");
- G_Ticker ();
+ D_RunTic(set);
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;
- }
- }
+
+ TicdupSquash(set);
}
+
NetUpdate (); // check for new console commands
}
}
diff --git a/src/doom/d_net.h b/src/doom/d_net.h
index f801d216..a3c1a43c 100644
--- a/src/doom/d_net.h
+++ b/src/doom/d_net.h
@@ -45,7 +45,6 @@ void TryRunTics (void);
// Called at start of game loop to initialize timers
void D_StartGameLoop(void);
-extern boolean drone;
extern boolean net_cl_new_sync;
#endif
diff --git a/src/doom/doomstat.h b/src/doom/doomstat.h
index 40147833..237234d8 100644
--- a/src/doom/doomstat.h
+++ b/src/doom/doomstat.h
@@ -255,7 +255,6 @@ extern int maxammo[NUMAMMO];
// File handling stuff.
extern char * savegamedir;
extern char basedefault[1024];
-extern FILE* debugfile;
// if true, load all graphics at level load
extern boolean precache;
@@ -289,7 +288,7 @@ extern int rndindex;
extern int maketic;
extern int nettics[MAXPLAYERS];
-extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
+extern ticcmd_t *netcmds;
extern int ticdup;
diff --git a/src/doom/dstrings.h b/src/doom/dstrings.h
index bdc6b2ce..d47fc1af 100644
--- a/src/doom/dstrings.h
+++ b/src/doom/dstrings.h
@@ -38,15 +38,6 @@
#define SAVEGAMENAME "doomsav"
-//
-// File locations,
-// relative to current position.
-// Path names are OS-sensitive.
-//
-#define DEVMAPS "devmaps"
-#define DEVDATA "devdata"
-
-
// QuitDOOM messages
// 8 per each game type
#define NUM_QUITMESSAGES 8
diff --git a/src/doom/g_game.c b/src/doom/g_game.c
index ad7155a0..5662d3f2 100644
--- a/src/doom/g_game.c
+++ b/src/doom/g_game.c
@@ -138,7 +138,7 @@ int gametic;
int levelstarttic; // gametic at level start
int totalkills, totalitems, totalsecret; // for intermission
-char demoname[32];
+char *demoname;
boolean demorecording;
boolean longtics; // cph's doom 1.91 longtics hack
boolean lowres_turn; // low resolution turning for longtics
@@ -986,9 +986,9 @@ void G_Ticker (void)
if (playeringame[i])
{
cmd = &players[i].cmd;
-
- memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t));
-
+
+ memcpy(cmd, &netcmds[i], sizeof(ticcmd_t));
+
if (demoplayback)
G_ReadDemoTiccmd (cmd);
if (demorecording)
@@ -1978,14 +1978,14 @@ void G_WriteDemoTiccmd (ticcmd_t* cmd)
//
// G_RecordDemo
//
-void G_RecordDemo (char* name)
+void G_RecordDemo (char *name)
{
int i;
int maxsize;
usergame = false;
- strcpy (demoname, name);
- strcat (demoname, ".lmp");
+ demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL);
+ sprintf(demoname, "%s.lmp", name);
maxsize = 0x20000;
//!
@@ -1996,8 +1996,8 @@ void G_RecordDemo (char* name)
// Specify the demo buffer size (KiB)
//
- i = M_CheckParm ("-maxdemo");
- if (i && i<myargc-1)
+ i = M_CheckParmWithArgs("-maxdemo", 1);
+ if (i)
maxsize = atoi(myargv[i+1])*1024;
demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
demoend = demobuffer + maxsize;
@@ -2145,16 +2145,11 @@ void G_DoPlayDemo (void)
for (i=0 ; i<MAXPLAYERS ; i++)
playeringame[i] = *demo_p++;
- //!
- // @category demo
- //
- // Play back a demo recorded in a netgame with a single player.
- //
-
- if (playeringame[1] || M_CheckParm("-netdemo") > 0)
- {
- netgame = true;
- netdemo = true;
+ if (playeringame[1] || M_CheckParm("-solo-net") > 0
+ || M_CheckParm("-netdemo") > 0)
+ {
+ netgame = true;
+ netdemo = true;
}
// don't spend a lot of time in loadlevel
diff --git a/src/doom/m_menu.c b/src/doom/m_menu.c
index a6f7bbfb..478e7f66 100644
--- a/src/doom/m_menu.c
+++ b/src/doom/m_menu.c
@@ -762,6 +762,8 @@ void M_DrawReadThis1(void)
switch (gameversion)
{
case exe_doom_1_9:
+ case exe_hacx:
+
if (gamemode == commercial)
{
// Doom 2
@@ -1440,23 +1442,23 @@ boolean M_Responder (event_t* ev)
if (ev->type == ev_joystick && joywait < I_GetTime())
{
- if (ev->data3 == -1)
+ if (ev->data3 < 0)
{
key = key_menu_up;
joywait = I_GetTime() + 5;
}
- else if (ev->data3 == 1)
+ else if (ev->data3 > 0)
{
key = key_menu_down;
joywait = I_GetTime() + 5;
}
- if (ev->data2 == -1)
+ if (ev->data2 < 0)
{
key = key_menu_left;
joywait = I_GetTime() + 2;
}
- else if (ev->data2 == 1)
+ else if (ev->data2 > 0)
{
key = key_menu_right;
joywait = I_GetTime() + 2;
diff --git a/src/doom/p_map.c b/src/doom/p_map.c
index 925e4398..78102bdf 100644
--- a/src/doom/p_map.c
+++ b/src/doom/p_map.c
@@ -1426,7 +1426,7 @@ static void SpechitOverrun(line_t *ld)
// Use the specified magic value when emulating spechit overruns.
//
- p = M_CheckParm("-spechit");
+ p = M_CheckParmWithArgs("-spechit", 1);
if (p > 0)
{
diff --git a/src/doom/p_setup.c b/src/doom/p_setup.c
index 7d9d4318..3fc95cab 100644
--- a/src/doom/p_setup.c
+++ b/src/doom/p_setup.c
@@ -755,17 +755,7 @@ P_SetupLevel
// Make sure all sounds are stopped before Z_FreeTags.
S_Start ();
-
-#if 0 // UNUSED
- if (debugfile)
- {
- Z_FreeTags (PU_LEVEL, INT_MAX);
- Z_FileDumpHeap (debugfile);
- }
- else
-#endif
- Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
-
+ Z_FreeTags (PU_LEVEL, PU_PURGELEVEL-1);
// UNUSED W_Profile ();
P_InitThinkers ();
diff --git a/src/doom/p_spec.c b/src/doom/p_spec.c
index 37beb850..90d0bb7c 100644
--- a/src/doom/p_spec.c
+++ b/src/doom/p_spec.c
@@ -1213,9 +1213,9 @@ static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,
// system. The default (if this option is not specified) is to
// emulate the behavior when running under Windows 98.
- p = M_CheckParm("-donut");
+ p = M_CheckParmWithArgs("-donut", 2);
- if (p > 0 && p < myargc - 2)
+ if (p > 0)
{
// Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008
//
@@ -1389,10 +1389,9 @@ void P_SpawnSpecials (void)
if (W_CheckNumForName(DEH_String("texture2")) >= 0)
episode = 2;
-
// See if -TIMER was specified.
- if (timelimit > 0)
+ if (timelimit > 0 && deathmatch)
{
levelTimer = true;
levelTimeCount = timelimit * 60 * TICRATE;
@@ -1401,7 +1400,7 @@ void P_SpawnSpecials (void)
{
levelTimer = false;
}
-
+
// Init special SECTORs.
sector = sectors;
for (i=0 ; i<numsectors ; i++, sector++)
diff --git a/src/doom/r_data.c b/src/doom/r_data.c
index b999e916..505f1ff7 100644
--- a/src/doom/r_data.c
+++ b/src/doom/r_data.c
@@ -413,6 +413,7 @@ R_GetColumn
static void GenerateTextureHashTable(void)
{
+ texture_t **rover;
int i;
int key;
@@ -429,12 +430,25 @@ static void GenerateTextureHashTable(void)
textures[i]->index = i;
- // Hook into hash table
+ // Vanilla Doom does a linear search of the texures array
+ // and stops at the first entry it finds. If there are two
+ // entries with the same name, the first one in the array
+ // wins. The new entry must therefore be added at the end
+ // of the hash chain, so that earlier entries win.
key = W_LumpNameHash(textures[i]->name) % numtextures;
- textures[i]->next = textures_hashtable[key];
- textures_hashtable[key] = textures[i];
+ rover = &textures_hashtable[key];
+
+ while (*rover != NULL)
+ {
+ rover = &(*rover)->next;
+ }
+
+ // Hook into hash table
+
+ textures[i]->next = NULL;
+ *rover = textures[i];
}
}
diff --git a/src/doomfeatures.h b/src/doomfeatures.h
index c5e6067a..1c994b70 100644
--- a/src/doomfeatures.h
+++ b/src/doomfeatures.h
@@ -36,8 +36,9 @@
#define FEATURE_DEHACKED 1
// Enables multiplayer support (network games)
+// STRIFE-TODO: multiplayer is currently broken ...
-//#define FEATURE_MULTIPLAYER 1
+// #define FEATURE_MULTIPLAYER 1
// Enables sound output
diff --git a/src/i_sdlsound.c b/src/i_sdlsound.c
index 7deb683d..1cfafa6f 100644
--- a/src/i_sdlsound.c
+++ b/src/i_sdlsound.c
@@ -53,6 +53,8 @@
#define MAX_SOUND_SLICE_TIME 70 /* ms */
#define NUM_CHANNELS 16
+static boolean setpanning_workaround = false;
+
static boolean sound_initialized = false;
static sfxinfo_t *channels_playing[NUM_CHANNELS];
@@ -632,10 +634,19 @@ static void I_SDL_UpdateSoundParams(int handle, int vol, int sep)
if (right < 0) right = 0;
else if (right > 255) right = 255;
+ // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
+ // function. A workaround is to call Mix_UnregisterAllEffects for
+ // the channel before calling it. This is undesirable as it may lead
+ // to the channel volumes resetting briefly.
+
+ if (setpanning_workaround)
+ {
+ Mix_UnregisterAllEffects(handle);
+ }
+
Mix_SetPanning(handle, left, right);
}
-
//
// Starting a sound means adding it
// to the current list of active sounds
@@ -822,8 +833,34 @@ static boolean I_SDL_InitSound(boolean _use_sfx_prefix)
}
#endif
+ // SDL_mixer version 1.2.8 and earlier has a bug in the Mix_SetPanning
+ // function that can cause the game to lock up. If we're using an old
+ // version, we need to apply a workaround. But the workaround has its
+ // own drawbacks ...
+
+ {
+ const SDL_version *mixer_version;
+ int v;
+
+ mixer_version = Mix_Linked_Version();
+ v = SDL_VERSIONNUM(mixer_version->major,
+ mixer_version->minor,
+ mixer_version->patch);
+
+ if (v <= SDL_VERSIONNUM(1, 2, 8))
+ {
+ setpanning_workaround = true;
+ fprintf(stderr, "\n"
+ "ATTENTION: You are using an old version of SDL_mixer!\n"
+ " This version has a bug that may cause "
+ "your sound to stutter.\n"
+ " Please upgrade to a newer version!\n"
+ "\n");
+ }
+ }
+
Mix_AllocateChannels(NUM_CHANNELS);
-
+
SDL_PauseAudio(0);
sound_initialized = true;
diff --git a/src/i_system.c b/src/i_system.c
index cd8ddcee..0a856ca9 100644
--- a/src/i_system.c
+++ b/src/i_system.c
@@ -189,7 +189,7 @@ byte *I_ZoneBase (int *size)
// Specify the heap size, in MiB (default 16).
//
- p = M_CheckParm("-mb");
+ p = M_CheckParmWithArgs("-mb", 1);
if (p > 0)
{
@@ -327,9 +327,9 @@ void I_Error (char *error, ...)
// Message first.
va_start(argptr, error);
- fprintf(stderr, "\nError: ");
+ //fprintf(stderr, "\nError: ");
vfprintf(stderr, error, argptr);
- fprintf(stderr, "\n");
+ fprintf(stderr, "\n\n");
va_end(argptr);
fflush(stderr);
@@ -362,7 +362,7 @@ void I_Error (char *error, ...)
msgbuf, strlen(msgbuf) + 1,
wmsgbuf, sizeof(wmsgbuf));
- MessageBoxW(NULL, wmsgbuf, L"Error", MB_OK);
+ MessageBoxW(NULL, wmsgbuf, L"", MB_OK);
}
#endif
diff --git a/src/i_video.c b/src/i_video.c
index 8c2cfeac..6bf2f179 100644
--- a/src/i_video.c
+++ b/src/i_video.c
@@ -31,6 +31,11 @@
#include <math.h>
#include <string.h>
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
#include "icon.c"
#include "config.h"
@@ -139,6 +144,12 @@ static SDL_Surface *screen;
static char *window_title = "";
+// Intermediate 8-bit buffer that we draw to instead of 'screen'.
+// This is used when we are rendering in 32-bit screen mode.
+// When in a real 8-bit screen mode, screenbuffer == screen.
+
+static SDL_Surface *screenbuffer = NULL;
+
// palette
static SDL_Color palette[256];
@@ -172,6 +183,10 @@ static boolean native_surface;
static int screen_width = SCREENWIDTH;
static int screen_height = SCREENHEIGHT;
+// Color depth.
+
+int screen_bpp = 8;
+
// Automatically adjust video settings if the selected mode is
// not a valid video mode.
@@ -911,17 +926,18 @@ static boolean BlitArea(int x1, int y1, int x2, int y2)
return true;
}
- x_offset = (screen->w - screen_mode->width) / 2;
- y_offset = (screen->h - screen_mode->height) / 2;
+ x_offset = (screenbuffer->w - screen_mode->width) / 2;
+ y_offset = (screenbuffer->h - screen_mode->height) / 2;
- if (SDL_LockSurface(screen) >= 0)
+ if (SDL_LockSurface(screenbuffer) >= 0)
{
I_InitScale(I_VideoBuffer,
- (byte *) screen->pixels + (y_offset * screen->pitch)
- + x_offset,
- screen->pitch);
+ (byte *) screenbuffer->pixels
+ + (y_offset * screenbuffer->pitch)
+ + x_offset,
+ screenbuffer->pitch);
result = screen_mode->DrawScreen(x1, y1, x2, y2);
- SDL_UnlockSurface(screen);
+ SDL_UnlockSurface(screenbuffer);
}
else
{
@@ -1055,19 +1071,37 @@ void I_FinishUpdate (void)
// draw to screen
BlitArea(0, 0, SCREENWIDTH, SCREENHEIGHT);
-
- // If we have a palette to set, the act of setting the palette
- // updates the screen
if (palette_to_set)
{
- SDL_SetColors(screen, palette, 0, 256);
+ SDL_SetColors(screenbuffer, palette, 0, 256);
palette_to_set = false;
+
+ // In native 8-bit mode, if we have a palette to set, the act
+ // of setting the palette updates the screen
+
+ if (screenbuffer == screen)
+ {
+ return;
+ }
}
- else
+
+ // In 8in32 mode, we must blit from the fake 8-bit screen buffer
+ // to the real screen before doing a screen flip.
+
+ if (screenbuffer != screen)
{
- SDL_Flip(screen);
+ SDL_Rect dst_rect;
+
+ // Center the buffer within the full screen space.
+
+ dst_rect.x = (screen->w - screenbuffer->w) / 2;
+ dst_rect.y = (screen->h - screenbuffer->h) / 2;
+
+ SDL_BlitSurface(screenbuffer, NULL, screen, &dst_rect);
}
+
+ SDL_Flip(screen);
}
@@ -1348,15 +1382,61 @@ static void AutoAdjustWindowed(void)
}
}
+// Auto-adjust to a valid color depth.
+
+static void AutoAdjustColorDepth(void)
+{
+ SDL_Rect **modes;
+ SDL_PixelFormat format;
+ const SDL_VideoInfo *info;
+ int flags;
+
+ if (fullscreen)
+ {
+ flags = SDL_FULLSCREEN;
+ }
+ else
+ {
+ flags = 0;
+ }
+
+ format.BitsPerPixel = screen_bpp;
+ format.BytesPerPixel = (screen_bpp + 7) / 8;
+
+ // Are any screen modes supported at the configured color depth?
+
+ modes = SDL_ListModes(&format, flags);
+
+ // If not, we must autoadjust to something sensible.
+
+ if (modes == NULL)
+ {
+ printf("I_InitGraphics: %ibpp color depth not supported.\n",
+ screen_bpp);
+
+ info = SDL_GetVideoInfo();
+
+ if (info != NULL && info->vfmt != NULL)
+ {
+ screen_bpp = info->vfmt->BitsPerPixel;
+ }
+ }
+}
+
// If the video mode set in the configuration file is not available,
// try to choose a different mode.
static void I_AutoAdjustSettings(void)
{
- int old_screen_w, old_screen_h;
+ int old_screen_w, old_screen_h, old_screen_bpp;
old_screen_w = screen_width;
old_screen_h = screen_height;
+ old_screen_bpp = screen_bpp;
+
+ // Possibly adjust color depth.
+
+ AutoAdjustColorDepth();
// If we are running fullscreen, try to autoadjust to a valid fullscreen
// mode. If this is impossible, switch to windowed.
@@ -1375,10 +1455,11 @@ static void I_AutoAdjustSettings(void)
// Have the settings changed? Show a message.
- if (screen_width != old_screen_w || screen_height != old_screen_h)
+ if (screen_width != old_screen_w || screen_height != old_screen_h
+ || screen_bpp != old_screen_bpp)
{
- printf("I_InitGraphics: Auto-adjusted to %ix%i.\n",
- screen_width, screen_height);
+ printf("I_InitGraphics: Auto-adjusted to %ix%ix%ibpp.\n",
+ screen_width, screen_height, screen_bpp);
printf("NOTE: Your video settings have been adjusted. "
"To disable this behavior,\n"
@@ -1544,7 +1625,7 @@ static void CheckCommandLine(void)
// Specify the screen width, in pixels.
//
- i = M_CheckParm("-width");
+ i = M_CheckParmWithArgs("-width", 1);
if (i > 0)
{
@@ -1558,7 +1639,7 @@ static void CheckCommandLine(void)
// Specify the screen height, in pixels.
//
- i = M_CheckParm("-height");
+ i = M_CheckParmWithArgs("-height", 1);
if (i > 0)
{
@@ -1567,12 +1648,39 @@ static void CheckCommandLine(void)
//!
// @category video
+ // @arg <bpp>
+ //
+ // Specify the color depth of the screen, in bits per pixel.
+ //
+
+ i = M_CheckParmWithArgs("-bpp", 1);
+
+ if (i > 0)
+ {
+ screen_bpp = atoi(myargv[i + 1]);
+ }
+
+ // Because we love Eternity:
+
+ //!
+ // @category video
+ //
+ // Set the color depth of the screen to 32 bits per pixel.
+ //
+
+ if (M_CheckParm("-8in32"))
+ {
+ screen_bpp = 32;
+ }
+
+ //!
+ // @category video
// @arg <WxY>
//
// Specify the screen mode (when running fullscreen) or the window
// dimensions (when running in windowed mode).
- i = M_CheckParm("-geometry");
+ i = M_CheckParmWithArgs("-geometry", 1);
if (i > 0)
{
@@ -1743,6 +1851,14 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
+ // If we are already running and in a true color mode, we need
+ // to free the screenbuffer surface before setting the new mode.
+
+ if (screenbuffer != NULL && screen != screenbuffer)
+ {
+ SDL_FreeSurface(screenbuffer);
+ }
+
// Generate lookup tables before setting the video mode.
if (mode != NULL && mode->InitMode != NULL)
@@ -1752,7 +1868,12 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
// Set the video mode.
- flags |= SDL_SWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF;
+ flags |= SDL_SWSURFACE | SDL_DOUBLEBUF;
+
+ if (screen_bpp == 8)
+ {
+ flags |= SDL_HWPALETTE;
+ }
if (fullscreen)
{
@@ -1760,18 +1881,30 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
}
else
{
+ // In windowed mode, the window can be resized while the game is
+ // running. This feature is disabled on OS X, as it adds an ugly
+ // scroll handle to the corner of the screen.
+
+#ifndef __MACOSX__
flags |= SDL_RESIZABLE;
+#endif
// villsa - center window
SDL_putenv("SDL_VIDEO_CENTERED=1");
}
- screen = SDL_SetVideoMode(w, h, 8, flags);
+ screen = SDL_SetVideoMode(w, h, screen_bpp, flags);
if (screen == NULL)
{
- I_Error("Error setting video mode: %s\n", SDL_GetError());
+ I_Error("Error setting video mode %ix%ix%ibpp: %s\n",
+ w, h, screen_bpp, SDL_GetError());
}
+ // Blank out the full screen area in case there is any junk in
+ // the borders that won't otherwise be overwritten.
+
+ SDL_FillRect(screen, NULL, 0);
+
// If mode was not set, it must be set now that we know the
// screen size.
@@ -1793,6 +1926,22 @@ static void SetVideoMode(screen_mode_t *mode, int w, int h)
}
}
+ // Create the screenbuffer surface; if we have a real 8-bit palettized
+ // screen, then we can use the screen as the screenbuffer.
+
+ if (screen->format->BitsPerPixel == 8)
+ {
+ screenbuffer = screen;
+ }
+ else
+ {
+ screenbuffer = SDL_CreateRGBSurface(SDL_SWSURFACE,
+ mode->width, mode->height, 8,
+ 0, 0, 0, 0);
+
+ SDL_FillRect(screenbuffer, NULL, 0);
+ }
+
// Save screen mode.
screen_mode = mode;
@@ -1910,24 +2059,13 @@ void I_InitGraphics(void)
// Start with a clear black screen
// (screen will be flipped after we set the palette)
- if (SDL_LockSurface(screen) >= 0)
- {
- byte *screenpixels;
- int y;
-
- screenpixels = (byte *) screen->pixels;
-
- for (y=0; y<screen->h; ++y)
- memset(screenpixels + screen->pitch * y, 0, screen->w);
-
- SDL_UnlockSurface(screen);
- }
+ SDL_FillRect(screenbuffer, NULL, 0);
// Set the palette
doompal = W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE);
I_SetPalette(doompal);
- SDL_SetColors(screen, palette, 0, 256);
+ SDL_SetColors(screenbuffer, palette, 0, 256);
CreateCursors();
@@ -1949,7 +2087,8 @@ void I_InitGraphics(void)
// Likewise if the screen pitch is not the same as the width
// If we have to multiply, drawing is done to a separate 320x200 buf
- native_surface = !SDL_MUSTLOCK(screen)
+ native_surface = screen == screenbuffer
+ && !SDL_MUSTLOCK(screen)
&& screen_mode == &mode_scale_1x
&& screen->pitch == SCREENWIDTH
&& aspect_ratio_correct;
@@ -2013,6 +2152,7 @@ void I_BindVideoVariables(void)
M_BindVariable("startup_delay", &startup_delay);
M_BindVariable("screen_width", &screen_width);
M_BindVariable("screen_height", &screen_height);
+ M_BindVariable("screen_bpp", &screen_bpp);
M_BindVariable("grabmouse", &grabmouse);
M_BindVariable("mouse_acceleration", &mouse_acceleration);
M_BindVariable("mouse_threshold", &mouse_threshold);
@@ -2020,5 +2160,26 @@ void I_BindVideoVariables(void)
M_BindVariable("usegamma", &usegamma);
M_BindVariable("vanilla_keyboard_mapping", &vanilla_keyboard_mapping);
M_BindVariable("novert", &novert);
+
+ // Windows Vista or later? Set screen color depth to
+ // 32 bits per pixel, as 8-bit palettized screen modes
+ // don't work properly in recent versions.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ {
+ OSVERSIONINFOEX version_info;
+
+ ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
+ version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ GetVersionEx((OSVERSIONINFO *) &version_info);
+
+ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && version_info.dwMajorVersion >= 6)
+ {
+ screen_bpp = 32;
+ }
+ }
+#endif
}
diff --git a/src/i_video.h b/src/i_video.h
index 82368967..c3c50c4b 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -116,6 +116,7 @@ void I_EnableLoadingDisk(void);
extern char *video_driver;
extern boolean screenvisible;
+
extern float mouse_acceleration;
extern int mouse_threshold;
extern int vanilla_keyboard_mapping;
diff --git a/src/m_argv.c b/src/m_argv.c
index 79702e56..59381b65 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -47,13 +47,13 @@ char** myargv;
// or 0 if not present
//
-int M_CheckParm (char *check)
+int M_CheckParmWithArgs(char *check, int num_args)
{
- int i;
+ int i;
- for (i = 1;i<myargc;i++)
+ for (i = 1; i < myargc - num_args; i++)
{
- if ( !strcasecmp(check, myargv[i]) )
+ if (!strcasecmp(check, myargv[i]))
return i;
}
@@ -72,6 +72,11 @@ boolean M_ParmExists(char *check)
return M_CheckParm(check) != 0;
}
+int M_CheckParm(char *check)
+{
+ return M_CheckParmWithArgs(check, 0);
+}
+
#define MAXARGVS 100
static void LoadResponseFile(int argv_index)
@@ -88,7 +93,7 @@ static void LoadResponseFile(int argv_index)
response_filename = myargv[argv_index] + 1;
// Read the response file into memory
- handle = fopen(response_filename, "r");
+ handle = fopen(response_filename, "rb");
if (handle == NULL)
{
diff --git a/src/m_argv.h b/src/m_argv.h
index 859f93e4..2fb76a2a 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -40,6 +40,10 @@ extern char** myargv;
// in the arg list (0 if not found).
int M_CheckParm (char* check);
+// Same as M_CheckParm, but checks that num_args arguments are available
+// following the specified argument.
+int M_CheckParmWithArgs(char *check, int num_args);
+
void M_FindResponseFile(void);
// Parameter has been specified?
diff --git a/src/m_config.c b/src/m_config.c
index 8332750b..bda9828c 100644
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -645,6 +645,12 @@ static default_t extra_defaults_list[] =
CONFIG_VARIABLE_INT(screen_height),
//!
+ // Color depth of the screen, in bits.
+ //
+
+ CONFIG_VARIABLE_INT(screen_bpp),
+
+ //!
// If this is non-zero, the mouse will be "grabbed" when running
// in windowed mode so that it can be used as an input device.
// When running full screen, this has no effect.
@@ -1288,8 +1294,18 @@ static void SaveDefaultCollection(default_collection_t *collection)
v = * (int *) defaults[i].location;
- if (defaults[i].untranslated
- && v == defaults[i].original_translated)
+ if (v == KEY_RSHIFT)
+ {
+ // Special case: for shift, force scan code for
+ // right shift, as this is what Vanilla uses.
+ // This overrides the change check below, to fix
+ // configuration files made by old versions that
+ // mistakenly used the scan code for left shift.
+
+ v = 54;
+ }
+ else if (defaults[i].untranslated
+ && v == defaults[i].original_translated)
{
// Has not been changed since the last time we
// read the config file.
@@ -1507,9 +1523,9 @@ void M_LoadDefaults (void)
// configuration file (for Doom) is named default.cfg.
//
- i = M_CheckParm ("-config");
+ i = M_CheckParmWithArgs("-config", 1);
- if (i && i<myargc-1)
+ if (i)
{
doom_defaults.filename = myargv[i+1];
printf (" default file: %s\n",doom_defaults.filename);
@@ -1530,9 +1546,9 @@ void M_LoadDefaults (void)
// configuration file for Doom is named chocolate-doom.cfg.
//
- i = M_CheckParm("-extraconfig");
+ i = M_CheckParmWithArgs("-extraconfig", 1);
- if (i && i<myargc-1)
+ if (i)
{
extra_defaults.filename = myargv[i+1];
printf(" extra configuration file: %s\n",
diff --git a/src/net_client.c b/src/net_client.c
index a1697944..ffc9b49b 100644
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -28,13 +28,12 @@
#include "config.h"
#include "doomtype.h"
-#include "doomstat.h"
#include "deh_main.h"
#include "deh_str.h"
-#include "g_game.h"
#include "i_system.h"
#include "i_timer.h"
#include "m_argv.h"
+#include "m_fixed.h"
#include "m_config.h"
#include "net_client.h"
#include "net_common.h"
@@ -47,6 +46,8 @@
#include "w_checksum.h"
#include "w_wad.h"
+extern void D_ReceiveTic(ticcmd_t *ticcmds, boolean *playeringame);
+
typedef enum
{
// waiting for the game to start
@@ -105,6 +106,10 @@ static net_clientstate_t client_state;
static net_addr_t *server_addr;
static net_context_t *client_context;
+// game settings, as received from the server when the game started
+
+static net_gamesettings_t settings;
+
// true if the client code is in use
boolean net_client_connected;
@@ -152,6 +157,10 @@ boolean net_waiting_for_start = false;
char *net_player_name = NULL;
+// Connected but not participating in the game (observer)
+
+boolean drone = false;
+
// The last ticcmd constructed
static ticcmd_t last_ticcmd;
@@ -187,53 +196,11 @@ static fixed_t average_latency;
#define NET_CL_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b))
-// Called when a player leaves the game
-
-static void NET_CL_PlayerQuitGame(player_t *player)
-{
- static char exitmsg[80];
-
- // Do this the same way as Vanilla Doom does, to allow dehacked
- // replacements of this message
-
- strncpy(exitmsg, DEH_String("Player 1 left the game"), sizeof(exitmsg));
- exitmsg[sizeof(exitmsg) - 1] = '\0';
-
- exitmsg[7] += player - players;
-
- players[consoleplayer].message = exitmsg;
-
- // TODO: check if it is sensible to do this:
-
- if (demorecording)
- {
- G_CheckDemoStatus ();
- }
-}
-
// Called when we become disconnected from the server
static void NET_CL_Disconnected(void)
{
- int i;
-
- // In drone mode, the game cannot continue once disconnected.
-
- if (drone)
- {
- I_Error("Disconnected from server in drone mode.");
- }
-
- // disconnected from server
-
- players[consoleplayer].message = "Disconnected from server";
- printf("Disconnected from server.\n");
-
- for (i=0; i<MAXPLAYERS; ++i)
- {
- if (i != consoleplayer)
- playeringame[i] = false;
- }
+ D_ReceiveTic(NULL, NULL);
}
// Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as
@@ -241,7 +208,8 @@ static void NET_CL_Disconnected(void)
// the d_net.c structures (netcmds/nettics) and save the new ticcmd
// back into recvwindow_cmd_base.
-static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
+static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq,
+ ticcmd_t *ticcmds)
{
int latency;
fixed_t adjustment;
@@ -303,38 +271,25 @@ static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
for (i=0; i<MAXPLAYERS; ++i)
{
- if (i == consoleplayer && !drone)
+ if (i == settings.consoleplayer && !drone)
{
continue;
}
- if (playeringame[i] && !cmd->playeringame[i])
- {
- NET_CL_PlayerQuitGame(&players[i]);
- }
-
- playeringame[i] = cmd->playeringame[i];
-
- if (playeringame[i])
+ if (cmd->playeringame[i])
{
net_ticdiff_t *diff;
- ticcmd_t ticcmd;
diff = &cmd->cmds[i];
// Use the ticcmd diff to patch the previous ticcmd to
// the new ticcmd
- NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmd);
-
- // Save in d_net.c structures
-
- netcmds[i][nettics[i] % BACKUPTICS] = ticcmd;
- ++nettics[i];
+ NET_TiccmdPatch(&recvwindow_cmd_base[i], diff, &ticcmds[i]);
// Store a copy for next time
- recvwindow_cmd_base[i] = ticcmd;
+ recvwindow_cmd_base[i] = ticcmds[i];
}
}
}
@@ -343,11 +298,15 @@ static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq)
static void NET_CL_AdvanceWindow(void)
{
+ ticcmd_t ticcmds[MAXPLAYERS];
+
while (recvwindow[0].active)
{
// Expand tic diff data into d_net.c structures
- NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start);
+ NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start,
+ ticcmds);
+ D_ReceiveTic(ticcmds, recvwindow[0].cmd.playeringame);
// Advance the window
@@ -375,66 +334,9 @@ static void NET_CL_Shutdown(void)
}
}
-void NET_CL_StartGame(void)
+void NET_CL_StartGame(net_gamesettings_t *settings)
{
net_packet_t *packet;
- net_gamesettings_t settings;
- int i;
-
- // Fill in game settings structure with appropriate parameters
- // for the new game
-
- settings.deathmatch = deathmatch;
- settings.episode = startepisode;
- settings.map = startmap;
- settings.skill = startskill;
- settings.loadgame = startloadgame;
- settings.gameversion = gameversion;
- settings.nomonsters = nomonsters;
- settings.fast_monsters = fastparm;
- settings.respawn_monsters = respawnparm;
- settings.timelimit = timelimit;
-
- //!
- // @category net
- //
- // Use original game sync code.
- //
-
- if (M_CheckParm("-oldsync") > 0)
- settings.new_sync = 0;
- else
- settings.new_sync = 1;
-
- //!
- // @category net
- // @arg <n>
- //
- // Send n extra tics in every packet as insurance against dropped
- // packets.
- //
-
- i = M_CheckParm("-extratics");
-
- if (i > 0)
- settings.extratics = atoi(myargv[i+1]);
- else
- settings.extratics = 1;
-
- //!
- // @category net
- // @arg <n>
- //
- // Reduce the resolution of the game by a factor of n, reducing
- // the amount of network bandwidth needed.
- //
-
- i = M_CheckParm("-dup");
-
- if (i > 0)
- settings.ticdup = atoi(myargv[i+1]);
- else
- settings.ticdup = 1;
// Start from a ticcmd of all zeros
@@ -445,7 +347,7 @@ void NET_CL_StartGame(void)
packet = NET_Conn_NewReliable(&client_connection,
NET_PACKET_TYPE_GAMESTART);
- NET_WriteSettings(packet, &settings);
+ NET_WriteSettings(packet, settings);
}
static void NET_CL_SendGameDataACK(void)
@@ -455,7 +357,7 @@ static void NET_CL_SendGameDataACK(void)
packet = NET_NewPacket(10);
NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_ACK);
- NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+ NET_WriteInt8(packet, recvwindow_start & 0xff);
NET_Conn_SendPacket(&client_connection, packet);
@@ -487,7 +389,7 @@ static void NET_CL_SendTics(int start, int end)
// Write the start tic and number of tics. Send only the low byte
// of start - it can be inferred by the server.
- NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+ NET_WriteInt8(packet, recvwindow_start & 0xff);
NET_WriteInt8(packet, start & 0xff);
NET_WriteInt8(packet, end - start + 1);
@@ -501,7 +403,7 @@ static void NET_CL_SendTics(int start, int end)
NET_WriteInt16(packet, average_latency / FRACUNIT);
- NET_WriteTiccmdDiff(packet, &sendobj->cmd, lowres_turn);
+ NET_WriteTiccmdDiff(packet, &sendobj->cmd, settings.lowres_turn);
}
// Send the packet
@@ -541,7 +443,7 @@ void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)
// Send to server.
- starttic = maketic - extratics;
+ starttic = maketic - settings.extratics;
endtic = maketic;
if (starttic < 0)
@@ -632,14 +534,7 @@ static void NET_CL_ParseWaitingData(net_packet_t *packet)
static void NET_CL_ParseGameStart(net_packet_t *packet)
{
- net_gamesettings_t settings;
- unsigned int num_players;
- signed int player_number;
- unsigned int i;
-
- if (!NET_ReadInt8(packet, &num_players)
- || !NET_ReadSInt8(packet, &player_number)
- || !NET_ReadSettings(packet, &settings))
+ if (!NET_ReadSettings(packet, &settings))
{
return;
}
@@ -649,14 +544,15 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
return;
}
- if (num_players > MAXPLAYERS || player_number >= (signed int) num_players)
+ if (settings.num_players > MAXPLAYERS
+ || settings.consoleplayer >= (signed int) settings.num_players)
{
// insane values
return;
}
- if ((drone && player_number >= 0)
- || (!drone && player_number < 0))
+ if ((drone && settings.consoleplayer >= 0)
+ || (!drone && settings.consoleplayer < 0))
{
// Invalid player number: must be positive for real players,
// negative for drones
@@ -664,49 +560,8 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
return;
}
- // Start the game
-
- if (!drone)
- {
- consoleplayer = player_number;
- }
- else
- {
- consoleplayer = 0;
- }
-
- for (i=0; i<MAXPLAYERS; ++i)
- {
- playeringame[i] = i < num_players;
- }
-
client_state = CLIENT_STATE_IN_GAME;
- deathmatch = settings.deathmatch;
- ticdup = settings.ticdup;
- extratics = settings.extratics;
- startepisode = settings.episode;
- startmap = settings.map;
- startskill = settings.skill;
- startloadgame = settings.loadgame;
- lowres_turn = settings.lowres_turn;
- nomonsters = settings.nomonsters;
- fastparm = settings.fast_monsters;
- respawnparm = settings.respawn_monsters;
- net_cl_new_sync = settings.new_sync != 0;
- timelimit = settings.timelimit;
-
- if (net_cl_new_sync == false)
- {
- printf("Syncing netgames like Vanilla Doom.\n");
- }
-
- if (lowres_turn)
- {
- printf("NOTE: Turning resolution is reduced; this is probably "
- "because there is a client recording a Vanilla demo.\n");
- }
-
// Clear the receive window
memset(recvwindow, 0, sizeof(recvwindow));
@@ -716,9 +571,6 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
// Clear the send queue
memset(&send_queue, 0x00, sizeof(send_queue));
-
- netgame = true;
- autostart = true;
}
static void NET_CL_SendResendRequest(int start, int end)
@@ -865,7 +717,7 @@ static void NET_CL_ParseGameData(net_packet_t *packet)
index = seq - recvwindow_start + i;
- if (!NET_ReadFullTiccmd(packet, &cmd, lowres_turn))
+ if (!NET_ReadFullTiccmd(packet, &cmd, settings.lowres_turn))
{
return;
}
@@ -1106,7 +958,7 @@ void NET_CL_Run(void)
}
}
-static void NET_CL_SendSYN(void)
+static void NET_CL_SendSYN(net_connect_data_t *data)
{
net_packet_t *packet;
@@ -1114,10 +966,7 @@ static void NET_CL_SendSYN(void)
NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
NET_WriteInt32(packet, NET_MAGIC_NUMBER);
NET_WriteString(packet, PACKAGE_STRING);
- NET_WriteInt16(packet, gamemode);
- NET_WriteInt16(packet, gamemission);
- NET_WriteInt8(packet, lowres_turn);
- NET_WriteInt8(packet, drone);
+ NET_WriteConnectData(packet, data);
NET_WriteMD5Sum(packet, net_local_wad_md5sum);
NET_WriteMD5Sum(packet, net_local_deh_md5sum);
NET_WriteInt8(packet, net_local_is_freedoom);
@@ -1128,20 +977,13 @@ static void NET_CL_SendSYN(void)
// connect to a server
-boolean NET_CL_Connect(net_addr_t *addr)
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data)
{
int start_time;
int last_send_time;
server_addr = addr;
- // Are we recording a demo? Possibly set lowres turn mode
-
- if (M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0)
- {
- lowres_turn = true;
- }
-
// Read checksums of our WAD directory and dehacked information
W_Checksum(net_local_wad_md5sum);
@@ -1155,7 +997,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
// necessary module
client_context = NET_NewContext();
-
+
// initialize module for client mode
if (!addr->module->InitClient())
@@ -1185,7 +1027,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
if (nowtime - last_send_time > 1000 || last_send_time < 0)
{
- NET_CL_SendSYN();
+ NET_CL_SendSYN(data);
last_send_time = nowtime;
}
@@ -1199,7 +1041,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
// run client code
NET_CL_Run();
-
+
// run the server, just incase we are doing a loopback
// connect
@@ -1215,6 +1057,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
// connected ok!
client_state = CLIENT_STATE_WAITING_START;
+ drone = data->drone;
return true;
}
@@ -1223,9 +1066,23 @@ boolean NET_CL_Connect(net_addr_t *addr)
// failed to connect
NET_CL_Shutdown();
-
+
+ return false;
+ }
+}
+
+// read game settings received from server
+
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings)
+{
+ if (client_state != CLIENT_STATE_IN_GAME)
+ {
return false;
}
+
+ memcpy(_settings, &settings, sizeof(net_gamesettings_t));
+
+ return true;
}
// disconnect from the server
diff --git a/src/net_client.h b/src/net_client.h
index 078a19a2..b071d32a 100644
--- a/src/net_client.h
+++ b/src/net_client.h
@@ -31,12 +31,13 @@
#define MAXPLAYERNAME 30
-boolean NET_CL_Connect(net_addr_t *addr);
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);
void NET_CL_Disconnect(void);
void NET_CL_Run(void);
void NET_CL_Init(void);
-void NET_CL_StartGame();
+void NET_CL_StartGame(net_gamesettings_t *settings);
void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings);
void NET_Init(void);
void NET_BindVariables(void);
@@ -59,6 +60,7 @@ extern md5_digest_t net_local_wad_md5sum;
extern md5_digest_t net_local_deh_md5sum;
extern unsigned int net_local_is_freedoom;
+extern boolean drone;
#endif /* #ifndef NET_CLIENT_H */
diff --git a/src/net_dedicated.c b/src/net_dedicated.c
index b47a7924..d8e50731 100644
--- a/src/net_dedicated.c
+++ b/src/net_dedicated.c
@@ -74,8 +74,8 @@ void NET_DedicatedServer(void)
CheckForClientOptions();
NET_SV_Init();
-
NET_SV_AddModule(&net_sdl_module);
+ NET_SV_RegisterWithMaster();
while (true)
{
diff --git a/src/net_defs.h b/src/net_defs.h
index a5fddc4d..1fa9a9af 100644
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -128,6 +128,30 @@ typedef enum
NET_PACKET_TYPE_QUERY_RESPONSE,
} net_packet_type_t;
+typedef enum
+{
+ NET_MASTER_PACKET_TYPE_ADD,
+ NET_MASTER_PACKET_TYPE_ADD_RESPONSE,
+ NET_MASTER_PACKET_TYPE_QUERY,
+ NET_MASTER_PACKET_TYPE_QUERY_RESPONSE
+} net_master_packet_type_t;
+
+// Settings specified when the client connects to the server.
+
+typedef struct
+{
+ int gamemode;
+ int gamemission;
+ int lowres_turn;
+ int drone;
+ // TODO: is_freedoom in here? WAD/DEH checksums?
+ // TODO: [Hexen] Requested player class
+
+} net_connect_data_t;
+
+// Game settings sent by client to server when initiating game start,
+// and received from the server by clients when the game starts.
+
typedef struct
{
int ticdup;
@@ -144,6 +168,15 @@ typedef struct
int new_sync;
int timelimit;
int loadgame;
+
+ // These fields are only used by the server when sending a game
+ // start message:
+
+ int num_players;
+ int consoleplayer;
+
+ // TODO: [Hexen] Array of player classes, one for each player.
+
} net_gamesettings_t;
#define NET_TICDIFF_FORWARD (1 << 0)
diff --git a/src/net_gui.c b/src/net_gui.c
index 8c848d1c..f2c4f1e5 100644
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -53,9 +53,11 @@ static void EscapePressed(TXT_UNCAST_ARG(widget), void *unused)
I_Quit();
}
-static void StartGame(TXT_UNCAST_ARG(widget), void *unused)
+static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(settings))
{
- NET_CL_StartGame();
+ TXT_CAST_ARG(net_gamesettings_t, settings);
+
+ NET_CL_StartGame(settings);
}
static void BuildGUI(void)
@@ -101,7 +103,7 @@ static void BuildGUI(void)
TXT_SetWindowAction(window, TXT_HORIZ_LEFT, cancel);
}
-static void UpdateGUI(void)
+static void UpdateGUI(net_gamesettings_t *settings)
{
txt_window_action_t *startgame;
char buf[50];
@@ -144,7 +146,7 @@ static void UpdateGUI(void)
if (net_client_controller)
{
startgame = TXT_NewWindowAction(' ', "Start game");
- TXT_SignalConnect(startgame, "pressed", StartGame, NULL);
+ TXT_SignalConnect(startgame, "pressed", StartGame, settings);
}
else
{
@@ -259,7 +261,7 @@ static void CheckMD5Sums(void)
had_warning = true;
}
-void NET_WaitForStart(void)
+void NET_WaitForStart(net_gamesettings_t *settings)
{
if (!TXT_Init())
{
@@ -268,13 +270,13 @@ void NET_WaitForStart(void)
}
I_SetWindowTitle("Waiting for game start");
- I_SetWindowIcon();
+ //I_SetWindowIcon();
BuildGUI();
while (net_waiting_for_start)
{
- UpdateGUI();
+ UpdateGUI(settings);
CheckMD5Sums();
TXT_DispatchEvents();
@@ -285,7 +287,7 @@ void NET_WaitForStart(void)
if (!net_client_connected)
{
- I_Error("Disconnected from server");
+ I_Error("Lost connection to server");
}
TXT_Sleep(100);
diff --git a/src/net_gui.h b/src/net_gui.h
index fdcc81be..9d40b0d0 100644
--- a/src/net_gui.h
+++ b/src/net_gui.h
@@ -30,7 +30,7 @@
#include "doomtype.h"
-extern void NET_WaitForStart();
+extern void NET_WaitForStart(net_gamesettings_t *settings);
#endif /* #ifndef NET_GUI_H */
diff --git a/src/net_loop.c b/src/net_loop.c
index 9f371bcb..acdc2cb6 100644
--- a/src/net_loop.c
+++ b/src/net_loop.c
@@ -137,9 +137,16 @@ static void NET_CL_FreeAddress(net_addr_t *addr)
static net_addr_t *NET_CL_ResolveAddress(char *address)
{
- client_addr.module = &net_loop_client_module;
+ if (address == NULL)
+ {
+ client_addr.module = &net_loop_client_module;
- return &client_addr;
+ return &client_addr;
+ }
+ else
+ {
+ return NULL;
+ }
}
net_module_t net_loop_client_module =
@@ -206,8 +213,15 @@ static void NET_SV_FreeAddress(net_addr_t *addr)
static net_addr_t *NET_SV_ResolveAddress(char *address)
{
- server_addr.module = &net_loop_server_module;
- return &server_addr;
+ if (address == NULL)
+ {
+ server_addr.module = &net_loop_server_module;
+ return &server_addr;
+ }
+ else
+ {
+ return NULL;
+ }
}
net_module_t net_loop_server_module =
diff --git a/src/net_query.c b/src/net_query.c
index b50b4292..ae56dea6 100644
--- a/src/net_query.c
+++ b/src/net_query.c
@@ -37,50 +37,166 @@
#include "net_structrw.h"
#include "net_sdl.h"
-typedef struct
+// DNS address of the Internet master server.
+
+#define MASTER_SERVER_ADDRESS "master.chocolate-doom.org:2342"
+
+// Time to wait for a response before declaring a timeout.
+
+#define QUERY_TIMEOUT_SECS 2
+
+// Number of query attempts to make before giving up on a server.
+
+#define QUERY_MAX_ATTEMPTS 3
+
+typedef enum
{
+ QUERY_TARGET_SERVER, // Normal server target.
+ QUERY_TARGET_MASTER, // The master server.
+ QUERY_TARGET_BROADCAST // Send a broadcast query
+} query_target_type_t;
+
+typedef enum
+{
+ QUERY_TARGET_QUEUED, // Query not yet sent
+ QUERY_TARGET_QUERIED, // Query sent, waiting response
+ QUERY_TARGET_RESPONDED, // Response received
+ QUERY_TARGET_NO_RESPONSE
+} query_target_state_t;
+
+typedef struct
+{
+ query_target_type_t type;
+ query_target_state_t state;
net_addr_t *addr;
net_querydata_t data;
-} queryresponse_t;
+ unsigned int ping_time;
+ unsigned int query_time;
+ unsigned int query_attempts;
+ boolean printed;
+} query_target_t;
+
+// Transmit a query packet
+
+static boolean registered_with_master = false;
static net_context_t *query_context;
-static queryresponse_t *responders;
-static int num_responses;
+static query_target_t *targets;
+static int num_targets;
+
+static boolean query_loop_running = false;
+static boolean printed_header = false;
+
+// Resolve the master server address.
+
+net_addr_t *NET_Query_ResolveMaster(net_context_t *context)
+{
+ net_addr_t *addr;
+
+ addr = NET_ResolveAddress(context, MASTER_SERVER_ADDRESS);
+
+ if (addr == NULL)
+ {
+ fprintf(stderr, "Warning: Failed to resolve address "
+ "for master server: %s\n", MASTER_SERVER_ADDRESS);
+ }
+
+ return addr;
+}
-// Add a new address to the list of hosts that has responded
+// Send a registration packet to the master server to register
+// ourselves with the global list.
-static queryresponse_t *AddResponder(net_addr_t *addr,
- net_querydata_t *data)
+void NET_Query_AddToMaster(net_addr_t *master_addr)
{
- queryresponse_t *response;
+ net_packet_t *packet;
+
+ packet = NET_NewPacket(10);
+ NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_ADD);
+ NET_SendPacket(master_addr, packet);
+ NET_FreePacket(packet);
+}
+
+// Process a packet received from the master server.
+
+void NET_Query_MasterResponse(net_packet_t *packet)
+{
+ unsigned int packet_type;
+ unsigned int result;
+
+ if (!NET_ReadInt16(packet, &packet_type)
+ || !NET_ReadInt16(packet, &result))
+ {
+ return;
+ }
+
+ if (packet_type == NET_MASTER_PACKET_TYPE_ADD_RESPONSE)
+ {
+ if (result != 0)
+ {
+ // Only show the message once.
+
+ if (!registered_with_master)
+ {
+ printf("Registered with master server at %s\n",
+ MASTER_SERVER_ADDRESS);
+ registered_with_master = true;
+ }
+ }
+ else
+ {
+ // Always show rejections.
- responders = realloc(responders,
- sizeof(queryresponse_t) * (num_responses + 1));
+ printf("Failed to register with master server at %s\n",
+ MASTER_SERVER_ADDRESS);
+ }
+ }
+}
- response = &responders[num_responses];
- response->addr = addr;
- response->data = *data;
- ++num_responses;
+// Send a query to the master server.
+
+static void NET_Query_SendMasterQuery(net_addr_t *addr)
+{
+ net_packet_t *packet;
- return response;
+ packet = NET_NewPacket(10);
+ NET_WriteInt16(packet, NET_MASTER_PACKET_TYPE_QUERY);
+ NET_SendPacket(addr, packet);
+ NET_FreePacket(packet);
}
-// Returns true if the reply is from a host that has not previously
-// responded.
+// Given the specified address, find the target associated. If no
+// target is found, and 'create' is true, a new target is created.
-static boolean CheckResponder(net_addr_t *addr)
+static query_target_t *GetTargetForAddr(net_addr_t *addr, boolean create)
{
+ query_target_t *target;
int i;
- for (i=0; i<num_responses; ++i)
+ for (i=0; i<num_targets; ++i)
{
- if (responders[i].addr == addr)
+ if (targets[i].addr == addr)
{
- return false;
+ return &targets[i];
}
}
- return true;
+ if (!create)
+ {
+ return NULL;
+ }
+
+ targets = realloc(targets, sizeof(query_target_t) * (num_targets + 1));
+
+ target = &targets[num_targets];
+ target->type = QUERY_TARGET_SERVER;
+ target->state = QUERY_TARGET_QUEUED;
+ target->printed = false;
+ target->query_attempts = 0;
+ target->addr = addr;
+ ++num_targets;
+
+ return target;
}
// Transmit a query packet
@@ -104,166 +220,254 @@ static void NET_Query_SendQuery(net_addr_t *addr)
NET_FreePacket(request);
}
-static void formatted_printf(int wide, char *s, ...)
+static void NET_Query_ParseResponse(net_addr_t *addr, net_packet_t *packet,
+ net_query_callback_t callback,
+ void *user_data)
{
- va_list args;
- int i;
-
- va_start(args, s);
- i = vprintf(s, args);
- va_end(args);
+ unsigned int packet_type;
+ net_querydata_t querydata;
+ query_target_t *target;
+
+ // Read the header
- while (i < wide)
+ if (!NET_ReadInt16(packet, &packet_type)
+ || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
{
- putchar(' ');
- ++i;
- }
-}
+ return;
+ }
-static char *GameDescription(GameMode_t mode, GameMission_t mission)
-{
- switch (mode)
+ // Read query data
+
+ if (!NET_ReadQueryData(packet, &querydata))
{
- case shareware:
- return "shareware";
- case registered:
- return "registered";
- case retail:
- return "ultimate";
- case commercial:
- if (mission == doom2)
- return "doom2";
- else if (mission == pack_tnt)
- return "tnt";
- else if (mission == pack_plut)
- return "plutonia";
- default:
- return "unknown";
+ return;
}
-}
-static void PrintHeader(void)
-{
- int i;
+ // Find the target that responded, or potentially add a new target
+ // if it was not already known (for LAN broadcast search)
- formatted_printf(18, "Address");
- formatted_printf(8, "Players");
- puts("Description");
+ target = GetTargetForAddr(addr, true);
- for (i=0; i<70; ++i)
- putchar('=');
- putchar('\n');
+ if (target->state != QUERY_TARGET_RESPONDED)
+ {
+ target->state = QUERY_TARGET_RESPONDED;
+ memcpy(&target->data, &querydata, sizeof(net_querydata_t));
+
+ // Calculate RTT.
+
+ target->ping_time = I_GetTimeMS() - target->query_time;
+
+ // Invoke callback to signal that we have a new address.
+
+ callback(addr, &target->data, target->ping_time, user_data);
+ }
}
-static void PrintResponse(queryresponse_t *response)
+// Parse a response packet from the master server.
+
+static void NET_Query_ParseMasterResponse(net_addr_t *master_addr,
+ net_packet_t *packet)
{
- formatted_printf(18, "%s: ", NET_AddrToString(response->addr));
- formatted_printf(8, "%i/%i", response->data.num_players,
- response->data.max_players);
+ unsigned int packet_type;
+ query_target_t *target;
+ char *addr_str;
+ net_addr_t *addr;
- if (response->data.gamemode != indetermined)
+ // Read the header. We are only interested in query responses.
+
+ if (!NET_ReadInt16(packet, &packet_type)
+ || packet_type != NET_MASTER_PACKET_TYPE_QUERY_RESPONSE)
{
- printf("(%s) ", GameDescription(response->data.gamemode,
- response->data.gamemission));
+ return;
}
- if (response->data.server_state)
+ // Read a list of strings containing the addresses of servers
+ // that the master knows about.
+
+ for (;;)
{
- printf("(game running) ");
+ addr_str = NET_ReadString(packet);
+
+ if (addr_str == NULL)
+ {
+ break;
+ }
+
+ // Resolve address and add to targets list if it is not already
+ // there.
+
+ addr = NET_ResolveAddress(query_context, addr_str);
+
+ if (addr != NULL)
+ {
+ GetTargetForAddr(addr, true);
+ }
}
- NET_SafePuts(response->data.description);
+ // Mark the master as having responded.
+
+ target = GetTargetForAddr(master_addr, true);
+ target->state = QUERY_TARGET_RESPONDED;
}
-static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet)
+static void NET_Query_ParsePacket(net_addr_t *addr, net_packet_t *packet,
+ net_query_callback_t callback,
+ void *user_data)
{
- unsigned int packet_type;
- net_querydata_t querydata;
- queryresponse_t *response;
+ query_target_t *target;
+
+ // This might be the master server responding.
- // Have we already received a packet from this host?
+ target = GetTargetForAddr(addr, false);
- if (!CheckResponder(addr))
+ if (target != NULL && target->type == QUERY_TARGET_MASTER)
{
- return;
+ NET_Query_ParseMasterResponse(addr, packet);
}
+ else
+ {
+ NET_Query_ParseResponse(addr, packet, callback, user_data);
+ }
+}
- // Read the header
+static void NET_Query_GetResponse(net_query_callback_t callback,
+ void *user_data)
+{
+ net_addr_t *addr;
+ net_packet_t *packet;
- if (!NET_ReadInt16(packet, &packet_type)
- || packet_type != NET_PACKET_TYPE_QUERY_RESPONSE)
+ if (NET_RecvPacket(query_context, &addr, &packet))
{
- return;
+ NET_Query_ParsePacket(addr, packet, callback, user_data);
+ NET_FreePacket(packet);
}
+}
- // Read query data
+// Find a target we have not yet queried and send a query.
- if (!NET_ReadQueryData(packet, &querydata))
+static void SendOneQuery(void)
+{
+ unsigned int now;
+ unsigned int i;
+
+ now = I_GetTimeMS();
+
+ for (i = 0; i < num_targets; ++i)
+ {
+ // Not queried yet?
+ // Or last query timed out without a response?
+
+ if (targets[i].state == QUERY_TARGET_QUEUED
+ || (targets[i].state == QUERY_TARGET_QUERIED
+ && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000))
+ {
+ break;
+ }
+ }
+
+ if (i >= num_targets)
{
return;
}
- if (num_responses <= 0)
+ // Found a target to query. Send a query; how to do this depends on
+ // the target type.
+
+ switch (targets[i].type)
{
- // If this is the first response, print the table header
+ case QUERY_TARGET_SERVER:
+ NET_Query_SendQuery(targets[i].addr);
+ break;
- PrintHeader();
+ case QUERY_TARGET_BROADCAST:
+ NET_Query_SendQuery(NULL);
+ break;
+
+ case QUERY_TARGET_MASTER:
+ NET_Query_SendMasterQuery(targets[i].addr);
+ break;
}
- response = AddResponder(addr, &querydata);
+ //printf("Queried %s\n", NET_AddrToString(targets[i].addr));
+ targets[i].state = QUERY_TARGET_QUERIED;
+ targets[i].query_time = I_GetTimeMS();
+ ++targets[i].query_attempts;
+}
+
+// Time out servers that have been queried and not responded.
- PrintResponse(response);
+static void CheckTargetTimeouts(void)
+{
+ unsigned int i;
+ unsigned int now;
+
+ now = I_GetTimeMS();
+
+ for (i = 0; i < num_targets; ++i)
+ {
+ // We declare a target to be "no response" when we've sent
+ // multiple query packets to it (QUERY_MAX_ATTEMPTS) and
+ // received no response to any of them.
+
+ if (targets[i].state == QUERY_TARGET_QUERIED
+ && targets[i].query_attempts >= QUERY_MAX_ATTEMPTS
+ && now - targets[i].query_time > QUERY_TIMEOUT_SECS * 1000)
+ {
+ targets[i].state = QUERY_TARGET_NO_RESPONSE;
+ }
+ }
}
-static void NET_Query_GetResponse(void)
+// If all targets have responded or timed out, returns true.
+
+static boolean AllTargetsDone(void)
{
- net_addr_t *addr;
- net_packet_t *packet;
+ unsigned int i;
- if (NET_RecvPacket(query_context, &addr, &packet))
+ for (i = 0; i < num_targets; ++i)
{
- NET_Query_ParsePacket(addr, packet);
- NET_FreePacket(packet);
+ if (targets[i].state != QUERY_TARGET_RESPONDED
+ && targets[i].state != QUERY_TARGET_NO_RESPONSE)
+ {
+ return false;
+ }
}
+
+ return true;
}
-static net_addr_t *NET_Query_QueryLoop(net_addr_t *addr,
- boolean find_one)
+// Stop the query loop
+
+static void NET_Query_ExitLoop(void)
{
- int start_time;
- int last_send_time;
+ query_loop_running = false;
+}
+
+// Loop waiting for responses.
+// The specified callback is invoked when a new server responds.
- last_send_time = -1;
- start_time = I_GetTimeMS();
+static void NET_Query_QueryLoop(net_query_callback_t callback,
+ void *user_data)
+{
+ query_loop_running = true;
- while (I_GetTimeMS() < start_time + 5000)
+ while (query_loop_running && !AllTargetsDone())
{
- // Send a query once every second
+ // Send a query. This will only send a single query.
+ // Because of the delay below, this is therefore rate limited.
- if (last_send_time < 0 || I_GetTimeMS() > last_send_time + 1000)
- {
- NET_Query_SendQuery(addr);
- last_send_time = I_GetTimeMS();
- }
+ SendOneQuery();
// Check for a response
- NET_Query_GetResponse();
+ NET_Query_GetResponse(callback, user_data);
- // Found a response?
-
- if (find_one && num_responses > 0)
- break;
-
// Don't thrash the CPU
-
- I_Sleep(100);
- }
- if (num_responses > 0)
- return responders[0].addr;
- else
- return NULL;
+ I_Sleep(50);
+
+ CheckTargetTimeouts();
+ }
}
void NET_Query_Init(void)
@@ -272,51 +476,256 @@ void NET_Query_Init(void)
NET_AddModule(query_context, &net_sdl_module);
net_sdl_module.InitClient();
- responders = NULL;
- num_responses = 0;
+ targets = NULL;
+ num_targets = 0;
+
+ printed_header = false;
}
-void NET_QueryAddress(char *addr)
+// Callback that exits the query loop when the first server is found.
+
+static void NET_Query_ExitCallback(net_addr_t *addr, net_querydata_t *data,
+ unsigned int ping_time, void *user_data)
{
- net_addr_t *net_addr;
-
- NET_Query_Init();
+ NET_Query_ExitLoop();
+}
- net_addr = NET_ResolveAddress(query_context, addr);
+// Search the targets list and find a target that has responded.
+// If none have responded, returns NULL.
+
+static query_target_t *FindFirstResponder(void)
+{
+ unsigned int i;
- if (net_addr == NULL)
+ for (i = 0; i < num_targets; ++i)
{
- I_Error("NET_QueryAddress: Host '%s' not found!", addr);
+ if (targets[i].type == QUERY_TARGET_SERVER
+ && targets[i].state == QUERY_TARGET_RESPONDED)
+ {
+ return &targets[i];
+ }
}
- printf("\nQuerying '%s'...\n\n", addr);
+ return NULL;
+}
- if (!NET_Query_QueryLoop(net_addr, true))
+// Return a count of the number of responses.
+
+static int GetNumResponses(void)
+{
+ unsigned int i;
+ int result;
+
+ result = 0;
+
+ for (i = 0; i < num_targets; ++i)
{
- I_Error("No response from '%s'", addr);
+ if (targets[i].type == QUERY_TARGET_SERVER
+ && targets[i].state == QUERY_TARGET_RESPONDED)
+ {
+ ++result;
+ }
}
- exit(0);
+ return result;
+}
+
+void NET_QueryAddress(char *addr_str)
+{
+ net_addr_t *addr;
+ query_target_t *target;
+
+ NET_Query_Init();
+
+ addr = NET_ResolveAddress(query_context, addr_str);
+
+ if (addr == NULL)
+ {
+ I_Error("NET_QueryAddress: Host '%s' not found!", addr_str);
+ }
+
+ // Add the address to the list of targets.
+
+ target = GetTargetForAddr(addr, true);
+
+ printf("\nQuerying '%s'...\n", addr_str);
+
+ // Run query loop.
+
+ NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
+
+ // Check if the target responded.
+
+ if (target->state == QUERY_TARGET_RESPONDED)
+ {
+ NET_QueryPrintCallback(addr, &target->data, target->ping_time, NULL);
+ }
+ else
+ {
+ I_Error("No response from '%s'", addr_str);
+ }
}
net_addr_t *NET_FindLANServer(void)
{
+ query_target_t *target;
+ query_target_t *responder;
+
+ NET_Query_Init();
+
+ // Add a broadcast target to the list.
+
+ target = GetTargetForAddr(NULL, true);
+ target->type = QUERY_TARGET_BROADCAST;
+
+ // Run the query loop, and stop at the first target found.
+
+ NET_Query_QueryLoop(NET_Query_ExitCallback, NULL);
+
+ responder = FindFirstResponder();
+
+ if (responder != NULL)
+ {
+ return responder->addr;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+int NET_LANQuery(net_query_callback_t callback, void *user_data)
+{
+ query_target_t *target;
+
NET_Query_Init();
- return NET_Query_QueryLoop(NULL, true);
+ // Add a broadcast target to the list.
+
+ target = GetTargetForAddr(NULL, true);
+ target->type = QUERY_TARGET_BROADCAST;
+
+ NET_Query_QueryLoop(callback, user_data);
+
+ return GetNumResponses();
}
-void NET_LANQuery(void)
+int NET_MasterQuery(net_query_callback_t callback, void *user_data)
{
+ net_addr_t *master;
+ query_target_t *target;
+
NET_Query_Init();
- printf("\nSearching for servers on local LAN ...\n\n");
+ // Resolve master address and add to targets list.
+
+ master = NET_Query_ResolveMaster(query_context);
+
+ if (master == NULL)
+ {
+ return 0;
+ }
+
+ target = GetTargetForAddr(master, true);
+ target->type = QUERY_TARGET_MASTER;
+
+ NET_Query_QueryLoop(callback, user_data);
+
+ // Check that we got a response from the master, and display
+ // a warning if we didn't.
+
+ if (target->state == QUERY_TARGET_NO_RESPONSE)
+ {
+ fprintf(stderr, "NET_MasterQuery: no response from master server.\n");
+ }
+
+ return GetNumResponses();
+}
+
+static void formatted_printf(int wide, char *s, ...)
+{
+ va_list args;
+ int i;
- if (!NET_Query_QueryLoop(NULL, false))
+ va_start(args, s);
+ i = vprintf(s, args);
+ va_end(args);
+
+ while (i < wide)
{
- I_Error("No servers found");
+ putchar(' ');
+ ++i;
+ }
+}
+
+static char *GameDescription(GameMode_t mode, GameMission_t mission)
+{
+ switch (mode)
+ {
+ case shareware:
+ return "shareware";
+ case registered:
+ return "registered";
+ case retail:
+ return "ultimate";
+ case commercial:
+ if (mission == doom2)
+ return "doom2";
+ else if (mission == pack_tnt)
+ return "tnt";
+ else if (mission == pack_plut)
+ return "plutonia";
+ default:
+ return "unknown";
+ }
+}
+
+static void PrintHeader(void)
+{
+ int i;
+
+ putchar('\n');
+ formatted_printf(5, "Ping");
+ formatted_printf(18, "Address");
+ formatted_printf(8, "Players");
+ puts("Description");
+
+ for (i=0; i<70; ++i)
+ putchar('=');
+ putchar('\n');
+}
+
+// Callback function that just prints information in a table.
+
+void NET_QueryPrintCallback(net_addr_t *addr,
+ net_querydata_t *data,
+ unsigned int ping_time,
+ void *user_data)
+{
+ // If this is the first server, print the header.
+
+ if (!printed_header)
+ {
+ PrintHeader();
+ printed_header = true;
+ }
+
+ formatted_printf(5, "%4i", ping_time);
+ formatted_printf(18, "%s: ", NET_AddrToString(addr));
+ formatted_printf(8, "%i/%i", data->num_players,
+ data->max_players);
+
+ if (data->gamemode != indetermined)
+ {
+ printf("(%s) ", GameDescription(data->gamemode,
+ data->gamemission));
+ }
+
+ if (data->server_state)
+ {
+ printf("(game running) ");
}
- exit(0);
+ NET_SafePuts(data->description);
}
diff --git a/src/net_query.h b/src/net_query.h
index f682d320..01e059cb 100644
--- a/src/net_query.h
+++ b/src/net_query.h
@@ -27,9 +27,22 @@
#include "net_defs.h"
+typedef void (*net_query_callback_t)(net_addr_t *addr,
+ net_querydata_t *querydata,
+ unsigned int ping_time,
+ void *user_data);
+
+extern int NET_LANQuery(net_query_callback_t callback, void *user_data);
+extern int NET_MasterQuery(net_query_callback_t callback, void *user_data);
extern void NET_QueryAddress(char *addr);
-extern void NET_LANQuery(void);
extern net_addr_t *NET_FindLANServer(void);
+extern void NET_QueryPrintCallback(net_addr_t *addr, net_querydata_t *data,
+ unsigned int ping_time, void *user_data);
+
+extern net_addr_t *NET_Query_ResolveMaster(net_context_t *context);
+extern void NET_Query_AddToMaster(net_addr_t *master_addr);
+extern void NET_Query_MasterResponse(net_packet_t *packet);
+
#endif /* #ifndef NET_QUERY_H */
diff --git a/src/net_sdl.c b/src/net_sdl.c
index aa7fbd6e..6a4f24dc 100644
--- a/src/net_sdl.c
+++ b/src/net_sdl.c
@@ -170,7 +170,7 @@ static boolean NET_SDL_InitClient(void)
// the default (2342).
//
- p = M_CheckParm("-port");
+ p = M_CheckParmWithArgs("-port", 1);
if (p > 0)
port = atoi(myargv[p+1]);
@@ -196,7 +196,7 @@ static boolean NET_SDL_InitServer(void)
{
int p;
- p = M_CheckParm("-port");
+ p = M_CheckParmWithArgs("-port", 1);
if (p > 0)
port = atoi(myargv[p+1]);
diff --git a/src/net_server.c b/src/net_server.c
index 904d932e..ae46be4a 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -41,10 +41,15 @@
#include "net_io.h"
#include "net_loop.h"
#include "net_packet.h"
+#include "net_query.h"
#include "net_server.h"
#include "net_sdl.h"
#include "net_structrw.h"
+// How often to refresh our registration with the master server.
+
+#define MASTER_REFRESH_PERIOD 20 * 60 /* 20 minutes */
+
typedef enum
{
// waiting for the game to start
@@ -65,6 +70,11 @@ typedef struct
int last_send_time;
char *name;
+ // Time that this client connected to the server.
+ // This is used to determine the controller (oldest client).
+
+ unsigned int connect_time;
+
// Last time new gamedata was received from this client
int last_gamedata_time;
@@ -128,6 +138,11 @@ static unsigned int sv_gamemode;
static unsigned int sv_gamemission;
static net_gamesettings_t sv_settings;
+// For registration with master server:
+
+static net_addr_t *master_server = NULL;
+static unsigned int master_refresh_time;
+
// receive window
static unsigned int recvwindow_start;
@@ -372,19 +387,29 @@ static void NET_SV_AdvanceWindow(void)
static net_client_t *NET_SV_Controller(void)
{
+ net_client_t *best;
int i;
- // first client in the list is the controller
+ // Find the oldest client (first to connect).
+
+ best = NULL;
for (i=0; i<MAXNETNODES; ++i)
{
- if (ClientConnected(&clients[i]) && !clients[i].drone)
+ // Can't be controller?
+
+ if (!ClientConnected(&clients[i]) || clients[i].drone)
{
- return &clients[i];
+ continue;
+ }
+
+ if (best == NULL || clients[i].connect_time < best->connect_time)
+ {
+ best = &clients[i];
}
}
- return NULL;
+ return best;
}
// Given an address, find the corresponding client
@@ -424,6 +449,7 @@ static void NET_SV_InitNewClient(net_client_t *client,
char *player_name)
{
client->active = true;
+ client->connect_time = I_GetTimeMS();
NET_Conn_InitServer(&client->connection, addr);
client->addr = addr;
client->last_send_time = -1;
@@ -447,10 +473,8 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
net_addr_t *addr)
{
unsigned int magic;
- unsigned int cl_gamemode, cl_gamemission;
- unsigned int cl_recording_lowres;
- unsigned int cl_drone;
unsigned int is_freedoom;
+ net_connect_data_t data;
md5_digest_t deh_md5sum, wad_md5sum;
char *player_name;
char *client_version;
@@ -501,10 +525,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// read the game mode and mission
- if (!NET_ReadInt16(packet, &cl_gamemode)
- || !NET_ReadInt16(packet, &cl_gamemission)
- || !NET_ReadInt8(packet, &cl_recording_lowres)
- || !NET_ReadInt8(packet, &cl_drone)
+ if (!NET_ReadConnectData(packet, &data)
|| !NET_ReadMD5Sum(packet, wad_md5sum)
|| !NET_ReadMD5Sum(packet, deh_md5sum)
|| !NET_ReadInt8(packet, &is_freedoom))
@@ -512,7 +533,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
return;
}
- if (!D_ValidGameMode(cl_gamemission, cl_gamemode))
+ if (!D_ValidGameMode(data.gamemission, data.gamemode))
{
return;
}
@@ -579,7 +600,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
NET_SV_AssignPlayers();
num_players = NET_SV_NumPlayers();
- if ((!cl_drone && num_players >= MAXPLAYERS)
+ if ((!data.drone && num_players >= MAXPLAYERS)
|| NET_SV_NumClients() >= MAXNETNODES)
{
NET_SV_SendReject(addr, "Server is full!");
@@ -592,10 +613,10 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// Adopt the game mode and mission of the first connecting client
- if (num_players == 0 && !cl_drone)
+ if (num_players == 0 && !data.drone)
{
- sv_gamemode = cl_gamemode;
- sv_gamemission = cl_gamemission;
+ sv_gamemode = data.gamemode;
+ sv_gamemission = data.gamemission;
}
// Save the MD5 checksums
@@ -607,7 +628,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
// Check the connecting client is playing the same game as all
// the other clients
- if (cl_gamemode != sv_gamemode || cl_gamemission != sv_gamemission)
+ if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission)
{
NET_SV_SendReject(addr, "You are playing the wrong game!");
return;
@@ -617,8 +638,8 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
NET_SV_InitNewClient(client, addr, player_name);
- client->recording_lowres = cl_recording_lowres;
- client->drone = cl_drone;
+ client->recording_lowres = data.lowres_turn;
+ client->drone = data.drone;
}
if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
@@ -681,6 +702,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
}
}
+ settings.num_players = NET_SV_NumPlayers();
+
nowtime = I_GetTimeMS();
// Send start packets to each connected node
@@ -695,8 +718,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
startpacket = NET_Conn_NewReliable(&clients[i].connection,
NET_PACKET_TYPE_GAMESTART);
- NET_WriteInt8(startpacket, NET_SV_NumPlayers());
- NET_WriteInt8(startpacket, clients[i].player_number);
+ settings.consoleplayer = clients[i].player_number;
+
NET_WriteSettings(startpacket, &settings);
}
@@ -1070,6 +1093,7 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
{
net_packet_t *reply;
net_querydata_t querydata;
+ int p;
// Version
@@ -1089,9 +1113,22 @@ void NET_SV_SendQueryResponse(net_addr_t *addr)
querydata.gamemode = sv_gamemode;
querydata.gamemission = sv_gamemission;
- // Server description. This is currently hard-coded.
+ //!
+ // @arg <name>
+ //
+ // When starting a network server, specify a name for the server.
+ //
+
+ p = M_CheckParmWithArgs("-servername", 1);
- querydata.description = "Chocolate Doom server";
+ if (p > 0)
+ {
+ querydata.description = myargv[p + 1];
+ }
+ else
+ {
+ querydata.description = "Unnamed server";
+ }
// Send it and we're done.
@@ -1109,6 +1146,14 @@ static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
net_client_t *client;
unsigned int packet_type;
+ // Response from master server?
+
+ if (addr != NULL && addr == master_server)
+ {
+ NET_Query_MasterResponse(packet);
+ return;
+ }
+
// Find which client this packet came from
client = NET_SV_FindClient(addr);
@@ -1514,6 +1559,32 @@ void NET_SV_Init(void)
server_initialized = true;
}
+void NET_SV_RegisterWithMaster(void)
+{
+ //!
+ // When running a server, don't register with the global master server.
+ //
+ // @category net
+ //
+
+ if (!M_CheckParm("-privateserver"))
+ {
+ master_server = NET_Query_ResolveMaster(server_context);
+ }
+ else
+ {
+ master_server = NULL;
+ }
+
+ // Send request.
+
+ if (master_server != NULL)
+ {
+ NET_Query_AddToMaster(master_server);
+ master_refresh_time = I_GetTimeMS();
+ }
+}
+
// Run server code to check for new packets/send packets as the server
// requires
@@ -1528,12 +1599,21 @@ void NET_SV_Run(void)
return;
}
- while (NET_RecvPacket(server_context, &addr, &packet))
+ while (NET_RecvPacket(server_context, &addr, &packet))
{
NET_SV_Packet(packet, addr);
NET_FreePacket(packet);
}
+ // Possibly refresh our registration with the master server.
+
+ if (master_server != NULL
+ && I_GetTimeMS() - master_refresh_time > MASTER_REFRESH_PERIOD * 1000)
+ {
+ NET_Query_AddToMaster(master_server);
+ master_refresh_time = I_GetTimeMS();
+ }
+
// "Run" any clients that may have things to do, independent of responses
// to received packets
diff --git a/src/net_server.h b/src/net_server.h
index 93b22fc3..1debbd79 100644
--- a/src/net_server.h
+++ b/src/net_server.h
@@ -41,5 +41,9 @@ void NET_SV_Shutdown(void);
void NET_SV_AddModule(net_module_t *module);
+// Register server with master server.
+
+void NET_SV_RegisterWithMaster(void);
+
#endif /* #ifndef NET_SERVER_H */
diff --git a/src/net_structrw.c b/src/net_structrw.c
index 01933ebb..7380e334 100644
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -30,6 +30,22 @@
#include "net_packet.h"
#include "net_structrw.h"
+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+ NET_WriteInt8(packet, data->gamemode);
+ NET_WriteInt8(packet, data->gamemission);
+ NET_WriteInt8(packet, data->lowres_turn);
+ NET_WriteInt8(packet, data->drone);
+}
+
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+ 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);
+}
+
void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
{
NET_WriteInt8(packet, settings->ticdup);
@@ -46,6 +62,8 @@ void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
NET_WriteInt8(packet, settings->new_sync);
NET_WriteInt32(packet, settings->timelimit);
NET_WriteInt8(packet, settings->loadgame);
+ NET_WriteInt8(packet, settings->num_players);
+ NET_WriteInt8(packet, settings->consoleplayer);
}
boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
@@ -63,7 +81,9 @@ boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
&& 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_ReadSInt8(packet, (signed int *) &settings->loadgame)
+ && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
+ && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
}
boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
diff --git a/src/net_structrw.h b/src/net_structrw.h
index 13209778..68971cd3 100644
--- a/src/net_structrw.h
+++ b/src/net_structrw.h
@@ -26,6 +26,9 @@
#include "net_defs.h"
#include "net_packet.h"
+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data);
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data);
+
extern void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings);
extern boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings);
diff --git a/src/setup/display.c b/src/setup/display.c
index 9fd0963b..f5f190f2 100644
--- a/src/setup/display.c
+++ b/src/setup/display.c
@@ -26,12 +26,40 @@
#include "libc_wince.h"
#endif
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
#include "textscreen.h"
#include "m_config.h"
#include "mode.h"
#include "display.h"
+extern void RestartTextscreen(void);
+
+typedef struct
+{
+ char *description;
+ int bpp;
+} pixel_depth_t;
+
+// List of supported pixel depths.
+
+static pixel_depth_t pixel_depths[] =
+{
+ { "8-bit", 8 },
+ { "16-bit", 16 },
+ { "24-bit", 24 },
+ { "32-bit", 32 },
+};
+
+// List of strings containing supported pixel depths.
+
+static char **supported_bpps;
+static int num_supported_bpps;
+
typedef struct
{
int w, h;
@@ -69,6 +97,7 @@ static screen_mode_t screen_modes_scaled[] =
// List of fullscreen modes generated at runtime
static screen_mode_t *screen_modes_fullscreen = NULL;
+static int num_screen_modes_fullscreen;
static int vidmode = 0;
@@ -78,6 +107,7 @@ static int aspect_ratio_correct = 1;
static int fullscreen = 1;
static int screen_width = 320;
static int screen_height = 200;
+static int screen_bpp = 8;
static int startup_delay = 1000;
static int graphical_startup = 1;
static int show_endoom = 1;
@@ -90,6 +120,10 @@ static int usegamma = 0;
static int selected_screen_width = 0, selected_screen_height;
+// Index into the supported_bpps of the selected pixel depth.
+
+static int selected_bpp = 0;
+
static int system_video_env_set;
// Set the SDL_VIDEODRIVER environment variable
@@ -133,6 +167,153 @@ void SetDisplayDriver(void)
}
}
+// Query SDL as to whether any fullscreen modes are available for the
+// specified pixel depth.
+
+static int PixelDepthSupported(int bpp)
+{
+ SDL_PixelFormat format;
+ SDL_Rect **modes;
+
+ format.BitsPerPixel = bpp;
+ format.BytesPerPixel = (bpp + 7) / 8;
+
+ modes = SDL_ListModes(&format, SDL_FULLSCREEN);
+
+ return modes != NULL;
+}
+
+// Query SDL and populate the supported_bpps array.
+
+static void IdentifyPixelDepths(void)
+{
+ unsigned int i;
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+
+ if (supported_bpps != NULL)
+ {
+ free(supported_bpps);
+ }
+
+ supported_bpps = malloc(sizeof(char *) * num_depths);
+ num_supported_bpps = 0;
+
+ // Check each bit depth to determine if modes are available.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ // If modes are available, add this bit depth to the list.
+
+ if (PixelDepthSupported(pixel_depths[i].bpp))
+ {
+ supported_bpps[num_supported_bpps] = pixel_depths[i].description;
+ ++num_supported_bpps;
+ }
+ }
+
+ // No supported pixel depths? That's kind of a problem. Add 8bpp
+ // as a fallback.
+
+ if (num_supported_bpps == 0)
+ {
+ supported_bpps[0] = pixel_depths[0].description;
+ ++num_supported_bpps;
+ }
+}
+
+// Get the screen pixel depth corresponding to what selected_bpp is set to.
+
+static int GetSelectedBPP(void)
+{
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+ unsigned int i;
+
+ // Find which pixel depth is selected, and set screen_bpp.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ if (pixel_depths[i].description == supported_bpps[selected_bpp])
+ {
+ return pixel_depths[i].bpp;
+ }
+ }
+
+ // Default fallback value.
+
+ return 8;
+}
+
+// Get the index into supported_bpps of the specified pixel depth string.
+
+static int GetSupportedBPPIndex(char *description)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_supported_bpps; ++i)
+ {
+ if (supported_bpps[i] == description)
+ {
+ return i;
+ }
+ }
+
+ // Shouldn't happen; fall back to the first in the list.
+
+ return 0;
+}
+
+// Set selected_bpp to match screen_bpp.
+
+static int TrySetSelectedBPP(void)
+{
+ unsigned int num_depths = sizeof(pixel_depths) / sizeof(*pixel_depths);
+ unsigned int i;
+
+ // Search pixel_depths, find the bpp that corresponds to screen_bpp,
+ // then set selected_bpp to match.
+
+ for (i = 0; i < num_depths; ++i)
+ {
+ if (pixel_depths[i].bpp == screen_bpp)
+ {
+ selected_bpp = GetSupportedBPPIndex(pixel_depths[i].description);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void SetSelectedBPP(void)
+{
+ const SDL_VideoInfo *info;
+
+ if (TrySetSelectedBPP())
+ {
+ return;
+ }
+
+ // screen_bpp does not match any supported pixel depth. Query SDL
+ // to find out what it recommends using.
+
+ info = SDL_GetVideoInfo();
+
+ if (info != NULL && info->vfmt != NULL)
+ {
+ screen_bpp = info->vfmt->BitsPerPixel;
+ }
+
+ // Try again.
+
+ if (!TrySetSelectedBPP())
+ {
+ // Give up and just use the first in the list.
+
+ selected_bpp = 0;
+ screen_bpp = GetSelectedBPP();
+ }
+}
+
static void ModeSelected(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(mode))
{
TXT_CAST_ARG(screen_mode_t, mode);
@@ -177,6 +358,7 @@ static int GoodFullscreenMode(screen_mode_t *mode)
static void BuildFullscreenModesList(void)
{
+ SDL_PixelFormat format;
SDL_Rect **modes;
screen_mode_t *m1;
screen_mode_t *m2;
@@ -194,7 +376,10 @@ static void BuildFullscreenModesList(void)
// Get a list of fullscreen modes and find out how many
// modes are in the list.
- modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
+ format.BitsPerPixel = screen_bpp;
+ format.BytesPerPixel = (screen_bpp + 7) / 8;
+
+ modes = SDL_ListModes(&format, SDL_FULLSCREEN);
if (modes == NULL || modes == (SDL_Rect **) -1)
{
@@ -229,6 +414,8 @@ static void BuildFullscreenModesList(void)
memcpy(m1, m2, sizeof(screen_mode_t));
memcpy(m2, &m, sizeof(screen_mode_t));
}
+
+ num_screen_modes_fullscreen = num_modes;
}
static int FindBestMode(screen_mode_t *modes)
@@ -295,7 +482,7 @@ static void GenerateModesTable(TXT_UNCAST_ARG(widget),
// Build the table
TXT_ClearTable(modes_table);
- TXT_SetColumnWidths(modes_table, 15, 15, 15);
+ TXT_SetColumnWidths(modes_table, 14, 14, 14, 14, 14);
for (i=0; modes[i].w != 0; ++i)
{
@@ -317,8 +504,25 @@ static void GenerateModesTable(TXT_UNCAST_ARG(widget),
vidmode = FindBestMode(modes);
- screen_width = modes[vidmode].w;
- screen_height = modes[vidmode].h;
+ if (vidmode > 0)
+ {
+ screen_width = modes[vidmode].w;
+ screen_height = modes[vidmode].h;
+ }
+}
+
+// Callback invoked when the BPP selector is changed.
+
+static void UpdateBPP(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(modes_table))
+{
+ TXT_CAST_ARG(txt_table_t, modes_table);
+
+ screen_bpp = GetSelectedBPP();
+
+ // Rebuild list of fullscreen modes.
+
+ BuildFullscreenModesList();
+ GenerateModesTable(NULL, modes_table);
}
#if defined(_WIN32) && !defined(_WIN32_WCE)
@@ -331,18 +535,6 @@ static char *win32_video_drivers[] =
"Windows GDI",
};
-// Restart the textscreen library. Used when the video_driver variable
-// is changed.
-
-static void RestartTextscreen(void)
-{
- TXT_Shutdown();
-
- SetDisplayDriver();
-
- TXT_Init();
-}
-
static void SetWin32VideoDriver(void)
{
if (!strcmp(video_driver, "windib"))
@@ -372,6 +564,11 @@ static void UpdateVideoDriver(TXT_UNCAST_ARG(widget),
RestartTextscreen();
+ // Rebuild the list of supported pixel depths.
+
+ IdentifyPixelDepths();
+ SetSelectedBPP();
+
// Rebuild the video modes list
BuildFullscreenModesList();
@@ -385,8 +582,18 @@ void ConfigDisplay(void)
{
txt_window_t *window;
txt_table_t *modes_table;
+ txt_table_t *bpp_table;
txt_checkbox_t *fs_checkbox;
txt_checkbox_t *ar_checkbox;
+ txt_dropdown_list_t *bpp_selector;
+ int num_columns;
+ int window_y;
+
+ // What color depths are supported? Generate supported_bpps array
+ // and set selected_bpp to match the current value of screen_bpp.
+
+ IdentifyPixelDepths();
+ SetSelectedBPP();
// First time in? Initialise selected_screen_{width,height}
@@ -400,16 +607,43 @@ void ConfigDisplay(void)
window = TXT_NewWindow("Display Configuration");
- TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP,
- TXT_SCREEN_W / 2, 5);
-
TXT_AddWidgets(window,
fs_checkbox = TXT_NewCheckBox("Fullscreen", &fullscreen),
ar_checkbox = TXT_NewCheckBox("Correct aspect ratio",
&aspect_ratio_correct),
NULL);
- modes_table = TXT_NewTable(3);
+ // Some machines can have lots of video modes. This tries to
+ // keep a limit of six lines by increasing the number of
+ // columns. In extreme cases, the window is moved up slightly.
+
+ BuildFullscreenModesList();
+
+ window_y = 5;
+
+ if (num_screen_modes_fullscreen <= 18)
+ {
+ num_columns = 3;
+ }
+ else if (num_screen_modes_fullscreen <= 24)
+ {
+ num_columns = 4;
+ }
+ else
+ {
+ num_columns = 5;
+ window_y -= 3;
+ }
+
+ modes_table = TXT_NewTable(num_columns);
+
+ // The window is set at a fixed vertical position. This keeps
+ // the top of the window stationary when switching between
+ // fullscreen and windowed mode (which causes the window's
+ // height to change).
+
+ TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP,
+ TXT_SCREEN_W / 2, window_y);
// On Windows, there is an extra control to change between
// the Windows GDI and DirectX video drivers.
@@ -442,6 +676,7 @@ void ConfigDisplay(void)
TXT_AddWidgets(window,
TXT_NewSeparator("Screen mode"),
+ bpp_table = TXT_NewTable(2),
modes_table,
TXT_NewSeparator("Misc."),
NULL);
@@ -458,6 +693,15 @@ void ConfigDisplay(void)
TXT_NewCheckBox("Show ENDOOM screen", &show_endoom));
}
+ TXT_AddWidgets(bpp_table,
+ TXT_NewLabel("Color depth: "),
+ bpp_selector = TXT_NewDropdownList(&selected_bpp,
+ supported_bpps,
+ num_supported_bpps),
+ NULL);
+
+
+ TXT_SignalConnect(bpp_selector, "changed", UpdateBPP, modes_table);
TXT_SignalConnect(fs_checkbox, "changed", GenerateModesTable, modes_table);
TXT_SignalConnect(ar_checkbox, "changed", GenerateModesTable, modes_table);
@@ -486,5 +730,25 @@ void BindDisplayVariables(void)
M_BindVariable("graphical_startup", &graphical_startup);
}
+ // Windows Vista or later? Set screen color depth to
+ // 32 bits per pixel, as 8-bit palettized screen modes
+ // don't work properly in recent versions.
+
+#if defined(_WIN32) && !defined(_WIN32_WCE)
+ {
+ OSVERSIONINFOEX version_info;
+
+ ZeroMemory(&version_info, sizeof(OSVERSIONINFOEX));
+ version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ GetVersionEx((OSVERSIONINFO *) &version_info);
+
+ if (version_info.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && version_info.dwMajorVersion >= 6)
+ {
+ screen_bpp = 32;
+ }
+ }
+#endif
}
diff --git a/src/setup/execute.c b/src/setup/execute.c
index 4be44149..f85b8af4 100644
--- a/src/setup/execute.c
+++ b/src/setup/execute.c
@@ -88,6 +88,42 @@ static char *TempFile(char *s)
return result;
}
+static int ArgumentNeedsEscape(char *arg)
+{
+ char *p;
+
+ for (p = arg; *p != '\0'; ++p)
+ {
+ if (isspace(*p))
+ {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+// Arguments passed to the setup tool should be passed through to the
+// game when launching a game. Calling this adds all arguments from
+// myargv to the output context.
+
+void PassThroughArguments(execute_context_t *context)
+{
+ int i;
+
+ for (i = 1; i < myargc; ++i)
+ {
+ if (ArgumentNeedsEscape(myargv[i]))
+ {
+ AddCmdLineParameter(context, "\"%s\"", myargv[i]);
+ }
+ else
+ {
+ AddCmdLineParameter(context, "%s", myargv[i]);
+ }
+ }
+}
+
execute_context_t *NewExecuteContext(void)
{
execute_context_t *result;
@@ -106,25 +142,6 @@ execute_context_t *NewExecuteContext(void)
return result;
}
-void AddConfigParameters(execute_context_t *context)
-{
- int p;
-
- p = M_CheckParm("-config");
-
- if (p > 0)
- {
- AddCmdLineParameter(context, "-config \"%s\"", myargv[p + 1]);
- }
-
- p = M_CheckParm("-extraconfig");
-
- if (p > 0)
- {
- AddCmdLineParameter(context, "-extraconfig \"%s\"", myargv[p + 1]);
- }
-}
-
void AddCmdLineParameter(execute_context_t *context, char *s, ...)
{
va_list args;
diff --git a/src/setup/execute.h b/src/setup/execute.h
index 24711a16..25f1f10a 100644
--- a/src/setup/execute.h
+++ b/src/setup/execute.h
@@ -35,7 +35,7 @@ typedef struct execute_context_s execute_context_t;
execute_context_t *NewExecuteContext(void);
void AddCmdLineParameter(execute_context_t *context, char *s, ...);
-void AddConfigParameters(execute_context_t *context);
+void PassThroughArguments(execute_context_t *context);
int ExecuteDoom(execute_context_t *context);
int FindInstalledIWADs(void);
diff --git a/src/setup/joystick.c b/src/setup/joystick.c
index fbe3a3f3..0fc00ea1 100644
--- a/src/setup/joystick.c
+++ b/src/setup/joystick.c
@@ -65,8 +65,8 @@ static int joystick_y_invert = 0;
static txt_button_t *joystick_button;
static int *all_joystick_buttons[] = {
- &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
- &joybuse, &joybstrafe, &joybjump
+ &joybstraferight, &joybstrafeleft, &joybfire, &joybspeed,
+ &joybuse, &joybstrafe, &joybprevweapon, &joybnextweapon, &joybjump
};
//
diff --git a/src/setup/mainmenu.c b/src/setup/mainmenu.c
index c3cb7db5..55496010 100644
--- a/src/setup/mainmenu.c
+++ b/src/setup/mainmenu.c
@@ -156,7 +156,7 @@ static void LaunchDoom(void *unused1, void *unused2)
// Launch Doom
exec = NewExecuteContext();
- AddConfigParameters(exec);
+ PassThroughArguments(exec);
ExecuteDoom(exec);
exit(0);
@@ -189,6 +189,7 @@ void MainMenu(void)
{
txt_window_t *window;
txt_window_action_t *quit_action;
+ txt_window_action_t *warp_action;
window = TXT_NewWindow("Main Menu");
@@ -230,8 +231,12 @@ void MainMenu(void)
NULL);
quit_action = TXT_NewWindowAction(KEY_ESCAPE, "Quit");
+ warp_action = TXT_NewWindowAction(KEY_F1, "Warp");
TXT_SignalConnect(quit_action, "pressed", QuitConfirm, NULL);
+ TXT_SignalConnect(warp_action, "pressed",
+ (TxtWidgetSignalFunc) WarpMenu, NULL);
TXT_SetWindowAction(window, TXT_HORIZ_LEFT, quit_action);
+ TXT_SetWindowAction(window, TXT_HORIZ_CENTER, warp_action);
TXT_SetKeyListener(window, MainMenuKeyPress, NULL);
}
@@ -292,11 +297,9 @@ static void SetIcon(void)
free(mask);
}
-//
-// Initialize and run the textscreen GUI.
-//
+// Initialize the textscreen library.
-static void RunGUI(void)
+static void InitTextscreen(void)
{
SetDisplayDriver();
@@ -308,6 +311,24 @@ static void RunGUI(void)
TXT_SetDesktopTitle(PACKAGE_NAME " Setup ver " PACKAGE_VERSION);
SetIcon();
+}
+
+// Restart the textscreen library. Used when the video_driver variable
+// is changed.
+
+void RestartTextscreen(void)
+{
+ TXT_Shutdown();
+ InitTextscreen();
+}
+
+//
+// Initialize and run the textscreen GUI.
+//
+
+static void RunGUI(void)
+{
+ InitTextscreen();
TXT_GUIMainLoop();
}
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c
index 24cd0670..aed89212 100644
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -70,10 +70,10 @@ static char *iwadfile;
static char *doom_skills[] =
{
- "I'm too young to die!",
+ "I'm too young to die.",
"Hey, not too rough.",
"Hurt me plenty.",
- "Ultra-violence",
+ "Ultra-Violence.",
"NIGHTMARE!",
};
@@ -144,6 +144,7 @@ static int fast = 0;
static int respawn = 0;
static int udpport = 2342;
static int timer = 0;
+static int privateserver = 0;
static txt_dropdown_list_t *skillbutton;
static txt_button_t *warpbutton;
@@ -209,7 +210,11 @@ static void AddIWADParameter(execute_context_t *exec)
}
}
-static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
+// Callback function invoked to launch the game.
+// This is used when starting a server and also when starting a
+// single player game via the "warp" menu.
+
+static void StartGame(int multiplayer)
{
execute_context_t *exec;
@@ -221,7 +226,6 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
AddExtraParameters(exec);
AddIWADParameter(exec);
- AddCmdLineParameter(exec, "-server");
AddCmdLineParameter(exec, "-skill %i", skill + 1);
if (gamemission == hexen)
@@ -244,20 +248,6 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
AddCmdLineParameter(exec, "-respawn");
}
- if (deathmatch == 1)
- {
- AddCmdLineParameter(exec, "-deathmatch");
- }
- else if (deathmatch == 2)
- {
- AddCmdLineParameter(exec, "-altdeath");
- }
-
- if (timer > 0)
- {
- AddCmdLineParameter(exec, "-timer %i", timer);
- }
-
if (warptype == WARP_ExMy)
{
// TODO: select IWAD based on warp type
@@ -268,20 +258,55 @@ static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(user_data))
AddCmdLineParameter(exec, "-warp %i", warpmap);
}
- AddCmdLineParameter(exec, "-port %i", udpport);
+ // Multiplayer-specific options:
+
+ if (multiplayer)
+ {
+ AddCmdLineParameter(exec, "-server");
+ AddCmdLineParameter(exec, "-port %i", udpport);
+
+ if (deathmatch == 1)
+ {
+ AddCmdLineParameter(exec, "-deathmatch");
+ }
+ else if (deathmatch == 2)
+ {
+ AddCmdLineParameter(exec, "-altdeath");
+ }
+
+ if (timer > 0)
+ {
+ AddCmdLineParameter(exec, "-timer %i", timer);
+ }
+
+ if (privateserver)
+ {
+ AddCmdLineParameter(exec, "-privateserver");
+ }
+ }
AddWADs(exec);
TXT_Shutdown();
M_SaveDefaults();
- AddConfigParameters(exec);
+ PassThroughArguments(exec);
ExecuteDoom(exec);
exit(0);
}
+static void StartServerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ StartGame(1);
+}
+
+static void StartSinglePlayerGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(unused))
+{
+ StartGame(0);
+}
+
static void UpdateWarpButton(void)
{
char buf[10];
@@ -544,12 +569,27 @@ static txt_widget_t *IWADSelector(void)
return result;
}
-static txt_window_action_t *StartGameAction(void)
+// Create the window action button to start the game. This invokes
+// a different callback depending on whether to start a multiplayer
+// or single player game.
+
+static txt_window_action_t *StartGameAction(int multiplayer)
{
txt_window_action_t *action;
+ TxtWidgetSignalFunc callback;
action = TXT_NewWindowAction(KEY_F10, "Start");
- TXT_SignalConnect(action, "pressed", StartGame, NULL);
+
+ if (multiplayer)
+ {
+ callback = StartServerGame;
+ }
+ else
+ {
+ callback = StartSinglePlayerGame;
+ }
+
+ TXT_SignalConnect(action, "pressed", callback, NULL);
return action;
}
@@ -591,7 +631,11 @@ static txt_window_action_t *WadWindowAction(void)
return action;
}
-void StartMultiGame(void)
+// "Start game" menu. This is used for the start server window
+// and the single player warp menu. The parameters specify
+// the window title and whether to display multiplayer options.
+
+static void StartGameMenu(char *window_title, int multiplayer)
{
txt_window_t *window;
txt_table_t *gameopt_table;
@@ -599,7 +643,7 @@ void StartMultiGame(void)
txt_widget_t *iwad_selector;
int num_mult_types = 2;
- window = TXT_NewWindow("Start multiplayer game");
+ window = TXT_NewWindow(window_title);
TXT_AddWidgets(window,
gameopt_table = TXT_NewTable(2),
@@ -609,14 +653,12 @@ void StartMultiGame(void)
TXT_NewCheckBox("Respawning monsters", &respawn),
TXT_NewSeparator("Advanced"),
advanced_table = TXT_NewTable(2),
- TXT_NewButton2("Add extra parameters...",
- OpenExtraParamsWindow, NULL),
NULL);
TXT_SetWindowAction(window, TXT_HORIZ_CENTER, WadWindowAction());
- TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction());
+ TXT_SetWindowAction(window, TXT_HORIZ_RIGHT, StartGameAction(multiplayer));
- TXT_SetColumnWidths(gameopt_table, 12, 12);
+ TXT_SetColumnWidths(gameopt_table, 12, 6);
if (gamemission == doom)
{
@@ -632,14 +674,8 @@ void StartMultiGame(void)
iwad_selector = IWADSelector(),
TXT_NewLabel("Skill"),
skillbutton = TXT_NewDropdownList(&skill, doom_skills, 5),
- TXT_NewLabel("Game type"),
- TXT_NewDropdownList(&deathmatch, gamemodes, num_mult_types),
TXT_NewLabel("Level warp"),
warpbutton = TXT_NewButton2("????", LevelSelectDialog, NULL),
- TXT_NewLabel("Time limit"),
- TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2),
- TXT_NewLabel("minutes"),
- NULL),
NULL);
if (gamemission == hexen)
@@ -651,19 +687,49 @@ void StartMultiGame(void)
NULL);
}
- TXT_SetColumnWidths(advanced_table, 12, 12);
+ if (multiplayer)
+ {
+ TXT_AddWidgets(gameopt_table,
+ TXT_NewLabel("Game type"),
+ TXT_NewDropdownList(&deathmatch, gamemodes, num_mult_types),
+ TXT_NewLabel("Time limit"),
+ TXT_NewHorizBox(TXT_NewIntInputBox(&timer, 2),
+ TXT_NewLabel("minutes"),
+ NULL),
+ NULL);
+
+ TXT_AddWidget(window,
+ TXT_NewInvertedCheckBox("Register with master server",
+ &privateserver));
+
+ TXT_AddWidgets(advanced_table,
+ TXT_NewLabel("UDP port"),
+ TXT_NewIntInputBox(&udpport, 5),
+ NULL);
+ }
- TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
+ TXT_AddWidget(window,
+ TXT_NewButton2("Add extra parameters...",
+ OpenExtraParamsWindow, NULL));
- TXT_AddWidgets(advanced_table,
- TXT_NewLabel("UDP port"),
- TXT_NewIntInputBox(&udpport, 5),
- NULL);
+ TXT_SetColumnWidths(advanced_table, 12, 6);
+
+ TXT_SignalConnect(iwad_selector, "changed", UpdateWarpType, NULL);
UpdateWarpType(NULL, NULL);
UpdateWarpButton();
}
+void StartMultiGame(void)
+{
+ StartGameMenu("Start multiplayer game", 1);
+}
+
+void WarpMenu(void)
+{
+ StartGameMenu("Level Warp", 0);
+}
+
static void DoJoinGame(void *unused1, void *unused2)
{
execute_context_t *exec;
@@ -695,7 +761,7 @@ static void DoJoinGame(void *unused1, void *unused2)
M_SaveDefaults();
- AddConfigParameters(exec);
+ PassThroughArguments(exec);
ExecuteDoom(exec);
diff --git a/src/setup/multiplayer.h b/src/setup/multiplayer.h
index 7490bc3c..afc8a2a8 100644
--- a/src/setup/multiplayer.h
+++ b/src/setup/multiplayer.h
@@ -23,6 +23,7 @@
#define SETUP_MULTIPLAYER_H
void StartMultiGame(void);
+void WarpMenu(void);
void JoinMultiGame(void);
void MultiplayerConfig(void);
diff --git a/src/setup/txt_joybinput.c b/src/setup/txt_joybinput.c
index 1e132962..cde3d2c2 100644
--- a/src/setup/txt_joybinput.c
+++ b/src/setup/txt_joybinput.c
@@ -206,6 +206,7 @@ static void TXT_JoystickInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, in
txt_widget_class_t txt_joystick_input_class =
{
+ TXT_AlwaysSelectable,
TXT_JoystickInputSizeCalc,
TXT_JoystickInputDrawer,
TXT_JoystickInputKeyPress,
diff --git a/src/setup/txt_keyinput.c b/src/setup/txt_keyinput.c
index 483c325f..08eb9d8c 100644
--- a/src/setup/txt_keyinput.c
+++ b/src/setup/txt_keyinput.c
@@ -171,6 +171,7 @@ static void TXT_KeyInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b)
txt_widget_class_t txt_key_input_class =
{
+ TXT_AlwaysSelectable,
TXT_KeyInputSizeCalc,
TXT_KeyInputDrawer,
TXT_KeyInputKeyPress,
diff --git a/src/setup/txt_mouseinput.c b/src/setup/txt_mouseinput.c
index 8b87e651..4f454c8c 100644
--- a/src/setup/txt_mouseinput.c
+++ b/src/setup/txt_mouseinput.c
@@ -164,6 +164,7 @@ static void TXT_MouseInputMousePress(TXT_UNCAST_ARG(widget), int x, int y, int b
txt_widget_class_t txt_mouse_input_class =
{
+ TXT_AlwaysSelectable,
TXT_MouseInputSizeCalc,
TXT_MouseInputDrawer,
TXT_MouseInputKeyPress,
diff --git a/src/strife/d_main.c b/src/strife/d_main.c
index 4260ca7d..95b1b643 100644
--- a/src/strife/d_main.c
+++ b/src/strife/d_main.c
@@ -126,8 +126,6 @@ int startmap;
boolean autostart;
int startloadgame;
-FILE* debugfile;
-
boolean advancedemo;
// villsa [STRIFE] workparm variable (similar to devparm?)
@@ -475,14 +473,6 @@ void D_DoomLoop (void)
if (demorecording)
G_BeginRecording ();
- if (M_CheckParm ("-debugfile"))
- {
- char filename[20];
- sprintf (filename,"debug%i.txt",consoleplayer);
- printf ("debug output to: %s\n",filename);
- debugfile = fopen (filename,"w");
- }
-
TryRunTics();
if(!showintro) // [STRIFE]
@@ -745,7 +735,7 @@ void D_QuitGame(void)
// These are from the original source: some of them are perhaps
// not used in any dehacked patches
-static char *banners[] =
+static char *banners[] =
{
// strife1.wad:
@@ -1040,9 +1030,9 @@ static void InitGameVersion(void)
// "ultimate" and "final".
//
- p = M_CheckParm("-gameversion");
+ p = M_CheckParmWithArgs("-gameversion", 1);
- if (p > 0)
+ if (p)
{
for (i=0; gameversions[i].description != NULL; ++i)
{
@@ -1431,6 +1421,21 @@ void D_DoomMain (void)
}
//!
+ // @category net
+ //
+ // Query the Internet master server for a global list of active
+ // servers.
+ //
+
+ if (M_CheckParm("-search"))
+ {
+ printf("\nSearching for servers on Internet ...\n");
+ p = NET_MasterQuery(NET_QueryPrintCallback, NULL);
+ printf("\n%i server(s) found.\n", p);
+ exit(0);
+ }
+
+ //!
// @arg <address>
// @category net
//
@@ -1438,11 +1443,12 @@ void D_DoomMain (void)
// address.
//
- p = M_CheckParm("-query");
+ p = M_CheckParmWithArgs("-query", 1);
- if (p > 0)
+ if (p)
{
NET_QueryAddress(myargv[p+1]);
+ exit(0);
}
//!
@@ -1451,8 +1457,13 @@ void D_DoomMain (void)
// Search the local LAN for running servers.
//
- if (M_CheckParm("-search"))
- NET_LANQuery();
+ if (M_CheckParm("-localsearch"))
+ {
+ printf("\nSearching for servers on local LAN ...\n");
+ p = NET_LANQuery(NET_QueryPrintCallback, NULL);
+ printf("\n%i server(s) found.\n", p);
+ exit(0);
+ }
#endif
@@ -1615,23 +1626,9 @@ void D_DoomMain (void)
// add any files specified on the command line with -file wadfile
// to the wad list
//
- // convenience hack to allow -wart e m to add a wad file
- // prepend a tilde to the filename so wadfile will be reloadable
- p = M_CheckParm ("-wart");
- if (p)
- {
- myargv[p][4] = 'p'; // big hack, change to -warp
- // Map name handling.
- // [STRIFE]: looks for f:/st/data
- p = atoi (myargv[p+1]);
- if (p<10)
- sprintf (file,"~f:/st/data/map0%i.wad", p);
- else
- sprintf (file,"~f:/st/data/map%i.wad", p);
-
- D_AddFile (file);
- }
+ // Debug:
+// W_PrintDirectory();
//!
// @arg <demo>
@@ -1641,7 +1638,7 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp.
//
- p = M_CheckParm ("-playdemo");
+ p = M_CheckParmWithArgs ("-playdemo", 1);
if (!p)
{
@@ -1653,11 +1650,11 @@ void D_DoomMain (void)
// Play back the demo named demo.lmp, determining the framerate
// of the screen.
//
- p = M_CheckParm ("-timedemo");
+ p = M_CheckParmWithArgs("-timedemo", 1);
}
- if (p && p < myargc-1)
+ if (p)
{
if (!strcasecmp(myargv[p+1] + strlen(myargv[p+1]) - 4, ".lmp"))
{
@@ -1746,9 +1743,9 @@ void D_DoomMain (void)
// 0 disables all monsters.
//
- p = M_CheckParm ("-skill");
+ p = M_CheckParmWithArgs("-skill", 1);
- if (p && p < myargc-1)
+ if (p)
{
startskill = myargv[p+1][0]-'1';
autostart = true;
@@ -1761,9 +1758,9 @@ void D_DoomMain (void)
// Start playing on episode n (1-4)
//
- p = M_CheckParm ("-episode");
+ p = M_CheckParmWithArgs("-episode", 1);
- if (p && p < myargc-1)
+ if (p)
{
startepisode = myargv[p+1][0]-'0';
startmap = 1;
@@ -1780,9 +1777,9 @@ void D_DoomMain (void)
// For multiplayer games: exit each level after n minutes.
//
- p = M_CheckParm ("-timer");
+ p = M_CheckParmWithArgs("-timer", 1);
- if (p && p < myargc-1 && deathmatch)
+ if (p)
{
timelimit = atoi(myargv[p+1]);
printf("timer: %i\n", timelimit);
@@ -1797,10 +1794,8 @@ void D_DoomMain (void)
p = M_CheckParm ("-avg");
- if (p && p < myargc-1 && deathmatch)
+ if (p)
{
- DEH_printf("Austin Virtual Gaming: Levels will end "
- "after 20 minutes\n");
timelimit = 20;
}
@@ -1812,9 +1807,9 @@ void D_DoomMain (void)
// (Doom 2)
//
- p = M_CheckParm ("-warp");
+ p = M_CheckParmWithArgs("-warp", 1);
- if (p && p < myargc-1)
+ if (p)
{
if (gamemode == commercial)
startmap = atoi (myargv[p+1]);
@@ -1858,9 +1853,9 @@ void D_DoomMain (void)
// Load the game in slot s.
//
- p = M_CheckParm ("-loadgame");
+ p = M_CheckParmWithArgs("-loadgame", 1);
- if (p && p < myargc-1)
+ if (p)
{
startloadgame = atoi(myargv[p+1]);
}
@@ -1956,17 +1951,17 @@ void D_DoomMain (void)
// Record a demo named x.lmp.
//
- p = M_CheckParm ("-record");
+ p = M_CheckParmWithArgs("-record", 1);
- if (p && p < myargc-1)
+ if (p)
{
G_RecordDemo (myargv[p+1]);
autostart = true;
}
D_IntroTick(); // [STRIFE]
- p = M_CheckParm ("-playdemo");
- if (p && p < myargc-1)
+ p = M_CheckParmWithArgs("-playdemo", 1);
+ if (p)
{
singledemo = true; // quit after one demo
G_DeferedPlayDemo (demolumpname);
@@ -1974,8 +1969,8 @@ void D_DoomMain (void)
}
D_IntroTick(); // [STRIFE]
- p = M_CheckParm ("-timedemo");
- if (p && p < myargc-1)
+ p = M_CheckParmWithArgs("-timedemo", 1);
+ if (p)
{
G_TimeDemo (demolumpname);
D_DoomLoop (); // never returns
diff --git a/src/strife/d_net.c b/src/strife/d_net.c
index 3480b0ad..7e1644e8 100644
--- a/src/strife/d_net.c
+++ b/src/strife/d_net.c
@@ -49,7 +49,6 @@
#include "net_sdl.h"
#include "net_loop.h"
-
//
// NETWORKING
//
@@ -292,6 +291,7 @@ void D_CheckNetGame (void)
if (i > 0)
{
addr = NET_FindLANServer();
+ NET_SV_RegisterWithMaster();
if (addr == NULL)
{
@@ -307,7 +307,7 @@ void D_CheckNetGame (void)
// address.
//
- i = M_CheckParm("-connect");
+ i = M_CheckParmWithArgs("-connect", 1);
if (i > 0)
{
@@ -382,12 +382,22 @@ void D_CheckNetGame (void)
// Show players here; the server might have specified a time limit
- if (timelimit > 0)
+ if (timelimit > 0 && deathmatch)
{
- DEH_printf("Levels will end after %d minute", timelimit);
- if (timelimit > 1)
- printf("s");
- printf(".\n");
+ // Gross hack to work like Vanilla:
+
+ if (timelimit == 20 && M_CheckParm("-avg"))
+ {
+ DEH_printf("Austin Virtual Gaming: Levels will end "
+ "after 20 minutes\n");
+ }
+ else
+ {
+ DEH_printf("Levels will end after %d minute", timelimit);
+ if (timelimit > 1)
+ printf("s");
+ printf(".\n");
+ }
}
}
@@ -399,9 +409,6 @@ void D_CheckNetGame (void)
//
void D_QuitNetGame (void)
{
- if (debugfile)
- fclose (debugfile);
-
#ifdef FEATURE_MULTIPLAYER
NET_SV_Shutdown();
diff --git a/src/strife/doomstat.h b/src/strife/doomstat.h
index ae513f78..8cb562b2 100644
--- a/src/strife/doomstat.h
+++ b/src/strife/doomstat.h
@@ -264,7 +264,6 @@ extern int maxammo[NUMAMMO];
// File handling stuff.
extern char * savegamedir;
extern char basedefault[1024];
-extern FILE* debugfile;
// if true, load all graphics at level load
extern boolean precache;
diff --git a/src/strife/dstrings.h b/src/strife/dstrings.h
index bdc6b2ce..d47fc1af 100644
--- a/src/strife/dstrings.h
+++ b/src/strife/dstrings.h
@@ -38,15 +38,6 @@
#define SAVEGAMENAME "doomsav"
-//
-// File locations,
-// relative to current position.
-// Path names are OS-sensitive.
-//
-#define DEVMAPS "devmaps"
-#define DEVDATA "devdata"
-
-
// QuitDOOM messages
// 8 per each game type
#define NUM_QUITMESSAGES 8
diff --git a/src/strife/g_game.c b/src/strife/g_game.c
index b8496a5b..c9d49c18 100644
--- a/src/strife/g_game.c
+++ b/src/strife/g_game.c
@@ -138,7 +138,7 @@ int gametic;
int levelstarttic; // gametic at level start
int totalkills, /*totalitems,*/ totalsecret; // for intermission
-char demoname[32];
+char *demoname;
boolean demorecording;
boolean longtics; // cph's doom 1.91 longtics hack
boolean lowres_turn; // low resolution turning for longtics
@@ -2280,8 +2280,8 @@ void G_RecordDemo (char* name)
int maxsize;
usergame = false;
- strcpy (demoname, name);
- strcat (demoname, ".lmp");
+ demoname = Z_Malloc(strlen(name) + 5, PU_STATIC, NULL);
+ sprintf(demoname, "%s.lmp", name);
maxsize = 0x20000;
//!
@@ -2292,8 +2292,8 @@ void G_RecordDemo (char* name)
// Specify the demo buffer size (KiB)
//
- i = M_CheckParm ("-maxdemo");
- if (i && i<myargc-1)
+ i = M_CheckParmWithArgs("-maxdemo", 1);
+ if (i)
maxsize = atoi(myargv[i+1])*1024;
demobuffer = Z_Malloc (maxsize,PU_STATIC,NULL);
demoend = demobuffer + maxsize;
@@ -2450,10 +2450,11 @@ void G_DoPlayDemo (void)
// Play back a demo recorded in a netgame with a single player.
//
- if (playeringame[1] || M_CheckParm("-netdemo") > 0)
- {
- netgame = true;
- netdemo = true;
+ if (playeringame[1] || M_CheckParm("-solo-net") > 0
+ || M_CheckParm("-netdemo") > 0)
+ {
+ netgame = true;
+ netdemo = true;
}
// don't spend a lot of time in loadlevel
diff --git a/src/strife/m_menu.c b/src/strife/m_menu.c
index 92564e96..4d16e3e8 100644
--- a/src/strife/m_menu.c
+++ b/src/strife/m_menu.c
@@ -1679,23 +1679,23 @@ boolean M_Responder (event_t* ev)
if (ev->type == ev_joystick && joywait < I_GetTime())
{
- if (ev->data3 == -1)
+ if (ev->data3 < 0)
{
key = key_menu_up;
joywait = I_GetTime() + 5;
}
- else if (ev->data3 == 1)
+ else if (ev->data3 > 0)
{
key = key_menu_down;
joywait = I_GetTime() + 5;
}
- if (ev->data2 == -1)
+ if (ev->data2 < 0)
{
key = key_menu_left;
joywait = I_GetTime() + 2;
}
- else if (ev->data2 == 1)
+ else if (ev->data2 > 0)
{
key = key_menu_right;
joywait = I_GetTime() + 2;
diff --git a/src/strife/p_map.c b/src/strife/p_map.c
index 6a1d526d..c557f968 100644
--- a/src/strife/p_map.c
+++ b/src/strife/p_map.c
@@ -1628,7 +1628,7 @@ static void SpechitOverrun(line_t *ld)
// Use the specified magic value when emulating spechit overruns.
//
- p = M_CheckParm("-spechit");
+ p = M_CheckParmWithArgs("-spechit", 1);
if (p > 0)
{
diff --git a/src/strife/p_spec.c b/src/strife/p_spec.c
index af070d55..e26d9144 100644
--- a/src/strife/p_spec.c
+++ b/src/strife/p_spec.c
@@ -1713,9 +1713,9 @@ static void DonutOverrun(fixed_t *s3_floorheight, short *s3_floorpic,
// system. The default (if this option is not specified) is to
// emulate the behavior when running under Windows 98.
- p = M_CheckParm("-donut");
+ p = M_CheckParmWithArgs("-donut", 2);
- if (p > 0 && p < myargc - 2)
+ if (p > 0)
{
// Dump of needed memory: (fixed_t)0000:0000 and (short)0000:0008
//
@@ -1893,10 +1893,9 @@ void P_SpawnSpecials (void)
if (W_CheckNumForName(DEH_String("texture2")) >= 0)
episode = 2;
-
// See if -TIMER was specified.
- if (timelimit > 0)
+ if (timelimit > 0 && deathmatch)
{
levelTimer = true;
levelTimeCount = timelimit * 60 * TICRATE;
diff --git a/src/strife/r_data.c b/src/strife/r_data.c
index 043df348..46138c85 100644
--- a/src/strife/r_data.c
+++ b/src/strife/r_data.c
@@ -409,6 +409,7 @@ R_GetColumn
static void GenerateTextureHashTable(void)
{
+ texture_t **rover;
int i;
int key;
@@ -425,12 +426,25 @@ static void GenerateTextureHashTable(void)
textures[i]->index = i;
- // Hook into hash table
+ // Vanilla Doom does a linear search of the texures array
+ // and stops at the first entry it finds. If there are two
+ // entries with the same name, the first one in the array
+ // wins. The new entry must therefore be added at the end
+ // of the hash chain, so that earlier entries win.
key = W_LumpNameHash(textures[i]->name) % numtextures;
- textures[i]->next = textures_hashtable[key];
- textures_hashtable[key] = textures[i];
+ rover = &textures_hashtable[key];
+
+ while (*rover != NULL)
+ {
+ rover = &(*rover)->next;
+ }
+
+ // Hook into hash table
+
+ textures[i]->next = NULL;
+ *rover = textures[i];
}
}
diff --git a/src/w_main.c b/src/w_main.c
index 92a394dd..ed285498 100644
--- a/src/w_main.c
+++ b/src/w_main.c
@@ -53,7 +53,7 @@ boolean W_ParseCommandLine(void)
// into the main IWAD. Multiple files may be specified.
//
- p = M_CheckParm("-merge");
+ p = M_CheckParmWithArgs("-merge", 1);
if (p > 0)
{
@@ -81,7 +81,7 @@ boolean W_ParseCommandLine(void)
// Simulates the behavior of NWT's -merge option. Multiple files
// may be specified.
- p = M_CheckParm("-nwtmerge");
+ p = M_CheckParmWithArgs("-nwtmerge", 1);
if (p > 0)
{
@@ -108,7 +108,7 @@ boolean W_ParseCommandLine(void)
// the main IWAD directory. Multiple files may be specified.
//
- p = M_CheckParm("-af");
+ p = M_CheckParmWithArgs("-af", 1);
if (p > 0)
{
@@ -133,7 +133,7 @@ boolean W_ParseCommandLine(void)
// into the main IWAD directory. Multiple files may be specified.
//
- p = M_CheckParm("-as");
+ p = M_CheckParmWithArgs("-as", 1);
if (p > 0)
{
@@ -156,7 +156,7 @@ boolean W_ParseCommandLine(void)
// Equivalent to "-af <files> -as <files>".
//
- p = M_CheckParm("-aa");
+ p = M_CheckParmWithArgs("-aa", 1);
if (p > 0)
{
@@ -182,7 +182,7 @@ boolean W_ParseCommandLine(void)
// Load the specified PWAD files.
//
- p = M_CheckParm ("-file");
+ p = M_CheckParmWithArgs ("-file", 1);
if (p)
{
// the parms after p are wadfile/lump names,