From 2fae58d56b8ee4b2b1b7b86071710cad863587d0 Mon Sep 17 00:00:00 2001 From: Yamagi Date: Sun, 17 Jan 2021 11:07:46 +0100 Subject: [PATCH] Implement `singleplayer` in the dedicated server. When set to `1`, both `deathmatch` and `coop` are forced to `0`. `maxclients` is forced to `1`. This makes it possible to play single player campaigns over the dedicated server. Closes #614. --- doc/040_cvarlist.md | 10 ++++++++++ src/server/sv_init.c | 40 +++++++++++++++++++++++++++++++++------- src/server/sv_main.c | 1 + 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 428af773..15aa675b 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -95,6 +95,16 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable` during gameplay and released otherwise (in menu, videos, console or if game is paused). +* **singleplayer**: Only available in the dedicated server. Vanilla + Quake II enforced that either `coop` or `deathmatch` is set to `1` + when running the dedicated server. That made it impossible to play + single player campaigns over the dedicated server. When set to `1`, + both `coop` and `deathmatch` are forced to `0` and `maxclients` is + forced to `1`. This can be used to run a dedicated server with an old + single player mod, where the source code isn't available, inside a + Windows 98 or XP VM and connect over network from an non Windows + system. + * **coop_pickup_weapons**: In coop a weapon can be picked up only once. For example, if the player already has the shotgun they cannot pickup a second shotgun found at a later time, thus not getting the ammo that diff --git a/src/server/sv_init.c b/src/server/sv_init.c index b5e493a0..449421c8 100644 --- a/src/server/sv_init.c +++ b/src/server/sv_init.c @@ -333,6 +333,12 @@ SV_InitGame(void) svs.initialized = true; + if (Cvar_VariableValue("singleplayer")) + { + Cvar_FullSet("coop", "0", CVAR_SERVERINFO | CVAR_LATCH); + Cvar_FullSet("deathmatch", "0", CVAR_SERVERINFO | CVAR_LATCH); + } + if (Cvar_VariableValue("coop") && Cvar_VariableValue("deathmatch")) { Com_Printf("Deathmatch and Coop both set, disabling Coop\n"); @@ -343,9 +349,12 @@ SV_InitGame(void) so unless they explicity set coop, force it to deathmatch */ if (dedicated->value) { - if (!Cvar_VariableValue("coop")) + if (!Cvar_VariableValue("singleplayer")) { - Cvar_FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH); + if (!Cvar_VariableValue("coop")) + { + Cvar_FullSet("deathmatch", "1", CVAR_SERVERINFO | CVAR_LATCH); + } } } @@ -358,9 +367,10 @@ SV_InitGame(void) } else if (maxclients->value > MAX_CLIENTS) { - Cvar_FullSet("maxclients", va("%i", - MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH); + Cvar_FullSet("maxclients", va("%i", MAX_CLIENTS), CVAR_SERVERINFO | CVAR_LATCH); } + + Cvar_FullSet("singleplayer", "0", 0); } else if (Cvar_VariableValue("coop")) { @@ -368,20 +378,36 @@ SV_InitGame(void) { Cvar_FullSet("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH); } + + Cvar_FullSet("singleplayer", "0", 0); } else /* non-deathmatch, non-coop is one player */ { Cvar_FullSet("maxclients", "1", CVAR_SERVERINFO | CVAR_LATCH); + Cvar_FullSet("singleplayer", "1", 0); } svs.spawncount = randk(); svs.clients = Z_Malloc(sizeof(client_t) * maxclients->value); svs.num_client_entities = maxclients->value * UPDATE_BACKUP * 64; - svs.client_entities = - Z_Malloc( sizeof(entity_state_t) * svs.num_client_entities); + svs.client_entities = Z_Malloc( sizeof(entity_state_t) * svs.num_client_entities); /* init network stuff */ - NET_Config((maxclients->value > 1)); + if (dedicated->value) + { + if (Cvar_VariableValue("singleplayer")) + { + NET_Config(true); + } + else + { + NET_Config((maxclients->value > 1)); + } + } + else + { + NET_Config((maxclients->value > 1)); + } /* heartbeats will always be sent to the id master */ svs.last_heartbeat = -99999; /* send immediately */ diff --git a/src/server/sv_main.c b/src/server/sv_main.c index af2b655f..8828cd34 100644 --- a/src/server/sv_main.c +++ b/src/server/sv_main.c @@ -584,6 +584,7 @@ SV_Init(void) rcon_password = Cvar_Get("rcon_password", "", 0); Cvar_Get("skill", "1", 0); + Cvar_Get("singleplayer", "0", 0); Cvar_Get("deathmatch", "0", CVAR_LATCH); Cvar_Get("coop", "0", CVAR_LATCH); Cvar_Get("dmflags", va("%i", DF_INSTANT_ITEMS), CVAR_SERVERINFO);