This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/host.c

937 lines
21 KiB
C
Raw Normal View History

2011-05-09 22:00:00 +02:00
/*
host.c - dedicated and normal host
Copyright (C) 2007 Uncle Mike
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 3 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.
*/
2007-06-21 22:00:00 +02:00
2008-06-09 22:00:00 +02:00
#include "common.h"
2010-10-07 22:00:00 +02:00
#include "netchan.h"
2010-12-16 22:00:00 +01:00
#include "protocol.h"
2011-04-06 22:00:00 +02:00
#include "mod_local.h"
2011-03-12 22:00:00 +01:00
#include "mathlib.h"
2008-08-04 22:00:00 +02:00
#include "input.h"
2012-05-21 22:00:00 +02:00
#include "features.h"
2013-10-23 22:00:00 +02:00
#include "render_api.h" // decallist_t
2007-06-21 22:00:00 +02:00
2011-03-12 22:00:00 +01:00
typedef void (*pfnChangeGame)( const char *progname );
pfnChangeGame pChangeGame = NULL;
HINSTANCE hCurrent; // hinstance of current .dll
2007-09-03 22:00:00 +02:00
host_parm_t host; // host parms
2011-03-12 22:00:00 +01:00
sysinfo_t SI;
2007-06-21 22:00:00 +02:00
2018-03-03 22:00:00 +01:00
CVAR_DEFINE( host_developer, "developer", "0", 0, "engine is in development-mode" );
2018-04-25 23:00:00 +02:00
CVAR_DEFINE_AUTO( sys_ticrate, "100", 0, "framerate in dedicated mode" );
2010-10-22 22:00:00 +02:00
convar_t *host_gameloaded;
2011-07-22 22:00:00 +02:00
convar_t *host_clientloaded;
2010-10-22 22:00:00 +02:00
convar_t *host_limitlocal;
convar_t *host_maxfps;
convar_t *host_framerate;
2011-02-11 22:00:00 +01:00
convar_t *con_gamemaps;
2012-05-13 22:00:00 +02:00
convar_t *build, *ver;
2007-11-17 22:00:00 +01:00
2009-09-28 22:00:00 +02:00
int Host_CompareFileTime( long ft1, long ft2 )
{
if( ft1 < ft2 )
{
return -1;
}
else if( ft1 > ft2 )
{
return 1;
}
return 0;
}
2009-10-13 22:00:00 +02:00
void Host_ShutdownServer( void )
{
2018-02-28 22:00:00 +01:00
SV_Shutdown( "Server was killed\n" );
2009-10-13 22:00:00 +02:00
}
2012-05-21 22:00:00 +02:00
/*
================
Host_PrintEngineFeatures
================
*/
void Host_PrintEngineFeatures( void )
{
2016-11-14 22:00:00 +01:00
if( FBitSet( host.features, ENGINE_WRITE_LARGE_COORD ))
2018-09-28 23:00:00 +02:00
Con_Reportf( "^3EXT:^7 big world support enabled\n" );
2016-11-14 22:00:00 +01:00
if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ))
2018-09-28 23:00:00 +02:00
Con_Reportf( "^3EXT:^7 deluxemap support enabled\n" );
2012-05-21 22:00:00 +02:00
2017-08-12 23:00:00 +02:00
if( FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT ))
2018-09-28 23:00:00 +02:00
Con_Reportf( "^3EXT:^7 Improved MOVETYPE_PUSH is used\n" );
2012-08-03 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
if( FBitSet( host.features, ENGINE_LARGE_LIGHTMAPS ))
2018-09-28 23:00:00 +02:00
Con_Reportf( "^3EXT:^7 Large lightmaps enabled\n" );
2012-12-22 21:00:00 +01:00
2016-11-14 22:00:00 +01:00
if( FBitSet( host.features, ENGINE_COMPENSATE_QUAKE_BUG ))
2018-09-28 23:00:00 +02:00
Con_Reportf( "^3EXT:^7 Compensate quake bug enabled\n" );
2012-05-21 22:00:00 +02:00
}
2009-10-13 22:00:00 +02:00
/*
================
Host_EndGame
================
*/
2018-02-25 22:00:00 +01:00
void Host_EndGame( qboolean abort, const char *message, ... )
2009-10-13 22:00:00 +02:00
{
va_list argptr;
2010-07-26 22:00:00 +02:00
static char string[MAX_SYSPATH];
2009-10-13 22:00:00 +02:00
va_start( argptr, message );
2016-11-14 22:00:00 +01:00
Q_vsnprintf( string, sizeof( string ), message, argptr );
2009-10-13 22:00:00 +02:00
va_end( argptr );
2018-09-08 23:00:00 +02:00
Con_Printf( "Host_EndGame: %s\n", string );
2014-05-11 22:00:00 +02:00
2018-02-28 22:00:00 +01:00
SV_Shutdown( "\n" );
2014-05-11 22:00:00 +02:00
CL_Disconnect();
2009-10-13 22:00:00 +02:00
2014-05-09 22:00:00 +02:00
// recreate world if needs
CL_ClearEdicts ();
2011-09-19 22:00:00 +02:00
// release all models
2018-02-25 22:00:00 +01:00
Mod_FreeAll();
2011-09-19 22:00:00 +02:00
2018-02-25 22:00:00 +01:00
if( abort ) Host_AbortCurrentFrame ();
2009-10-13 22:00:00 +02:00
}
2007-09-10 22:00:00 +02:00
/*
================
Host_AbortCurrentFrame
aborts the current host frame and goes on with the next one
================
*/
void Host_AbortCurrentFrame( void )
{
2008-05-18 22:00:00 +02:00
longjmp( host.abortframe, 1 );
2007-09-10 22:00:00 +02:00
}
2017-02-13 22:00:00 +01:00
/*
==================
Host_CheckSleep
==================
*/
void Host_CheckSleep( void )
{
if( host.type == HOST_DEDICATED )
{
// let the dedicated server some sleep
2018-04-25 23:00:00 +02:00
Sys_Sleep( 1 );
2017-02-13 22:00:00 +01:00
}
else
{
2018-02-25 22:00:00 +01:00
if( host.status == HOST_NOFOCUS )
2017-02-13 22:00:00 +01:00
{
2018-02-28 22:00:00 +01:00
if( SV_Active() && CL_IsInGame( ))
2017-02-13 22:00:00 +01:00
Sys_Sleep( 1 ); // listenserver
else Sys_Sleep( 20 ); // sleep 20 ms otherwise
}
2018-02-25 22:00:00 +01:00
else if( host.status == HOST_SLEEP )
2017-02-13 22:00:00 +01:00
{
// completely sleep in minimized state
Sys_Sleep( 20 );
}
}
}
2011-03-12 22:00:00 +01:00
void Host_NewInstance( const char *name, const char *finalmsg )
{
if( !pChangeGame ) return;
host.change_game = true;
Q_strncpy( host.finalmsg, finalmsg, sizeof( host.finalmsg ));
2011-04-08 22:00:00 +02:00
pChangeGame( name ); // call from hl.exe
2011-03-12 22:00:00 +01:00
}
2010-03-25 22:00:00 +01:00
/*
=================
Host_ChangeGame_f
Change game modification
=================
*/
2008-08-04 22:00:00 +02:00
void Host_ChangeGame_f( void )
{
int i;
if( Cmd_Argc() != 2 )
{
2018-03-03 22:00:00 +01:00
Con_Printf( S_USAGE "game <directory>\n" );
2008-08-04 22:00:00 +02:00
return;
}
// validate gamedir
2011-03-12 22:00:00 +01:00
for( i = 0; i < SI.numgames; i++ )
2008-08-04 22:00:00 +02:00
{
2011-03-12 22:00:00 +01:00
if( !Q_stricmp( SI.games[i]->gamefolder, Cmd_Argv( 1 )))
2008-08-04 22:00:00 +02:00
break;
}
2011-03-12 22:00:00 +01:00
if( i == SI.numgames )
{
2018-03-03 22:00:00 +01:00
Con_Printf( "%s not exist\n", Cmd_Argv( 1 ));
2011-03-12 22:00:00 +01:00
}
2011-03-09 22:00:00 +01:00
else if( !Q_stricmp( GI->gamefolder, Cmd_Argv( 1 )))
2011-03-12 22:00:00 +01:00
{
2018-03-03 22:00:00 +01:00
Con_Printf( "%s already active\n", Cmd_Argv( 1 ));
2011-03-12 22:00:00 +01:00
}
else
{
2012-12-22 21:00:00 +01:00
const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 ));
const char *arg2 = va( "change game to '%s'", SI.games[i]->title );
Host_NewInstance( arg1, arg2 );
2011-03-12 22:00:00 +01:00
}
2008-08-04 22:00:00 +02:00
}
2011-03-08 22:00:00 +01:00
/*
===============
Host_Exec_f
===============
*/
void Host_Exec_f( void )
{
string cfgpath;
2014-05-09 22:00:00 +02:00
char *f, *txt;
2011-03-08 22:00:00 +01:00
size_t len;
if( Cmd_Argc() != 2 )
{
2018-03-03 22:00:00 +01:00
Con_Printf( S_USAGE "exec <filename>\n" );
2011-03-08 22:00:00 +01:00
return;
}
2017-03-14 22:00:00 +01:00
if( !Q_stricmp( "game.cfg", Cmd_Argv( 1 )))
2011-04-08 22:00:00 +02:00
{
2017-03-14 22:00:00 +01:00
// don't execute game.cfg in singleplayer
2017-02-21 22:00:00 +01:00
if( SV_GetMaxClients() == 1 )
2011-04-08 22:00:00 +02:00
return;
}
2011-03-09 22:00:00 +01:00
Q_strncpy( cfgpath, Cmd_Argv( 1 ), sizeof( cfgpath ));
2018-02-28 22:00:00 +01:00
COM_DefaultExtension( cfgpath, ".cfg" ); // append as default
2011-03-08 22:00:00 +01:00
f = FS_LoadFile( cfgpath, &len, false );
if( !f )
{
2018-09-28 23:00:00 +02:00
Con_Reportf( "couldn't exec %s\n", Cmd_Argv( 1 ));
2011-03-08 22:00:00 +01:00
return;
}
2017-03-31 23:00:00 +02:00
if( !Q_stricmp( "config.cfg", Cmd_Argv( 1 )))
host.config_executed = true;
2014-05-09 22:00:00 +02:00
// adds \n\0 at end of the file
2018-05-26 23:00:00 +02:00
txt = Z_Calloc( len + 2 );
2016-11-17 22:00:00 +01:00
memcpy( txt, f, len );
2014-05-09 22:00:00 +02:00
Q_strncat( txt, "\n", len + 2 );
2011-03-08 22:00:00 +01:00
Mem_Free( f );
2014-05-09 22:00:00 +02:00
2017-04-01 23:00:00 +02:00
if( !host.apply_game_config )
2018-09-28 23:00:00 +02:00
Con_Printf( "execing %s\n", Cmd_Argv( 1 ));
2014-05-09 22:00:00 +02:00
Cbuf_InsertText( txt );
Mem_Free( txt );
2011-03-08 22:00:00 +01:00
}
2011-03-09 22:00:00 +01:00
/*
===============
Host_MemStats_f
===============
*/
void Host_MemStats_f( void )
{
switch( Cmd_Argc( ))
{
case 1:
Mem_PrintList( 1<<30 );
Mem_PrintStats();
break;
case 2:
Mem_PrintList( Q_atoi( Cmd_Argv( 1 )) * 1024 );
Mem_PrintStats();
break;
default:
2018-03-03 22:00:00 +01:00
Con_Printf( S_USAGE "memlist <all>\n" );
2011-03-09 22:00:00 +01:00
break;
}
}
2008-08-10 22:00:00 +02:00
void Host_Minimize_f( void )
{
if( host.hWnd ) ShowWindow( host.hWnd, SW_MINIMIZE );
}
2017-02-13 22:00:00 +01:00
/*
=================
Host_IsLocalGame
singleplayer game detect
=================
*/
2010-10-26 22:00:00 +02:00
qboolean Host_IsLocalGame( void )
2010-06-20 22:00:00 +02:00
{
2017-02-13 22:00:00 +01:00
if( SV_Active( ))
{
return ( SV_GetMaxClients() == 1 ) ? true : false;
}
else
{
return ( CL_GetMaxClients() == 1 ) ? true : false;
}
2015-09-16 23:00:00 +02:00
}
qboolean Host_IsLocalClient( void )
{
// only the local client have the active server
2018-03-07 22:00:00 +01:00
if( CL_Initialized( ) && SV_Initialized( ))
2015-09-16 23:00:00 +02:00
return true;
return false;
2010-06-20 22:00:00 +02:00
}
2010-10-28 22:00:00 +02:00
/*
=================
Host_RegisterDecal
=================
*/
2016-11-14 22:00:00 +01:00
qboolean Host_RegisterDecal( const char *name, int *count )
2010-10-28 22:00:00 +02:00
{
2018-02-05 22:00:00 +01:00
char shortname[MAX_QPATH];
2010-10-28 22:00:00 +02:00
int i;
2018-03-05 22:00:00 +01:00
if( !COM_CheckString( name ))
2010-10-28 22:00:00 +02:00
return 0;
2018-02-28 22:00:00 +01:00
COM_FileBase( name, shortname );
2010-10-28 22:00:00 +02:00
for( i = 1; i < MAX_DECALS && host.draw_decals[i][0]; i++ )
{
2011-03-09 22:00:00 +01:00
if( !Q_stricmp( host.draw_decals[i], shortname ))
2010-10-28 22:00:00 +02:00
return true;
}
if( i == MAX_DECALS )
{
2018-09-28 23:00:00 +02:00
Con_DPrintf( S_ERROR "MAX_DECALS limit exceeded (%d)\n", MAX_DECALS );
2010-10-28 22:00:00 +02:00
return false;
}
// register new decal
2011-03-09 22:00:00 +01:00
Q_strncpy( host.draw_decals[i], shortname, sizeof( host.draw_decals[i] ));
2016-11-14 22:00:00 +01:00
*count += 1;
2010-10-28 22:00:00 +02:00
return true;
}
/*
=================
Host_InitDecals
=================
*/
void Host_InitDecals( void )
{
2016-11-14 22:00:00 +01:00
int i, num_decals = 0;
2018-03-05 22:00:00 +01:00
search_t *t;
2010-10-28 22:00:00 +02:00
2018-06-10 23:00:00 +02:00
// NOTE: only once resource without which engine can't continue work
if( !FS_FileExists( "gfx/conchars", false ))
Sys_Error( "W_LoadWadFile: couldn't load gfx.wad\n" );
2016-11-17 22:00:00 +01:00
memset( host.draw_decals, 0, sizeof( host.draw_decals ));
2010-10-28 22:00:00 +02:00
2018-03-05 22:00:00 +01:00
// lookup all the decals in decals.wad (basedir, gamedir, falldir)
2011-03-08 22:00:00 +01:00
t = FS_Search( "decals.wad/*.*", true, false );
2010-10-28 22:00:00 +02:00
for( i = 0; t && i < t->numfilenames; i++ )
{
2016-11-14 22:00:00 +01:00
if( !Host_RegisterDecal( t->filenames[i], &num_decals ))
2010-10-28 22:00:00 +02:00
break;
}
if( t ) Mem_Free( t );
2018-04-16 23:00:00 +02:00
Con_Reportf( "InitDecals: %i decals\n", num_decals );
2010-10-28 22:00:00 +02:00
}
2009-09-20 22:00:00 +02:00
/*
2011-03-28 22:00:00 +02:00
===================
2016-11-14 22:00:00 +01:00
Host_GetCommands
2008-07-06 22:00:00 +02:00
2011-03-28 22:00:00 +02:00
Add them exactly as if they had been typed at the console
===================
2008-07-06 22:00:00 +02:00
*/
2016-11-14 22:00:00 +01:00
void Host_GetCommands( void )
2008-07-06 22:00:00 +02:00
{
2011-03-28 22:00:00 +02:00
char *cmd;
2008-07-06 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
if( host.type != HOST_DEDICATED )
return;
cmd = Con_Input();
if( cmd ) Cbuf_AddText( cmd );
2017-03-11 22:00:00 +01:00
Cbuf_Execute ();
2016-11-14 22:00:00 +01:00
}
/*
===================
2017-02-13 22:00:00 +01:00
Host_CalcFPS
2016-11-14 22:00:00 +01:00
2017-02-13 22:00:00 +01:00
compute actual FPS for various modes
2016-11-14 22:00:00 +01:00
===================
*/
2017-02-13 22:00:00 +01:00
double Host_CalcFPS( void )
2016-11-14 22:00:00 +01:00
{
2017-02-13 22:00:00 +01:00
double fps = 0.0;
2016-11-14 22:00:00 +01:00
2018-02-02 22:00:00 +01:00
// NOTE: we should play demos with same fps as it was recorded
2017-02-13 22:00:00 +01:00
if( CL_IsPlaybackDemo() || CL_IsRecordDemo( ))
2018-04-25 23:00:00 +02:00
{
2017-02-13 22:00:00 +01:00
fps = CL_GetDemoFramerate();
2018-04-25 23:00:00 +02:00
}
2017-02-13 22:00:00 +01:00
else if( Host_IsLocalGame( ))
2018-04-25 23:00:00 +02:00
{
2017-02-13 22:00:00 +01:00
fps = host_maxfps->value;
2018-04-25 23:00:00 +02:00
}
else if( host.type == HOST_DEDICATED )
{
fps = sys_ticrate.value;
}
2016-11-14 22:00:00 +01:00
else
2008-07-06 22:00:00 +02:00
{
2017-02-13 22:00:00 +01:00
fps = host_maxfps->value;
2016-11-14 22:00:00 +01:00
fps = bound( MIN_FPS, fps, MAX_FPS );
2017-02-13 22:00:00 +01:00
}
2016-11-14 22:00:00 +01:00
2017-02-13 22:00:00 +01:00
// probably left part of this condition is redundant :-)
2018-02-12 22:00:00 +01:00
if( host.type != HOST_DEDICATED && Host_IsLocalGame( ) && !CL_IsTimeDemo( ))
2017-02-13 22:00:00 +01:00
{
// ajdust fps for vertical synchronization
2018-10-09 23:00:00 +02:00
if( CVAR_TO_BOOL( gl_vsync ))
2016-11-14 22:00:00 +01:00
{
2017-02-13 22:00:00 +01:00
if( vid_displayfrequency->value != 0.0f )
fps = vid_displayfrequency->value;
else fps = 60.0; // default
2016-11-14 22:00:00 +01:00
}
2008-07-06 22:00:00 +02:00
}
2016-11-14 22:00:00 +01:00
2017-02-13 22:00:00 +01:00
return fps;
2008-07-06 22:00:00 +02:00
}
2010-07-23 22:00:00 +02:00
/*
2010-10-09 22:00:00 +02:00
===================
Host_FilterTime
Returns false if the time is too short to run a frame
===================
2010-07-23 22:00:00 +02:00
*/
2010-10-26 22:00:00 +02:00
qboolean Host_FilterTime( float time )
2010-07-23 22:00:00 +02:00
{
2010-10-09 22:00:00 +02:00
static double oldtime;
2017-02-13 22:00:00 +01:00
double fps;
2010-06-20 22:00:00 +02:00
2010-10-09 22:00:00 +02:00
host.realtime += time;
2017-02-13 22:00:00 +01:00
fps = Host_CalcFPS( );
2010-10-09 22:00:00 +02:00
2016-11-14 22:00:00 +01:00
// clamp the fps in multiplayer games
2017-02-13 22:00:00 +01:00
if( fps != 0.0 )
2010-07-23 22:00:00 +02:00
{
2010-10-09 22:00:00 +02:00
// limit fps to withing tolerable range
fps = bound( MIN_FPS, fps, MAX_FPS );
2018-04-25 23:00:00 +02:00
if( host.type == HOST_DEDICATED )
{
if(( host.realtime - oldtime ) < ( 1.0 / ( fps + 1.0 )))
return false;
}
else
{
if(( host.realtime - oldtime ) < ( 1.0 / fps ))
return false;
}
2009-09-20 22:00:00 +02:00
}
2010-10-09 22:00:00 +02:00
host.frametime = host.realtime - oldtime;
host.realframetime = bound( MIN_FRAMETIME, host.frametime, MAX_FRAMETIME );
oldtime = host.realtime;
2017-02-13 22:00:00 +01:00
// NOTE: allow only in singleplayer while demos are not active
2018-02-12 22:00:00 +01:00
if( host_framerate->value > 0.0f && Host_IsLocalGame() && !CL_IsPlaybackDemo() && !CL_IsRecordDemo( ))
2017-02-13 22:00:00 +01:00
host.frametime = bound( MIN_FRAMETIME, host_framerate->value, MAX_FRAMETIME );
else host.frametime = bound( MIN_FRAMETIME, host.frametime, MAX_FRAMETIME );
2010-10-09 22:00:00 +02:00
return true;
2009-09-16 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
/*
=================
Host_Frame
=================
*/
2010-10-09 22:00:00 +02:00
void Host_Frame( float time )
2008-07-06 22:00:00 +02:00
{
2017-02-13 22:00:00 +01:00
Host_CheckSleep();
2010-07-23 22:00:00 +02:00
2017-02-13 22:00:00 +01:00
// decide the simulation time
if( !Host_FilterTime( time ))
return;
2008-07-06 22:00:00 +02:00
2017-02-13 22:00:00 +01:00
Host_InputFrame (); // input frame
2017-02-21 22:00:00 +01:00
Host_ClientBegin (); // begin client
2017-03-11 22:00:00 +01:00
Host_GetCommands (); // dedicated in
2017-02-13 22:00:00 +01:00
Host_ServerFrame (); // server frame
Host_ClientFrame (); // client frame
2016-11-14 22:00:00 +01:00
2017-02-13 22:00:00 +01:00
host.framecount++;
2008-07-06 22:00:00 +02:00
}
2010-10-09 22:00:00 +02:00
2007-09-10 22:00:00 +02:00
/*
=================
Host_Error
=================
*/
void Host_Error( const char *error, ... )
{
2010-07-26 22:00:00 +02:00
static char hosterror1[MAX_SYSPATH];
static char hosterror2[MAX_SYSPATH];
2010-10-26 22:00:00 +02:00
static qboolean recursive = false;
2007-09-10 22:00:00 +02:00
va_list argptr;
2011-09-19 22:00:00 +02:00
if( host.mouse_visible && !CL_IsInMenu( ))
2011-03-19 22:00:00 +01:00
{
2011-09-19 22:00:00 +02:00
// hide VGUI mouse
2011-03-19 22:00:00 +01:00
while( ShowCursor( false ) >= 0 );
host.mouse_visible = false;
}
2007-09-10 22:00:00 +02:00
va_start( argptr, error );
2011-03-09 22:00:00 +01:00
Q_vsprintf( hosterror1, error, argptr );
2007-09-10 22:00:00 +02:00
va_end( argptr );
2011-03-09 22:00:00 +01:00
CL_WriteMessageHistory (); // before Q_error call
2010-03-27 22:00:00 +01:00
2011-02-25 22:00:00 +01:00
if( host.framecount < 3 )
2008-11-04 22:00:00 +01:00
{
2011-03-12 22:00:00 +01:00
Sys_Error( "Host_InitError: %s", hosterror1 );
2008-11-04 22:00:00 +01:00
}
2008-11-05 22:00:00 +01:00
else if( host.framecount == host.errorframe )
{
2011-03-12 22:00:00 +01:00
Sys_Error( "Host_MultiError: %s", hosterror2 );
2008-11-05 22:00:00 +01:00
return;
}
2011-02-25 22:00:00 +01:00
else
{
2018-03-03 22:00:00 +01:00
if( host.allow_console )
2011-09-19 22:00:00 +02:00
{
UI_SetActiveMenu( false );
Key_SetKeyDest( key_console );
2018-03-03 22:00:00 +01:00
Con_Printf( "Host_Error: %s", hosterror1 );
2011-09-19 22:00:00 +02:00
}
else MSGBOX2( hosterror1 );
2011-02-25 22:00:00 +01:00
}
// host is shutting down. don't invoke infinite loop
2018-02-25 22:00:00 +01:00
if( host.status == HOST_SHUTDOWN ) return;
2007-09-10 22:00:00 +02:00
2008-08-04 22:00:00 +02:00
if( recursive )
2007-09-10 22:00:00 +02:00
{
2018-03-03 22:00:00 +01:00
Con_Printf( "Host_RecursiveError: %s", hosterror2 );
2011-03-12 22:00:00 +01:00
Sys_Error( hosterror1 );
2007-11-21 22:00:00 +01:00
return; // don't multiple executes
2007-09-10 22:00:00 +02:00
}
2007-11-21 22:00:00 +01:00
2007-09-10 22:00:00 +02:00
recursive = true;
2011-03-09 22:00:00 +01:00
Q_strncpy( hosterror2, hosterror1, MAX_SYSPATH );
2008-11-06 22:00:00 +01:00
host.errorframe = host.framecount; // to avoid multply calls per frame
2011-04-08 22:00:00 +02:00
Q_sprintf( host.finalmsg, "Server crashed: %s", hosterror1 );
2007-09-10 22:00:00 +02:00
2011-09-19 22:00:00 +02:00
// clearing cmd buffer to prevent execute any commands
2018-03-07 22:00:00 +01:00
COM_InitHostState();
2011-09-19 22:00:00 +02:00
Cbuf_Clear();
2018-02-28 22:00:00 +01:00
Host_ShutdownServer();
2011-09-19 22:00:00 +02:00
CL_Drop(); // drop clients
2012-03-24 21:00:00 +01:00
// recreate world if needs
CL_ClearEdicts ();
2011-09-19 22:00:00 +02:00
// release all models
2018-02-25 22:00:00 +01:00
Mod_FreeAll();
2007-09-10 22:00:00 +02:00
recursive = false;
Host_AbortCurrentFrame();
}
void Host_Error_f( void )
{
2010-02-18 22:00:00 +01:00
const char *error = Cmd_Argv( 1 );
if( !*error ) error = "Invoked host error";
Host_Error( "%s\n", error );
}
void Sys_Error_f( void )
{
const char *error = Cmd_Argv( 1 );
if( !*error ) error = "Invoked sys error";
2011-03-12 22:00:00 +01:00
Sys_Error( "%s\n", error );
2007-11-17 22:00:00 +01:00
}
/*
=================
Host_Crash_f
=================
*/
2009-10-02 22:00:00 +02:00
static void Host_Crash_f( void )
2007-11-17 22:00:00 +01:00
{
*(int *)0 = 0xffffffff;
}
2012-12-21 21:00:00 +01:00
/*
=================
Host_InitCommon
=================
*/
2017-03-14 22:00:00 +01:00
void Host_InitCommon( const char *hostname, qboolean bChangeGame )
2008-07-06 22:00:00 +02:00
{
2011-03-12 22:00:00 +01:00
MEMORYSTATUS lpBuffer;
char dev_level[4];
2018-04-25 23:00:00 +02:00
char ticrate[16];
2017-03-14 22:00:00 +01:00
char progname[128];
char cmdline[128];
qboolean parse_cmdline = false;
2011-03-12 22:00:00 +01:00
char szTemp[MAX_SYSPATH];
2018-03-03 22:00:00 +01:00
int developer = 0;
2011-09-19 22:00:00 +02:00
string szRootPath;
2017-03-14 22:00:00 +01:00
char *in, *out;
2018-04-25 23:00:00 +02:00
double fps;
2011-03-09 22:00:00 +01:00
2011-03-12 22:00:00 +01:00
lpBuffer.dwLength = sizeof( MEMORYSTATUS );
GlobalMemoryStatus( &lpBuffer );
2008-07-06 22:00:00 +02:00
2011-04-05 22:00:00 +02:00
if( !GetCurrentDirectory( sizeof( host.rootdir ), host.rootdir ))
2016-11-17 22:00:00 +01:00
Sys_Error( "couldn't determine current directory\n" );
2011-04-05 22:00:00 +02:00
if( host.rootdir[Q_strlen( host.rootdir ) - 1] == '/' )
host.rootdir[Q_strlen( host.rootdir ) - 1] = 0;
2011-03-12 22:00:00 +01:00
host.oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
2010-11-28 22:00:00 +01:00
host.hInst = GetModuleHandle( NULL );
2011-03-12 22:00:00 +01:00
host.change_game = bChangeGame;
2017-03-31 23:00:00 +02:00
host.config_executed = false;
2018-03-03 22:00:00 +01:00
host.status = HOST_INIT; // initialzation started
2011-03-12 22:00:00 +01:00
2018-03-03 22:00:00 +01:00
Memory_Init(); // init memory subsystem
2011-03-12 22:00:00 +01:00
2017-03-14 22:00:00 +01:00
progname[0] = cmdline[0] = '\0';
in = (char *)hostname;
out = progname;
while( *in != '\0' )
{
if( parse_cmdline )
{
*out++ = *in++;
}
else
{
2018-10-16 23:00:00 +02:00
// now we found cmdline
if( *in == ' ' && ( in[1] == '+' || in[1] == '-' ))
2017-03-14 22:00:00 +01:00
{
parse_cmdline = true;
*out++ = '\0';
out = cmdline;
}
else *out++ = *in++;
}
}
*out = '\0'; // write terminator
Sys_ParseCommandLine( GetCommandLine( ), false );
2011-03-12 22:00:00 +01:00
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
2011-03-09 22:00:00 +01:00
host.mempool = Mem_AllocPool( "Zone Engine" );
2010-11-28 22:00:00 +01:00
2018-10-16 23:00:00 +02:00
// get name of executable
if( GetModuleFileName( NULL, szTemp, sizeof( szTemp )))
COM_FileBase( szTemp, SI.exeName );
2018-04-19 23:00:00 +02:00
// HACKHACK: Quake console is always allowed
2018-10-16 23:00:00 +02:00
if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" ))
2018-03-03 22:00:00 +01:00
host.allow_console = true;
2016-11-19 22:00:00 +01:00
2011-03-12 22:00:00 +01:00
if( Sys_CheckParm( "-dev" ))
{
2018-03-03 22:00:00 +01:00
host.allow_console = true;
developer = DEV_NORMAL;
2011-03-12 22:00:00 +01:00
if( Sys_GetParmFromCmdLine( "-dev", dev_level ))
{
if( Q_isdigit( dev_level ))
2018-03-03 22:00:00 +01:00
developer = bound( DEV_NONE, abs( Q_atoi( dev_level )), DEV_EXTENDED );
2011-03-12 22:00:00 +01:00
}
}
host.type = HOST_NORMAL; // predict state
host.con_showalways = true;
2018-02-28 22:00:00 +01:00
COM_ExtractFilePath( szTemp, szRootPath );
2011-09-19 22:00:00 +02:00
if( Q_stricmp( host.rootdir, szRootPath ))
{
Q_strncpy( host.rootdir, szRootPath, sizeof( host.rootdir ));
SetCurrentDirectory( host.rootdir );
}
2017-07-12 23:00:00 +02:00
if( SI.exeName[0] == '#' ) host.type = HOST_DEDICATED;
2011-03-12 22:00:00 +01:00
// determine host type
if( progname[0] == '#' )
{
2017-07-12 23:00:00 +02:00
Q_strncpy( SI.basedirName, progname + 1, sizeof( SI.basedirName ));
2011-03-12 22:00:00 +01:00
host.type = HOST_DEDICATED;
}
2017-07-12 23:00:00 +02:00
else Q_strncpy( SI.basedirName, progname, sizeof( SI.basedirName ));
2011-03-12 22:00:00 +01:00
2017-03-14 22:00:00 +01:00
if( Sys_CheckParm( "-dedicated" ))
host.type = HOST_DEDICATED;
2011-03-12 22:00:00 +01:00
if( host.type == HOST_DEDICATED )
{
// check for duplicate dedicated server
host.hMutex = CreateMutex( NULL, 0, "Xash Dedicated Server" );
2013-02-24 21:00:00 +01:00
2011-03-12 22:00:00 +01:00
if( !host.hMutex )
{
MSGBOX( "Dedicated server already running" );
Sys_Quit();
return;
}
2017-03-14 22:00:00 +01:00
Sys_MergeCommandLine( cmdline );
2011-03-13 22:00:00 +01:00
2011-03-12 22:00:00 +01:00
CloseHandle( host.hMutex );
host.hMutex = CreateSemaphore( NULL, 0, 1, "Xash Dedicated Server" );
2018-03-03 22:00:00 +01:00
host.allow_console = true;
2011-03-12 22:00:00 +01:00
}
else
{
// don't show console as default
2018-03-03 22:00:00 +01:00
if( developer <= DEV_NORMAL )
host.con_showalways = false;
2011-03-12 22:00:00 +01:00
}
2018-03-03 22:00:00 +01:00
// member console allowing
host.allow_console_init = host.allow_console;
2012-02-08 21:00:00 +01:00
2018-04-25 23:00:00 +02:00
timeBeginPeriod( 1 );
2017-02-15 22:00:00 +01:00
Con_CreateConsole(); // system console used by dedicated server or show fatal errors
2011-03-12 22:00:00 +01:00
2017-02-15 22:00:00 +01:00
// NOTE: this message couldn't be passed into game console but it doesn't matter
2018-09-28 23:00:00 +02:00
Con_Reportf( "Sys_LoadLibrary: Loading xash.dll - ok\n" );
2011-03-12 22:00:00 +01:00
2017-02-15 22:00:00 +01:00
// get default screen res
VID_InitDefaultResolution();
2018-02-25 22:00:00 +01:00
// init host state machine
COM_InitHostState();
2011-03-09 22:00:00 +01:00
// startup cmds and cvars subsystem
Cmd_Init();
Cvar_Init();
2011-03-08 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
// share developer level across all dlls
2018-03-03 22:00:00 +01:00
Q_snprintf( dev_level, sizeof( dev_level ), "%i", developer );
Cvar_DirectSet( &host_developer, dev_level );
2018-04-25 23:00:00 +02:00
Cvar_RegisterVariable( &sys_ticrate );
if( Sys_GetParmFromCmdLine( "-sys_ticrate", ticrate ))
{
fps = bound( MIN_FPS, atof( ticrate ), MAX_FPS );
Cvar_SetValue( "sys_ticrate", fps );
}
2018-03-03 22:00:00 +01:00
Con_Init(); // early console running to catch all the messages
2011-03-09 22:00:00 +01:00
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
2011-03-12 22:00:00 +01:00
2011-03-08 22:00:00 +01:00
FS_Init();
2011-02-28 22:00:00 +01:00
Image_Init();
Sound_Init();
2009-09-13 22:00:00 +02:00
FS_LoadGameInfo( NULL );
2011-03-13 22:00:00 +01:00
Q_strncpy( host.gamefolder, GI->gamefolder, sizeof( host.gamefolder ));
2011-02-28 22:00:00 +01:00
2011-09-19 22:00:00 +02:00
if( GI->secure )
{
// clear all developer levels when game is protected
2018-03-03 22:00:00 +01:00
Cvar_DirectSet( &host_developer, "0" );
host.allow_console_init = false;
2011-09-19 22:00:00 +02:00
host.con_showalways = false;
2018-03-03 22:00:00 +01:00
host.allow_console = false;
2011-09-19 22:00:00 +02:00
}
2011-03-13 22:00:00 +01:00
HPAK_Init();
2010-10-28 22:00:00 +02:00
2008-07-06 22:00:00 +02:00
IN_Init();
2011-03-12 22:00:00 +01:00
Key_Init();
2008-07-06 22:00:00 +02:00
}
void Host_FreeCommon( void )
{
2011-02-28 22:00:00 +01:00
Image_Shutdown();
Sound_Shutdown();
2010-10-07 22:00:00 +02:00
Netchan_Shutdown();
2018-03-11 22:00:00 +01:00
HPAK_FlushHostQueue();
2011-03-08 22:00:00 +01:00
FS_Shutdown();
2008-07-06 22:00:00 +02:00
}
2007-11-17 22:00:00 +01:00
/*
=================
2011-03-12 22:00:00 +01:00
Host_Main
2007-11-17 22:00:00 +01:00
=================
*/
2011-03-12 22:00:00 +01:00
int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func )
2007-11-17 22:00:00 +01:00
{
2011-03-12 22:00:00 +01:00
static double oldtime, newtime;
2007-11-17 22:00:00 +01:00
2012-12-21 21:00:00 +01:00
pChangeGame = func; // may be NULL
2011-03-10 22:00:00 +01:00
2011-03-12 22:00:00 +01:00
Host_InitCommon( progname, bChangeGame );
2007-11-17 22:00:00 +01:00
// init commands and vars
2018-03-03 22:00:00 +01:00
if( host_developer.value >= DEV_EXTENDED )
2007-11-17 22:00:00 +01:00
{
2010-02-18 22:00:00 +01:00
Cmd_AddCommand ( "sys_error", Sys_Error_f, "just throw a fatal error to test shutdown procedures");
Cmd_AddCommand ( "host_error", Host_Error_f, "just throw a host error to test shutdown procedures");
Cmd_AddCommand ( "crash", Host_Crash_f, "a way to force a bus error for development reasons");
2018-03-04 22:00:00 +01:00
}
2008-07-17 22:00:00 +02:00
2018-04-25 23:00:00 +02:00
host_maxfps = Cvar_Get( "fps_max", "72", FCVAR_ARCHIVE, "host fps upper limit" );
2009-02-03 22:00:00 +01:00
host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" );
2017-02-12 22:00:00 +01:00
host_gameloaded = Cvar_Get( "host_gameloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded game.dll" );
host_clientloaded = Cvar_Get( "host_clientloaded", "0", FCVAR_READ_ONLY, "inidcates a loaded client.dll" );
2010-10-14 22:00:00 +02:00
host_limitlocal = Cvar_Get( "host_limitlocal", "0", 0, "apply cl_cmdrate and rate to loopback connection" );
2017-02-12 22:00:00 +01:00
con_gamemaps = Cvar_Get( "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" );
2018-09-08 23:00:00 +02:00
build = Cvar_Get( "buildnum", va( "%i", Q_buildnum()), FCVAR_READ_ONLY, "returns a current build number" );
2018-08-23 23:00:00 +02:00
ver = Cvar_Get( "ver", va( "%i/%s (hw build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildnum()), FCVAR_READ_ONLY, "shows an engine version" );
2010-04-17 22:00:00 +02:00
2010-11-21 22:00:00 +01:00
Mod_Init();
2007-11-17 22:00:00 +01:00
NET_Init();
Netchan_Init();
2008-07-11 22:00:00 +02:00
2011-03-22 22:00:00 +01:00
// allow to change game from the console
if( pChangeGame != NULL )
{
Cmd_AddCommand( "game", Host_ChangeGame_f, "change game" );
2017-02-12 22:00:00 +01:00
Cvar_Get( "host_allow_changegame", "1", FCVAR_READ_ONLY, "allows to change games" );
2011-03-22 22:00:00 +01:00
}
else
{
2017-02-12 22:00:00 +01:00
Cvar_Get( "host_allow_changegame", "0", FCVAR_READ_ONLY, "allows to change games" );
2011-03-22 22:00:00 +01:00
}
2007-11-17 22:00:00 +01:00
SV_Init();
CL_Init();
2008-07-06 22:00:00 +02:00
2009-07-12 22:00:00 +02:00
if( host.type == HOST_DEDICATED )
{
2012-04-06 22:00:00 +02:00
Con_InitConsoleCommands ();
2009-09-10 22:00:00 +02:00
Cmd_AddCommand( "quit", Sys_Quit, "quit the game" );
Cmd_AddCommand( "exit", Sys_Quit, "quit the game" );
2009-10-02 22:00:00 +02:00
}
2017-03-14 22:00:00 +01:00
else Cmd_AddCommand( "minimize", Host_Minimize_f, "minimize main window to tray" );
2009-10-02 22:00:00 +02:00
2008-11-04 22:00:00 +01:00
host.errorframe = 0;
2010-10-09 22:00:00 +02:00
2011-03-09 22:00:00 +01:00
// post initializations
switch( host.type )
{
case HOST_NORMAL:
2011-03-12 22:00:00 +01:00
Con_ShowConsole( false ); // hide console
2011-03-09 22:00:00 +01:00
// execute startup config and cmdline
2017-07-12 23:00:00 +02:00
Cbuf_AddText( va( "exec %s.rc\n", SI.rcName ));
2011-04-06 22:00:00 +02:00
Cbuf_Execute();
2017-03-31 23:00:00 +02:00
if( !host.config_executed )
{
Cbuf_AddText( "exec config.cfg\n" );
Cbuf_Execute();
}
2011-03-09 22:00:00 +01:00
break;
2017-03-26 23:00:00 +02:00
case HOST_DEDICATED:
// allways parse commandline in dedicated-mode
host.stuffcmds_pending = true;
break;
2011-03-09 22:00:00 +01:00
}
2011-03-12 22:00:00 +01:00
host.change_game = false; // done
2011-03-09 22:00:00 +01:00
Cmd_RemoveCommand( "setgl" );
2017-03-26 23:00:00 +02:00
Cbuf_ExecStuffCmds(); // execute stuffcmds (commandline)
SCR_CheckStartupVids(); // must be last
2010-10-09 22:00:00 +02:00
2016-11-19 22:00:00 +01:00
oldtime = Sys_DoubleTime() - 0.1;
2010-10-09 22:00:00 +02:00
2018-03-13 22:00:00 +01:00
if( host.type == HOST_DEDICATED && GameState->nextstate == STATE_RUNFRAME )
Con_Printf( "type 'map <mapname>' to run server... (TAB-autocomplete is working too)\n" );
2017-09-04 23:00:00 +02:00
2007-11-17 22:00:00 +01:00
// main window message loop
2012-03-01 21:00:00 +01:00
while( !host.crashed )
2007-11-17 22:00:00 +01:00
{
2010-10-09 22:00:00 +02:00
newtime = Sys_DoubleTime ();
2018-02-25 22:00:00 +01:00
COM_Frame( newtime - oldtime );
2010-10-09 22:00:00 +02:00
oldtime = newtime;
2007-11-17 22:00:00 +01:00
}
2011-03-12 22:00:00 +01:00
// never reached
return 0;
2007-11-17 22:00:00 +01:00
}
/*
=================
Host_Shutdown
=================
*/
2011-03-12 22:00:00 +01:00
void EXPORT Host_Shutdown( void )
2007-11-17 22:00:00 +01:00
{
2011-03-12 22:00:00 +01:00
if( host.shutdown_issued ) return;
host.shutdown_issued = true;
2009-09-28 22:00:00 +02:00
2018-02-25 22:00:00 +01:00
if( host.status != HOST_ERR_FATAL ) host.status = HOST_SHUTDOWN; // prepare host to normal shutdown
2011-04-08 22:00:00 +02:00
if( !host.change_game ) Q_strncpy( host.finalmsg, "Server shutdown", sizeof( host.finalmsg ));
2008-06-06 22:00:00 +02:00
2011-03-15 22:00:00 +01:00
if( host.type == HOST_NORMAL )
Host_WriteConfig();
2018-04-18 23:00:00 +02:00
SV_Shutdown( "Server shutdown\n" );
2010-06-16 22:00:00 +02:00
CL_Shutdown();
2010-12-13 22:00:00 +01:00
Mod_Shutdown();
2010-02-28 22:00:00 +01:00
NET_Shutdown();
2007-11-17 22:00:00 +01:00
Host_FreeCommon();
2011-03-12 22:00:00 +01:00
Con_DestroyConsole();
2018-03-14 22:00:00 +01:00
// must be last, console uses this
2018-03-13 22:00:00 +01:00
Mem_FreePool( &host.mempool );
2011-03-12 22:00:00 +01:00
// restore filter
if( host.oldFilter ) SetUnhandledExceptionFilter( host.oldFilter );
2010-10-26 22:00:00 +02:00
}
// main DLL entry point
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
2011-03-12 22:00:00 +01:00
hCurrent = hinstDLL;
2010-10-26 22:00:00 +02:00
return TRUE;
2007-06-21 22:00:00 +02:00
}