2018-04-13 18:23:45 +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 .
*/
2019-11-24 01:52:08 +01:00
# include "build.h"
2018-04-13 20:28:46 +02:00
# ifdef XASH_SDL
# include <SDL.h>
# endif // XASH_SDL
# include <stdarg.h> // va_args
# include <errno.h> // errno
# include <string.h> // strerror
2019-11-24 01:52:08 +01:00
# if !XASH_WIN32
2018-04-13 20:28:46 +02:00
# include <unistd.h> // fork
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# endif
2019-11-24 01:52:08 +01:00
# if XASH_EMSCRIPTEN
2018-04-13 20:28:46 +02:00
# include <emscripten/emscripten.h>
# endif
# include <errno.h>
2018-04-13 18:23:45 +02:00
# include "common.h"
2018-06-19 15:59:53 +02:00
# include "base_cmd.h"
# include "client.h"
2018-04-13 18:23:45 +02:00
# include "netchan.h"
# include "protocol.h"
# include "mod_local.h"
2020-02-10 11:07:06 +01:00
# include "xash3d_mathlib.h"
2018-04-13 18:23:45 +02:00
# include "input.h"
2018-04-13 18:26:48 +02:00
# include "enginefeatures.h"
2018-04-13 18:23:45 +02:00
# include "render_api.h" // decallist_t
2021-06-20 18:55:31 +02:00
# include "tests.h"
2018-04-13 20:28:46 +02:00
2018-04-13 18:23:45 +02:00
pfnChangeGame pChangeGame = NULL ;
2018-04-13 20:28:46 +02:00
host_parm_t host ; // host parms
2018-04-13 18:23:45 +02:00
sysinfo_t SI ;
2021-06-20 18:55:31 +02:00
# ifdef XASH_ENGINE_TESTS
struct tests_stats_s tests_stats ;
# endif
2021-11-03 18:42:44 +01:00
CVAR_DEFINE ( host_developer , " developer " , " 0 " , FCVAR_FILTERABLE , " engine is in development-mode " ) ;
2022-03-03 03:38:36 +01:00
CVAR_DEFINE_AUTO ( sys_timescale , " 1.0 " , FCVAR_CHEAT | FCVAR_FILTERABLE , " scale frame time " ) ;
2018-04-26 02:09:36 +02:00
CVAR_DEFINE_AUTO ( sys_ticrate , " 100 " , 0 , " framerate in dedicated mode " ) ;
2019-05-19 14:01:23 +02:00
2023-05-19 06:43:40 +02:00
static CVAR_DEFINE_AUTO ( host_serverstate , " 0 " , FCVAR_READ_ONLY , " displays current server state " ) ;
static CVAR_DEFINE_AUTO ( host_gameloaded , " 0 " , FCVAR_READ_ONLY , " inidcates a loaded game.dll " ) ;
static CVAR_DEFINE_AUTO ( host_clientloaded , " 0 " , FCVAR_READ_ONLY , " inidcates a loaded client.dll " ) ;
CVAR_DEFINE_AUTO ( host_limitlocal , " 0 " , 0 , " apply cl_cmdrate and rate to loopback connection " ) ;
CVAR_DEFINE ( host_maxfps , " fps_max " , " 72 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " host fps upper limit " ) ;
static CVAR_DEFINE_AUTO ( host_framerate , " 0 " , FCVAR_FILTERABLE , " locks frame timing to this value in seconds " ) ;
static CVAR_DEFINE ( host_sleeptime , " sleeptime " , " 1 " , FCVAR_ARCHIVE | FCVAR_FILTERABLE , " milliseconds to sleep for each frame. higher values reduce fps accuracy " ) ;
CVAR_DEFINE ( con_gamemaps , " con_mapfilter " , " 1 " , FCVAR_ARCHIVE , " when true show only maps in game folder " ) ;
2018-04-13 18:23:45 +02:00
2020-03-04 06:55:22 +01:00
void Sys_PrintUsage ( void )
{
2023-04-28 03:41:39 +02:00
string version_str ;
2020-03-04 06:55:22 +01:00
const char * usage_str ;
2023-04-28 03:41:39 +02:00
Q_snprintf ( version_str , sizeof ( version_str ) ,
XASH_ENGINE_NAME " %i/ " XASH_VERSION " (%s-%s build %i) " , PROTOCOL_VERSION , Q_buildos ( ) , Q_buildarch ( ) , Q_buildnum ( ) ) ;
2020-03-04 06:55:22 +01:00
2023-04-28 03:41:39 +02:00
# if XASH_WIN32
# define XASH_EXE "(xash).exe"
# else
# define XASH_EXE "(xash)"
# endif
2022-03-10 02:52:16 +01:00
2023-04-28 03:41:39 +02:00
# define O( x, y ) " "x" "y"\n"
2022-03-10 02:52:16 +01:00
2023-04-28 03:41:39 +02:00
usage_str = S_USAGE XASH_EXE " [options] [+command] [+command2 arg] ... \n "
2020-03-04 06:55:22 +01:00
2023-04-28 03:41:39 +02:00
" \n Common options: \n "
O ( " -dev [level] " , " set log verbosity 0-2 " )
O ( " -log " , " write log to \" engine.log \" " )
O ( " -nowriteconfig " , " disable config save " )
O ( " -noch " , " disable crashhandler " )
# if XASH_WIN32 // !!!!
O ( " -minidumps " , " enable writing minidumps when game is crashed " )
# endif
O ( " -rodir <path> " , " set read-only base directory " )
O ( " -bugcomp " , " enable precise bug compatibility. Will break games that don't require it " )
O ( " " , " Refer to engine documentation for more info " )
O ( " -disablehelp " , " disable this message " )
2020-03-04 06:55:22 +01:00
# if !XASH_DEDICATED
2023-04-28 03:41:39 +02:00
O ( " -dedicated " , " run engine in dedicated mode " )
# endif
2020-03-04 06:55:22 +01:00
2023-04-28 03:41:39 +02:00
" \n Networking options: \n "
O ( " -noip " , " disable IPv4 " )
O ( " -ip <ip> " , " set IPv4 address " )
O ( " -port <port> " , " set IPv4 port " )
O ( " -noip6 " , " disable IPv6 " )
O ( " -ip6 <ip> " , " set IPv6 address " )
O ( " -port6 <port> " , " set IPv6 port " )
O ( " -clockwindow <cw> " , " adjust clockwindow used to ignore client commands to prevent speed hacks " )
" \n Game options: \n "
O ( " -dll <path> " , " override server DLL path " )
# if !XASH_DEDICATED
O ( " -clientlib <path> " , " override client DLL path " )
O ( " -console " , " run engine with console enabled " )
O ( " -toconsole " , " run engine witn console open " )
O ( " -oldfont " , " enable unused Quake font in Half-Life " )
O ( " -width <n> " , " set window width " )
O ( " -height <n> " , " set window height " )
O ( " -fullscreen " , " run engine in fullscreen mode " )
O ( " -windowed " , " run engine in windowed mode " )
O ( " -ref <name> " , " use selected renderer dll " )
O ( " -gldebug " , " enable OpenGL debug log " )
# if XASH_WIN32
O ( " -noavi " , " disable AVI support " )
O ( " -nointro " , " disable intro video " )
# endif
O ( " -noenginejoy " , " disable engine builtin joystick support " )
O ( " -noenginemouse " , " disable engine builtin mouse support " )
O ( " -nosound " , " disable sound output " )
# endif
2020-03-04 06:55:22 +01:00
2023-04-28 03:41:39 +02:00
" \n Platform-specific options: \n "
# if !XASH_MOBILE_PLATFORM
O ( " -daemonize " , " run engine as a daemon " )
# endif
# if XASH_SDL == 2
O ( " -sdl_joy_old_api " , " use SDL legacy joystick API " )
O ( " -sdl_renderer <n> " , " use alternative SDL_Renderer for software " )
# endif // XASH_SDL
2022-03-10 02:52:16 +01:00
# if XASH_ANDROID
O ( " -nativeegl " , " use native egl implementation. Use if screen does not update or black " )
# endif // XASH_ANDROID
# if XASH_DOS
2020-03-04 06:55:22 +01:00
O ( " -novesa " , " disable vesa " )
2022-03-10 02:52:16 +01:00
# endif // XASH_DOS
# if XASH_VIDEO == VIDEO_FBDEV
2020-03-04 06:55:22 +01:00
O ( " -fbdev <path> " , " open selected framebuffer " )
O ( " -ttygfx " , " set graphics mode in tty " )
O ( " -doublebuffer " , " enable doublebuffering " )
2022-03-10 02:52:16 +01:00
# endif // XASH_VIDEO == VIDEO_FBDEV
# if XASH_SOUND == SOUND_ALSA
2020-03-04 06:55:22 +01:00
O ( " -alsadev <dev> " , " open selected ALSA device " )
2022-03-10 02:52:16 +01:00
# endif // XASH_SOUND == SOUND_ALSA
2023-04-28 03:41:39 +02:00
;
# undef O
2020-03-04 06:55:22 +01:00
2023-04-28 03:41:39 +02:00
// HACKHACK: pretty output in dedicated
# if XASH_MESSAGEBOX != MSGBOX_STDERR
Platform_MessageBox ( version_str , usage_str , false ) ;
# else
fprintf ( stderr , " %s \n %s " , version_str , usage_str ) ;
2020-03-04 06:55:22 +01:00
# endif
2023-04-28 03:41:39 +02:00
Sys_Quit ( ) ;
2020-03-04 06:55:22 +01:00
}
2018-12-05 17:57:05 +01:00
int Host_CompareFileTime ( int ft1 , int ft2 )
2018-04-13 18:23:45 +02:00
{
if ( ft1 < ft2 )
{
return - 1 ;
}
else if ( ft1 > ft2 )
{
return 1 ;
}
return 0 ;
}
void Host_ShutdownServer ( void )
{
SV_Shutdown ( " Server was killed \n " ) ;
}
/*
= = = = = = = = = = = = = = = =
Host_PrintEngineFeatures
= = = = = = = = = = = = = = = =
*/
void Host_PrintEngineFeatures ( void )
{
if ( FBitSet ( host . features , ENGINE_WRITE_LARGE_COORD ) )
2018-10-04 08:08:48 +02:00
Con_Reportf ( " ^3EXT:^7 big world support enabled \n " ) ;
2018-04-13 18:23:45 +02:00
if ( FBitSet ( host . features , ENGINE_LOAD_DELUXEDATA ) )
2018-10-04 08:08:48 +02:00
Con_Reportf ( " ^3EXT:^7 deluxemap support enabled \n " ) ;
2018-04-13 18:23:45 +02:00
if ( FBitSet ( host . features , ENGINE_PHYSICS_PUSHER_EXT ) )
2018-10-04 08:08:48 +02:00
Con_Reportf ( " ^3EXT:^7 Improved MOVETYPE_PUSH is used \n " ) ;
2018-04-13 18:23:45 +02:00
if ( FBitSet ( host . features , ENGINE_LARGE_LIGHTMAPS ) )
2018-10-04 08:08:48 +02:00
Con_Reportf ( " ^3EXT:^7 Large lightmaps enabled \n " ) ;
2018-04-13 18:23:45 +02:00
if ( FBitSet ( host . features , ENGINE_COMPENSATE_QUAKE_BUG ) )
2018-10-04 08:08:48 +02:00
Con_Reportf ( " ^3EXT:^7 Compensate quake bug enabled \n " ) ;
2018-04-13 18:23:45 +02:00
}
2018-06-19 15:59:53 +02:00
/*
= = = = = = = = = = = = = =
Host_IsQuakeCompatible
= = = = = = = = = = = = = =
*/
qboolean Host_IsQuakeCompatible ( void )
{
// feature set
if ( FBitSet ( host . features , ENGINE_QUAKE_COMPATIBLE ) )
return true ;
2019-11-24 01:52:08 +01:00
# if !XASH_DEDICATED
2018-06-19 15:59:53 +02:00
// quake demo playing
if ( cls . demoplayback = = DEMO_QUAKE1 )
return true ;
# endif // XASH_DEDICATED
return false ;
}
2018-04-13 18:23:45 +02:00
/*
= = = = = = = = = = = = = = = =
Host_EndGame
= = = = = = = = = = = = = = = =
*/
void Host_EndGame ( qboolean abort , const char * message , . . . )
{
va_list argptr ;
static char string [ MAX_SYSPATH ] ;
2021-01-03 02:28:45 +01:00
2018-04-13 18:23:45 +02:00
va_start ( argptr , message ) ;
Q_vsnprintf ( string , sizeof ( string ) , message , argptr ) ;
va_end ( argptr ) ;
2018-10-04 08:08:48 +02:00
Con_Printf ( " Host_EndGame: %s \n " , string ) ;
2018-04-13 18:23:45 +02:00
2021-01-03 02:28:45 +01:00
SV_Shutdown ( " \n " ) ;
2019-11-24 01:52:08 +01:00
# if !XASH_DEDICATED
2018-04-13 18:23:45 +02:00
CL_Disconnect ( ) ;
// recreate world if needs
CL_ClearEdicts ( ) ;
2018-04-18 17:32:30 +02:00
# endif
2021-01-03 02:28:45 +01:00
2018-04-13 18:23:45 +02:00
// release all models
Mod_FreeAll ( ) ;
if ( abort ) Host_AbortCurrentFrame ( ) ;
}
/*
= = = = = = = = = = = = = = = =
Host_AbortCurrentFrame
aborts the current host frame and goes on with the next one
= = = = = = = = = = = = = = = =
*/
void Host_AbortCurrentFrame ( void )
{
longjmp ( host . abortframe , 1 ) ;
}
/*
= = = = = = = = = = = = = = = = = =
2022-12-05 01:10:30 +01:00
Host_CalcSleep
2018-04-13 18:23:45 +02:00
= = = = = = = = = = = = = = = = = =
*/
2022-12-05 01:10:30 +01:00
static int Host_CalcSleep ( void )
2018-04-13 18:23:45 +02:00
{
2022-11-10 11:05:03 +01:00
# ifndef XASH_DEDICATED
// never sleep in timedemo for benchmarking purposes
// also don't sleep with vsync for less lag
2023-05-19 04:55:06 +02:00
if ( CL_IsTimeDemo ( ) | | gl_vsync . value )
2022-12-05 01:10:30 +01:00
return 0 ;
2022-11-10 11:05:03 +01:00
# endif
2018-05-01 16:27:14 +02:00
if ( Host_IsDedicated ( ) )
2018-04-13 18:23:45 +02:00
{
// let the dedicated server some sleep
2023-05-19 06:43:40 +02:00
return host_sleeptime . value ;
2018-04-13 18:23:45 +02:00
}
2022-12-13 08:54:03 +01:00
switch ( host . status )
2022-12-05 01:10:30 +01:00
{
2022-12-13 08:54:03 +01:00
case HOST_NOFOCUS :
if ( SV_Active ( ) & & CL_IsInGame ( ) )
2023-05-19 06:43:40 +02:00
return host_sleeptime . value ;
2022-12-13 08:54:03 +01:00
// fallthrough
case HOST_SLEEP :
2022-12-05 01:10:30 +01:00
return 20 ;
2018-04-13 18:23:45 +02:00
}
2022-12-05 01:10:30 +01:00
2023-05-19 06:43:40 +02:00
return host_sleeptime . value ;
2018-04-13 18:23:45 +02: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 ) ) ;
2022-06-13 02:42:20 +02:00
2022-06-13 03:05:50 +02:00
if ( ! Sys_NewInstance ( name ) )
2022-06-13 02:42:20 +02:00
pChangeGame ( name ) ; // call from hl.exe
2018-04-13 18:23:45 +02:00
}
/*
= = = = = = = = = = = = = = = = =
Host_ChangeGame_f
Change game modification
= = = = = = = = = = = = = = = = =
*/
void Host_ChangeGame_f ( void )
{
int i ;
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( S_USAGE " game <directory> \n " ) ;
return ;
}
// validate gamedir
2022-07-01 18:37:21 +02:00
for ( i = 0 ; i < FI - > numgames ; i + + )
2018-04-13 18:23:45 +02:00
{
2022-07-01 18:37:21 +02:00
if ( ! Q_stricmp ( FI - > games [ i ] - > gamefolder , Cmd_Argv ( 1 ) ) )
2018-04-13 18:23:45 +02:00
break ;
}
2022-07-01 18:37:21 +02:00
if ( i = = FI - > numgames )
2018-04-13 18:23:45 +02:00
{
Con_Printf ( " %s not exist \n " , Cmd_Argv ( 1 ) ) ;
}
else if ( ! Q_stricmp ( GI - > gamefolder , Cmd_Argv ( 1 ) ) )
{
2021-01-03 02:28:45 +01:00
Con_Printf ( " %s already active \n " , Cmd_Argv ( 1 ) ) ;
2018-04-13 18:23:45 +02:00
}
else
{
2023-03-13 04:08:36 +01:00
char finalmsg [ MAX_VA_STRING ] ;
2018-04-13 18:23:45 +02:00
2023-03-13 04:08:36 +01:00
Q_snprintf ( finalmsg , sizeof ( finalmsg ) , " change game to '%s' " , FI - > games [ i ] - > title ) ;
Host_NewInstance ( Cmd_Argv ( 1 ) , finalmsg ) ;
2018-04-13 18:23:45 +02:00
}
}
/*
= = = = = = = = = = = = = = =
Host_Exec_f
= = = = = = = = = = = = = = =
*/
void Host_Exec_f ( void )
{
2021-11-03 15:28:42 +01:00
string cfgpath ;
2019-07-13 22:25:03 +02:00
byte * f ;
char * txt ;
2019-05-02 18:12:23 +02:00
fs_offset_t len ;
2021-11-27 03:27:45 +01:00
const char * arg ;
2018-04-13 18:23:45 +02:00
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( S_USAGE " exec <filename> \n " ) ;
return ;
}
2021-11-03 15:28:42 +01:00
arg = Cmd_Argv ( 1 ) ;
# ifndef XASH_DEDICATED
if ( ! Cmd_CurrentCommandIsPrivileged ( ) )
{
const char * unprivilegedWhitelist [ ] =
{
NULL , " mapdefault.cfg " , " scout.cfg " , " sniper.cfg " ,
" soldier.cfg " , " demoman.cfg " , " medic.cfg " , " hwguy.cfg " ,
" pyro.cfg " , " spy.cfg " , " engineer.cfg " , " civilian.cfg "
} ;
int i ;
2023-03-13 04:08:36 +01:00
char temp [ MAX_VA_STRING ] ;
2021-11-03 15:28:42 +01:00
qboolean allow = false ;
2023-03-13 04:08:36 +01:00
Q_snprintf ( temp , sizeof ( temp ) , " %s.cfg " , clgame . mapname ) ;
unprivilegedWhitelist [ 0 ] = temp ;
2021-11-03 15:28:42 +01:00
for ( i = 0 ; i < ARRAYSIZE ( unprivilegedWhitelist ) ; i + + )
{
if ( ! Q_strcmp ( arg , unprivilegedWhitelist [ i ] ) )
{
allow = true ;
break ;
}
}
if ( ! allow )
{
Con_Printf ( " exec %s: not privileged or in whitelist \n " , arg ) ;
return ;
}
}
# endif // XASH_DEDICATED
if ( ! Q_stricmp ( " game.cfg " , arg ) )
2018-04-13 18:23:45 +02:00
{
// don't execute game.cfg in singleplayer
if ( SV_GetMaxClients ( ) = = 1 )
return ;
}
2021-11-03 15:28:42 +01:00
Q_strncpy ( cfgpath , arg , sizeof ( cfgpath ) ) ;
2023-04-26 03:57:28 +02:00
COM_DefaultExtension ( cfgpath , " .cfg " , sizeof ( cfgpath ) ) ; // append as default
2018-04-13 18:23:45 +02:00
f = FS_LoadFile ( cfgpath , & len , false ) ;
if ( ! f )
{
2018-10-04 08:08:48 +02:00
Con_Reportf ( " couldn't exec %s \n " , Cmd_Argv ( 1 ) ) ;
2018-04-13 18:23:45 +02:00
return ;
}
2021-11-03 15:28:42 +01:00
if ( ! Q_stricmp ( " config.cfg " , arg ) )
2018-04-13 18:23:45 +02:00
host . config_executed = true ;
// adds \n\0 at end of the file
2018-06-09 00:28:35 +02:00
txt = Z_Calloc ( len + 2 ) ;
2018-04-13 18:23:45 +02:00
memcpy ( txt , f , len ) ;
Q_strncat ( txt , " \n " , len + 2 ) ;
Mem_Free ( f ) ;
if ( ! host . apply_game_config )
2021-11-03 15:28:42 +01:00
Con_Printf ( " execing %s \n " , arg ) ;
2018-04-13 18:23:45 +02:00
Cbuf_InsertText ( txt ) ;
Mem_Free ( txt ) ;
}
/*
= = = = = = = = = = = = = = =
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 :
Con_Printf ( S_USAGE " memlist <all> \n " ) ;
break ;
}
}
void Host_Minimize_f ( void )
{
2019-11-02 06:54:58 +01:00
# ifdef XASH_SDL
2018-04-13 20:28:46 +02:00
if ( host . hWnd ) SDL_MinimizeWindow ( host . hWnd ) ;
2019-11-02 06:54:58 +01:00
# endif
2018-04-13 18:23:45 +02:00
}
/*
= = = = = = = = = = = = = = = = =
Host_IsLocalGame
singleplayer game detect
= = = = = = = = = = = = = = = = =
*/
qboolean Host_IsLocalGame ( void )
{
if ( SV_Active ( ) )
{
return ( SV_GetMaxClients ( ) = = 1 ) ? true : false ;
}
else
{
return ( CL_GetMaxClients ( ) = = 1 ) ? true : false ;
}
}
qboolean Host_IsLocalClient ( void )
{
// only the local client have the active server
if ( CL_Initialized ( ) & & SV_Initialized ( ) )
return true ;
return false ;
}
/*
= = = = = = = = = = = = = = = = =
Host_RegisterDecal
= = = = = = = = = = = = = = = = =
*/
qboolean Host_RegisterDecal ( const char * name , int * count )
{
char shortname [ MAX_QPATH ] ;
int i ;
if ( ! COM_CheckString ( name ) )
return 0 ;
2023-04-26 03:04:48 +02:00
COM_FileBase ( name , shortname , sizeof ( shortname ) ) ;
2018-04-13 18:23:45 +02:00
for ( i = 1 ; i < MAX_DECALS & & host . draw_decals [ i ] [ 0 ] ; i + + )
{
if ( ! Q_stricmp ( host . draw_decals [ i ] , shortname ) )
return true ;
}
if ( i = = MAX_DECALS )
{
2018-10-04 08:08:48 +02:00
Con_DPrintf ( S_ERROR " MAX_DECALS limit exceeded (%d) \n " , MAX_DECALS ) ;
2018-04-13 18:23:45 +02:00
return false ;
}
// register new decal
Q_strncpy ( host . draw_decals [ i ] , shortname , sizeof ( host . draw_decals [ i ] ) ) ;
* count + = 1 ;
return true ;
}
/*
= = = = = = = = = = = = = = = = =
Host_InitDecals
= = = = = = = = = = = = = = = = =
*/
void Host_InitDecals ( void )
{
int i , num_decals = 0 ;
search_t * t ;
2018-06-12 11:14:56 +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 " ) ;
2018-04-13 18:23:45 +02:00
memset ( host . draw_decals , 0 , sizeof ( host . draw_decals ) ) ;
// lookup all the decals in decals.wad (basedir, gamedir, falldir)
t = FS_Search ( " decals.wad/*.* " , true , false ) ;
for ( i = 0 ; t & & i < t - > numfilenames ; i + + )
{
if ( ! Host_RegisterDecal ( t - > filenames [ i ] , & num_decals ) )
break ;
}
if ( t ) Mem_Free ( t ) ;
2018-04-19 22:11:24 +02:00
Con_Reportf ( " InitDecals: %i decals \n " , num_decals ) ;
2018-04-13 18:23:45 +02:00
}
/*
= = = = = = = = = = = = = = = = = = =
Host_GetCommands
Add them exactly as if they had been typed at the console
= = = = = = = = = = = = = = = = = = =
*/
void Host_GetCommands ( void )
{
char * cmd ;
2018-04-14 03:08:28 +02:00
while ( ( cmd = Sys_Input ( ) ) )
{
Cbuf_AddText ( cmd ) ;
Cbuf_Execute ( ) ;
}
2018-04-13 18:23:45 +02:00
}
/*
= = = = = = = = = = = = = = = = = = =
Host_CalcFPS
compute actual FPS for various modes
= = = = = = = = = = = = = = = = = = =
*/
double Host_CalcFPS ( void )
{
double fps = 0.0 ;
2019-01-11 21:20:35 +01:00
if ( Host_IsDedicated ( ) )
{
fps = sys_ticrate . value ;
}
2019-11-24 01:52:08 +01:00
# if !XASH_DEDICATED
2019-01-11 21:20:35 +01:00
else if ( CL_IsPlaybackDemo ( ) | | CL_IsRecordDemo ( ) ) // NOTE: we should play demos with same fps as it was recorded
2018-04-26 02:09:36 +02:00
{
2018-04-13 18:23:45 +02:00
fps = CL_GetDemoFramerate ( ) ;
2018-04-26 02:09:36 +02:00
}
2018-04-13 18:23:45 +02:00
else if ( Host_IsLocalGame ( ) )
2018-04-26 02:09:36 +02:00
{
2023-05-19 04:55:06 +02:00
if ( ! gl_vsync . value )
2023-05-19 06:43:40 +02:00
fps = host_maxfps . value ;
2018-04-26 02:09:36 +02:00
}
2018-04-26 02:23:00 +02:00
else
2018-04-13 18:23:45 +02:00
{
2023-05-19 04:55:06 +02:00
if ( ! gl_vsync . value )
2018-04-13 18:23:45 +02:00
{
2023-05-19 06:43:40 +02:00
fps = host_maxfps . value ;
2022-11-10 11:05:03 +01:00
if ( fps = = 0.0 ) fps = MAX_FPS ;
fps = bound ( MIN_FPS , fps , MAX_FPS ) ;
2018-04-13 18:23:45 +02:00
}
}
2018-04-18 17:32:30 +02:00
# endif
2018-04-13 18:23:45 +02:00
return fps ;
}
/*
= = = = = = = = = = = = = = = = = = =
Host_FilterTime
Returns false if the time is too short to run a frame
= = = = = = = = = = = = = = = = = = =
*/
qboolean Host_FilterTime ( float time )
{
static double oldtime ;
2022-12-05 01:10:30 +01:00
double fps , scale = sys_timescale . value ;
2018-04-13 18:23:45 +02:00
2022-03-03 03:38:36 +01:00
host . realtime + = time * scale ;
2022-12-05 01:10:30 +01:00
fps = Host_CalcFPS ( ) ;
2018-04-13 18:23:45 +02:00
// clamp the fps in multiplayer games
if ( fps ! = 0.0 )
{
2022-12-05 01:10:30 +01:00
static int sleeps ;
double targetframetime ;
int sleeptime = Host_CalcSleep ( ) ;
2018-04-13 18:23:45 +02:00
// limit fps to withing tolerable range
fps = bound ( MIN_FPS , fps , MAX_FPS ) ;
2022-12-05 01:10:30 +01:00
if ( Host_IsDedicated ( ) )
targetframetime = ( 1.0 / ( fps + 1.0 ) ) ;
else targetframetime = ( 1.0 / fps ) ;
if ( ( host . realtime - oldtime ) < targetframetime * scale )
2018-04-26 02:09:36 +02:00
{
2022-12-05 01:10:30 +01:00
if ( sleeptime > 0 & & sleeps > 0 )
{
Sys_Sleep ( sleeptime ) ;
sleeps - - ;
}
return false ;
2018-04-26 02:09:36 +02:00
}
2022-12-05 01:10:30 +01:00
if ( sleeptime > 0 & & sleeps < = 0 )
2018-04-26 02:09:36 +02:00
{
2022-12-05 01:10:30 +01:00
if ( host . status = = HOST_FRAME )
{
// give few sleeps this frame with small margin
double targetsleeptime = targetframetime - host . pureframetime * 2 ;
// don't sleep if we can't keep up with the framerate
if ( targetsleeptime > 0 )
sleeps = targetsleeptime / ( sleeptime * 0.001 ) ;
else sleeps = 0 ;
}
else
{
// always sleep at least once in minimized/nofocus state
sleeps = 1 ;
}
2018-04-26 02:09:36 +02:00
}
2018-04-13 18:23:45 +02:00
}
host . frametime = host . realtime - oldtime ;
host . realframetime = bound ( MIN_FRAMETIME , host . frametime , MAX_FRAMETIME ) ;
oldtime = host . realtime ;
// NOTE: allow only in singleplayer while demos are not active
2023-05-19 06:43:40 +02:00
if ( host_framerate . value > 0.0f & & Host_IsLocalGame ( ) & & ! CL_IsPlaybackDemo ( ) & & ! CL_IsRecordDemo ( ) )
host . frametime = bound ( MIN_FRAMETIME , host_framerate . value * scale , MAX_FRAMETIME ) ;
2018-04-13 18:23:45 +02:00
else host . frametime = bound ( MIN_FRAMETIME , host . frametime , MAX_FRAMETIME ) ;
return true ;
}
/*
= = = = = = = = = = = = = = = = =
Host_Frame
= = = = = = = = = = = = = = = = =
*/
void Host_Frame ( float time )
{
2022-12-05 01:10:30 +01:00
double t1 , t2 ;
2018-04-13 18:23:45 +02:00
// decide the simulation time
if ( ! Host_FilterTime ( time ) )
return ;
2022-12-05 01:10:30 +01:00
t1 = Sys_DoubleTime ( ) ;
if ( host . framecount = = 0 )
Con_DPrintf ( " Time to first frame: %.3f seconds \n " , t1 - host . starttime ) ;
2018-04-13 18:23:45 +02:00
Host_InputFrame ( ) ; // input frame
Host_ClientBegin ( ) ; // begin client
Host_GetCommands ( ) ; // dedicated in
Host_ServerFrame ( ) ; // server frame
Host_ClientFrame ( ) ; // client frame
2019-02-01 23:15:59 +01:00
HTTP_Run ( ) ; // both server and client
2018-04-13 18:23:45 +02:00
2022-12-05 01:10:30 +01:00
t2 = Sys_DoubleTime ( ) ;
host . pureframetime = t2 - t1 ;
2018-04-13 18:23:45 +02:00
host . framecount + + ;
}
/*
= = = = = = = = = = = = = = = = =
Host_Error
= = = = = = = = = = = = = = = = =
*/
2020-01-19 02:15:54 +01:00
void GAME_EXPORT Host_Error ( const char * error , . . . )
2018-04-13 18:23:45 +02:00
{
static char hosterror1 [ MAX_SYSPATH ] ;
static char hosterror2 [ MAX_SYSPATH ] ;
static qboolean recursive = false ;
va_list argptr ;
va_start ( argptr , error ) ;
2023-04-23 17:17:06 +02:00
Q_vsnprintf ( hosterror1 , sizeof ( hosterror1 ) , error , argptr ) ;
2018-04-13 18:23:45 +02:00
va_end ( argptr ) ;
CL_WriteMessageHistory ( ) ; // before Q_error call
if ( host . framecount < 3 )
{
Sys_Error ( " Host_InitError: %s " , hosterror1 ) ;
}
else if ( host . framecount = = host . errorframe )
{
Sys_Error ( " Host_MultiError: %s " , hosterror2 ) ;
}
else
{
if ( host . allow_console )
{
UI_SetActiveMenu ( false ) ;
Key_SetKeyDest ( key_console ) ;
Con_Printf ( " Host_Error: %s " , hosterror1 ) ;
}
else MSGBOX2 ( hosterror1 ) ;
}
// host is shutting down. don't invoke infinite loop
if ( host . status = = HOST_SHUTDOWN ) return ;
if ( recursive )
2021-01-03 02:28:45 +01:00
{
2018-04-13 18:23:45 +02:00
Con_Printf ( " Host_RecursiveError: %s " , hosterror2 ) ;
2018-04-23 20:49:37 +02:00
Sys_Error ( " %s " , hosterror1 ) ;
2018-04-13 18:23:45 +02:00
}
recursive = true ;
Q_strncpy ( hosterror2 , hosterror1 , MAX_SYSPATH ) ;
host . errorframe = host . framecount ; // to avoid multply calls per frame
2023-04-23 21:56:05 +02:00
Q_snprintf ( host . finalmsg , sizeof ( host . finalmsg ) , " Server crashed: %s " , hosterror1 ) ;
2018-04-13 18:23:45 +02:00
// clearing cmd buffer to prevent execute any commands
COM_InitHostState ( ) ;
Cbuf_Clear ( ) ;
Host_ShutdownServer ( ) ;
CL_Drop ( ) ; // drop clients
// recreate world if needs
CL_ClearEdicts ( ) ;
// release all models
Mod_FreeAll ( ) ;
recursive = false ;
Host_AbortCurrentFrame ( ) ;
}
void Host_Error_f ( void )
{
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 " ;
Sys_Error ( " %s \n " , error ) ;
}
/*
= = = = = = = = = = = = = = = = =
Host_Crash_f
= = = = = = = = = = = = = = = = =
*/
static void Host_Crash_f ( void )
{
2019-12-01 14:19:25 +01:00
* ( volatile int * ) 0 = 0xffffffff ;
2018-04-13 18:23:45 +02:00
}
2020-04-14 19:01:37 +02:00
/*
= = = = = = = = = = = = = = = = =
Host_Userconfigd_f
= = = = = = = = = = = = = = = = =
*/
void Host_Userconfigd_f ( void )
{
search_t * t ;
int i ;
t = FS_Search ( " userconfig.d/*.cfg " , true , false ) ;
if ( ! t ) return ;
for ( i = 0 ; i < t - > numfilenames ; i + + )
{
2023-03-13 03:31:27 +01:00
Cbuf_AddTextf ( " exec %s \n " , t - > filenames [ i ] ) ;
2020-04-14 19:01:37 +02:00
}
Mem_Free ( t ) ;
}
2021-06-20 18:55:31 +02:00
# if XASH_ENGINE_TESTS
2021-06-20 19:24:20 +02:00
static void Host_RunTests ( int stage )
2021-06-20 18:55:31 +02:00
{
2021-06-20 19:24:20 +02:00
switch ( stage )
{
case 0 : // early engine load
memset ( & tests_stats , 0 , sizeof ( tests_stats ) ) ;
2022-06-09 02:07:19 +02:00
TEST_LIST_0 ;
2021-12-31 01:49:11 +01:00
# if !XASH_DEDICATED
2022-06-09 02:07:19 +02:00
TEST_LIST_0_CLIENT ;
2021-12-31 01:49:11 +01:00
# endif /* XASH_DEDICATED */
2021-06-20 19:24:20 +02:00
break ;
case 1 : // after FS load
2022-06-09 02:07:19 +02:00
TEST_LIST_1 ;
2022-02-22 07:34:28 +01:00
# if !XASH_DEDICATED
2022-06-09 02:07:19 +02:00
TEST_LIST_1_CLIENT ;
2022-02-22 07:34:28 +01:00
# endif
2021-06-20 19:24:20 +02:00
Msg ( " Done! %d passed, %d failed \n " , tests_stats . passed , tests_stats . failed ) ;
Sys_Quit ( ) ;
}
2021-06-20 18:55:31 +02:00
}
# endif
2018-04-13 18:23:45 +02:00
/*
= = = = = = = = = = = = = = = = =
Host_InitCommon
= = = = = = = = = = = = = = = = =
*/
2018-04-13 20:28:46 +02:00
void Host_InitCommon ( int argc , char * * argv , const char * progname , qboolean bChangeGame )
2018-04-13 18:23:45 +02:00
{
char dev_level [ 4 ] ;
2019-11-24 01:02:12 +01:00
int developer = DEFAULT_DEV ;
2018-04-13 20:28:46 +02:00
const char * baseDir ;
2018-04-26 16:11:01 +02:00
char ticrate [ 16 ] ;
2020-11-22 07:49:39 +01:00
int len ;
2018-04-13 18:23:45 +02:00
2018-04-13 20:28:46 +02:00
// some commands may turn engine into infinite loop,
// e.g. xash.exe +game xash -game xash
// so we clear all cmd_args, but leave dbg states as well
Sys_ParseCommandLine ( argc , argv ) ;
2018-04-13 18:23:45 +02:00
2020-03-04 06:55:22 +01:00
if ( ! Sys_CheckParm ( " -disablehelp " ) )
{
2022-08-10 05:55:14 +02:00
if ( Sys_CheckParm ( " -help " ) | | Sys_CheckParm ( " -h " ) | | Sys_CheckParm ( " --help " ) )
{
2020-03-04 06:55:22 +01:00
Sys_PrintUsage ( ) ;
2022-08-10 05:55:14 +02:00
}
2020-03-04 06:55:22 +01:00
}
2018-04-13 20:28:46 +02:00
if ( ! Sys_CheckParm ( " -noch " ) )
Sys_SetupCrashHandler ( ) ;
2018-04-13 18:23:45 +02:00
2018-04-13 20:28:46 +02:00
host . enabledll = ! Sys_CheckParm ( " -nodll " ) ;
2022-06-13 02:42:20 +02:00
host . change_game = bChangeGame | | Sys_CheckParm ( " -changegame " ) ;
2018-04-13 20:28:46 +02:00
host . config_executed = false ;
host . status = HOST_INIT ; // initialzation started
Memory_Init ( ) ; // init memory subsystem
2018-04-13 18:23:45 +02:00
host . mempool = Mem_AllocPool ( " Zone Engine " ) ;
2023-02-09 03:56:11 +01:00
host . allow_console = DEFAULT_ALLOWCONSOLE ;
2018-04-21 10:06:55 +02:00
// HACKHACK: Quake console is always allowed
2018-10-27 23:39:29 +02:00
// TODO: determine if we are running QWrap more reliable
2023-02-09 03:56:11 +01:00
if ( ! host . allow_console & & ( Sys_CheckParm ( " -console " ) | | ! Q_stricmp ( SI . exeName , " quake " ) ) )
2018-04-13 18:23:45 +02:00
host . allow_console = true ;
if ( Sys_CheckParm ( " -dev " ) )
{
host . allow_console = true ;
developer = DEV_NORMAL ;
if ( Sys_GetParmFromCmdLine ( " -dev " , dev_level ) )
{
if ( Q_isdigit ( dev_level ) )
developer = bound ( DEV_NONE , abs ( Q_atoi ( dev_level ) ) , DEV_EXTENDED ) ;
}
}
2021-06-20 18:55:31 +02:00
# if XASH_ENGINE_TESTS
if ( Sys_CheckParm ( " -runtests " ) )
{
host . allow_console = true ;
developer = DEV_EXTENDED ;
}
# endif
2018-04-13 18:23:45 +02:00
host . con_showalways = true ;
2019-11-24 01:52:08 +01:00
# if XASH_DEDICATED
2018-04-13 20:28:46 +02:00
host . type = HOST_DEDICATED ; // predict state
# else
if ( Sys_CheckParm ( " -dedicated " ) | | progname [ 0 ] = = ' # ' )
{
host . type = HOST_DEDICATED ;
}
else
2018-04-13 18:23:45 +02:00
{
2018-04-13 20:28:46 +02:00
host . type = HOST_NORMAL ;
2018-04-13 18:23:45 +02:00
}
2018-04-13 20:28:46 +02:00
# endif
2018-04-13 18:23:45 +02:00
2018-04-13 20:28:46 +02:00
// set default gamedir
if ( progname [ 0 ] = = ' # ' )
progname + + ;
Q_strncpy ( SI . exeName , progname , sizeof ( SI . exeName ) ) ;
2018-04-17 02:56:39 +02:00
Q_strncpy ( SI . basedirName , progname , sizeof ( SI . exeName ) ) ;
2018-04-13 18:23:45 +02:00
2018-04-13 20:28:46 +02:00
if ( Host_IsDedicated ( ) )
{
Sys_MergeCommandLine ( ) ;
2018-04-13 18:23:45 +02:00
host . allow_console = true ;
}
else
{
// don't show console as default
if ( developer < = DEV_NORMAL )
host . con_showalways = false ;
}
// member console allowing
host . allow_console_init = host . allow_console ;
2022-08-10 05:55:14 +02:00
if ( Sys_CheckParm ( " -bugcomp " ) )
{
// add argument check here when we add other levels
// of bugcompatibility
host . bugcomp = BUGCOMP_GOLDSRC ;
}
2018-04-26 02:23:00 +02:00
// timeBeginPeriod( 1 ); // a1ba: Do we need this?
2018-04-13 18:23:45 +02:00
// NOTE: this message couldn't be passed into game console but it doesn't matter
2020-02-08 17:00:39 +01:00
// Con_Reportf( "Sys_LoadLibrary: Loading xash.dll - ok\n" );
2018-04-13 18:23:45 +02:00
// get default screen res
VID_InitDefaultResolution ( ) ;
// init host state machine
COM_InitHostState ( ) ;
2018-06-01 20:28:25 +02:00
// init hashed commands
BaseCmd_Init ( ) ;
2018-04-13 18:23:45 +02:00
// startup cmds and cvars subsystem
Cmd_Init ( ) ;
Cvar_Init ( ) ;
// share developer level across all dlls
Q_snprintf ( dev_level , sizeof ( dev_level ) , " %i " , developer ) ;
Cvar_DirectSet ( & host_developer , dev_level ) ;
2018-04-26 02:09:36 +02:00
Cvar_RegisterVariable ( & sys_ticrate ) ;
if ( Sys_GetParmFromCmdLine ( " -sys_ticrate " , ticrate ) )
{
2018-04-26 16:11:01 +02:00
double fps = bound ( MIN_FPS , atof ( ticrate ) , MAX_FPS ) ;
2018-04-26 02:09:36 +02:00
Cvar_SetValue ( " sys_ticrate " , fps ) ;
}
2018-04-13 18:23:45 +02:00
Con_Init ( ) ; // early console running to catch all the messages
2020-02-08 17:00:39 +01:00
2021-06-20 18:55:31 +02:00
# if XASH_ENGINE_TESTS
if ( Sys_CheckParm ( " -runtests " ) )
2021-06-20 19:24:20 +02:00
Host_RunTests ( 0 ) ;
2021-06-20 18:55:31 +02:00
# endif
2020-02-08 17:00:39 +01:00
Platform_Init ( ) ;
2020-04-19 12:02:36 +02:00
baseDir = getenv ( " XASH3D_BASEDIR " ) ;
if ( COM_CheckString ( baseDir ) )
2020-02-08 17:00:39 +01:00
{
2023-01-04 17:39:52 +01:00
Q_strncpy ( host . rootdir , baseDir , sizeof ( host . rootdir ) ) ;
2020-02-08 17:00:39 +01:00
}
else
{
# if TARGET_OS_IOS
const char * IOS_GetDocsDir ( ) ;
Q_strncpy ( host . rootdir , IOS_GetDocsDir ( ) , sizeof ( host . rootdir ) ) ;
2023-02-13 20:53:17 +01:00
# elif XASH_PSVITA
if ( ! PSVita_GetBasePath ( host . rootdir , sizeof ( host . rootdir ) ) )
{
Sys_Error ( " couldn't find xash3d data directory " ) ;
host . rootdir [ 0 ] = 0 ;
}
2023-02-05 02:09:32 +01:00
# elif (XASH_SDL == 2) && !XASH_NSWITCH // GetBasePath not impl'd in switch-sdl2
2020-02-08 17:00:39 +01:00
char * szBasePath ;
if ( ! ( szBasePath = SDL_GetBasePath ( ) ) )
Sys_Error ( " couldn't determine current directory: %s " , SDL_GetError ( ) ) ;
2023-01-04 17:39:52 +01:00
Q_strncpy ( host . rootdir , szBasePath , sizeof ( host . rootdir ) ) ;
2020-02-08 17:00:39 +01:00
SDL_free ( szBasePath ) ;
# else
2023-01-04 17:39:52 +01:00
if ( ! getcwd ( host . rootdir , sizeof ( host . rootdir ) ) )
2020-02-08 17:00:39 +01:00
{
Sys_Error ( " couldn't determine current directory: %s " , strerror ( errno ) ) ;
host . rootdir [ 0 ] = 0 ;
}
# endif
}
2023-01-04 17:39:52 +01:00
# if XASH_WIN32
COM_FixSlashes ( host . rootdir ) ;
# endif
2020-11-22 07:49:39 +01:00
len = Q_strlen ( host . rootdir ) ;
2022-05-25 02:38:19 +02:00
if ( len & & host . rootdir [ len - 1 ] = = ' / ' )
2020-11-22 07:49:39 +01:00
host . rootdir [ len - 1 ] = 0 ;
2020-02-08 17:00:39 +01:00
// get readonly root. The order is: check for arg, then env.
// if still not got it, rodir is disabled.
2020-11-22 07:49:39 +01:00
host . rodir [ 0 ] = ' \0 ' ;
2020-02-08 17:00:39 +01:00
if ( ! Sys_GetParmFromCmdLine ( " -rodir " , host . rodir ) )
{
2020-04-19 12:02:36 +02:00
char * roDir = getenv ( " XASH3D_RODIR " ) ;
2020-02-08 17:00:39 +01:00
2020-04-19 12:02:36 +02:00
if ( COM_CheckString ( roDir ) )
2020-02-08 17:00:39 +01:00
Q_strncpy ( host . rodir , roDir , sizeof ( host . rodir ) ) ;
}
2023-01-04 17:39:52 +01:00
# if XASH_WIN32
COM_FixSlashes ( host . rootdir ) ;
# endif
2020-11-22 07:49:39 +01:00
len = Q_strlen ( host . rodir ) ;
if ( len & & host . rodir [ len - 1 ] = = ' / ' )
host . rodir [ len - 1 ] = 0 ;
2020-02-08 17:00:39 +01:00
2022-07-01 18:37:21 +02:00
if ( ! COM_CheckStringEmpty ( host . rootdir ) )
{
Sys_Error ( " Changing working directory failed (empty working directory) \n " ) ;
return ;
}
2022-08-06 19:15:18 +02:00
FS_LoadProgs ( ) ;
2022-07-01 18:37:21 +02:00
2023-05-26 21:29:34 +02:00
// TODO: this function will cause engine to stop in case of fail
// when it will have an option to return string error, restore Sys_Error
FS_SetCurrentDirectory ( host . rootdir ) ;
2020-02-08 17:00:39 +01:00
2022-07-01 18:37:21 +02:00
FS_Init ( ) ;
2020-02-08 17:00:39 +01:00
Sys_InitLog ( ) ;
2022-08-17 13:43:55 +02:00
// print bugcompatibility level here, after log was initialized
if ( host . bugcomp = = BUGCOMP_GOLDSRC )
{
Con_Printf ( " ^3BUGCOMP^7: GoldSrc bug-compatibility enabled \n " ) ;
}
2021-11-03 15:28:42 +01:00
Cmd_AddCommand ( " exec " , Host_Exec_f , " execute a script file " ) ;
2018-04-13 18:23:45 +02:00
Cmd_AddCommand ( " memlist " , Host_MemStats_f , " prints memory pool information " ) ;
2021-11-02 07:51:47 +01:00
Cmd_AddRestrictedCommand ( " userconfigd " , Host_Userconfigd_f , " execute all scripts from userconfig.d " ) ;
2018-04-13 18:23:45 +02:00
Image_Init ( ) ;
Sound_Init ( ) ;
2021-06-20 19:24:20 +02:00
# if XASH_ENGINE_TESTS
if ( Sys_CheckParm ( " -runtests " ) )
Host_RunTests ( 1 ) ;
# endif
2018-04-13 18:23:45 +02:00
FS_LoadGameInfo ( NULL ) ;
2022-11-27 02:44:41 +01:00
Cvar_PostFSInit ( ) ;
2022-07-01 18:37:21 +02:00
if ( FS_FileExists ( va ( " %s.rc " , SI . basedirName ) , false ) )
Q_strncpy ( SI . rcName , SI . basedirName , sizeof ( SI . rcName ) ) ; // e.g. valve.rc
else Q_strncpy ( SI . rcName , SI . exeName , sizeof ( SI . rcName ) ) ; // e.g. quake.rc
2018-04-13 18:23:45 +02:00
Q_strncpy ( host . gamefolder , GI - > gamefolder , sizeof ( host . gamefolder ) ) ;
2022-07-01 18:37:21 +02:00
Image_CheckPaletteQ1 ( ) ;
Host_InitDecals ( ) ; // reload decals
2018-04-21 10:16:33 +02:00
// DEPRECATED: by FWGS fork
#if 0
2018-04-13 18:23:45 +02:00
if ( GI - > secure )
{
// clear all developer levels when game is protected
Cvar_DirectSet ( & host_developer , " 0 " ) ;
host . allow_console_init = false ;
host . con_showalways = false ;
host . allow_console = false ;
}
2018-04-21 10:16:33 +02:00
# endif
2018-04-13 18:23:45 +02:00
HPAK_Init ( ) ;
IN_Init ( ) ;
Key_Init ( ) ;
}
void Host_FreeCommon ( void )
{
Image_Shutdown ( ) ;
Sound_Shutdown ( ) ;
Netchan_Shutdown ( ) ;
HPAK_FlushHostQueue ( ) ;
FS_Shutdown ( ) ;
}
/*
= = = = = = = = = = = = = = = = =
Host_Main
= = = = = = = = = = = = = = = = =
*/
2018-04-13 20:28:46 +02:00
int EXPORT Host_Main ( int argc , char * * argv , const char * progname , int bChangeGame , pfnChangeGame func )
2018-04-13 18:23:45 +02:00
{
static double oldtime , newtime ;
2022-12-05 01:10:30 +01:00
host . starttime = Sys_DoubleTime ( ) ;
2018-04-13 18:23:45 +02:00
pChangeGame = func ; // may be NULL
2018-04-13 20:28:46 +02:00
Host_InitCommon ( argc , argv , progname , bChangeGame ) ;
2018-04-13 18:23:45 +02:00
// init commands and vars
if ( host_developer . value > = DEV_EXTENDED )
{
2021-11-02 07:51:47 +01:00
Cmd_AddRestrictedCommand ( " sys_error " , Sys_Error_f , " just throw a fatal error to test shutdown procedures " ) ;
Cmd_AddRestrictedCommand ( " host_error " , Host_Error_f , " just throw a host error to test shutdown procedures " ) ;
Cmd_AddRestrictedCommand ( " crash " , Host_Crash_f , " a way to force a bus error for development reasons " ) ;
2018-04-13 18:23:45 +02:00
}
2023-05-19 06:43:40 +02:00
Cvar_RegisterVariable ( & host_serverstate ) ;
Cvar_RegisterVariable ( & host_maxfps ) ;
Cvar_RegisterVariable ( & host_framerate ) ;
Cvar_RegisterVariable ( & host_sleeptime ) ;
Cvar_RegisterVariable ( & host_gameloaded ) ;
Cvar_RegisterVariable ( & host_clientloaded ) ;
Cvar_RegisterVariable ( & host_limitlocal ) ;
Cvar_RegisterVariable ( & con_gamemaps ) ;
2022-03-03 03:38:36 +01:00
Cvar_RegisterVariable ( & sys_timescale ) ;
2018-10-04 09:27:14 +02:00
2023-05-19 06:43:40 +02:00
Cvar_Getf ( " buildnum " , FCVAR_READ_ONLY , " returns a current build number " , " %i " , Q_buildnum_compat ( ) ) ;
Cvar_Getf ( " ver " , FCVAR_READ_ONLY , " shows an engine version " , " %i/%s (hw build %i) " , PROTOCOL_VERSION , XASH_COMPAT_VERSION , Q_buildnum_compat ( ) ) ;
2023-03-13 03:19:32 +01:00
Cvar_Getf ( " host_ver " , FCVAR_READ_ONLY , " detailed info about this build " , " %i " XASH_VERSION " %s %s %s " , Q_buildnum ( ) , Q_buildos ( ) , Q_buildarch ( ) , Q_buildcommit ( ) ) ;
Cvar_Getf ( " host_lowmemorymode " , FCVAR_READ_ONLY , " indicates if engine compiled for low RAM consumption (0 - normal, 1 - low engine limits, 2 - low protocol limits) " , " %i " , XASH_LOW_MEMORY ) ;
2018-11-28 23:54:25 +01:00
2018-04-13 18:23:45 +02:00
Mod_Init ( ) ;
NET_Init ( ) ;
2018-06-01 19:44:16 +02:00
NET_InitMasters ( ) ;
2018-04-13 18:23:45 +02:00
Netchan_Init ( ) ;
// allow to change game from the console
if ( pChangeGame ! = NULL )
{
2021-11-02 07:51:47 +01:00
Cmd_AddRestrictedCommand ( " game " , Host_ChangeGame_f , " change game " ) ;
2018-04-13 18:23:45 +02:00
Cvar_Get ( " host_allow_changegame " , " 1 " , FCVAR_READ_ONLY , " allows to change games " ) ;
}
else
{
Cvar_Get ( " host_allow_changegame " , " 0 " , FCVAR_READ_ONLY , " allows to change games " ) ;
}
SV_Init ( ) ;
CL_Init ( ) ;
2019-02-01 23:15:59 +01:00
HTTP_Init ( ) ;
2018-04-17 02:56:39 +02:00
ID_Init ( ) ;
2018-05-01 16:27:14 +02:00
if ( Host_IsDedicated ( ) )
2018-04-13 18:23:45 +02:00
{
2018-04-14 03:08:28 +02:00
# ifdef _WIN32
Wcon_InitConsoleCommands ( ) ;
# endif
2018-04-13 18:23:45 +02:00
2021-11-02 07:51:47 +01:00
Cmd_AddRestrictedCommand ( " quit " , Sys_Quit , " quit the game " ) ;
Cmd_AddRestrictedCommand ( " exit " , Sys_Quit , " quit the game " ) ;
2018-04-13 18:23:45 +02:00
}
2021-11-02 07:51:47 +01:00
else Cmd_AddRestrictedCommand ( " minimize " , Host_Minimize_f , " minimize main window to tray " ) ;
2018-04-13 18:23:45 +02:00
2023-04-03 05:04:48 +02:00
HPAK_CheckIntegrity ( CUSTOM_RES_PATH ) ;
2018-04-13 18:23:45 +02:00
host . errorframe = 0 ;
// post initializations
switch ( host . type )
{
case HOST_NORMAL :
2018-04-14 03:08:28 +02:00
# ifdef _WIN32
Wcon_ShowConsole ( false ) ; // hide console
# endif
2018-04-13 18:23:45 +02:00
// execute startup config and cmdline
2023-03-13 03:31:27 +01:00
Cbuf_AddTextf ( " exec %s.rc \n " , SI . rcName ) ;
2018-04-13 18:23:45 +02:00
Cbuf_Execute ( ) ;
if ( ! host . config_executed )
{
Cbuf_AddText ( " exec config.cfg \n " ) ;
Cbuf_Execute ( ) ;
}
2021-01-03 02:28:45 +01:00
// exec all files from userconfig.d
2020-04-14 19:01:37 +02:00
Host_Userconfigd_f ( ) ;
2018-04-13 18:23:45 +02:00
break ;
case HOST_DEDICATED :
// allways parse commandline in dedicated-mode
host . stuffcmds_pending = true ;
break ;
}
host . change_game = false ; // done
Cmd_RemoveCommand ( " setgl " ) ;
Cbuf_ExecStuffCmds ( ) ; // execute stuffcmds (commandline)
SCR_CheckStartupVids ( ) ; // must be last
oldtime = Sys_DoubleTime ( ) - 0.1 ;
2022-12-04 08:13:00 +01:00
if ( Host_IsDedicated ( ) )
2020-05-02 18:26:19 +02:00
{
2022-12-13 08:54:03 +01:00
// in dedicated server input system can't set HOST_FRAME status
// so set it here as we're finished initializing
host . status = HOST_FRAME ;
2022-12-04 08:13:00 +01:00
if ( GameState - > nextstate = = STATE_RUNFRAME )
Con_Printf ( " Type 'map <mapname>' to start game... (TAB-autocomplete is working too) \n " ) ;
2020-05-02 18:26:19 +02:00
// execute server.cfg after commandline
// so we have a chance to set servercfgfile
2023-03-13 03:31:27 +01:00
Cbuf_AddTextf ( " exec %s \n " , Cvar_VariableString ( " servercfgfile " ) ) ;
2020-05-02 18:26:19 +02:00
Cbuf_Execute ( ) ;
}
2018-04-13 18:23:45 +02:00
// main window message loop
while ( ! host . crashed )
{
newtime = Sys_DoubleTime ( ) ;
COM_Frame ( newtime - oldtime ) ;
oldtime = newtime ;
}
// never reached
return 0 ;
}
/*
= = = = = = = = = = = = = = = = =
Host_Shutdown
= = = = = = = = = = = = = = = = =
*/
void EXPORT Host_Shutdown ( void )
{
if ( host . shutdown_issued ) return ;
host . shutdown_issued = true ;
if ( host . status ! = HOST_ERR_FATAL ) host . status = HOST_SHUTDOWN ; // prepare host to normal shutdown
if ( ! host . change_game ) Q_strncpy ( host . finalmsg , " Server shutdown " , sizeof ( host . finalmsg ) ) ;
2019-11-24 01:52:08 +01:00
# if !XASH_DEDICATED
2018-04-13 18:23:45 +02:00
if ( host . type = = HOST_NORMAL )
Host_WriteConfig ( ) ;
2018-04-18 17:32:30 +02:00
# endif
2018-04-13 18:23:45 +02:00
2018-04-20 10:41:02 +02:00
SV_Shutdown ( " Server shutdown \n " ) ;
2019-07-07 14:30:25 +02:00
SV_ShutdownFilter ( ) ;
2018-04-13 18:23:45 +02:00
CL_Shutdown ( ) ;
Mod_Shutdown ( ) ;
NET_Shutdown ( ) ;
2019-02-01 23:15:59 +01:00
HTTP_Shutdown ( ) ;
2018-04-13 18:23:45 +02:00
Host_FreeCommon ( ) ;
2020-02-08 17:00:39 +01:00
Platform_Shutdown ( ) ;
2018-04-13 18:23:45 +02:00
// must be last, console uses this
Mem_FreePool ( & host . mempool ) ;
2018-04-13 20:28:46 +02:00
// restore filter
Sys_RestoreCrashHandler ( ) ;
2018-05-03 18:51:23 +02:00
Sys_CloseLog ( ) ;
2018-04-13 18:23:45 +02:00
}