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

1068 lines
25 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
2010-10-22 22:00:00 +02:00
convar_t *host_serverstate;
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
2008-07-31 22:00:00 +02:00
// these cvars will be duplicated on each client across network
2010-12-02 22:00:00 +01:00
int Host_ServerState( void )
{
return Cvar_VariableInteger( "host_serverstate" );
}
2008-07-06 22:00:00 +02: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 )
{
2017-08-23 23:00:00 +02:00
if( !SV_Active( )) return;
2011-04-08 22:00:00 +02:00
Q_strncpy( host.finalmsg, "Server was killed", MAX_STRING );
2009-10-13 22:00:00 +02:00
SV_Shutdown( false );
}
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 ))
MsgDev( D_REPORT, "^3EXT:^7 big world support enabled\n" );
if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ))
MsgDev( D_REPORT, "^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 ))
MsgDev( D_REPORT, "^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 ))
MsgDev( D_REPORT, "^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 ))
MsgDev( D_REPORT, "^3EXT:^7 Compensate quake bug enabled\n" );
2013-11-03 21:00:00 +01:00
2016-11-14 22:00:00 +01:00
if( FBitSet( host.features, ENGINE_FIXED_FRAMERATE ))
2017-02-13 22:00:00 +01:00
MsgDev( D_REPORT, "^3EXT:^7 runnung server at constant fps\n" );
2012-05-21 22:00:00 +02:00
}
2010-03-25 22:00:00 +01:00
/*
================
Host_NewGame
================
*/
2010-10-26 22:00:00 +02:00
qboolean Host_NewGame( const char *mapName, qboolean loadGame )
2010-03-25 22:00:00 +01:00
{
2010-10-26 22:00:00 +02:00
qboolean iRet;
2010-03-25 22:00:00 +01:00
iRet = SV_NewGame( mapName, loadGame );
return iRet;
}
2014-09-05 22:00:00 +02:00
2009-10-13 22:00:00 +02:00
/*
================
Host_EndGame
================
*/
void Host_EndGame( const char *message, ... )
{
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 );
MsgDev( D_INFO, "Host_EndGame: %s\n", string );
2017-08-23 23:00:00 +02:00
if( SV_Active( ))
2009-10-13 22:00:00 +02:00
{
2011-04-08 22:00:00 +02:00
Q_snprintf( host.finalmsg, sizeof( host.finalmsg ), "Host_EndGame: %s", string );
2009-10-13 22:00:00 +02:00
SV_Shutdown( false );
}
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
2014-05-17 22:00:00 +02:00
Mod_ClearAll( true );
2011-09-19 22:00:00 +02:00
2009-10-13 22:00:00 +02:00
Host_AbortCurrentFrame ();
}
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
}
2007-11-14 22:00:00 +01:00
/*
==================
Host_SetServerState
==================
*/
void Host_SetServerState( int state )
{
2017-02-12 22:00:00 +01:00
Cvar_FullSet( "host_serverstate", va( "%i", state ), FCVAR_READ_ONLY );
2007-11-14 22:00:00 +01: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
Sys_Sleep( 1 );
}
else
{
if( host.state == HOST_NOFOCUS )
{
if( Host_ServerState() && CL_IsInGame( ))
Sys_Sleep( 1 ); // listenserver
else Sys_Sleep( 20 ); // sleep 20 ms otherwise
}
else if( host.state == HOST_SLEEP )
{
// 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 )
{
2009-09-10 22:00:00 +02:00
Msg( "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 )
{
Msg( "%s not exist\n", Cmd_Argv( 1 ));
}
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
{
2009-09-10 22:00:00 +02:00
Msg( "%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 )
{
Msg( "Usage: exec <filename>\n" );
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 ));
2011-03-08 22:00:00 +01:00
FS_DefaultExtension( cfgpath, ".cfg" ); // append as default
f = FS_LoadFile( cfgpath, &len, false );
if( !f )
{
MsgDev( D_NOTE, "couldn't exec %s\n", Cmd_Argv( 1 ));
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
txt = Z_Malloc( 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 )
MsgDev( D_INFO, "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:
Msg( "Usage: memlist <all>\n" );
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
2017-02-13 22:00:00 +01:00
if( CL_Active( ) && SV_Active( ))
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;
2016-11-14 22:00:00 +01:00
if( !name || !*name )
2010-10-28 22:00:00 +02:00
return 0;
FS_FileBase( name, shortname );
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 )
{
MsgDev( D_ERROR, "Host_RegisterDecal: MAX_DECALS limit exceeded\n" );
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 )
{
search_t *t;
2016-11-14 22:00:00 +01:00
int i, num_decals = 0;
2010-10-28 22:00:00 +02:00
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
// lookup all decals in decals.wad
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 );
2010-11-15 22:00:00 +01:00
MsgDev( D_NOTE, "InitDecals: %i decals\n", num_decals );
2010-10-28 22:00:00 +02:00
}
2012-03-01 21:00:00 +01:00
/*
=================
Host_RestartAmbientSounds
Write ambient sounds into demo
=================
*/
void Host_RestartAmbientSounds( void )
{
2016-11-14 22:00:00 +01:00
soundlist_t soundInfo[128];
2012-08-31 22:00:00 +02:00
string curtrack, looptrack;
2012-03-01 21:00:00 +01:00
int i, nSounds;
2016-11-14 22:00:00 +01:00
long position;
2012-03-01 21:00:00 +01:00
2016-11-14 22:00:00 +01:00
if( !SV_Active( )) return;
2012-03-01 21:00:00 +01:00
2016-11-14 22:00:00 +01:00
nSounds = S_GetCurrentStaticSounds( soundInfo, 128 );
2012-03-01 21:00:00 +01:00
for( i = 0; i < nSounds; i++ )
{
2016-11-14 22:00:00 +01:00
soundlist_t *si = &soundInfo[i];
if( !si->looping || si->entnum == -1 )
2012-03-01 21:00:00 +01:00
continue;
2012-08-29 22:00:00 +02:00
MsgDev( D_NOTE, "Restarting sound %s...\n", soundInfo[i].name );
2016-11-14 22:00:00 +01:00
S_StopSound( si->entnum, si->channel, si->name );
SV_StartSound( pfnPEntityOfEntIndex( si->entnum ), CHAN_STATIC, si->name, si->volume, si->attenuation, 0, si->pitch );
2012-03-01 21:00:00 +01:00
}
2012-08-31 22:00:00 +02:00
// restart soundtrack
if( S_StreamGetCurrentState( curtrack, looptrack, &position ))
{
SV_StartMusic( curtrack, looptrack, position );
}
2012-03-01 21:00:00 +01:00
}
2013-10-23 22:00:00 +02:00
/*
=================
Host_RestartDecals
Write all the decals into demo
=================
*/
void Host_RestartDecals( void )
{
decallist_t *entry;
int decalIndex;
int modelIndex;
sizebuf_t *msg;
int i;
2016-11-14 22:00:00 +01:00
if( !SV_Active( )) return;
2013-10-23 22:00:00 +02:00
// g-cont. add space for studiodecals if present
host.decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 );
host.numdecals = R_CreateDecalList( host.decalList, false );
// remove decals from map
R_ClearAllDecals();
// write decals into reliable datagram
msg = SV_GetReliableDatagram();
// restore decals and write them into network message
for( i = 0; i < host.numdecals; i++ )
{
entry = &host.decalList[i];
2013-12-01 21:00:00 +01:00
modelIndex = pfnPEntityOfEntIndex( entry->entityIndex )->v.modelindex;
// game override
if( SV_RestoreCustomDecal( entry, pfnPEntityOfEntIndex( entry->entityIndex ), false ))
continue;
2013-10-23 22:00:00 +02:00
decalIndex = pfnDecalIndex( entry->name );
// BSP and studio decals has different messages
if( entry->flags & FDECAL_STUDIO )
{
// NOTE: studio decal trace start saved into impactPlaneNormal
SV_CreateStudioDecal( msg, entry->position, entry->impactPlaneNormal, decalIndex, entry->entityIndex,
modelIndex, entry->flags, &entry->studio_state );
}
else
{
SV_CreateDecal( msg, entry->position, decalIndex, entry->entityIndex, modelIndex, entry->flags, entry->scale );
}
}
Z_Free( host.decalList );
host.decalList = NULL;
host.numdecals = 0;
}
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( ))
fps = CL_GetDemoFramerate();
else if( Host_IsLocalGame( ))
fps = host_maxfps->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;
if( fps == 0.0 ) fps = HOST_FPS; // default for multiplayer
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 :-)
if( host.type != HOST_DEDICATED && Host_IsLocalGame( ))
{
// ajdust fps for vertical synchronization
if( gl_vsync != NULL && gl_vsync->value )
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 );
2017-02-13 22:00:00 +01:00
if(( host.realtime - oldtime ) < ( 1.0 / fps ))
2010-10-09 22:00:00 +02:00
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
if( host_framerate->value > 0.0f && Host_IsLocalGame() && !CL_IsPlaybackDemo() && !CL_IsRecordDemo())
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
{
2008-12-20 22:00:00 +01:00
if( setjmp( host.abortframe ))
2008-07-06 22:00:00 +02:00
return;
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-11-14 22:00:00 +01:00
/*
================
Host_Print
Handles cursor positioning, line wrapping, etc
All console printing must go through this in order to be logged to disk
If no console is visible, the text will appear at the top of the game window
================
*/
void Host_Print( const char *txt )
{
2008-07-12 22:00:00 +02:00
if( host.rd.target )
2007-11-14 22:00:00 +01:00
{
2011-03-09 22:00:00 +01:00
if(( Q_strlen( txt ) + Q_strlen( host.rd.buffer )) > ( host.rd.buffersize - 1 ))
2007-11-14 22:00:00 +01:00
{
2008-07-12 22:00:00 +02:00
if( host.rd.flush )
2007-11-14 22:00:00 +01:00
{
2008-07-12 22:00:00 +02:00
host.rd.flush( host.rd.address, host.rd.target, host.rd.buffer );
2007-11-14 22:00:00 +01:00
*host.rd.buffer = 0;
}
}
2011-03-09 22:00:00 +01:00
Q_strcat( host.rd.buffer, txt );
2007-11-14 22:00:00 +01:00
return;
}
2017-03-14 22:00:00 +01:00
2007-11-14 22:00:00 +01:00
Con_Print( txt ); // echo to client console
}
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
{
2011-09-19 22:00:00 +02:00
if( host.developer > 0 )
{
UI_SetActiveMenu( false );
Key_SetKeyDest( key_console );
Msg( "Host_Error: %s", hosterror1 );
}
else MSGBOX2( hosterror1 );
2011-02-25 22:00:00 +01:00
}
// host is shutting down. don't invoke infinite loop
if( host.state == 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
{
2008-11-06 22:00:00 +01:00
Msg( "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
Cbuf_Clear();
2011-03-24 22:00:00 +01:00
SV_Shutdown( false );
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
2014-05-17 22:00:00 +02:00
Mod_ClearAll( false );
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
}
2010-03-27 22:00:00 +01:00
void Net_Error_f( void )
{
2011-03-09 22:00:00 +01:00
Q_strncpy( host.finalmsg, Cmd_Argv( 1 ), sizeof( host.finalmsg ));
2010-03-27 22:00:00 +01:00
SV_ForceError();
}
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];
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];
2011-09-19 22:00:00 +02:00
string szRootPath;
2017-03-14 22:00:00 +01:00
char *in, *out;
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;
host.state = HOST_INIT; // initialzation started
2012-02-08 21:00:00 +01:00
host.developer = host.old_developer = 0;
2017-03-31 23:00:00 +02:00
host.config_executed = false;
2011-03-12 22:00:00 +01:00
2017-02-26 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
{
if( *in == ' ' )
{
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
2016-11-19 22:00:00 +01:00
if( Sys_CheckParm( "-console" ))
host.developer = 1;
2011-03-12 22:00:00 +01:00
if( Sys_CheckParm( "-dev" ))
{
if( Sys_GetParmFromCmdLine( "-dev", dev_level ))
{
if( Q_isdigit( dev_level ))
host.developer = abs( Q_atoi( dev_level ));
else host.developer++; // -dev == 1, -dev -console == 2
}
else host.developer++; // -dev == 1, -dev -console == 2
}
host.type = HOST_NORMAL; // predict state
host.con_showalways = true;
// we can specified custom name, from Sys_NewInstance
if( GetModuleFileName( NULL, szTemp, sizeof( szTemp )) && !host.change_game )
2017-07-12 23:00:00 +02:00
FS_FileBase( szTemp, SI.exeName );
2011-03-12 22:00:00 +01:00
2011-09-19 22:00:00 +02:00
FS_ExtractFilePath( szTemp, szRootPath );
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" );
if( host.developer < 3 ) host.developer = 3; // otherwise we see empty console
}
else
{
// don't show console as default
if( host.developer < D_WARN ) host.con_showalways = false;
}
2012-02-08 21:00:00 +01:00
host.old_developer = host.developer;
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
2011-03-12 22:00:00 +01:00
MsgDev( D_NOTE, "Sys_LoadLibrary: Loading xash.dll - ok\n" );
2017-02-15 22:00:00 +01:00
// get default screen res
VID_InitDefaultResolution();
2011-03-09 22:00:00 +01:00
// startup cmds and cvars subsystem
Cmd_Init();
Cvar_Init();
2017-02-15 22:00:00 +01:00
Con_Init(); // early console running to catch all the messages
2011-03-08 22:00:00 +01:00
2011-03-09 22:00:00 +01:00
// share developer level across all dlls
2011-03-11 22:00:00 +01:00
Q_snprintf( dev_level, sizeof( dev_level ), "%i", host.developer );
2017-08-23 23:00:00 +02:00
Cvar_Get( "developer", dev_level, FCVAR_READ_ONLY, "current developer level" );
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
2017-02-12 22:00:00 +01:00
Cvar_FullSet( "developer", "0", FCVAR_READ_ONLY );
2012-02-08 21:00:00 +01:00
host.developer = host.old_developer = 0;
2011-09-19 22:00:00 +02:00
host.con_showalways = false;
}
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();
2011-03-08 22:00:00 +01:00
FS_Shutdown();
2011-03-09 22:00:00 +01:00
Mem_FreePool( &host.mempool );
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
2010-03-25 22:00:00 +01:00
if( host.developer >= 3 )
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");
2010-03-27 22:00:00 +01:00
Cmd_AddCommand ( "net_error", Net_Error_f, "send network bad message from random place");
2007-11-17 22:00:00 +01:00
}
2008-07-17 22:00:00 +02:00
2017-02-12 22:00:00 +01: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_serverstate = Cvar_Get( "host_serverstate", "0", FCVAR_READ_ONLY, "displays current server state" );
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" );
build = Cvar_Get( "build", va( "%i", Q_buildnum()), FCVAR_READ_ONLY, "returns a current build number" );
2018-02-02 22:00:00 +01:00
ver = Cvar_Get( "ver", va( "%i/%g (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( "setr" ); // remove potentially backdoor for change render settings
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
2017-09-04 23:00:00 +02:00
if( host.type == HOST_DEDICATED && !SV_Active( ))
MsgDev( D_INFO, "type 'map <mapname>' to run server... (TAB-autocomplete is working too)\n" );
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 ();
Host_Frame( newtime - oldtime );
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
2011-03-12 22:00:00 +01:00
if( host.state != HOST_ERR_FATAL ) host.state = 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();
2010-06-16 22:00:00 +02:00
SV_Shutdown( false );
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();
// 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
}