quakeforge/nq/source/host_cmd.c
Bill Currie d69355f521 [renderer] Support multiple entity queues
While there's currently only the one still, this will allow the entities
to be multiply queued for multi-pass rendering (eg, shadows). As the
avoidance of putting an entity in the same queue more than once relies
on the entity id, all entities now come from the scene (which is stored
in cl_world in the client code for nq and qw), thus the extensive
changes in the clients.
2022-03-05 02:05:39 +09:00

1503 lines
35 KiB
C

/*
host_cmd.c
@description@
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include "QF/cbuf.h"
#include "QF/cmd.h"
#include "QF/console.h"
#include "QF/cvar.h"
#include "QF/dstring.h"
#include "QF/idparse.h"
#include "QF/keys.h"
#include "QF/model.h"
#include "QF/msg.h"
#include "QF/plist.h"
#include "QF/screen.h"
#include "QF/script.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "client/world.h"
#include "compat.h"
#include "world.h"
#include "nq/include/client.h"
#include "nq/include/host.h"
#include "nq/include/server.h"
#include "nq/include/sv_progs.h"
int current_skill;
void
Host_Quit_f (void)
{
if (!con_module)
Sys_Printf ("I hope you wanted to quit\n");
CL_Disconnect ();
Host_ShutdownServer (false);
Sys_Quit ();
}
static void
Host_Status_f (void)
{
client_t *client;
int seconds;
int minutes;
int hours = 0;
unsigned j;
__attribute__((format(PRINTF, 1, 2))) void (*print) (const char *fmt, ...);
if (cmd_source == src_command) {
if (!sv.active) {
CL_Cmd_ForwardToServer ();
return;
}
print = Sys_Printf;
} else
print = SV_ClientPrintf;
print ("host: %s\n", Cvar_VariableString ("hostname"));
print ("version: %4.2s\n", PACKAGE_VERSION);
if (tcpipAvailable)
print ("tcp/ip: %s\n", my_tcpip_address);
print ("map: %s\n", sv.name);
print ("players: %i active (%i max)\n\n", net_activeconnections,
svs.maxclients);
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) {
if (!client->active)
continue;
seconds = (int) (net_time - client->netconnection->connecttime);
minutes = seconds / 60;
if (minutes) {
seconds -= (minutes * 60);
hours = minutes / 60;
if (hours)
minutes -= (hours * 60);
} else
hours = 0;
print ("#%-2u %-16.16s %3i %2i:%02i:%02i\n", j + 1, client->name,
(int) SVfloat (client->edict, frags), hours, minutes, seconds);
print (" %s\n", client->netconnection->address);
}
}
/*
Host_God_f
Sets client to godmode
*/
static void
Host_God_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (*sv_globals.deathmatch && !host_client->privileged)
return;
SVfloat (sv_player, flags) = (int) SVfloat (sv_player, flags) ^ FL_GODMODE;
if (!((int) SVfloat (sv_player, flags) & FL_GODMODE))
SV_ClientPrintf ("godmode OFF\n");
else
SV_ClientPrintf ("godmode ON\n");
}
static void
Host_Notarget_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (*sv_globals.deathmatch && !host_client->privileged)
return;
SVfloat (sv_player, flags) = (int) SVfloat (sv_player, flags) ^
FL_NOTARGET;
if (!((int) SVfloat (sv_player, flags) & FL_NOTARGET))
SV_ClientPrintf ("notarget OFF\n");
else
SV_ClientPrintf ("notarget ON\n");
}
qboolean noclip_anglehack;
static void
Host_Noclip_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (*sv_globals.deathmatch && !host_client->privileged)
return;
if (SVfloat (sv_player, movetype) != MOVETYPE_NOCLIP) {
noclip_anglehack = true;
SVfloat (sv_player, movetype) = MOVETYPE_NOCLIP;
SV_ClientPrintf ("noclip ON\n");
} else {
noclip_anglehack = false;
SVfloat (sv_player, movetype) = MOVETYPE_WALK;
SV_ClientPrintf ("noclip OFF\n");
}
}
/*
Host_Fly_f
Sets client to flymode
*/
static void
Host_Fly_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (*sv_globals.deathmatch && !host_client->privileged)
return;
if (SVfloat (sv_player, movetype) != MOVETYPE_FLY) {
SVfloat (sv_player, movetype) = MOVETYPE_FLY;
SV_ClientPrintf ("flymode ON\n");
} else {
SVfloat (sv_player, movetype) = MOVETYPE_WALK;
SV_ClientPrintf ("flymode OFF\n");
}
}
static void
Host_Ping_f (void)
{
unsigned i, j;
float total;
client_t *client;
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
SV_ClientPrintf ("Client ping times:\n");
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) {
if (!client->active)
continue;
total = 0;
for (j = 0; j < NUM_PING_TIMES; j++)
total += client->ping_times[j];
total /= NUM_PING_TIMES;
SV_ClientPrintf ("%4i %s\n", (int) (total * 1000), client->name);
}
}
// SERVER TRANSITIONS =========================================================
static const char *
nice_time (float time)
{
int t = time + 0.5;
if (t < 60) {
return va (0, "%ds", t);
} else if (t < 3600) {
return va (0, "%dm%02ds", t / 60, t % 60);
} else if (t < 86400) {
return va (0, "%dh%02dm%02ds", t / 3600, (t / 60) % 60, t % 60);
} else {
return va (0, "%dd%02dh%02dm%02ds",
t / 86400, (t / 3600) % 24, (t / 60) % 60, t % 60);
}
}
/*
Host_Map_f
handle a
map <servername>
command from the console. Active clients are kicked off.
*/
static void
Host_Map_f (void)
{
char name[MAX_QPATH];
const char *expanded;
QFile *f;
if (cmd_source != src_command)
return;
if (Cmd_Argc () > 2) {
Sys_Printf ("map <levelname> : continue game on a new level\n");
return;
}
if (Cmd_Argc () == 1) {
Sys_Printf ("map is %s \"%s\" (%s)\n", sv.name,
PR_GetString (&sv_pr_state, SVstring (sv.edicts, message)),
nice_time (sv.time));
return;
}
// check to make sure the level exists
expanded = va (0, "maps/%s.bsp", Cmd_Argv (1));
f = QFS_FOpenFile (expanded);
if (!f) {
Sys_Printf ("Can't find %s\n", expanded);
return;
}
Qclose (f);
cls.demonum = -1; // stop demo loop in case this fails
CL_Disconnect ();
Host_ShutdownServer (false);
cl.loading = true;
CL_UpdateScreen (cl.time);
svs.serverflags = 0; // haven't completed an episode yet
strcpy (name, Cmd_Argv (1));
SV_SpawnServer (name);
if (!sv.active)
return;
if (cls.state != ca_dedicated) {
Cmd_ExecuteString ("connect local", src_command);
}
}
/*
Host_Changelevel_f
Goes to a new map, taking all clients along
*/
static void
Host_Changelevel_f (void)
{
char level[MAX_QPATH];
if (Cmd_Argc () != 2) {
Sys_Printf ("changelevel <levelname> : continue game on a new "
"level\n");
return;
}
if (!sv.active || cls.demoplayback) {
Sys_Printf ("Only the server may changelevel\n");
return;
}
SV_SaveSpawnparms ();
strcpy (level, Cmd_Argv (1));
SV_SpawnServer (level);
}
/*
Host_Restart_f
Restarts the current server for a dead player
*/
static void
Host_Restart_f (void)
{
char mapname[MAX_QPATH];
if (cls.demoplayback || !sv.active)
return;
if (cmd_source != src_command)
return;
strcpy (mapname, sv.name); // must copy out, because it gets
// cleared in sv_spawnserver
SV_SpawnServer (mapname);
}
/*
Host_Reconnect_f
This command causes the client to wait for the signon messages again.
This is sent just before a server changes levels
*/
static void
Host_Reconnect_f (void)
{
CL_SetState (ca_connected);
}
/*
Host_Connect_f
User command to connect to server
*/
static void
Host_Connect_f (void)
{
char name[MAX_QPATH];
cls.demonum = -1; // stop demo loop in case this fails
if (cls.demoplayback) {
CL_StopPlayback ();
CL_Disconnect ();
}
strcpy (name, Cmd_Argv (1));
CL_EstablishConnection (name);
Host_Reconnect_f ();
}
// LOAD / SAVE GAME ===========================================================
#define SAVEGAME_VERSION 5
static plitem_t *
spawn_parms_array (void)
{
plitem_t *parms = PL_NewArray ();
int i;
const char *parm;
for (i = 0; i < NUM_SPAWN_PARMS; i++) {
parm = va (0, "%.9g", svs.clients->spawn_parms[i]);
PL_A_AddObject (parms, PL_NewString (parm));
}
return parms;
}
static plitem_t *
lightstyles_array (void)
{
plitem_t *styles = PL_NewArray ();
const char *st;
int i;
for (i = 0; i < MAX_LIGHTSTYLES; i++) {
st = "m";
if (sv.lightstyles[i])
st = sv.lightstyles[i];
PL_A_AddObject (styles, PL_NewString (st));
}
return styles;
}
static plitem_t *
entities_array (void)
{
plitem_t *entities = PL_NewArray ();
pr_uint_t i;
for (i = 0; i < sv.num_edicts; i++) {
PL_A_AddObject (entities,
ED_EntityDict (&sv_pr_state,
EDICT_NUM (&sv_pr_state, i)));
}
return entities;
}
static plitem_t *
game_dict (void)
{
plitem_t *game = PL_NewDictionary (0);
PL_D_AddObject (game, "comment",
PL_NewString (va (0, "%-21s kills:%3i/%3i", cl.levelname,
cl.stats[STAT_MONSTERS],
cl.stats[STAT_TOTALMONSTERS])));
PL_D_AddObject (game, "spawn_parms", spawn_parms_array ());
PL_D_AddObject (game, "current_skill",
PL_NewString (va (0, "%d", current_skill)));
PL_D_AddObject (game, "name", PL_NewString (sv.name));
// sv.time is a double, so it gets 17 digits
PL_D_AddObject (game, "time", PL_NewString (va (0, "%.17g", sv.time)));
PL_D_AddObject (game, "lightstyles", lightstyles_array ());
PL_D_AddObject (game, "globals", ED_GlobalsDict (&sv_pr_state));
PL_D_AddObject (game, "entities", entities_array ());
return game;
}
static plitem_t *
convert_to_game_dict (script_t *script)
{
plitem_t *game = PL_NewDictionary (0);
plitem_t *item;
plitem_t *list;
int skill;
int i;
// savegame comment (ignored)
Script_GetToken (script, 1);
PL_D_AddObject (game, "comment", PL_NewString (script->token->str));
// spawn_parms
item = PL_NewArray ();
for (i = 0; i < NUM_SPAWN_PARMS; i++) {
Script_GetToken (script, 1);
PL_A_AddObject (item, PL_NewString (script->token->str));
}
PL_D_AddObject (game, "spawn_parms", item);
// this silliness is so we can load 1.06 save files, which have float skill
// values
Script_GetToken (script, 1);
skill = (int) (atof (script->token->str) + 0.1);
PL_D_AddObject (game, "current_skill", PL_NewString (va (0, "%d", skill)));
Script_GetToken (script, 1);
PL_D_AddObject (game, "name", PL_NewString (script->token->str));
Script_GetToken (script, 1);
PL_D_AddObject (game, "time", PL_NewString (script->token->str));
// load the light styles
item = PL_NewArray ();
for (i = 0; i < MAX_LIGHTSTYLES; i++) {
Script_GetToken (script, 1);
PL_A_AddObject (item, PL_NewString (script->token->str));
//char *s;
//s = Hunk_Alloc (0, strlen (script->token->str) + 1);
//strcpy (s, script->token->str);
//sv.lightstyles[i] = s;
}
PL_D_AddObject (game, "lightstyles", item);
// load the edicts out of the savegame file
list = ED_ConvertToPlist (script, 0, 0);
item = PL_RemoveObjectAtIndex (list, 0);
PL_D_AddObject (game, "globals", item);
PL_D_AddObject (game, "entities", list);
return game;
}
#define MAX_QUICK 5
static void
Host_Savegame_f (void)
{
dstring_t *name;
const char *save_name;
char *save_text;
QFile *f;
char *bup1, *bup2 = 0;
if (cmd_source != src_command)
return;
if (!sv.active) {
Sys_Printf ("Not playing a local game.\n");
return;
}
if (cl.intermission) {
Sys_Printf ("Can't save in intermission.\n");
return;
}
if (svs.maxclients != 1) {
Sys_Printf ("Can't save multiplayer games.\n");
return;
}
if (Cmd_Argc () != 2) {
Sys_Printf ("save <savename> : save a game\n");
return;
}
if (strstr (Cmd_Argv (1), "..")) {
Sys_Printf ("Relative pathnames are not allowed.\n");
return;
}
for (unsigned i = 0; i < svs.maxclients; i++) {
if (svs.clients[i].active && (SVfloat (svs.clients[i].edict, health)
<= 0)) {
Sys_Printf ("Can't savegame with a dead player\n");
return;
}
}
save_name = Cmd_Argv (1);
name = dstring_newstr ();
if (strcmp (save_name, "quick") == 0) {
bup2 = nva ("%s/%s%d.sav", qfs_gamedir->dir.def, save_name, MAX_QUICK);
QFS_Remove (bup2);
for (int i = MAX_QUICK - 1; i > 0; i--) {
bup1 = nva ("%s/%s%d.sav", qfs_gamedir->dir.def, save_name, i);
QFS_Rename (bup1, bup2);
free (bup2);
bup2 = bup1;
}
}
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, save_name);
QFS_DefaultExtension (name, ".sav");
if (bup2) {
QFS_Rename (name->str, bup2);
free (bup2);
}
Sys_Printf ("Saving game to %s...\n", name->str);
f = QFS_WOpen (name->str, 0);
dstring_delete (name);
if (!f) {
Sys_Printf ("ERROR: couldn't open.\n");
return;
}
save_text = PL_WritePropertyList (game_dict ());
Qprintf (f, "%s\n%s", PACKAGE_NAME, save_text);
free (save_text);
Qclose (f);
Sys_Printf ("done.\n");
}
static void
Host_Loadgame_f (void)
{
dstring_t *name = 0;
QFile *f;
char *mapname = 0;
script_t *script = 0;
plitem_t *game = 0;
plitem_t *list;
plitem_t *item;
char *script_data = 0;
int i;
int entnum;
int count;
int version;
float spawn_parms[NUM_SPAWN_PARMS];
if (cmd_source != src_command)
goto end;
if (Cmd_Argc () != 2) {
Sys_Printf ("load <savename> : load a game\n");
goto end;
}
cls.demonum = -1; // stop demo loop in case this fails
name = dstring_newstr ();
dsprintf (name, "%s/%s", qfs_gamedir->dir.def, Cmd_Argv (1));
QFS_DefaultExtension (name, ".sav");
cl.loading = true;
CL_UpdateScreen (cl.time);
Sys_Printf ("Loading game from %s...\n", name->str);
f = QFS_Open (name->str, "rz");
if (!f) {
Sys_Printf ("ERROR: couldn't open.\n");
goto end;
}
script_data = malloc (Qfilesize (f) + 1);
i = Qread (f, script_data, Qfilesize (f));
script_data[i] = 0;
Qclose (f);
script = Script_New ();
script->single = ""; // disable {}()': lexing
Script_Start (script, name->str, script_data);
Script_GetToken (script, 1);
if (strequal (script->token->str, PACKAGE_NAME)) {
if (!Script_TokenAvailable (script, 1)) {
Sys_Printf ("Unexpected EOF reading %s\n", name->str);
goto end;
}
game = PL_GetPropertyList (script->p, 0);
} else {
sscanf (script->token->str, "%i", &version);
if (version != SAVEGAME_VERSION) {
Sys_Printf ("Savegame is version %i, not %i\n", version,
SAVEGAME_VERSION);
goto end;
}
game = convert_to_game_dict (script);
}
memset (spawn_parms, 0, sizeof (spawn_parms));
item = PL_ObjectForKey (game, "spawn_parms");
for (i = 0; i < NUM_SPAWN_PARMS; i++) {
if (i >= PL_A_NumObjects (item))
break;
spawn_parms[i] = atof (PL_String (PL_ObjectAtIndex (item, i)));
}
current_skill = atoi (PL_String (PL_ObjectForKey (game, "current_skill")));
Cvar_SetValue (skill, current_skill);
mapname = strdup (PL_String (PL_ObjectForKey (game, "name")));
CL_Disconnect_f ();
SV_SpawnServer (mapname);
if (!sv.active) {
Sys_Printf ("Couldn't load map %s\n", mapname);
goto end;
}
sv.paused = true; // pause until all clients connect
sv.loadgame = true;
list = PL_ObjectForKey (game, "lightstyles");
for (i = 0; i < MAX_LIGHTSTYLES; i++) {
const char *style;
char *str;
if (i >= PL_A_NumObjects (list))
break;
item = PL_ObjectAtIndex (list, i);
style = PL_String (item);
sv.lightstyles[i] = str = Hunk_Alloc (0, strlen (style) + 1);
strcpy (str, style);
}
ED_InitGlobals (&sv_pr_state, PL_ObjectForKey (game, "globals"));
list = PL_ObjectForKey (game, "entities");
count = PL_A_NumObjects (list);
if (count > sv.max_edicts)
Host_Error ("too many entities in saved game. adjust max_edicts\n");
for (entnum = 0; entnum < count; entnum++) {
plitem_t *entity = PL_ObjectAtIndex (list, entnum);
edict_t *ent = EDICT_NUM (&sv_pr_state, entnum);
memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4);
ent->free = false;
ED_InitEntity (&sv_pr_state, entity, ent);
// link it into the bsp tree
if (!ent->free)
SV_LinkEdict (ent, false);
}
sv.num_edicts = entnum;
sv.time = atof (PL_String (PL_ObjectForKey (game, "time")));
for (i = 0; i < NUM_SPAWN_PARMS; i++)
svs.clients->spawn_parms[i] = spawn_parms[i];
if (cls.state != ca_dedicated) {
CL_EstablishConnection ("local");
Host_Reconnect_f ();
}
end:
if (game)
PL_Free (game);
if (mapname)
free (mapname);
if (script)
Script_Delete (script);
if (script_data)
free (script_data);
if (name)
dstring_delete (name);
}
//============================================================================
static void
Host_Name_f (void)
{
const char *newName;
if (Cmd_Argc () == 1) {
Sys_Printf ("\"name\" is \"%s\"\n", cl_name->string);
return;
}
if (Cmd_Argc () == 2)
newName = Cmd_Argv (1);
else
newName = Cmd_Args (1);
if (cmd_source == src_command) {
if (strcmp (cl_name->string, newName) == 0)
return;
Cvar_Set (cl_name, va (0, "%.15s", newName));
if (cls.state >= ca_connected)
CL_Cmd_ForwardToServer ();
return;
}
if (host_client->name[0] && strcmp (host_client->name, "unconnected"))
if (strcmp (host_client->name, newName) != 0)
Sys_Printf ("%s renamed to %s\n", host_client->name, newName);
strcpy (host_client->name, newName);
SVstring (host_client->edict, netname) =
PR_SetString (&sv_pr_state, host_client->name);
// send notification to all clients
MSG_WriteByte (&sv.reliable_datagram, svc_updatename);
MSG_WriteByte (&sv.reliable_datagram, host_client - svs.clients);
MSG_WriteString (&sv.reliable_datagram, host_client->name);
}
static void
Host_Version_f (void)
{
Sys_Printf ("Version %s\n", PACKAGE_VERSION);
Sys_Printf ("Exe: " __TIME__ " " __DATE__ "\n");
}
static void
Host_Say (qboolean teamonly)
{
client_t *client;
client_t *save;
unsigned j;
char *p;
char text[64];
qboolean fromServer = false;
if (cmd_source == src_command) {
if (cls.state == ca_dedicated) {
fromServer = true;
teamonly = false;
} else {
CL_Cmd_ForwardToServer ();
return;
}
}
if (Cmd_Argc () < 2)
return;
save = host_client;
p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1);
strcpy (p, Cmd_Args (1));
// remove quotes if present
if (*p == '"') {
p++;
p[strlen (p) - 1] = 0;
}
// turn on color set 1
if (!fromServer)
snprintf (text, sizeof (text), "%c%s: ", 1, save->name);
else
snprintf (text, sizeof (text), "%c<%s> ", 1, hostname->string);
j = sizeof (text) - 2 - strlen (text); // -2 for /n and null terminator
if (strlen (p) > j)
p[j] = 0;
strcat (text, p);
strcat (text, "\n");
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) {
if (!client || !client->active || !client->spawned)
continue;
if (teamplay->int_val && teamonly && SVfloat (client->edict, team) !=
SVfloat (save->edict, team))
continue;
host_client = client;
SV_ClientPrintf ("%s", text);
}
host_client = save;
Sys_Printf ("%s", &text[1]);
}
static void
Host_Say_f (void)
{
Host_Say (false);
}
static void
Host_Say_Team_f (void)
{
Host_Say (true);
}
static void
Host_Tell_f (void)
{
client_t *client;
client_t *save;
unsigned j;
char *p;
char text[64];
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (Cmd_Argc () < 3)
return;
strcpy (text, host_client->name);
strcat (text, ": ");
p = Hunk_TempAlloc (0, strlen (Cmd_Args (1)) + 1);
strcpy (p, Cmd_Args (1));
// remove quotes if present
if (*p == '"') {
p++;
p[strlen (p) - 1] = 0;
}
// check length & truncate if necessary
j = sizeof (text) - 2 - strlen (text); // -2 for /n and null terminator
if (strlen (p) > j)
p[j] = 0;
strcat (text, p);
strcat (text, "\n");
save = host_client;
for (j = 0, client = svs.clients; j < svs.maxclients; j++, client++) {
if (!client->active || !client->spawned)
continue;
if (strcasecmp (client->name, Cmd_Argv (1)))
continue;
host_client = client;
SV_ClientPrintf ("%s", text);
break;
}
host_client = save;
}
static void
Host_Kill_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (SVfloat (sv_player, health) <= 0) {
SV_ClientPrintf ("Can't suicide -- already dead!\n");
return;
}
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientKill);
}
static void
Host_Pause_f (void)
{
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (!pausable->int_val)
SV_ClientPrintf ("Pause not allowed.\n");
else {
sv.paused ^= 1;
if (sv.paused) {
SV_BroadcastPrintf ("%s paused the game\n",
PR_GetString (&sv_pr_state,
SVstring (sv_player, netname)));
} else {
SV_BroadcastPrintf ("%s unpaused the game\n",
PR_GetString (&sv_pr_state,
SVstring (sv_player, netname)));
}
// send notification to all clients
MSG_WriteByte (&sv.reliable_datagram, svc_setpause);
MSG_WriteByte (&sv.reliable_datagram, sv.paused);
}
}
//===========================================================================
static void
Host_PreSpawn_f (void)
{
if (cmd_source == src_command) {
Sys_Printf ("prespawn is not valid from the console\n");
return;
}
if (host_client->spawned) {
Sys_Printf ("prespawn not valid -- already spawned\n");
return;
}
SZ_Write (&host_client->message, sv.signon.data, sv.signon.cursize);
MSG_WriteByte (&host_client->message, svc_signonnum);
MSG_WriteByte (&host_client->message, 2);
host_client->sendsignon = true;
}
static void
Host_Spawn_f (void)
{
unsigned i;
client_t *client;
edict_t *ent;
float *sendangles;
if (cmd_source == src_command) {
Sys_Printf ("spawn is not valid from the console\n");
return;
}
if (host_client->spawned) {
Sys_Printf ("Spawn not valid -- already spawned\n");
return;
}
// run the entrance script
if (sv.loadgame) { // loaded games are fully inited already
// if this is the last client to be connected, unpause
sv.paused = false;
} else {
// set up the edict
ent = host_client->edict;
memset (&E_fld (ent, 0), 0, sv_pr_state.progs->entityfields * 4);
SVfloat (ent, colormap) = NUM_FOR_EDICT (&sv_pr_state, ent);
SVfloat (ent, team) = (host_client->colors & 15) + 1;
SVstring (ent, netname) = PR_SetString (&sv_pr_state,
host_client->name);
// copy spawn parms out of the client_t
for (i = 0; i < NUM_SPAWN_PARMS; i++)
sv_globals.parms[i] = host_client->spawn_parms[i];
// call the spawn function
*sv_globals.time = sv.time;
*sv_globals.self = EDICT_TO_PROG (&sv_pr_state, sv_player);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.ClientConnect);
if ((Sys_DoubleTime () - host_client->netconnection->connecttime) <=
sv.time) Sys_Printf ("%s entered the game\n", host_client->name);
PR_ExecuteProgram (&sv_pr_state, sv_funcs.PutClientInServer);
}
// send all current names, colors, and frag counts
SZ_Clear (&host_client->message);
// send time of update
MSG_WriteByte (&host_client->message, svc_time);
MSG_WriteFloat (&host_client->message, sv.time);
for (i = 0, client = svs.clients; i < svs.maxclients; i++, client++) {
MSG_WriteByte (&host_client->message, svc_updatename);
MSG_WriteByte (&host_client->message, i);
MSG_WriteString (&host_client->message, client->name);
MSG_WriteByte (&host_client->message, svc_updatefrags);
MSG_WriteByte (&host_client->message, i);
MSG_WriteShort (&host_client->message, client->old_frags);
MSG_WriteByte (&host_client->message, svc_updatecolors);
MSG_WriteByte (&host_client->message, i);
MSG_WriteByte (&host_client->message, client->colors);
}
// send all current light styles
for (i = 0; i < MAX_LIGHTSTYLES; i++) {
MSG_WriteByte (&host_client->message, svc_lightstyle);
MSG_WriteByte (&host_client->message, (char) i);
MSG_WriteString (&host_client->message, sv.lightstyles[i]);
}
// send some stats
MSG_WriteByte (&host_client->message, svc_updatestat);
MSG_WriteByte (&host_client->message, STAT_TOTALSECRETS);
MSG_WriteLong (&host_client->message,
*sv_globals.total_secrets);
MSG_WriteByte (&host_client->message, svc_updatestat);
MSG_WriteByte (&host_client->message, STAT_TOTALMONSTERS);
MSG_WriteLong (&host_client->message,
*sv_globals.total_monsters);
MSG_WriteByte (&host_client->message, svc_updatestat);
MSG_WriteByte (&host_client->message, STAT_SECRETS);
MSG_WriteLong (&host_client->message,
*sv_globals.found_secrets);
MSG_WriteByte (&host_client->message, svc_updatestat);
MSG_WriteByte (&host_client->message, STAT_MONSTERS);
MSG_WriteLong (&host_client->message,
*sv_globals.killed_monsters);
// send a fixangle
// Never send a roll angle, because savegames can catch the server
// in a state where it is expecting the client to correct the angle
// and it won't happen if the game was just loaded, so you wind up
// with a permanent head tilt
ent = EDICT_NUM (&sv_pr_state, 1 + (host_client - svs.clients));
MSG_WriteByte (&host_client->message, svc_setangle);
sendangles = sv.loadgame ? SVvector (ent, v_angle): SVvector (ent, angles);
MSG_WriteAngle (&host_client->message, sendangles[0]);
MSG_WriteAngle (&host_client->message, sendangles[1]);
MSG_WriteAngle (&host_client->message, 0);
SV_WriteClientdataToMessage (sv_player, &host_client->message);
MSG_WriteByte (&host_client->message, svc_signonnum);
MSG_WriteByte (&host_client->message, 3);
host_client->sendsignon = true;
}
static void
Host_Begin_f (void)
{
if (cmd_source == src_command) {
Sys_Printf ("begin is not valid from the console\n");
return;
}
host_client->spawned = true;
}
//===========================================================================
/*
Host_Kick_f
Kicks a user off of the server
*/
static void
Host_Kick_f (void)
{
const char *who;
const char *message = NULL;
client_t *save;
unsigned i;
qboolean byNumber = false;
if (cmd_source == src_command) {
if (!sv.active) {
CL_Cmd_ForwardToServer ();
return;
}
}
else if (*sv_globals.deathmatch
&& !host_client->privileged) return;
save = host_client;
if (Cmd_Argc () > 2 && strcmp (Cmd_Argv (1), "#") == 0) {
i = atof (Cmd_Argv (2)) - 1;
if (i >= svs.maxclients)
return;
if (!svs.clients[i].active)
return;
host_client = &svs.clients[i];
byNumber = true;
} else {
for (i = 0, host_client = svs.clients; i < svs.maxclients;
i++, host_client++) {
if (!host_client->active)
continue;
if (strcasecmp (host_client->name, Cmd_Argv (1)) == 0)
break;
}
}
if (i < svs.maxclients) {
if (cmd_source == src_command)
if (cls.state == ca_dedicated)
who = "Console";
else
who = cl_name->string;
else
who = save->name;
// can't kick yourself!
if (host_client == save)
return;
if (Cmd_Argc () > 2) {
message = COM_Parse (Cmd_Args (1));
if (byNumber) {
message++; // skip the #
while (*message == ' ') // skip white space
message++;
message += strlen (Cmd_Argv (2)); // skip the number
}
while (*message && *message == ' ')
message++;
}
if (message)
SV_ClientPrintf ("Kicked by %s: %s\n", who, message);
else
SV_ClientPrintf ("Kicked by %s\n", who);
SV_DropClient (false);
}
host_client = save;
}
// DEBUGGING TOOLS ============================================================
// FIXME: This cheat should be re-implemented OUTSIDE the engine!
#if 0
void
Host_Give_f (void)
{
char *t;
int v;
if (cmd_source == src_command) {
CL_Cmd_ForwardToServer ();
return;
}
if (*sv_globals.deathmatch && !host_client->privileged)
return;
t = Cmd_Argv (1);
v = atoi (Cmd_Argv (2));
switch (t[0]) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// MED 01/04/97 added hipnotic give stuff
if (hipnotic) {
if (t[0] == '6') {
if (t[1] == 'a')
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items)
| HIT_PROXIMITY_GUN;
else
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items)
| IT_GRENADE_LAUNCHER;
} else if (t[0] == '9')
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items) | HIT_LASER_CANNON;
else if (t[0] == '0')
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items) | HIT_MJOLNIR;
else if (t[0] >= '2')
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items)
| (IT_SHOTGUN << (t[0] - '2'));
} else {
if (t[0] >= '2')
SVfloat (sv_player, items) =
(int) SVfloat (sv_player, items)
| (IT_SHOTGUN << (t[0] - '2'));
}
break;
case 's':
if (rogue) {
SVfloat (sv_player, ammo_shells1) = v;
}
SVfloat (sv_player, ammo_shells) = v;
break;
case 'n':
if (rogue) {
SVfloat (sv_player, ammo_nails1) = v;
if (SVfloat (sv_player, weapon) <= IT_LIGHTNING)
SVfloat (sv_player, ammo_nails) = v;
} else {
SVfloat (sv_player, ammo_nails) = v;
}
break;
case 'l':
if (rogue) {
SVfloat (sv_player, ammo_lava_nails) = v;
if (SVfloat (sv_player, weapon) > IT_LIGHTNING)
SVfloat (sv_player, ammo_nails) = v;
}
break;
case 'r':
if (rogue) {
SVfloat (sv_player, ammo_rockets1) = v;
if (SVfloat (sv_player, weapon) <= IT_LIGHTNING)
SVfloat (sv_player, ammo_rockets) = v;
} else {
SVfloat (sv_player, ammo_rockets) = v;
}
break;
case 'm':
if (rogue) {
SVfloat (sv_player, ammo_multi_rockets) = 0;
if (SVfloat (sv_player, weapon) > IT_LIGHTNING)
SVfloat (sv_player, ammo_rockets) = v;
}
break;
case 'h':
SVfloat (sv_player, health) = v;
break;
case 'c':
if (rogue) {
SVfloat (sv_player, ammo_cells1) = v;
if (SVfloat (sv_player, weapon) <= IT_LIGHTNING)
SVfloat (sv_player, ammo_cells) = v;
} else {
SVfloat (sv_player, ammo_cells) = v;
}
break;
case 'p':
if (rogue) {
SVfloat (sv_player, ammo_plasma) = v;
if (SVfloat (sv_player, weapon) > IT_LIGHTNING)
SVfloat (sv_player, ammo_cells) = v;
}
break;
}
}
#endif
static edict_t *
FindViewthing (void)
{
pr_uint_t i;
edict_t *e;
for (i = 0; i < sv.num_edicts; i++) {
e = EDICT_NUM (&sv_pr_state, i);
if (!strcmp (PR_GetString (&sv_pr_state, SVstring (e, classname)),
"viewthing"))
return e;
}
Sys_Printf ("No viewthing on map\n");
return NULL;
}
static void
Host_Viewmodel_f (void)
{
edict_t *e;
model_t *m;
e = FindViewthing ();
if (!e)
return;
m = Mod_ForName (Cmd_Argv (1), false);
if (!m) {
Sys_Printf ("Can't load %s\n", Cmd_Argv (1));
return;
}
SVfloat (e, frame) = 0;
cl_world.models.a[(int) SVfloat (e, modelindex)] = m;
}
static void
Host_Viewframe_f (void)
{
edict_t *e;
int f;
model_t *m;
e = FindViewthing ();
if (!e)
return;
m = cl_world.models.a[(int) SVfloat (e, modelindex)];
f = atoi (Cmd_Argv (1));
if (f >= m->numframes)
f = m->numframes - 1;
SVfloat (e, frame) = f;
}
static void
PrintFrameName (model_t *m, int frame)
{
aliashdr_t *hdr;
maliasframedesc_t *pframedesc;
hdr = Cache_TryGet (&m->cache);
if (!hdr)
return;
pframedesc = &hdr->frames[frame];
Sys_Printf ("frame %i: %s\n", frame, pframedesc->name);
Cache_Release (&m->cache);
}
static void
Host_Viewnext_f (void)
{
edict_t *e;
model_t *m;
e = FindViewthing ();
if (!e)
return;
m = cl_world.models.a[(int) SVfloat (e, modelindex)];
SVfloat (e, frame) = SVfloat (e, frame) + 1;
if (SVfloat (e, frame) >= m->numframes)
SVfloat (e, frame) = m->numframes - 1;
PrintFrameName (m, SVfloat (e, frame));
}
static void
Host_Viewprev_f (void)
{
edict_t *e;
model_t *m;
e = FindViewthing ();
if (!e)
return;
m = cl_world.models.a[(int) SVfloat (e, modelindex)];
SVfloat (e, frame) = SVfloat (e, frame) - 1;
if (SVfloat (e, frame) < 0)
SVfloat (e, frame) = 0;
PrintFrameName (m, SVfloat (e, frame));
}
// DEMO LOOP CONTROL ==========================================================
static void
Host_Startdemos_f (void)
{
int i, c;
if (cls.state == ca_dedicated) {
if (!sv.active)
Cbuf_AddText (host_cbuf, "map start\n");
return;
}
c = Cmd_Argc () - 1;
if (c > MAX_DEMOS) {
Sys_Printf ("Max %i demos in demoloop\n", MAX_DEMOS);
c = MAX_DEMOS;
}
Sys_Printf ("%i demo(s) in loop\n", c);
for (i = 1; i < c + 1; i++)
strncpy (cls.demos[i - 1], Cmd_Argv (i), sizeof (cls.demos[0]) - 1);
if (!sv.active && cls.demonum != -1 && !cls.demoplayback) {
cls.demonum = 0;
CL_NextDemo ();
} else
cls.demonum = -1;
}
/*
Host_Demos_f
Return to looping demos
*/
static void
Host_Demos_f (void)
{
if (cls.state == ca_dedicated)
return;
if (cls.demonum == -1)
cls.demonum = 1;
CL_Disconnect_f ();
CL_NextDemo ();
}
/*
Host_Stopdemo_f
Return to looping demos
*/
static void
Host_Stopdemo_f (void)
{
if (cls.state == ca_dedicated)
return;
if (!cls.demoplayback)
return;
CL_StopPlayback ();
CL_Disconnect ();
}
//=============================================================================
void
Host_InitCommands (void)
{
Cmd_AddCommand ("status", Host_Status_f, "No Description");
if (!con_module || isDedicated)
Cmd_AddCommand ("quit", Host_Quit_f, "No Description");
Cmd_AddCommand ("god", Host_God_f, "No Description");
Cmd_AddCommand ("notarget", Host_Notarget_f, "No Description");
Cmd_AddCommand ("fly", Host_Fly_f, "No Description");
Cmd_AddCommand ("map", Host_Map_f, "No Description");
Cmd_AddCommand ("restart", Host_Restart_f, "No Description");
Cmd_AddCommand ("changelevel", Host_Changelevel_f, "No Description");
Cmd_AddCommand ("connect", Host_Connect_f, "No Description");
Cmd_AddCommand ("reconnect", Host_Reconnect_f, "No Description");
Cmd_AddCommand ("name", Host_Name_f, "No Description");
Cmd_AddCommand ("noclip", Host_Noclip_f, "No Description");
Cmd_AddCommand ("version", Host_Version_f, "No Description");
Cmd_AddCommand ("say", Host_Say_f, "No Description");
Cmd_AddCommand ("say_team", Host_Say_Team_f, "No Description");
Cmd_AddCommand ("tell", Host_Tell_f, "No Description");
Cmd_AddCommand ("kill", Host_Kill_f, "No Description");
Cmd_AddCommand ("pause", Host_Pause_f, "No Description");
Cmd_AddCommand ("spawn", Host_Spawn_f, "No Description");
Cmd_AddCommand ("begin", Host_Begin_f, "No Description");
Cmd_AddCommand ("prespawn", Host_PreSpawn_f, "No Description");
Cmd_AddCommand ("kick", Host_Kick_f, "No Description");
Cmd_AddCommand ("ping", Host_Ping_f, "No Description");
Cmd_AddCommand ("load", Host_Loadgame_f, "No Description");
Cmd_AddCommand ("save", Host_Savegame_f, "No Description");
Cmd_AddCommand ("startdemos", Host_Startdemos_f, "No Description");
Cmd_AddCommand ("demos", Host_Demos_f, "No Description");
Cmd_AddCommand ("stopdemo", Host_Stopdemo_f, "No Description");
Cmd_AddCommand ("viewmodel", Host_Viewmodel_f, "No Description");
Cmd_AddCommand ("viewframe", Host_Viewframe_f, "No Description");
Cmd_AddCommand ("viewnext", Host_Viewnext_f, "No Description");
Cmd_AddCommand ("viewprev", Host_Viewprev_f, "No Description");
Cmd_AddCommand ("mcache", Mod_Print, "No Description");
}