mirror of
https://git.code.sf.net/p/quake/quakeforge-old
synced 2024-11-25 21:31:18 +00:00
c3f5581b0a
Unchained, Ultimate, Ultra, Up Yours, Underworld, Underground, Unified, Unity, etc. You know the drill. This takes care of the "standalone" problem with the wrong name, and the recent snafu with multiple developers working on the same files simultaneously...expect me (and probably others) to start locking dirs when updates are taking place. And yes, this update is really as large as it looks. Software only at the moment, but I will have the makefile updated to build the GL builds as well.
958 lines
20 KiB
C
958 lines
20 KiB
C
/*
|
|
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 the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// host.c -- coordinates spawning and killing of local servers
|
|
|
|
#include "quakedef.h"
|
|
#include "r_local.h"
|
|
|
|
/*
|
|
|
|
A server can allways be started, even if the system started out as a client
|
|
to a remote system.
|
|
|
|
A client can NOT be started if the system started as a dedicated server.
|
|
|
|
Memory is cleared / released when a server or client begins, not when they end.
|
|
|
|
*/
|
|
|
|
quakeparms_t host_parms;
|
|
|
|
qboolean host_initialized; // true if into command execution
|
|
|
|
double host_frametime;
|
|
double host_time;
|
|
double realtime; // without any filtering or bounding
|
|
double oldrealtime; // last frame run
|
|
int host_framecount;
|
|
|
|
int host_hunklevel;
|
|
|
|
int minimum_memory;
|
|
|
|
client_t *host_client; // current client
|
|
|
|
jmp_buf host_abortserver;
|
|
|
|
byte *host_basepal;
|
|
byte *host_colormap;
|
|
|
|
cvar_t host_framerate = {"host_framerate","0"}; // set for slow motion
|
|
cvar_t host_speeds = {"host_speeds","0"}; // set for running times
|
|
|
|
cvar_t sys_ticrate = {"sys_ticrate","0.05"};
|
|
cvar_t serverprofile = {"serverprofile","0"};
|
|
|
|
cvar_t fraglimit = {"fraglimit","0",false,true};
|
|
cvar_t timelimit = {"timelimit","0",false,true};
|
|
cvar_t teamplay = {"teamplay","0",false,true};
|
|
|
|
cvar_t samelevel = {"samelevel","0"};
|
|
cvar_t noexit = {"noexit","0",false,true};
|
|
|
|
#ifdef QUAKE2
|
|
cvar_t developer = {"developer","1"}; // should be 0 for release!
|
|
#else
|
|
cvar_t developer = {"developer","0"};
|
|
#endif
|
|
|
|
cvar_t skill = {"skill","1"}; // 0 - 3
|
|
cvar_t deathmatch = {"deathmatch","0"}; // 0, 1, or 2
|
|
cvar_t coop = {"coop","0"}; // 0 or 1
|
|
|
|
cvar_t pausable = {"pausable","1"};
|
|
|
|
cvar_t temp1 = {"temp1","0"};
|
|
|
|
|
|
/*
|
|
================
|
|
Host_EndGame
|
|
================
|
|
*/
|
|
void Host_EndGame (char *message, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,message);
|
|
vsprintf (string,message,argptr);
|
|
va_end (argptr);
|
|
Con_DPrintf ("Host_EndGame: %s\n",string);
|
|
|
|
if (sv.active)
|
|
Host_ShutdownServer (false);
|
|
|
|
if (cls.state == ca_dedicated)
|
|
Sys_Error ("Host_EndGame: %s\n",string); // dedicated servers exit
|
|
|
|
if (cls.demonum != -1)
|
|
CL_NextDemo ();
|
|
else
|
|
CL_Disconnect ();
|
|
|
|
longjmp (host_abortserver, 1);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Host_Error
|
|
|
|
This shuts down both the client and server
|
|
================
|
|
*/
|
|
void Host_Error (char *error, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
static qboolean inerror = false;
|
|
|
|
if (inerror)
|
|
Sys_Error ("Host_Error: recursively entered");
|
|
inerror = true;
|
|
|
|
SCR_EndLoadingPlaque (); // reenable screen updates
|
|
|
|
va_start (argptr,error);
|
|
vsprintf (string,error,argptr);
|
|
va_end (argptr);
|
|
Con_Printf ("Host_Error: %s\n",string);
|
|
|
|
if (sv.active)
|
|
Host_ShutdownServer (false);
|
|
|
|
if (cls.state == ca_dedicated)
|
|
Sys_Error ("Host_Error: %s\n",string); // dedicated servers exit
|
|
|
|
CL_Disconnect ();
|
|
cls.demonum = -1;
|
|
|
|
inerror = false;
|
|
|
|
longjmp (host_abortserver, 1);
|
|
}
|
|
|
|
/*
|
|
================
|
|
Host_FindMaxClients
|
|
================
|
|
*/
|
|
void Host_FindMaxClients (void)
|
|
{
|
|
int i;
|
|
|
|
svs.maxclients = 1;
|
|
|
|
i = COM_CheckParm ("-dedicated");
|
|
if (i)
|
|
{
|
|
cls.state = ca_dedicated;
|
|
if (i != (com_argc - 1))
|
|
{
|
|
svs.maxclients = Q_atoi (com_argv[i+1]);
|
|
}
|
|
else
|
|
svs.maxclients = 8;
|
|
}
|
|
else
|
|
cls.state = ca_disconnected;
|
|
|
|
i = COM_CheckParm ("-listen");
|
|
if (i)
|
|
{
|
|
if (cls.state == ca_dedicated)
|
|
Sys_Error ("Only one of -dedicated or -listen can be specified");
|
|
if (i != (com_argc - 1))
|
|
svs.maxclients = Q_atoi (com_argv[i+1]);
|
|
else
|
|
svs.maxclients = 8;
|
|
}
|
|
if (svs.maxclients < 1)
|
|
svs.maxclients = 8;
|
|
else if (svs.maxclients > MAX_SCOREBOARD)
|
|
svs.maxclients = MAX_SCOREBOARD;
|
|
|
|
svs.maxclientslimit = svs.maxclients;
|
|
if (svs.maxclientslimit < 4)
|
|
svs.maxclientslimit = 4;
|
|
svs.clients = Hunk_AllocName (svs.maxclientslimit*sizeof(client_t), "clients");
|
|
|
|
if (svs.maxclients > 1)
|
|
Cvar_SetValue ("deathmatch", 1.0);
|
|
else
|
|
Cvar_SetValue ("deathmatch", 0.0);
|
|
}
|
|
|
|
|
|
/*
|
|
=======================
|
|
Host_InitLocal
|
|
======================
|
|
*/
|
|
void Host_InitLocal (void)
|
|
{
|
|
Host_InitCommands ();
|
|
|
|
Cvar_RegisterVariable (&host_framerate);
|
|
Cvar_RegisterVariable (&host_speeds);
|
|
|
|
Cvar_RegisterVariable (&sys_ticrate);
|
|
Cvar_RegisterVariable (&serverprofile);
|
|
|
|
Cvar_RegisterVariable (&fraglimit);
|
|
Cvar_RegisterVariable (&timelimit);
|
|
Cvar_RegisterVariable (&teamplay);
|
|
Cvar_RegisterVariable (&samelevel);
|
|
Cvar_RegisterVariable (&noexit);
|
|
Cvar_RegisterVariable (&skill);
|
|
Cvar_RegisterVariable (&developer);
|
|
Cvar_RegisterVariable (&deathmatch);
|
|
Cvar_RegisterVariable (&coop);
|
|
|
|
Cvar_RegisterVariable (&pausable);
|
|
|
|
Cvar_RegisterVariable (&temp1);
|
|
|
|
Host_FindMaxClients ();
|
|
|
|
host_time = 1.0; // so a think at time 0 won't get called
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Host_WriteConfiguration
|
|
|
|
Writes key bindings and archived cvars to config.cfg
|
|
===============
|
|
*/
|
|
void Host_WriteConfiguration (void)
|
|
{
|
|
FILE *f;
|
|
|
|
// dedicated servers initialize the host but don't parse and set the
|
|
// config.cfg cvars
|
|
if (host_initialized & !isDedicated)
|
|
{
|
|
f = fopen (va("%s/config.cfg",com_gamedir), "w");
|
|
if (!f)
|
|
{
|
|
Con_Printf ("Couldn't write config.cfg.\n");
|
|
return;
|
|
}
|
|
|
|
Key_WriteBindings (f);
|
|
Cvar_WriteVariables (f);
|
|
|
|
fclose (f);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
SV_ClientPrintf
|
|
|
|
Sends text across to be displayed
|
|
FIXME: make this just a stuffed echo?
|
|
=================
|
|
*/
|
|
void SV_ClientPrintf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (string, fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_print);
|
|
MSG_WriteString (&host_client->message, string);
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SV_BroadcastPrintf
|
|
|
|
Sends text to all active clients
|
|
=================
|
|
*/
|
|
void SV_BroadcastPrintf (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
int i;
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (string, fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
for (i=0 ; i<svs.maxclients ; i++)
|
|
if (svs.clients[i].active && svs.clients[i].spawned)
|
|
{
|
|
MSG_WriteByte (&svs.clients[i].message, svc_print);
|
|
MSG_WriteString (&svs.clients[i].message, string);
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
Host_ClientCommands
|
|
|
|
Send text over to the client to be executed
|
|
=================
|
|
*/
|
|
void Host_ClientCommands (char *fmt, ...)
|
|
{
|
|
va_list argptr;
|
|
char string[1024];
|
|
|
|
va_start (argptr,fmt);
|
|
vsprintf (string, fmt,argptr);
|
|
va_end (argptr);
|
|
|
|
MSG_WriteByte (&host_client->message, svc_stufftext);
|
|
MSG_WriteString (&host_client->message, string);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
SV_DropClient
|
|
|
|
Called when the player is getting totally kicked off the host
|
|
if (crash = true), don't bother sending signofs
|
|
=====================
|
|
*/
|
|
void SV_DropClient (qboolean crash)
|
|
{
|
|
int saveSelf;
|
|
int i;
|
|
client_t *client;
|
|
|
|
if (!crash)
|
|
{
|
|
// send any final messages (don't check for errors)
|
|
if (NET_CanSendMessage (host_client->netconnection))
|
|
{
|
|
MSG_WriteByte (&host_client->message, svc_disconnect);
|
|
NET_SendMessage (host_client->netconnection, &host_client->message);
|
|
}
|
|
|
|
if (host_client->edict && host_client->spawned)
|
|
{
|
|
// call the prog function for removing a client
|
|
// this will set the body to a dead frame, among other things
|
|
saveSelf = pr_global_struct->self;
|
|
pr_global_struct->self = EDICT_TO_PROG(host_client->edict);
|
|
PR_ExecuteProgram (pr_global_struct->ClientDisconnect);
|
|
pr_global_struct->self = saveSelf;
|
|
}
|
|
|
|
Sys_Printf ("Client %s removed\n",host_client->name);
|
|
}
|
|
|
|
// break the net connection
|
|
NET_Close (host_client->netconnection);
|
|
host_client->netconnection = NULL;
|
|
|
|
// free the client (the body stays around)
|
|
host_client->active = false;
|
|
host_client->name[0] = 0;
|
|
host_client->old_frags = -999999;
|
|
net_activeconnections--;
|
|
|
|
// send notification to all clients
|
|
for (i=0, client = svs.clients ; i<svs.maxclients ; i++, client++)
|
|
{
|
|
if (!client->active)
|
|
continue;
|
|
MSG_WriteByte (&client->message, svc_updatename);
|
|
MSG_WriteByte (&client->message, host_client - svs.clients);
|
|
MSG_WriteString (&client->message, "");
|
|
MSG_WriteByte (&client->message, svc_updatefrags);
|
|
MSG_WriteByte (&client->message, host_client - svs.clients);
|
|
MSG_WriteShort (&client->message, 0);
|
|
MSG_WriteByte (&client->message, svc_updatecolors);
|
|
MSG_WriteByte (&client->message, host_client - svs.clients);
|
|
MSG_WriteByte (&client->message, 0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Host_ShutdownServer
|
|
|
|
This only happens at the end of a game, not between levels
|
|
==================
|
|
*/
|
|
void Host_ShutdownServer(qboolean crash)
|
|
{
|
|
int i;
|
|
int count;
|
|
sizebuf_t buf;
|
|
char message[4];
|
|
double start;
|
|
|
|
if (!sv.active)
|
|
return;
|
|
|
|
sv.active = false;
|
|
|
|
// stop all client sounds immediately
|
|
if (cls.state == ca_connected)
|
|
CL_Disconnect ();
|
|
|
|
// flush any pending messages - like the score!!!
|
|
start = Sys_DoubleTime();
|
|
do
|
|
{
|
|
count = 0;
|
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
{
|
|
if (host_client->active && host_client->message.cursize)
|
|
{
|
|
if (NET_CanSendMessage (host_client->netconnection))
|
|
{
|
|
NET_SendMessage(host_client->netconnection, &host_client->message);
|
|
SZ_Clear (&host_client->message);
|
|
}
|
|
else
|
|
{
|
|
NET_GetMessage(host_client->netconnection);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
if ((Sys_DoubleTime() - start) > 3.0)
|
|
break;
|
|
}
|
|
while (count);
|
|
|
|
// make sure all the clients know we're disconnecting
|
|
buf.data = message;
|
|
buf.maxsize = 4;
|
|
buf.cursize = 0;
|
|
MSG_WriteByte(&buf, svc_disconnect);
|
|
count = NET_SendToAll(&buf, 5);
|
|
if (count)
|
|
Con_Printf("Host_ShutdownServer: NET_SendToAll failed for %u clients\n", count);
|
|
|
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
|
if (host_client->active)
|
|
SV_DropClient(crash);
|
|
|
|
//
|
|
// clear structures
|
|
//
|
|
memset (&sv, 0, sizeof(sv));
|
|
memset (svs.clients, 0, svs.maxclientslimit*sizeof(client_t));
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
Host_ClearMemory
|
|
|
|
This clears all the memory used by both the client and server, but does
|
|
not reinitialize anything.
|
|
================
|
|
*/
|
|
void Host_ClearMemory (void)
|
|
{
|
|
Con_DPrintf ("Clearing memory\n");
|
|
D_FlushCaches ();
|
|
Mod_ClearAll ();
|
|
if (host_hunklevel)
|
|
Hunk_FreeToLowMark (host_hunklevel);
|
|
|
|
cls.signon = 0;
|
|
memset (&sv, 0, sizeof(sv));
|
|
memset (&cl, 0, sizeof(cl));
|
|
}
|
|
|
|
|
|
//============================================================================
|
|
|
|
|
|
/*
|
|
===================
|
|
Host_FilterTime
|
|
|
|
Returns false if the time is too short to run a frame
|
|
===================
|
|
*/
|
|
qboolean Host_FilterTime (float time)
|
|
{
|
|
realtime += time;
|
|
|
|
if (!cls.timedemo && realtime - oldrealtime < 1.0/72.0)
|
|
return false; // framerate is too high
|
|
|
|
host_frametime = realtime - oldrealtime;
|
|
oldrealtime = realtime;
|
|
|
|
if (host_framerate.value > 0)
|
|
host_frametime = host_framerate.value;
|
|
else
|
|
{ // don't allow really long or short frames
|
|
if (host_frametime > 0.1)
|
|
host_frametime = 0.1;
|
|
if (host_frametime < 0.001)
|
|
host_frametime = 0.001;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
===================
|
|
Host_GetConsoleCommands
|
|
|
|
Add them exactly as if they had been typed at the console
|
|
===================
|
|
*/
|
|
void Host_GetConsoleCommands (void)
|
|
{
|
|
char *cmd;
|
|
|
|
while (1)
|
|
{
|
|
cmd = Sys_ConsoleInput ();
|
|
if (!cmd)
|
|
break;
|
|
Cbuf_AddText (cmd);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_ServerFrame
|
|
|
|
==================
|
|
*/
|
|
#ifdef FPS_20
|
|
|
|
void _Host_ServerFrame (void)
|
|
{
|
|
// run the world state
|
|
pr_global_struct->frametime = host_frametime;
|
|
|
|
// read client messages
|
|
SV_RunClients ();
|
|
|
|
// move things around and think
|
|
// always pause in single player if in console or menus
|
|
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
|
|
SV_Physics ();
|
|
}
|
|
|
|
void Host_ServerFrame (void)
|
|
{
|
|
float save_host_frametime;
|
|
float temp_host_frametime;
|
|
|
|
// run the world state
|
|
pr_global_struct->frametime = host_frametime;
|
|
|
|
// set the time and clear the general datagram
|
|
SV_ClearDatagram ();
|
|
|
|
// check for new clients
|
|
SV_CheckForNewClients ();
|
|
|
|
temp_host_frametime = save_host_frametime = host_frametime;
|
|
while(temp_host_frametime > (1.0/72.0))
|
|
{
|
|
if (temp_host_frametime > 0.05)
|
|
host_frametime = 0.05;
|
|
else
|
|
host_frametime = temp_host_frametime;
|
|
temp_host_frametime -= host_frametime;
|
|
_Host_ServerFrame ();
|
|
}
|
|
host_frametime = save_host_frametime;
|
|
|
|
// send all messages to the clients
|
|
SV_SendClientMessages ();
|
|
}
|
|
|
|
#else
|
|
|
|
void Host_ServerFrame (void)
|
|
{
|
|
// run the world state
|
|
pr_global_struct->frametime = host_frametime;
|
|
|
|
// set the time and clear the general datagram
|
|
SV_ClearDatagram ();
|
|
|
|
// check for new clients
|
|
SV_CheckForNewClients ();
|
|
|
|
// read client messages
|
|
SV_RunClients ();
|
|
|
|
// move things around and think
|
|
// always pause in single player if in console or menus
|
|
if (!sv.paused && (svs.maxclients > 1 || key_dest == key_game) )
|
|
SV_Physics ();
|
|
|
|
// send all messages to the clients
|
|
SV_SendClientMessages ();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
==================
|
|
Host_Frame
|
|
|
|
Runs all active servers
|
|
==================
|
|
*/
|
|
void _Host_Frame (float time)
|
|
{
|
|
static double time1 = 0;
|
|
static double time2 = 0;
|
|
static double time3 = 0;
|
|
int pass1, pass2, pass3;
|
|
|
|
if (setjmp (host_abortserver) )
|
|
return; // something bad happened, or the server disconnected
|
|
|
|
// keep the random time dependent
|
|
rand ();
|
|
|
|
// decide the simulation time
|
|
if (!Host_FilterTime (time))
|
|
return; // don't run too fast, or packets will flood out
|
|
|
|
// get new key events
|
|
Sys_SendKeyEvents ();
|
|
|
|
// allow mice or other external controllers to add commands
|
|
IN_Commands ();
|
|
|
|
// process console commands
|
|
Cbuf_Execute ();
|
|
|
|
NET_Poll();
|
|
|
|
// if running the server locally, make intentions now
|
|
if (sv.active)
|
|
CL_SendCmd ();
|
|
|
|
//-------------------
|
|
//
|
|
// server operations
|
|
//
|
|
//-------------------
|
|
|
|
// check for commands typed to the host
|
|
Host_GetConsoleCommands ();
|
|
|
|
if (sv.active)
|
|
Host_ServerFrame ();
|
|
|
|
//-------------------
|
|
//
|
|
// client operations
|
|
//
|
|
//-------------------
|
|
|
|
// if running the server remotely, send intentions now after
|
|
// the incoming messages have been read
|
|
if (!sv.active)
|
|
CL_SendCmd ();
|
|
|
|
host_time += host_frametime;
|
|
|
|
// fetch results from server
|
|
if (cls.state == ca_connected)
|
|
{
|
|
CL_ReadFromServer ();
|
|
}
|
|
|
|
// update video
|
|
if (host_speeds.value)
|
|
time1 = Sys_DoubleTime ();
|
|
|
|
SCR_UpdateScreen ();
|
|
|
|
if (host_speeds.value)
|
|
time2 = Sys_DoubleTime ();
|
|
|
|
// update audio
|
|
if (cls.signon == SIGNONS)
|
|
{
|
|
S_Update (r_origin, vpn, vright, vup);
|
|
CL_DecayLights ();
|
|
}
|
|
else
|
|
S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
|
|
|
|
CDAudio_Update();
|
|
|
|
if (host_speeds.value)
|
|
{
|
|
pass1 = (time1 - time3)*1000;
|
|
time3 = Sys_DoubleTime ();
|
|
pass2 = (time2 - time1)*1000;
|
|
pass3 = (time3 - time2)*1000;
|
|
Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
|
|
pass1+pass2+pass3, pass1, pass2, pass3);
|
|
}
|
|
|
|
host_framecount++;
|
|
}
|
|
|
|
void Host_Frame (float time)
|
|
{
|
|
double time1, time2;
|
|
static double timetotal;
|
|
static int timecount;
|
|
int i, c, m;
|
|
|
|
if (!serverprofile.value)
|
|
{
|
|
_Host_Frame (time);
|
|
return;
|
|
}
|
|
|
|
time1 = Sys_DoubleTime ();
|
|
_Host_Frame (time);
|
|
time2 = Sys_DoubleTime ();
|
|
|
|
timetotal += time2 - time1;
|
|
timecount++;
|
|
|
|
if (timecount < 1000)
|
|
return;
|
|
|
|
m = timetotal*1000/timecount;
|
|
timecount = 0;
|
|
timetotal = 0;
|
|
c = 0;
|
|
for (i=0 ; i<svs.maxclients ; i++)
|
|
{
|
|
if (svs.clients[i].active)
|
|
c++;
|
|
}
|
|
|
|
Con_Printf ("serverprofile: %2i clients %2i msec\n", c, m);
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
|
|
extern int vcrFile;
|
|
#define VCR_SIGNATURE 0x56435231
|
|
// "VCR1"
|
|
|
|
void Host_InitVCR (quakeparms_t *parms)
|
|
{
|
|
int i, len, n;
|
|
char *p;
|
|
|
|
if (COM_CheckParm("-playback"))
|
|
{
|
|
if (com_argc != 2)
|
|
Sys_Error("No other parameters allowed with -playback\n");
|
|
|
|
Sys_FileOpenRead("quake.vcr", &vcrFile);
|
|
if (vcrFile == -1)
|
|
Sys_Error("playback file not found\n");
|
|
|
|
Sys_FileRead (vcrFile, &i, sizeof(int));
|
|
if (i != VCR_SIGNATURE)
|
|
Sys_Error("Invalid signature in vcr file\n");
|
|
|
|
Sys_FileRead (vcrFile, &com_argc, sizeof(int));
|
|
com_argv = malloc(com_argc * sizeof(char *));
|
|
com_argv[0] = parms->argv[0];
|
|
for (i = 0; i < com_argc; i++)
|
|
{
|
|
Sys_FileRead (vcrFile, &len, sizeof(int));
|
|
p = malloc(len);
|
|
Sys_FileRead (vcrFile, p, len);
|
|
com_argv[i+1] = p;
|
|
}
|
|
com_argc++; /* add one for arg[0] */
|
|
parms->argc = com_argc;
|
|
parms->argv = com_argv;
|
|
}
|
|
|
|
if ( (n = COM_CheckParm("-record")) != 0)
|
|
{
|
|
vcrFile = Sys_FileOpenWrite("quake.vcr");
|
|
|
|
i = VCR_SIGNATURE;
|
|
Sys_FileWrite(vcrFile, &i, sizeof(int));
|
|
i = com_argc - 1;
|
|
Sys_FileWrite(vcrFile, &i, sizeof(int));
|
|
for (i = 1; i < com_argc; i++)
|
|
{
|
|
if (i == n)
|
|
{
|
|
len = 10;
|
|
Sys_FileWrite(vcrFile, &len, sizeof(int));
|
|
Sys_FileWrite(vcrFile, "-playback", len);
|
|
continue;
|
|
}
|
|
len = Q_strlen(com_argv[i]) + 1;
|
|
Sys_FileWrite(vcrFile, &len, sizeof(int));
|
|
Sys_FileWrite(vcrFile, com_argv[i], len);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
====================
|
|
Host_Init
|
|
====================
|
|
*/
|
|
void Host_Init (quakeparms_t *parms)
|
|
{
|
|
|
|
if (standard_quake)
|
|
minimum_memory = MINIMUM_MEMORY;
|
|
else
|
|
minimum_memory = MINIMUM_MEMORY_LEVELPAK;
|
|
|
|
if (COM_CheckParm ("-minmemory"))
|
|
parms->memsize = minimum_memory;
|
|
|
|
host_parms = *parms;
|
|
|
|
if (parms->memsize < minimum_memory)
|
|
Sys_Error ("Only %4.1f megs of memory available, can't execute game", parms->memsize / (float)0x100000);
|
|
|
|
com_argc = parms->argc;
|
|
com_argv = parms->argv;
|
|
|
|
Memory_Init (parms->membase, parms->memsize);
|
|
Cbuf_Init ();
|
|
Cmd_Init ();
|
|
V_Init ();
|
|
Chase_Init ();
|
|
Host_InitVCR (parms);
|
|
COM_Init (parms->basedir);
|
|
Host_InitLocal ();
|
|
W_LoadWadFile ("gfx.wad");
|
|
Key_Init ();
|
|
Con_Init ();
|
|
M_Init ();
|
|
PR_Init ();
|
|
Mod_Init ();
|
|
NET_Init ();
|
|
SV_Init ();
|
|
|
|
Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
|
|
Con_Printf ("%4.1f megabyte heap\n",parms->memsize/ (1024*1024.0));
|
|
|
|
R_InitTextures (); // needed even for dedicated servers
|
|
|
|
if (cls.state != ca_dedicated)
|
|
{
|
|
host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
|
|
if (!host_basepal)
|
|
Sys_Error ("Couldn't load gfx/palette.lmp");
|
|
host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
|
|
if (!host_colormap)
|
|
Sys_Error ("Couldn't load gfx/colormap.lmp");
|
|
|
|
#ifndef _WIN32 // on non win32, mouse comes before video for security reasons
|
|
IN_Init ();
|
|
#endif
|
|
VID_Init (host_basepal);
|
|
|
|
Draw_Init ();
|
|
SCR_Init ();
|
|
R_Init ();
|
|
#ifndef _WIN32
|
|
// on Win32, sound initialization has to come before video initialization, so we
|
|
// can put up a popup if the sound hardware is in use
|
|
// S_Init ();
|
|
#else
|
|
|
|
#ifdef GLQUAKE
|
|
// FIXME: doesn't use the new one-window approach yet
|
|
// S_Init ();
|
|
#endif
|
|
|
|
#endif // _WIN32
|
|
CDAudio_Init ();
|
|
Sbar_Init ();
|
|
CL_Init ();
|
|
#ifdef _WIN32 // on non win32, mouse comes before video for security reasons
|
|
IN_Init ();
|
|
#endif
|
|
}
|
|
|
|
Cbuf_InsertText ("exec quake.rc\n");
|
|
|
|
Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
|
|
host_hunklevel = Hunk_LowMark ();
|
|
|
|
host_initialized = true;
|
|
|
|
Sys_Printf ("========Quake Initialized=========\n");
|
|
}
|
|
|
|
|
|
/*
|
|
===============
|
|
Host_Shutdown
|
|
|
|
FIXME: this is a callback from Sys_Quit and Sys_Error. It would be better
|
|
to run quit through here before the final handoff to the sys code.
|
|
===============
|
|
*/
|
|
void Host_Shutdown(void)
|
|
{
|
|
static qboolean isdown = false;
|
|
|
|
if (isdown)
|
|
{
|
|
printf ("recursive shutdown\n");
|
|
return;
|
|
}
|
|
isdown = true;
|
|
|
|
// keep Con_Printf from trying to update the screen
|
|
scr_disabled_for_loading = true;
|
|
|
|
Host_WriteConfiguration ();
|
|
|
|
CDAudio_Shutdown ();
|
|
NET_Shutdown ();
|
|
S_Shutdown();
|
|
IN_Shutdown ();
|
|
|
|
if (cls.state != ca_dedicated)
|
|
{
|
|
VID_Shutdown();
|
|
}
|
|
}
|
|
|