From 6441d5bfa2b64407a1e9b28fb552b60514004e55 Mon Sep 17 00:00:00 2001 From: g-cont Date: Wed, 28 Feb 2018 00:00:00 +0300 Subject: [PATCH] 28 Feb 2018 --- common/render_api.h | 2 +- engine/client/cl_gameui.c | 2 +- engine/client/cl_main.c | 2 +- engine/client/cl_parse.c | 83 +++++---- engine/client/gl_decals.c | 6 +- engine/common/common.c | 10 +- engine/common/common.h | 32 +--- engine/common/cvar.c | 4 +- engine/common/host.c | 6 +- engine/common/host_state.c | 103 ++++------- engine/common/model.c | 3 - engine/common/net_chan.c | 8 +- engine/physint.h | 2 +- engine/server/server.h | 21 ++- engine/server/sv_client.c | 186 +++++++++++-------- engine/server/sv_cmds.c | 75 ++++---- engine/server/sv_custom.c | 4 +- engine/server/sv_frame.c | 2 +- engine/server/sv_game.c | 30 ++- engine/server/sv_init.c | 370 +++++++++++++++++++++---------------- engine/server/sv_log.c | 8 +- engine/server/sv_main.c | 53 +++--- engine/server/sv_phys.c | 8 +- engine/server/sv_pmove.c | 3 + engine/server/sv_save.c | 75 +++++--- 25 files changed, 580 insertions(+), 518 deletions(-) diff --git a/common/render_api.h b/common/render_api.h index 1a66c914..5a594ec6 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -248,7 +248,7 @@ typedef struct render_interface_s // handle decals which hit mod_studio or mod_sprite void (*R_StudioDecalShoot)( int decalTexture, struct cl_entity_s *ent, const float *start, const float *pos, int flags, modelstate_t *state ); // prepare studio decals for save - int (*R_CreateStudioDecalList)( decallist_t *pList, int count, qboolean changelevel ); + int (*R_CreateStudioDecalList)( decallist_t *pList, int count ); // clear decals by engine request (e.g. for demo recording or vid_restart) void (*R_ClearStudioDecals)( void ); // grab r_speeds message diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 411b78b8..871b2f8b 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -839,7 +839,7 @@ int pfnCheckGameDll( void ) { void *hInst; - if( SV_Active( )) return true; + if( SV_Initialized( )) return true; if(( hInst = Com_LoadLibrary( GI->game_dll, true )) != NULL ) { diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 2bc01aa0..3c0b00cc 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2353,7 +2353,6 @@ void CL_InitLocal( void ) bottomcolor = Cvar_Get( "bottomcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player bottom color" ); cl_lw = Cvar_Get( "cl_lw", "1", FCVAR_ARCHIVE|FCVAR_USERINFO, "enable client weapon predicting" ); Cvar_Get( "cl_lc", "1", FCVAR_ARCHIVE|FCVAR_USERINFO, "enable lag compensation" ); - Cvar_Get( "msg", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "message filter for server notifications" ); Cvar_Get( "password", "", FCVAR_USERINFO, "server password" ); Cvar_Get( "team", "", FCVAR_USERINFO, "player team" ); Cvar_Get( "skin", "", FCVAR_USERINFO, "player skin" ); @@ -2388,6 +2387,7 @@ void CL_InitLocal( void ) Cmd_AddCommand ("drop", NULL, "drop current/specified item or weapon" ); Cmd_AddCommand ("gametitle", NULL, "show game logo" ); Cmd_AddCommand ("god", NULL, "enable godmode" ); + Cmd_AddCommand ("fly", NULL, "enable fly mode" ); Cmd_AddCommand ("fov", NULL, "set client field of view" ); Cmd_AddCommand ("log", NULL, "logging server events" ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index f8b79bd4..27b31a41 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -737,46 +737,67 @@ CL_ParseServerData */ void CL_ParseServerData( sizebuf_t *msg ) { - string gamefolder; + int i, servercount, checksum; + int playernum, maxclients; + char gamefolder[MAX_QPATH]; qboolean background; - int i; MsgDev( D_NOTE, "Serverdata packet received.\n" ); cls.demowaiting = false; // server is changed - clgame.load_sequence++; // now all hud sprites are invalid - - // wipe the client_t struct - if( !cls.changelevel && !cls.changedemo ) - CL_ClearState (); cls.state = ca_connected; + clgame.load_sequence++; // now all hud sprites are invalid + cls.signon = 0; // reset signon state // Re-init hud video, especially if we changed game directories clgame.dllFuncs.pfnVidInit(); // parse protocol version number - i = MSG_ReadLong( msg ); - cls.serverProtocol = i; + cls.serverProtocol = MSG_ReadLong( msg ); - if( i != PROTOCOL_VERSION ) - Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); + if( cls.serverProtocol != PROTOCOL_VERSION ) + Host_Error( "Server use invalid protocol (%i should be %i)\n", cls.serverProtocol, PROTOCOL_VERSION ); - cl.servercount = MSG_ReadLong( msg ); - cl.checksum = MSG_ReadLong( msg ); - cl.playernum = MSG_ReadByte( msg ); - cl.maxclients = MSG_ReadByte( msg ); + servercount = MSG_ReadLong( msg ); + checksum = MSG_ReadLong( msg ); + playernum = MSG_ReadByte( msg ); + maxclients = MSG_ReadByte( msg ); clgame.maxEntities = MSG_ReadWord( msg ); clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS ); clgame.maxModels = MSG_ReadWord( msg ); Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); background = MSG_ReadOneBit( msg ); + cls.changelevel = MSG_ReadOneBit( msg ); Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_STRING ); host.features = (uint)MSG_ReadLong( msg ); if( clgame.maxModels > MAX_MODELS ) MsgDev( D_WARN, "server model limit is above client model limit %i > %i\n", clgame.maxModels, MAX_MODELS ); + if( cls.changelevel && cls.demoplayback ) + cls.changedemo = true; + + // wipe the client_t struct + CL_ClearState (); + + // fill the client struct + cl.servercount = servercount; + cl.checksum = checksum; + cl.playernum = playernum; + cl.maxclients = maxclients; + + // set the background state + if( cls.demoplayback && ( cls.demonum != -1 )) + { + host.mouse_visible = false; + cl.background = true; + } + else cl.background = background; + + if( cls.changedemo ) + SCR_BeginLoadingPlaque( cl.background ); + if( Con_FixedFont( )) { // seperate the printfs so the server message can have a color @@ -805,15 +826,6 @@ void CL_ParseServerData( sizebuf_t *msg ) } else Cvar_Reset( "r_decals" ); - // set the background state - if( cls.demoplayback && ( cls.demonum != -1 )) - { - // re-init mouse - host.mouse_visible = false; - cl.background = true; - } - else cl.background = background; - if( cl.background ) // tell the game parts about background state Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); @@ -829,13 +841,11 @@ void CL_ParseServerData( sizebuf_t *msg ) else if( !cls.demoplayback ) Key_SetKeyDest( key_menu ); - cl.viewentity = cl.playernum + 1; // always keep viewent an actual - + // will be changed later + cl.viewentity = cl.playernum + 1; gameui.globals->maxClients = cl.maxclients; Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle )); - - if( !cls.changelevel && !cls.changedemo ) - CL_InitEdicts (); // re-arrange edicts + CL_InitEdicts (); // re-arrange edicts // get splash name if( cls.demoplayback && ( cls.demonum != -1 )) @@ -2204,14 +2214,13 @@ ACTION MESSAGES ===================== CL_ParseServerMessage -INTERNAL RESOURCE +dispatch messages ===================== */ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) { - char *s; - int i, cmd, param1, param2; size_t bufStart, playerbytes; + int cmd, param1, param2; cls_message_debug.parsing = true; // begin parsing starting_count = MSG_GetNumBytesRead( msg ); // updates each frame @@ -2304,18 +2313,10 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) CL_ParseServerTime( msg ); break; case svc_print: - i = MSG_ReadByte( msg ); MsgDev( D_INFO, "^5%s", MSG_ReadString( msg )); - if( i == PRINT_CHAT ) - { - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) - S_StartLocalSound( "misc/talk.wav", VOL_NORM, false ); - else S_StartLocalSound( "common/menu2.wav", VOL_NORM, false ); - } break; case svc_stufftext: - s = MSG_ReadString( msg ); - Cbuf_AddText( s ); + Cbuf_AddText( MSG_ReadString( msg )); break; case svc_setangle: CL_ParseSetAngle( msg ); diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index aff200bc..580a568b 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -1138,7 +1138,7 @@ static int DecalDepthCompare( const void *a, const void *b ) // Input : *pList - // Output : int //----------------------------------------------------------------------------- -int R_CreateDecalList( decallist_t *pList, qboolean changelevel ) +int R_CreateDecalList( decallist_t *pList ) { int total = 0; int i, depth; @@ -1151,7 +1151,7 @@ int R_CreateDecalList( decallist_t *pList, qboolean changelevel ) decal_t *pdecals; // decal is in use and is not a custom decal - if( decal->psurface == NULL || ( decal->flags & FDECAL_DONTSAVE )) + if( decal->psurface == NULL || FBitSet( decal->flags, FDECAL_DONTSAVE )) continue; // compute depth @@ -1177,7 +1177,7 @@ int R_CreateDecalList( decallist_t *pList, qboolean changelevel ) if( clgame.drawFuncs.R_CreateStudioDecalList ) { - total += clgame.drawFuncs.R_CreateStudioDecalList( pList, total, changelevel ); + total += clgame.drawFuncs.R_CreateStudioDecalList( pList, total ); } } diff --git a/engine/common/common.c b/engine/common/common.c index 817aca7f..eb147e98 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -344,16 +344,10 @@ COM_AddAppDirectoryToSearchPath */ void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ) { - string dir; - - if( !pszBaseDir || !appName ) - { - MsgDev( D_ERROR, "COM_AddDirectorySearchPath: bad directory or appname\n" ); + if( !COM_CheckString( pszBaseDir )) return; - } - Q_snprintf( dir, sizeof( dir ), "%s/%s", pszBaseDir, appName ); - FS_AddGameDirectory( dir, FS_GAMEDIR_PATH ); + FS_AddGameDirectory( pszBaseDir, FS_GAMEDIR_PATH ); } /* diff --git a/engine/common/common.h b/engine/common/common.h index 4edfa03b..869a119d 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -289,15 +289,6 @@ typedef enum RD_PACKET } rdtype_t; -// game print level -typedef enum -{ - PRINT_LOW, // pickup messages - PRINT_MEDIUM, // death messages - PRINT_HIGH, // critical messages - PRINT_CHAT, // chat messages -} messagelevel_t; - #include "net_ws.h" typedef struct host_redirect_s @@ -766,7 +757,7 @@ float pfnTime( void ); */ #define Z_Malloc( size ) Mem_Alloc( host.mempool, size ) #define Z_Realloc( ptr, size ) Mem_Realloc( host.mempool, ptr, size ) -#define Z_Free( ptr ) if( ptr ) Mem_Free( ptr ) +#define Z_Free( ptr ) if( ptr != NULL ) Mem_Free( ptr ) // // crclib.c @@ -853,9 +844,7 @@ qboolean CL_IsInConsole( void ); qboolean CL_IsThirdPerson( void ); qboolean CL_IsIntermission( void ); qboolean CL_Initialized( void ); -qboolean CL_IsTimeDemo( void ); char *CL_Userinfo( void ); -float CL_GetLerpFrac( void ); void CL_CharEvent( int key ); qboolean CL_DisableVisibility( void ); int CL_PointContents( const vec3_t point ); @@ -868,15 +857,13 @@ struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); void SV_StartMusic( const char *curtrack, const char *looptrack, long position ); void SV_CreateDecal( struct sizebuf_s *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale ); -void SV_CreateStudioDecal( struct sizebuf_s *msg, const float *origin, const float *start, int decalIndex, int entityIndex, int modelIndex, -int flags, struct modelstate_s *state ); +void SV_CreateStudioDecal( struct sizebuf_s *bf, const float *v0, const float *v1, int decal, int ent, int model, int flags, struct modelstate_s *s ); void Log_Printf( const char *fmt, ... ); struct sizebuf_s *SV_GetReliableDatagram( void ); void SV_BroadcastCommand( const char *fmt, ... ); -void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start ); qboolean SV_RestoreCustomDecal( struct decallist_s *entry, edict_t *pEdict, qboolean adjacent ); -void SV_BroadcastPrintf( struct sv_client_s *ignore, int level, char *fmt, ... ); -int R_CreateDecalList( struct decallist_s *pList, qboolean changelevel ); +void SV_BroadcastPrintf( struct sv_client_s *ignore, char *fmt, ... ); +int R_CreateDecalList( struct decallist_s *pList ); void R_DecalRemoveAll( int texture ); void R_ClearAllDecals( void ); void R_ClearStaticEntities( void ); @@ -884,8 +871,6 @@ qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *posi struct cl_entity_s *CL_GetEntityByIndex( int index ); struct player_info_s *CL_GetPlayerInfo( int playerIndex ); void CL_ServerCommand( qboolean reliable, char *fmt, ... ); -void SV_ActivateServer( void ); -void SV_DeactivateServer( void ); const char *CL_MsgInfo( int cmd ); void SV_DrawDebugTriangles( void ); void SV_DrawOrthoTriangles( void ); @@ -895,19 +880,22 @@ void CL_ExtraUpdate( void ); int CL_GetMaxClients( void ); int SV_GetMaxClients( void ); qboolean CL_IsRecordDemo( void ); +qboolean CL_IsTimeDemo( void ); qboolean CL_IsPlaybackDemo( void ); qboolean CL_IsBackgroundDemo( void ); qboolean CL_IsBackgroundMap( void ); +qboolean SV_Initialized( void ); qboolean CL_LoadProgs( const char *name ); qboolean SV_GetComment( const char *savename, char *comment ); qboolean SV_NewGame( const char *mapName, qboolean loadGame ); void SV_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); void CL_ClipPMoveToEntity( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); void CL_Particle( const vec3_t origin, int color, float life, int zpos, int zvel ); // debug thing -void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLandmarkName, qboolean loadGame ); -qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean background ); void SV_SysError( const char *error_string ); -qboolean SV_LoadGame( const char *pName ); +void SV_ShutdownGame( void ); +void SV_ExecLoadLevel( void ); +void SV_ExecLoadGame( void ); +void SV_ExecChangeLevel( void ); void SV_ClearSaveDir( void ); void SV_InitGameProgs( void ); void SV_FreeGameProgs( void ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 75b9c4cb..bd2af59c 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -79,12 +79,12 @@ static qboolean Cvar_UpdateInfo( convar_t *var, const char *value, qboolean noti if( FBitSet( var->flags, FCVAR_PROTECTED )) { Log_Printf( "Server cvar \"%s\" = \"%s\"\n", var->name, "***PROTECTED***" ); - SV_BroadcastPrintf( NULL, PRINT_HIGH, "\"%s\" changed to \"%s\"\n", var->name, "***PROTECTED***" ); + SV_BroadcastPrintf( NULL, "\"%s\" changed to \"%s\"\n", var->name, "***PROTECTED***" ); } else { Log_Printf( "Server cvar \"%s\" = \"%s\"\n", var->name, value ); - SV_BroadcastPrintf( NULL, PRINT_HIGH, "\"%s\" changed to \"%s\"\n", var->name, value ); + SV_BroadcastPrintf( NULL, "\"%s\" changed to \"%s\"\n", var->name, value ); } } } diff --git a/engine/common/host.c b/engine/common/host.c index 9bc6e588..104d8bd0 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -59,7 +59,7 @@ int Host_CompareFileTime( long ft1, long ft2 ) void Host_ShutdownServer( void ) { - if( !SV_Active( )) return; + if( !SV_Initialized( )) return; Q_strncpy( host.finalmsg, "Server was killed", MAX_STRING ); SV_Shutdown( false ); } @@ -106,7 +106,7 @@ void Host_EndGame( qboolean abort, const char *message, ... ) MsgDev( D_INFO, "Host_EndGame: %s\n", string ); - if( SV_Active( )) + if( SV_Initialized( )) { Q_snprintf( host.finalmsg, sizeof( host.finalmsg ), "Host_EndGame: %s", string ); SV_Shutdown( false ); @@ -442,7 +442,7 @@ void Host_RestartDecals( void ) // 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 ); + host.numdecals = R_CreateDecalList( host.decalList ); // remove decals from map R_ClearAllDecals(); diff --git a/engine/common/host_state.c b/engine/common/host_state.c index cc392199..65868a2f 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -14,29 +14,20 @@ GNU General Public License for more details. */ #include "common.h" -#include "netchan.h" -#include "protocol.h" -#include "mod_local.h" -#include "mathlib.h" -#include "input.h" -#include "features.h" -#include "render_api.h" // decallist_t void COM_InitHostState( void ) { memset( GameState, 0, sizeof( game_status_t )); - GameState->curstate = STATE_RUNFRAME; - GameState->nextstate = STATE_RUNFRAME; } -static void HostState_SetState( host_state_t newState, qboolean clearNext ) +static void Host_SetState( host_state_t newState, qboolean clearNext ) { if( clearNext ) GameState->nextstate = newState; GameState->curstate = newState; } -static void HostState_SetNextState( host_state_t nextState ) +static void Host_SetNextState( host_state_t nextState ) { ASSERT( GameState->curstate == STATE_RUNFRAME ); GameState->nextstate = nextState; @@ -44,8 +35,11 @@ static void HostState_SetNextState( host_state_t nextState ) void COM_NewGame( char const *pMapName ) { + if( GameState->nextstate != STATE_RUNFRAME ) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); - HostState_SetNextState( STATE_LOAD_LEVEL ); + Host_SetNextState( STATE_LOAD_LEVEL ); GameState->backgroundMap = false; GameState->landmarkName[0] = 0; @@ -54,8 +48,11 @@ void COM_NewGame( char const *pMapName ) void COM_LoadLevel( char const *pMapName, qboolean background ) { + if( GameState->nextstate != STATE_RUNFRAME ) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); - HostState_SetNextState( STATE_LOAD_LEVEL ); + Host_SetNextState( STATE_LOAD_LEVEL ); GameState->backgroundMap = background; GameState->landmarkName[0] = 0; @@ -64,8 +61,11 @@ void COM_LoadLevel( char const *pMapName, qboolean background ) void COM_LoadGame( char const *pMapName ) { + if( GameState->nextstate != STATE_RUNFRAME ) + return; + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); - HostState_SetNextState( STATE_LOAD_GAME ); + Host_SetNextState( STATE_LOAD_GAME ); GameState->backgroundMap = false; GameState->newGame = false; GameState->loadGame = true; @@ -73,6 +73,9 @@ void COM_LoadGame( char const *pMapName ) void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName ) { + if( GameState->nextstate != STATE_RUNFRAME ) + return; + Q_strncpy( GameState->levelName, pNewLevel, sizeof( GameState->levelName )); if( COM_CheckString( pLandmarkName )) @@ -86,68 +89,27 @@ void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName ) GameState->loadGame = false; } - HostState_SetNextState( STATE_CHANGELEVEL ); + Host_SetNextState( STATE_CHANGELEVEL ); GameState->newGame = false; } -void HostState_LoadLevel( void ) +void Host_ShutdownGame( void ) { - if( SV_SpawnServer( GameState->levelName, NULL, GameState->backgroundMap )) - { - SV_LevelInit( GameState->levelName, NULL, NULL, false ); - SV_ActivateServer (); - } - - HostState_SetState( STATE_RUNFRAME, true ); -} - -void HostState_LoadGame( void ) -{ - if( SV_SpawnServer( GameState->levelName, NULL, false )) - { - SV_LevelInit( GameState->levelName, NULL, NULL, true ); - SV_ActivateServer (); - } - - HostState_SetState( STATE_RUNFRAME, true ); -} - -void HostState_ChangeLevel( void ) -{ - SV_ChangeLevel( GameState->loadGame, GameState->levelName, GameState->landmarkName ); - HostState_SetState( STATE_RUNFRAME, true ); -} - -void HostState_ShutdownGame( void ) -{ - if( !GameState->loadGame ) - SV_ClearSaveDir(); - - S_StopBackgroundTrack(); - - if( GameState->newGame ) - { - Host_EndGame( false, DEFAULT_ENDGAME_MESSAGE ); - } - else - { - S_StopAllSounds( true ); - SV_DeactivateServer(); - } + SV_ShutdownGame(); switch( GameState->nextstate ) { case STATE_LOAD_GAME: case STATE_LOAD_LEVEL: - HostState_SetState( GameState->nextstate, true ); + Host_SetState( GameState->nextstate, true ); break; default: - HostState_SetState( STATE_RUNFRAME, true ); + Host_SetState( STATE_RUNFRAME, true ); break; } } -void HostState_Run( float time ) +void Host_RunFrame( float time ) { // engine main frame Host_Frame( time ); @@ -161,14 +123,14 @@ void HostState_Run( float time ) SCR_BeginLoadingPlaque( GameState->backgroundMap ); // intentionally fallthrough case STATE_GAME_SHUTDOWN: - HostState_SetState( STATE_GAME_SHUTDOWN, false ); + Host_SetState( STATE_GAME_SHUTDOWN, false ); break; case STATE_CHANGELEVEL: SCR_BeginLoadingPlaque( false ); - HostState_SetState( GameState->nextstate, true ); + Host_SetState( GameState->nextstate, true ); break; default: - HostState_SetState( STATE_RUNFRAME, true ); + Host_SetState( STATE_RUNFRAME, true ); break; } } @@ -185,19 +147,22 @@ void COM_Frame( float time ) switch( GameState->curstate ) { case STATE_LOAD_LEVEL: - HostState_LoadLevel(); + SV_ExecLoadLevel(); + Host_SetState( STATE_RUNFRAME, true ); break; case STATE_LOAD_GAME: - HostState_LoadGame(); + SV_ExecLoadGame(); + Host_SetState( STATE_RUNFRAME, true ); break; case STATE_CHANGELEVEL: - HostState_ChangeLevel(); + SV_ExecChangeLevel(); + Host_SetState( STATE_RUNFRAME, true ); break; case STATE_RUNFRAME: - HostState_Run( time ); + Host_RunFrame( time ); break; case STATE_GAME_SHUTDOWN: - HostState_ShutdownGame(); + Host_ShutdownGame(); break; } diff --git a/engine/common/model.c b/engine/common/model.c index 0014384b..dd19d8b4 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -107,7 +107,6 @@ static void Mod_FreeModel( model_t *mod ) if( !mod || !mod->name[0] ) return; - Msg( "release %s\n", mod->name ); Mod_FreeUserData( mod ); // select the properly unloader @@ -479,8 +478,6 @@ void Mod_FreeUnused( void ) model_t *mod; int i; - Msg( "Mod_FreeUnused()\n" ); - for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) { if( mod->needload == NL_UNREFERENCED ) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 019dc465..18c70a90 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -96,6 +96,12 @@ sizebuf_t net_message; byte *net_mempool; byte net_message_buffer[NET_MAX_MESSAGE]; +const char *ns_strings[NS_COUNT] = +{ + "Client", + "Server", +}; + /* =============== Netchan_Init @@ -714,7 +720,7 @@ void Netchan_CheckForCompletion( netchan_t *chan, int stream, int intotalbuffers if( c == intotalbuffers ) { chan->incomingready[stream] = true; - MsgDev( D_NOTE, "\nincoming is complete %i bytes waiting\n", size ); + MsgDev( D_NOTE, "\n%s: incoming is complete %i bytes waiting\n", ns_strings[chan->sock], size ); } } diff --git a/engine/physint.h b/engine/physint.h index a3115766..8a79e9cc 100644 --- a/engine/physint.h +++ b/engine/physint.h @@ -142,7 +142,7 @@ typedef struct physics_interface_s // called through save\restore process void (*pfnCreateEntitiesInTransitionList)( SAVERESTOREDATA*, int levelMask ); // called through save\restore process - void (*pfnCreateEntitiesInRestoreList)( SAVERESTOREDATA*, int createPlayers ); + void (*pfnCreateEntitiesInRestoreList)( SAVERESTOREDATA* ); // allocate custom string (e.g. using user implementation of stringtable, not engine strings) string_t (*pfnAllocString)( const char *szValue ); // make custom string (e.g. using user implementation of stringtable, not engine strings) diff --git a/engine/server/server.h b/engine/server/server.h index 5f1f30f3..8ab1f7f1 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -246,8 +246,6 @@ typedef struct sv_client_s edict_t *edict; // EDICT_NUM(clientnum+1) edict_t *pViewEntity; // svc_setview member - int messagelevel; // for filtering printed messages - edict_t *viewentity[MAX_VIEWENTS]; // list of portal cameras in player PVS int num_viewents; // num of portal cameras that can merge PVS @@ -485,10 +483,11 @@ void Master_Packet( void ); // // sv_init.c // -void SV_ActivateServer( void ); -void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLandmarkName, qboolean loadGame ); +void SV_ActivateServer( int runPhysics ); qboolean SV_SpawnServer( const char *server, const char *startspot, qboolean background ); model_t *SV_ModelHandle( int modelindex ); +void SV_DeactivateServer( void ); +char *SV_EntityScript( void ); // // sv_phys.c @@ -519,8 +518,8 @@ void SV_WaterMove( edict_t *ent ); // sv_send.c // void SV_SendClientMessages( void ); -void SV_ClientPrintf( sv_client_t *cl, int level, char *fmt, ... ); -void SV_BroadcastPrintf( sv_client_t *ignore, int level, char *fmt, ... ); +void SV_ClientPrintf( sv_client_t *cl, char *fmt, ... ); +void SV_BroadcastPrintf( sv_client_t *ignore, char *fmt, ... ); void SV_BroadcastCommand( const char *fmt, ... ); // @@ -536,6 +535,7 @@ const char *SV_GetClientIDString( sv_client_t *cl ); void SV_FullClientUpdate( sv_client_t *cl, sizebuf_t *msg ); void SV_FullUpdateMovevars( sv_client_t *cl, sizebuf_t *msg ); void SV_GetPlayerStats( sv_client_t *cl, int *ping, int *packet_loss ); +void SV_SendServerdata( sizebuf_t *msg, sv_client_t *cl ); qboolean SV_ClientConnect( edict_t *ent, char *userinfo ); void SV_ClientThink( sv_client_t *cl, usercmd_t *cmd ); void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ); @@ -543,6 +543,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ); edict_t *SV_FakeConnect( const char *netname ); void SV_ExecuteClientCommand( sv_client_t *cl, char *s ); void SV_RunCmd( sv_client_t *cl, usercmd_t *ucmd, int random_seed ); +void SV_BuildReconnect( sizebuf_t *msg ); qboolean SV_IsPlayerIndex( int idx ); int SV_CalcPing( sv_client_t *cl ); void SV_InitClientMove( void ); @@ -572,9 +573,9 @@ int SV_TransferConsistencyInfo( void ); // // sv_frame.c // +void SV_InactivateClients( void ); void SV_WriteFrameToClient( sv_client_t *client, sizebuf_t *msg ); void SV_BuildClientFrame( sv_client_t *client ); -void SV_InactivateClients( void ); void SV_SendMessagesToAll( void ); void SV_SkipUpdates( void ); @@ -625,7 +626,7 @@ char *SV_Localinfo( void ); _inline edict_t *SV_EDICT_NUM( int n, const char * file, const int line ) { - if((n >= 0) && (n < svgame.globals->maxEntities)) + if((n >= 0) && (n < GI->max_edicts)) return svgame.edicts + n; Host_Error( "SV_EDICT_NUM: bad number %i (called at %s:%i)\n", n, file, line ); return NULL; @@ -645,10 +646,12 @@ void SV_ServerLog_f( sv_client_t *cl ); void SV_ClearSaveDir( void ); void SV_SaveGame( const char *pName ); qboolean SV_LoadGame( const char *pName ); -int SV_LoadGameState( char const *level, qboolean createPlayers ); +int SV_LoadGameState( char const *level, qboolean changelevel ); +void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start ); void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ); const char *SV_GetLatestSave( void ); void SV_InitSaveRestore( void ); +void SV_ClearGameState( void ); // // sv_pmove.c diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index dbc1e95d..35838c93 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -535,10 +535,7 @@ qboolean SV_ClientConnect( edict_t *ent, char *userinfo ) char *pszName, *pszAddress; char szRejectReason[MAX_INFO_STRING]; - // make sure we start with known default - if( !sv.loadgame ) ent->v.flags = 0; szRejectReason[0] = '\0'; - pszName = Info_ValueForKey( userinfo, "name" ); pszAddress = Info_ValueForKey( userinfo, "ip" ); @@ -642,7 +639,6 @@ void SV_FlushRedirect( netadr_t adr, int dest, char *buf ) case RD_CLIENT: if( !svs.currentPlayer ) return; // client not set MSG_BeginServerCmd( &svs.currentPlayer->netchan.message, svc_print ); - MSG_WriteByte( &svs.currentPlayer->netchan.message, PRINT_HIGH ); MSG_WriteString( &svs.currentPlayer->netchan.message, buf ); break; case RD_NONE: @@ -1204,35 +1200,7 @@ void SV_PutClientInServer( sv_client_t *cl ) MSG_Init( &msg, "Spawn", msg_buf, sizeof( msg_buf )); - if( !sv.loadgame ) - { - if( Q_atoi( Info_ValueForKey( cl->userinfo, "hltv" ))) - SetBits( cl->flags, FCL_HLTV_PROXY ); - - if( FBitSet( cl->flags, FCL_HLTV_PROXY )) - SetBits( ent->v.flags, FL_PROXY ); - else ent->v.flags = 0; - - ent->v.netname = MAKE_STRING( cl->name ); - ent->v.colormap = NUM_FOR_EDICT( ent ); // ??? - - // fisrt entering - svgame.globals->time = sv.time; - svgame.dllFuncs.pfnClientPutInServer( ent ); - - if( sv.background ) // don't attack player in background mode - SetBits( ent->v.flags, FL_GODMODE|FL_NOTARGET ); - - cl->pViewEntity = NULL; // reset pViewEntity - - if( svgame.globals->cdAudioTrack ) - { - MSG_BeginServerCmd( &msg, svc_stufftext ); - MSG_WriteString( &msg, va( "cd loop %3d\n", svgame.globals->cdAudioTrack )); - svgame.globals->cdAudioTrack = 0; - } - } - else + if( sv.loadgame ) { // NOTE: we needs to setup angles on restore here if( ent->v.fixangle == 1 ) @@ -1275,6 +1243,37 @@ void SV_PutClientInServer( sv_client_t *cl ) if( sv.viewentity > 0 && sv.viewentity < GI->max_edicts ) cl->pViewEntity = EDICT_NUM( sv.viewentity ); else cl->pViewEntity = NULL; + + sv.loadgame = false; + sv.paused = false; + } + else + { + if( Q_atoi( Info_ValueForKey( cl->userinfo, "hltv" ))) + SetBits( cl->flags, FCL_HLTV_PROXY ); + + if( FBitSet( cl->flags, FCL_HLTV_PROXY )) + SetBits( ent->v.flags, FL_PROXY ); + else ent->v.flags = 0; + + ent->v.netname = MAKE_STRING( cl->name ); + ent->v.colormap = NUM_FOR_EDICT( ent ); // ??? + + // fisrt entering + svgame.globals->time = sv.time; + svgame.dllFuncs.pfnClientPutInServer( ent ); + + if( sv.background ) // don't attack player in background mode + SetBits( ent->v.flags, FL_GODMODE|FL_NOTARGET ); + + cl->pViewEntity = NULL; // reset pViewEntity + + if( svgame.globals->cdAudioTrack ) + { + MSG_BeginServerCmd( &msg, svc_stufftext ); + MSG_WriteString( &msg, va( "cd loop %3d\n", svgame.globals->cdAudioTrack )); + svgame.globals->cdAudioTrack = 0; + } } // enable dev-mode to prevent crash cheat-protecting from Invasion mod @@ -1314,11 +1313,6 @@ void SV_PutClientInServer( sv_client_t *cl ) Netchan_CreateFragments( &cl->netchan, &msg ); Netchan_FragSend( &cl->netchan ); } - - // clear any temp states - sv.changelevel = false; - sv.loadgame = false; - sv.paused = false; } /* @@ -1351,13 +1345,27 @@ void SV_TogglePause( const char *msg ) sv.paused ^= 1; - if( msg ) SV_BroadcastPrintf( NULL, PRINT_HIGH, "%s", msg ); + if( COM_CheckString( msg )) + SV_BroadcastPrintf( NULL, "%s", msg ); // send notification to all clients MSG_BeginServerCmd( &sv.reliable_datagram, svc_setpause ); MSG_WriteOneBit( &sv.reliable_datagram, sv.paused ); } +/* +================ +SV_SendReconnect + +Tell all the clients that the server is changing levels +================ +*/ +void SV_BuildReconnect( sizebuf_t *msg ) +{ + MSG_BeginServerCmd( msg, svc_stufftext ); + MSG_WriteString( msg, "reconnect\n" ); +} + /* ================== SV_WriteDeltaDescriptionToClient @@ -1389,20 +1397,30 @@ This will be sent on the initial connection and upon each server load. */ void SV_SendServerdata( sizebuf_t *msg, sv_client_t *cl ) { + string message; int i; + // Only send this message to developer console, or multiplayer clients. + if(( host.developer ) || ( svs.maxclients > 1 )) + { + MSG_BeginServerCmd( msg, svc_print ); + Q_snprintf( message, sizeof( message ), "%c\nBUILD %d SERVER (%i CRC)\nServer # %i\n", 2, Q_buildnum(), 0, svs.spawncount ); + MSG_WriteString( msg, message ); + } + // send the serverdata MSG_BeginServerCmd( msg, svc_serverdata ); MSG_WriteLong( msg, PROTOCOL_VERSION ); MSG_WriteLong( msg, svs.spawncount ); MSG_WriteLong( msg, sv.worldmapCRC ); MSG_WriteByte( msg, cl - svs.clients ); - MSG_WriteByte( msg, svgame.globals->maxClients ); - MSG_WriteWord( msg, svgame.globals->maxEntities ); + MSG_WriteByte( msg, svs.maxclients ); + MSG_WriteWord( msg, GI->max_edicts ); MSG_WriteWord( msg, MAX_MODELS ); MSG_WriteString( msg, sv.name ); - MSG_WriteString( msg, STRING( EDICT_NUM( 0 )->v.message )); // Map Message + MSG_WriteString( msg, STRING( svgame.edicts->v.message )); // Map Message MSG_WriteOneBit( msg, sv.background ); // tell client about background map + MSG_WriteOneBit( msg, svgame.globals->changelevel ); MSG_WriteString( msg, GI->gamefolder ); MSG_WriteLong( msg, host.features ); @@ -1418,6 +1436,21 @@ void SV_SendServerdata( sizebuf_t *msg, sv_client_t *cl ) // now client know delta and can reading encoded messages SV_FullUpdateMovevars( cl, msg ); + + // send the user messages registration + for( i = 1; i < MAX_USER_MESSAGES && svgame.msg[i].name[0]; i++ ) + SV_SendUserReg( msg, &svgame.msg[i] ); + + for( i = 0; i < MAX_LIGHTSTYLES; i++ ) + { + if( !sv.lightstyles[i].pattern[0] ) + continue; // unused style + + MSG_BeginServerCmd( msg, svc_lightstyle ); + MSG_WriteByte( msg, i ); // stylenum + MSG_WriteString( msg, sv.lightstyles[i].pattern ); + MSG_WriteFloat( msg, sv.lightstyles[i].time ); + } } /* @@ -1453,21 +1486,6 @@ void SV_New_f( sv_client_t *cl ) // send the serverdata SV_SendServerdata( &msg, cl ); - // send the user messages registration - for( i = 1; i < MAX_USER_MESSAGES && svgame.msg[i].name[0]; i++ ) - SV_SendUserReg( &msg, &svgame.msg[i] ); - - for( i = 0; i < MAX_LIGHTSTYLES; i++ ) - { - if( !sv.lightstyles[i].pattern[0] ) - continue; // unused style - - MSG_BeginServerCmd( &msg, svc_lightstyle ); - MSG_WriteByte( &msg, i ); // stylenum - MSG_WriteString( &msg, sv.lightstyles[i].pattern ); - MSG_WriteFloat( &msg, sv.lightstyles[i].time ); - } - // server info string MSG_BeginServerCmd( &msg, svc_stufftext ); MSG_WriteString( &msg, va( "fullserverinfo \"%s\"\n", SV_Serverinfo( ))); @@ -1662,13 +1680,13 @@ void SV_Pause_f( sv_client_t *cl ) if( !sv_pausable->value ) { - SV_ClientPrintf( cl, PRINT_HIGH, "Pause not allowed.\n" ); + SV_ClientPrintf( cl, "Pause not allowed.\n" ); return; } if( FBitSet( cl->flags, FCL_HLTV_PROXY )) { - SV_ClientPrintf( cl, PRINT_HIGH, "Spectators can not pause.\n" ); + SV_ClientPrintf( cl, "Spectators can not pause.\n" ); return; } @@ -1753,19 +1771,18 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ) if( Q_strlen( val )) cl->netchan.rate = bound( MIN_RATE, Q_atoi( val ), MAX_RATE ); else cl->netchan.rate = DEFAULT_RATE; - - // msg command - val = Info_ValueForKey( cl->userinfo, "msg" ); - if( Q_strlen( val )) cl->messagelevel = Q_atoi( val ); + // movement prediction if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_nopred" ))) ClearBits( cl->flags, FCL_PREDICT_MOVEMENT ); else SetBits( cl->flags, FCL_PREDICT_MOVEMENT ); + // lag compensation if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_lc" ))) SetBits( cl->flags, FCL_LAG_COMPENSATION ); else ClearBits( cl->flags, FCL_LAG_COMPENSATION ); + // weapon perdiction if( Q_atoi( Info_ValueForKey( cl->userinfo, "cl_lw" ))) SetBits( cl->flags, FCL_LOCAL_WEAPONS ); else ClearBits( cl->flags, FCL_LOCAL_WEAPONS ); @@ -1830,13 +1847,37 @@ static void SV_Noclip_f( sv_client_t *cl ) if( pEntity->v.movetype != MOVETYPE_NOCLIP ) { + SV_ClientPrintf( cl, "noclip ON\n" ); pEntity->v.movetype = MOVETYPE_NOCLIP; - SV_ClientPrintf( cl, PRINT_HIGH, "noclip ON\n" ); } else { + SV_ClientPrintf( cl, "noclip OFF\n" ); + pEntity->v.movetype = MOVETYPE_WALK; + } +} + +/* +================== +SV_Fly_f +================== +*/ +static void SV_Fly_f( sv_client_t *cl ) +{ + edict_t *pEntity = cl->edict; + + if( !Cvar_VariableInteger( "sv_cheats" ) || sv.background ) + return; + + if( pEntity->v.movetype != MOVETYPE_FLY ) + { + SV_ClientPrintf( cl, "flymode ON\n" ); + pEntity->v.movetype = MOVETYPE_FLY; + } + else + { + SV_ClientPrintf( cl, "flymode OFF\n" ); pEntity->v.movetype = MOVETYPE_WALK; - SV_ClientPrintf( cl, PRINT_HIGH, "noclip OFF\n" ); } } @@ -1855,8 +1896,8 @@ static void SV_Godmode_f( sv_client_t *cl ) pEntity->v.flags = pEntity->v.flags ^ FL_GODMODE; if( !FBitSet( pEntity->v.flags, FL_GODMODE )) - SV_ClientPrintf( cl, PRINT_HIGH, "godmode OFF\n" ); - else SV_ClientPrintf( cl, PRINT_HIGH, "godmode ON\n" ); + SV_ClientPrintf( cl, "godmode OFF\n" ); + else SV_ClientPrintf( cl, "godmode ON\n" ); } /* @@ -1874,8 +1915,8 @@ static void SV_Notarget_f( sv_client_t *cl ) pEntity->v.flags = pEntity->v.flags ^ FL_NOTARGET; if( !FBitSet( pEntity->v.flags, FL_NOTARGET )) - SV_ClientPrintf( cl, PRINT_HIGH, "notarget OFF\n" ); - else SV_ClientPrintf( cl, PRINT_HIGH, "notarget ON\n" ); + SV_ClientPrintf( cl, "notarget OFF\n" ); + else SV_ClientPrintf( cl, "notarget ON\n" ); } /* @@ -2013,7 +2054,7 @@ void SV_Spawn_f( sv_client_t *cl ) { MSG_BeginServerCmd( &sv.reliable_datagram, svc_setpause ); MSG_WriteByte( &sv.reliable_datagram, sv.paused ); - SV_ClientPrintf( cl, PRINT_HIGH, "Server is paused.\n" ); + SV_ClientPrintf( cl, "Server is paused.\n" ); } } @@ -2037,6 +2078,7 @@ void SV_Begin_f( sv_client_t *cl ) ucmd_t ucmds[] = { { "new", SV_New_f }, +{ "fly", SV_Fly_f }, { "god", SV_Godmode_f }, { "begin", SV_Begin_f }, { "spawn", SV_Spawn_f }, @@ -2278,7 +2320,7 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) cl->packet_loss = packet_loss; // check for pause or frozen - if( sv.paused || sv.loadgame || sv.background || !CL_IsInGame() || SV_PlayerIsFrozen( player )) + if( sv.paused || !CL_IsInGame() || SV_PlayerIsFrozen( player )) { for( i = 0; i < numcmds; i++ ) { @@ -2288,7 +2330,7 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) cmds[i].upmove = 0; cmds[i].buttons = 0; - if( SV_PlayerIsFrozen( player ) || sv.background ) + if( SV_PlayerIsFrozen( player )) cmds[i].impulse = 0; VectorCopy( cmds[i].viewangles, player->v.v_angle ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 2e3a0148..8c757249 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -23,12 +23,12 @@ SV_ClientPrintf Sends text across to be displayed if the level passes ================= */ -void SV_ClientPrintf( sv_client_t *cl, int level, char *fmt, ... ) +void SV_ClientPrintf( sv_client_t *cl, char *fmt, ... ) { char string[MAX_SYSPATH]; va_list argptr; - if( level < cl->messagelevel || FBitSet( cl->flags, FCL_FAKECLIENT )) + if( FBitSet( cl->flags, FCL_FAKECLIENT )) return; va_start( argptr, fmt ); @@ -36,7 +36,6 @@ void SV_ClientPrintf( sv_client_t *cl, int level, char *fmt, ... ) va_end( argptr ); MSG_BeginServerCmd( &cl->netchan.message, svc_print ); - MSG_WriteByte( &cl->netchan.message, level ); MSG_WriteString( &cl->netchan.message, string ); } @@ -47,7 +46,7 @@ SV_BroadcastPrintf Sends text to all active clients ================= */ -void SV_BroadcastPrintf( sv_client_t *ignore, int level, char *fmt, ... ) +void SV_BroadcastPrintf( sv_client_t *ignore, char *fmt, ... ) { char string[MAX_SYSPATH]; va_list argptr; @@ -66,16 +65,17 @@ void SV_BroadcastPrintf( sv_client_t *ignore, int level, char *fmt, ... ) for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ ) { - if( level < cl->messagelevel || FBitSet( cl->flags, FCL_FAKECLIENT )) + if( FBitSet( cl->flags, FCL_FAKECLIENT )) continue; if( cl == ignore || cl->state != cs_spawned ) continue; MSG_BeginServerCmd( &cl->netchan.message, svc_print ); - MSG_WriteByte( &cl->netchan.message, level ); MSG_WriteString( &cl->netchan.message, string ); } + + MsgDev( D_REPORT, string ); } /* @@ -235,12 +235,7 @@ void SV_Map_f( void ) if( !SV_ValidateMap( mapname, true )) return; - // changing singleplayer to multiplayer or back. refresh the player count - if( FBitSet( sv_maxclients->flags, FCVAR_CHANGED )) - Host_ShutdownServer(); - Cvar_DirectSet( sv_hostmap, mapname ); - COM_LoadLevel( mapname, false ); } @@ -261,7 +256,7 @@ void SV_MapBackground_f( void ) return; } - if( sv.state == ss_active && !sv.background ) + if( SV_Active() && !sv.background ) { MsgDev( D_ERROR, "can't set background map while game is active\n" ); return; @@ -274,14 +269,10 @@ void SV_MapBackground_f( void ) if( !SV_ValidateMap( mapname, false )) return; - Q_strncpy( host.finalmsg, "", MAX_STRING ); - SV_Shutdown( true ); - NET_Config( false ); // close network sockets - - // reset all multiplayer cvars + // background map is always run as singleplayer Cvar_FullSet( "maxplayers", "1", FCVAR_LATCH ); - Cvar_SetValue( "deathmatch", 0 ); - Cvar_SetValue( "coop", 0 ); + Cvar_FullSet( "deathmatch", "0", FCVAR_LATCH ); + Cvar_FullSet( "coop", "0", FCVAR_LATCH ); COM_LoadLevel( mapname, true ); } @@ -512,10 +503,17 @@ void SV_ChangeLevel_f( void ) } if( sv.background ) + { COM_LoadLevel( mapname, false ); - else if( c == 2 ) - COM_ChangeLevel( Cmd_Argv( 1 ), NULL ); - else COM_ChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 )); + } + else + { + // g-cont: inactivate clients to avoid fired "trigger_changelevel" multiple times + SV_InactivateClients (); + + if( c == 2 ) COM_ChangeLevel( Cmd_Argv( 1 ), NULL ); + else COM_ChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 )); + } } /* @@ -570,8 +568,8 @@ void SV_Kick_f( void ) } Log_Printf( "Kick: \"%s<%i>\" was kicked\n", svs.currentPlayer->name, svs.currentPlayer->userid ); - SV_BroadcastPrintf( svs.currentPlayer, PRINT_HIGH, "%s was kicked\n", svs.currentPlayer->name ); - SV_ClientPrintf( svs.currentPlayer, PRINT_HIGH, "You were kicked from the game\n" ); + SV_BroadcastPrintf( svs.currentPlayer, "%s was kicked\n", svs.currentPlayer->name ); + SV_ClientPrintf( svs.currentPlayer, "You were kicked from the game\n" ); SV_DropClient( svs.currentPlayer ); } @@ -589,7 +587,7 @@ void SV_Kill_f( void ) if( svs.currentPlayer->edict->v.health <= 0.0f ) { - SV_ClientPrintf( svs.currentPlayer, PRINT_HIGH, "Can't suicide - already dead!\n"); + SV_ClientPrintf( svs.currentPlayer, "Can't suicide - already dead!\n"); return; } @@ -678,9 +676,7 @@ SV_ConSay_f */ void SV_ConSay_f( void ) { - char *p, text[MAX_SYSPATH]; - sv_client_t *client; - int i; + char *p, text[MAX_SYSPATH]; if( Cmd_Argc() < 2 ) return; @@ -700,14 +696,7 @@ void SV_ConSay_f( void ) } Q_strncat( text, p, MAX_SYSPATH ); - - for( i = 0, client = svs.clients; i < svs.maxclients; i++, client++ ) - { - if( client->state != cs_spawned ) - continue; - - SV_ClientPrintf( client, PRINT_CHAT, "%s\n", text ); - } + SV_BroadcastPrintf( NULL, "%s\n", text ); Log_Printf( "Server say: \"%s\"\n", p ); } @@ -849,8 +838,8 @@ void SV_PlayersOnly_f( void ) sv.hostflags = sv.hostflags ^ SVF_PLAYERSONLY; if( !FBitSet( sv.hostflags, SVF_PLAYERSONLY )) - SV_BroadcastPrintf( NULL, PRINT_HIGH, "Resume server physic\n" ); - else SV_BroadcastPrintf( NULL, PRINT_HIGH, "Freeze server physic\n" ); + SV_BroadcastPrintf( NULL, "Resume game physic\n" ); + else SV_BroadcastPrintf( NULL, "Freeze game physic\n" ); } /* @@ -871,8 +860,8 @@ void SV_EdictUsage_f( void ) active = pfnNumberOfEntities(); Msg( "%5i edicts is used\n", active ); - Msg( "%5i edicts is free\n", svgame.globals->maxEntities - active ); - Msg( "%5i total\n", svgame.globals->maxEntities ); + Msg( "%5i edicts is free\n", GI->max_edicts - active ); + Msg( "%5i total\n", GI->max_edicts ); } /* @@ -937,7 +926,6 @@ void SV_InitHostCommands( void ) Cmd_AddCommand( "map_background", SV_MapBackground_f, "set background map" ); Cmd_AddCommand( "load", SV_Load_f, "load a saved game file" ); Cmd_AddCommand( "loadquick", SV_QuickLoad_f, "load a quick-saved game file" ); - Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" ); } } @@ -961,17 +949,18 @@ void SV_InitOperatorCommands( void ) Cmd_AddCommand( "entpatch", SV_EntPatch_f, "write entity patch to allow external editing" ); Cmd_AddCommand( "edict_usage", SV_EdictUsage_f, "show info about edicts usage" ); Cmd_AddCommand( "entity_info", SV_EntityInfo_f, "show more info about edicts" ); + Cmd_AddCommand( "shutdownserver", SV_KillServer_f, "shutdown current server" ); if( host.type == HOST_NORMAL ) { Cmd_AddCommand( "save", SV_Save_f, "save the game to a file" ); Cmd_AddCommand( "savequick", SV_QuickSave_f, "save the game to the quicksave" ); Cmd_AddCommand( "autosave", SV_AutoSave_f, "save the game to 'autosave' file" ); + Cmd_AddCommand( "killsave", SV_DeleteSave_f, "delete a saved game file and saveshot" ); } else if( host.type == HOST_DEDICATED ) { Cmd_AddCommand( "say", SV_ConSay_f, "send a chat message to everyone on the server" ); - Cmd_AddCommand( "killserver", SV_KillServer_f, "shutdown current server" ); } } @@ -998,6 +987,7 @@ void SV_KillOperatorCommands( void ) Cmd_RemoveCommand( "entpatch" ); Cmd_RemoveCommand( "edict_usage" ); Cmd_RemoveCommand( "entity_info" ); + Cmd_RemoveCommand( "shutdownserver" ); if( host.type == HOST_NORMAL ) { @@ -1009,6 +999,5 @@ void SV_KillOperatorCommands( void ) else if( host.type == HOST_DEDICATED ) { Cmd_RemoveCommand( "say" ); - Cmd_RemoveCommand( "killserver" ); } } \ No newline at end of file diff --git a/engine/server/sv_custom.c b/engine/server/sv_custom.c index 1b81dbd7..f0a20bea 100644 --- a/engine/server/sv_custom.c +++ b/engine/server/sv_custom.c @@ -177,8 +177,8 @@ void SV_ParseConsistencyResponse( sv_client_t *cl, sizebuf_t *msg ) dropmessage[0] = 0; if( svgame.dllFuncs.pfnInconsistentFile( cl->edict, sv.resources[badresindex - 1].szFileName, dropmessage )) { - if( Q_strlen( dropmessage ) > 0 ) - SV_ClientPrintf( cl, PRINT_HIGH, dropmessage ); + if( COM_CheckString( dropmessage )) + SV_ClientPrintf( cl, dropmessage ); SV_DropClient( cl ); } } diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index f1b5edc9..b0e0c8af 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -862,7 +862,7 @@ void SV_SendClientMessages( void ) { MSG_Clear( &cl->netchan.message ); MSG_Clear( &cl->datagram ); - SV_BroadcastPrintf( NULL, PRINT_HIGH, "%s overflowed\n", cl->name ); + SV_BroadcastPrintf( NULL, "%s overflowed\n", cl->name ); MsgDev( D_WARN, "reliable overflow for %s\n", cl->name ); SV_DropClient( cl ); SetBits( cl->flags, FCL_SEND_NET_MESSAGE ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index d9b06046..f9e3e9df 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -838,7 +838,7 @@ edict_t *SV_AllocEdict( void ) edict_t *pEdict; int i; - for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) + for( i = svs.maxclients + 1; i < svgame.numEntities; i++ ) { pEdict = EDICT_NUM( i ); // the first couple seconds of server time can involve a lot of @@ -850,8 +850,8 @@ edict_t *SV_AllocEdict( void ) } } - if( i >= svgame.globals->maxEntities ) - Sys_Error( "ED_AllocEdict: no free edicts (max is %d)\n", svgame.globals->maxEntities ); + if( i >= GI->max_edicts ) + Sys_Error( "ED_AllocEdict: no free edicts (max is %d)\n", GI->max_edicts ); svgame.numEntities++; pEdict = EDICT_NUM( i ); @@ -1389,15 +1389,15 @@ int SV_CheckClientPVS( int check, qboolean bMergePVS ) edict_t *ent = NULL; // cycle to the next one - check = bound( 1, check, svgame.globals->maxClients ); + check = bound( 1, check, svs.maxclients ); - if( check == svgame.globals->maxClients ) + if( check == svs.maxclients ) i = 1; // reset cycle else i = check + 1; for( ;; i++ ) { - if( i == ( svgame.globals->maxClients + 1 )) + if( i == ( svs.maxclients + 1 )) i = 1; ent = EDICT_NUM( i ); @@ -1580,7 +1580,7 @@ void pfnRemoveEntity( edict_t* e ) } // never free client or world entity - if( NUM_FOR_EDICT( e ) < ( svgame.globals->maxClients + 1 )) + if( NUM_FOR_EDICT( e ) < ( svs.maxclients + 1 )) { MsgDev( D_ERROR, "SV_RemoveEntity: can't delete %s\n", (e == EDICT_NUM( 0 )) ? "world" : "client" ); return; @@ -3243,10 +3243,8 @@ void pfnClientPrintf( edict_t* pEdict, PRINT_TYPE ptype, const char *szMsg ) switch( ptype ) { case print_console: - SV_ClientPrintf( client, PRINT_HIGH, "%s", szMsg ); - break; case print_chat: - SV_ClientPrintf( client, PRINT_CHAT, "%s", szMsg ); + SV_ClientPrintf( client, "%s", szMsg ); break; case print_center: MSG_BeginServerCmd( &client->netchan.message, svc_centerprint ); @@ -3263,9 +3261,7 @@ pfnServerPrint */ void pfnServerPrint( const char *szMsg ) { - // while loading in-progress we can sending message only for local client - if( sv.state != ss_active ) MsgDev( D_INFO, "%s", szMsg ); - else SV_BroadcastPrintf( NULL, PRINT_HIGH, "%s", szMsg ); + SV_BroadcastPrintf( NULL, "%s", szMsg ); } /* @@ -3489,8 +3485,6 @@ void pfnRunPlayerMove( edict_t *pClient, const float *v_angle, float fmove, floa usercmd_t cmd; uint seed; - if( sv.paused ) return; - if(( cl = SV_ClientFromEdict( pClient, true )) == NULL ) { MsgDev( D_ERROR, "SV_ClientThink: fakeclient is not spawned!\n" ); @@ -4926,10 +4920,10 @@ qboolean SV_LoadProgs( const char *name ) svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = svs.maxclients; - svgame.edicts = Mem_Alloc( svgame.mempool, sizeof( edict_t ) * svgame.globals->maxEntities ); - svgame.numEntities = svgame.globals->maxClients + 1; // clients + world + svgame.edicts = Mem_Alloc( svgame.mempool, sizeof( edict_t ) * GI->max_edicts ); + svgame.numEntities = svs.maxclients + 1; // clients + world - for( i = 0, e = svgame.edicts; i < svgame.globals->maxEntities; i++, e++ ) + for( i = 0, e = svgame.edicts; i < GI->max_edicts; i++, e++ ) e->free = true; // mark all edicts as freed // clear user messages diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 6ad130d7..5a844468 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -477,12 +477,11 @@ void SV_FreeOldEntities( void ) int i; // at end of frame kill all entities which supposed to it - for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) + for( i = svs.maxclients + 1; i < svgame.numEntities; i++ ) { ent = EDICT_NUM( i ); - if( ent->free ) continue; - if( ent->v.flags & FL_KILLME ) + if( !ent->free && FBitSet( ent->v.flags, FL_KILLME )) SV_FreeEdict( ent ); } @@ -497,39 +496,48 @@ SV_ActivateServer activate server on changed map, run physics ================ */ -void SV_ActivateServer( void ) +void SV_ActivateServer( int runPhysics ) { - int i, numFrames; + int i, numFrames; + byte msg_buf[MAX_INIT_MSG]; + sizebuf_t msg; + sv_client_t *cl; if( !svs.initialized ) return; - // Activate the DLL server code - svgame.dllFuncs.pfnServerActivate( svgame.edicts, svgame.numEntities, svgame.globals->maxClients ); + MSG_Init( &msg, "NewServer", msg_buf, sizeof( msg_buf )); - if( sv.loadgame || svgame.globals->changelevel ) - { - sv.frametime = bound( 0.001, sv_changetime.value, 0.1 ); - numFrames = 1; - } - else if( svs.maxclients <= 1 ) + // always clearing newunit variable + Cvar_SetValue( "sv_newunit", 0 ); + + // relese all intermediate entities + SV_FreeOldEntities (); + + // Activate the DLL server code + svgame.globals->time = sv.time; + svgame.dllFuncs.pfnServerActivate( svgame.edicts, svgame.numEntities, svs.maxclients ); + + // parse user-specified resources + SV_CreateGenericResources(); + + sv.state = ss_active; + + if( runPhysics ) { sv.frametime = bound( 0.1, sv_spawntime.value, 0.8 ); - numFrames = 2; + numFrames = (svs.maxclients <= 1) ? 2 : 8; } else { - sv.frametime = 0.1f; - numFrames = 8; + sv.frametime = bound( 0.001, sv_changetime.value, 0.1 ); + numFrames = 0; } // run some frames to allow everything to settle for( i = 0; i < numFrames; i++ ) SV_Physics(); - // parse user-specified resources - SV_CreateGenericResources(); - // create a baseline for more efficient communications SV_CreateBaseline(); @@ -540,18 +548,26 @@ void SV_ActivateServer( void ) sv.num_consistency = SV_TransferConsistencyInfo(); // send serverinfo to all connected clients - for( i = 0; i < svs.maxclients; i++ ) + for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ ) { - if( svs.clients[i].state >= cs_connected ) - { - Netchan_Clear( &svs.clients[i].netchan ); - svs.clients[i].delta_sequence = -1; - } + if( cl->state < cs_connected ) + continue; + + Netchan_Clear( &cl->netchan ); + cl->delta_sequence = -1; + + if( svs.maxclients <= 1 ) + SV_SendServerdata( &msg, cl ); + else SV_BuildReconnect( &cl->netchan.message ); + + Netchan_CreateFragments( &cl->netchan, &msg ); + Netchan_FragSend( &cl->netchan ); + MSG_Clear( &msg ); } // invoke to refresh all movevars memset( &svgame.oldmovevars, 0, sizeof( movevars_t )); - svgame.globals->changelevel = false; // changelevel ends here + svgame.globals->changelevel = false; // setup hostflags sv.hostflags = 0; @@ -559,9 +575,9 @@ void SV_ActivateServer( void ) HPAK_FlushHostQueue(); // tell what kind of server has been started. - if( svgame.globals->maxClients > 1 ) + if( svs.maxclients > 1 ) { - MsgDev( D_INFO, "%i player server started\n", svgame.globals->maxClients ); + MsgDev( D_INFO, "%i player server started\n", svs.maxclients ); } else { @@ -574,11 +590,7 @@ void SV_ActivateServer( void ) if( host.type == HOST_DEDICATED ) Mod_FreeUnused (); - sv.state = ss_active; host.movevars_changed = true; - sv.changelevel = false; - sv.paused = false; - Host_SetServerState( sv.state ); MsgDev( D_INFO, "level loaded at %.2f sec\n", Sys_DoubleTime() - svs.timestart ); @@ -629,65 +641,12 @@ void SV_DeactivateServer( void ) svgame.globals->maxEntities = GI->max_edicts; svgame.globals->maxClients = svs.maxclients; - svgame.numEntities = svgame.globals->maxClients + 1; // clients + world + svgame.numEntities = svs.maxclients + 1; // clients + world svgame.globals->startspot = 0; svgame.globals->mapname = 0; - - // clear states - sv.changelevel = false; - sv.background = false; - sv.loadgame = false; sv.state = ss_dead; } -/* -================ -SV_LevelInit - -Spawn all entities -================ -*/ -void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLandmarkName, qboolean loadGame ) -{ - if( !svs.initialized ) - return; - - if( loadGame ) - { - if( !SV_LoadGameState( pMapName, 1 )) - { - SV_SpawnEntities( pMapName, SV_EntityScript( )); - } - - if( pOldLevel ) - { - SV_LoadAdjacentEnts( pOldLevel, pLandmarkName ); - } - - if( sv_newunit.value ) - { - SV_ClearSaveDir(); - } - } - else - { - svgame.dllFuncs.pfnResetGlobalState(); - SV_SpawnEntities( pMapName, SV_EntityScript( )); - svgame.globals->frametime = 0.0f; - - if( sv_newunit.value ) - { - SV_ClearSaveDir(); - } - } - - // always clearing newunit variable - Cvar_SetValue( "sv_newunit", 0 ); - - // relese all intermediate entities - SV_FreeOldEntities (); -} - /* ============== SV_InitGame @@ -695,34 +654,100 @@ SV_InitGame A brand new game has been started ============== */ -void SV_InitGame( void ) +qboolean SV_InitGame( void ) { - edict_t *ent; - int i, load = sv.loadgame; - if( svs.initialized ) + return true; // already initialized ? + + // first initialize? + if( !SV_LoadProgs( GI->game_dll )) { - // cause any connected clients to reconnect - Q_strncpy( host.finalmsg, "Server restarted", MAX_STRING ); - SV_Shutdown( true ); + MsgDev( D_ERROR, "SV_InitGame: can't initialize %s\n", GI->game_dll ); + return false; // failed to loading server.dll + } + + // alloc baseline slots + svs.baselines = Z_Malloc( sizeof( entity_state_t ) * GI->max_edicts ); + // client frames will be allocated in SV_ClientConnect + svs.initialized = true; + + return true; +} + +/* +============== +SV_ShutdownGame + +prepare to close server +============== +*/ +void SV_ShutdownGame( void ) +{ + if( !GameState->loadGame ) + SV_ClearGameState(); + + S_StopBackgroundTrack(); + + if( GameState->newGame ) + { + Host_EndGame( false, DEFAULT_ENDGAME_MESSAGE ); } else { - // init game after host error - if( !svgame.hInstance ) - { - if( !SV_LoadProgs( GI->game_dll )) - { - MsgDev( D_ERROR, "SV_InitGame: can't initialize %s\n", GI->game_dll ); - return; // can't loading - } - } + S_StopAllSounds( true ); + SV_DeactivateServer(); + } +} - // make sure the client is down - CL_Drop(); +/* +================ +SV_AllocClientFrames + +allocate delta-compression frames for each client +================ +*/ +void SV_AllocClientFrames( void ) +{ + sv_client_t *cl; + int i; + + for( i = 0, cl = svs.clients; i < svs.maxclients; i++, cl++ ) + { + cl->frames = Z_Realloc( cl->frames, SV_UPDATE_BACKUP * sizeof( client_frame_t )); + } +} + +/* +================ +SV_SetupClients + +determine the game type and prepare clients +================ +*/ +void SV_SetupClients( void ) +{ + qboolean changed_maxclients = false; + + // check if clients count was really changed + if( svs.maxclients != (int)sv_maxclients->value ) + changed_maxclients = true; + svs.maxclients = sv_maxclients->value; // copy the actual value from cvar + + if( svs.maxclients == 1 ) + { + if( deathmatch.value ) + { + changed_maxclients = true; + svs.maxclients = 8; + } + else if( coop.value ) + { + changed_maxclients = true; + svs.maxclients = 4; + } } - svs.maxclients = sv_maxclients->value; // copy the actual value from cvar + if( !changed_maxclients ) return; // nothing to change // dedicated servers are can't be single player and are usually DM if( host.type == HOST_DEDICATED ) @@ -741,13 +766,10 @@ void SV_InitGame( void ) SV_UPDATE_BACKUP = ( svs.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; svgame.globals->maxClients = svs.maxclients; - svs.clients = Z_Malloc( sizeof( sv_client_t ) * svs.maxclients ); + svs.clients = Z_Realloc( svs.clients, sizeof( sv_client_t ) * svs.maxclients ); svs.num_client_entities = svs.maxclients * SV_UPDATE_BACKUP * NUM_PACKET_ENTITIES; - svs.packet_entities = Z_Malloc( sizeof( entity_state_t ) * svs.num_client_entities ); - svs.baselines = Z_Malloc( sizeof( entity_state_t ) * GI->max_edicts ); - if( !load ) MsgDev( D_INFO, "%s alloced by server packet entities\n", Q_memprint( sizeof( entity_state_t ) * svs.num_client_entities )); - - // client frames will be allocated in SV_DirectConnect + svs.packet_entities = Z_Realloc( svs.packet_entities, sizeof( entity_state_t ) * svs.num_client_entities ); + MsgDev( D_INFO, "%s alloced by server packet entities\n", Q_memprint( sizeof( entity_state_t ) * svs.num_client_entities )); // init network stuff NET_Config(( svs.maxclients > 1 )); @@ -755,24 +777,11 @@ void SV_InitGame( void ) // copy gamemode into svgame.globals svgame.globals->deathmatch = deathmatch.value; svgame.globals->coop = coop.value; + svgame.numEntities = svs.maxclients + 1; // clients + world - // heartbeats will always be sent to the id master - svs.last_heartbeat = MAX_HEARTBEAT; // send immediately - - // set client fields on player ents - for( i = 0; i < svgame.globals->maxClients; i++ ) - { - // setup all the clients - ent = EDICT_NUM( i + 1 ); - svs.clients[i].edict = ent; - SV_InitEdict( ent ); - } - - // get actual movevars - SV_UpdateMovevars( true ); - - svgame.numEntities = svgame.globals->maxClients + 1; // clients + world - svs.initialized = true; + ClearBits( sv_maxclients->flags, FCVAR_CHANGED ); + ClearBits( deathmatch.flags, FCVAR_CHANGED ); + ClearBits( coop.flags, FCVAR_CHANGED ); } /* @@ -786,28 +795,18 @@ clients along with it. qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean background ) { int i, current_skill; - qboolean loadgame, paused; - qboolean changelevel; + edict_t *ent; - // save state - loadgame = sv.loadgame; - changelevel = sv.changelevel; - paused = sv.paused; - - if( sv.state == ss_dead ) - SV_InitGame(); // the game is just starting - - NET_Config(( svs.maxclients > 1 )); // init network stuff - ClearBits( sv_maxclients->flags, FCVAR_CHANGED ); - - if( !svs.initialized ) + if( !SV_InitGame( )) return false; Log_Open(); Log_Printf( "Loading map \"%s\"\n", mapname ); Log_PrintServerVars(); - svgame.globals->changelevel = false; // will be restored later if needed + SV_SetupClients(); + SV_AllocClientFrames(); + svs.timestart = Sys_DoubleTime(); svs.spawncount++; // any partially connected client will be restarted @@ -824,13 +823,8 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba Host_SetServerState( sv.state ); memset( &sv, 0, sizeof( sv )); // wipe the entire per-level structure - // restore state - sv.paused = paused; - sv.loadgame = loadgame; + sv.time = svgame.globals->time = 1.0f; // server spawn time it's always 1.0 second sv.background = background; - sv.changelevel = changelevel; - sv.time = 1.0f; // server spawn time it's always 1.0 second - svgame.globals->time = sv.time; // initialize buffers MSG_Init( &sv.signon, "Signon", sv.signon_buf, sizeof( sv.signon_buf )); @@ -839,19 +833,10 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba MSG_Init( &sv.reliable_datagram, "Reliable Datagram", sv.reliable_datagram_buf, sizeof( sv.reliable_datagram_buf )); MSG_Init( &sv.spec_datagram, "Spectator Datagram", sv.spectator_buf, sizeof( sv.spectator_buf )); - // leave slots at start for clients only - for( i = 0; i < svs.maxclients; i++ ) - { - // needs to reconnect - if( svs.clients[i].state > cs_connected ) - svs.clients[i].state = cs_connected; - } - // make cvars consistant if( coop.value ) Cvar_SetValue( "deathmatch", 0 ); current_skill = Q_rint( skill.value ); current_skill = bound( 0, current_skill, 3 ); - Cvar_SetValue( "skill", (float)current_skill ); if( sv.background ) @@ -885,11 +870,29 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba SetBits( sv.model_precache_flags[i+1], RES_FATALIFMISSING ); } + // leave slots at start for clients only + for( i = 0; i < svs.maxclients; i++ ) + { + // needs to reconnect + if( svs.clients[i].state > cs_connected ) + svs.clients[i].state = cs_connected; + + ent = EDICT_NUM( i + 1 ); + svs.clients[i].edict = ent; + SV_InitEdict( ent ); + } + + // heartbeats will always be sent to the id master + svs.last_heartbeat = MAX_HEARTBEAT; // send immediately + // precache and static commands can be issued during map initialization sv.state = ss_loading; Host_SetServerState( sv.state ); + // get actual movevars + SV_UpdateMovevars( true ); + // clear physics interaction links SV_ClearWorld(); @@ -897,6 +900,11 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba } qboolean SV_Active( void ) +{ + return (sv.state != ss_dead); +} + +qboolean SV_Initialized( void ) { return svs.initialized; } @@ -920,4 +928,50 @@ void SV_FreeGameProgs( void ) // unload progs (free cvars and commands) SV_UnloadProgs(); +} + +/* +================ +SV_ExecLoadLevel + +State machine exec new map +================ +*/ +void SV_ExecLoadLevel( void ) +{ + if( SV_SpawnServer( GameState->levelName, NULL, GameState->backgroundMap )) + { + SV_SpawnEntities( GameState->levelName, SV_EntityScript( )); + SV_ActivateServer( true ); + } +} + +/* +================ +SV_ExecLoadGame + +State machine exec load saved game +================ +*/ +void SV_ExecLoadGame( void ) +{ + if( SV_SpawnServer( GameState->levelName, NULL, false )) + { + if( !SV_LoadGameState( GameState->levelName, false )) + SV_SpawnEntities( GameState->levelName, SV_EntityScript( )); + sv.loadgame = sv.paused = true; // pause until all clients connect + SV_ActivateServer( false ); + } +} + +/* +================ +SV_ExecChangeLevel + +State machine exec changelevel path +================ +*/ +void SV_ExecChangeLevel( void ) +{ + SV_ChangeLevel( GameState->loadGame, GameState->levelName, GameState->landmarkName ); } \ No newline at end of file diff --git a/engine/server/sv_log.c b/engine/server/sv_log.c index a7c4522c..cece81bf 100644 --- a/engine/server/sv_log.c +++ b/engine/server/sv_log.c @@ -166,11 +166,11 @@ void SV_ServerLog_f( sv_client_t *cl ) if( Cmd_Argc() != 2 ) { - SV_ClientPrintf( cl, PRINT_HIGH, "usage: log < on|off >\n" ); + SV_ClientPrintf( cl, "usage: log < on|off >\n" ); if( svs.log.active ) - SV_ClientPrintf( cl, PRINT_HIGH, "currently logging\n" ); - else SV_ClientPrintf( cl, PRINT_HIGH, "not currently logging\n" ); + SV_ClientPrintf( cl, "currently logging\n" ); + else SV_ClientPrintf( cl, "not currently logging\n" ); return; } @@ -186,6 +186,6 @@ void SV_ServerLog_f( sv_client_t *cl ) } else { - SV_ClientPrintf( cl, PRINT_HIGH, "log: unknown parameter %s\n", Cmd_Argv( 1 )); + SV_ClientPrintf( cl, "log: unknown parameter %s\n", Cmd_Argv( 1 )); } } \ No newline at end of file diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 38c84ce7..67eb0872 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -59,8 +59,8 @@ CVAR_DEFINE_AUTO( mapcyclefile, "mapcycle.txt", 0, "name of multiplayer map cycl CVAR_DEFINE_AUTO( motdfile, "motd.txt", 0, "name of 'message of the day' file" ); CVAR_DEFINE_AUTO( logsdir, "logs", 0, "place to store multiplayer logs" ); CVAR_DEFINE_AUTO( bannedcfgfile, "banned.cfg", 0, "name of list of banned users" ); -CVAR_DEFINE_AUTO( deathmatch, "0", 0, "deathmatch mode in multiplayer game" ); -CVAR_DEFINE_AUTO( coop, "0", 0, "cooperative mode in multiplayer game" ); +CVAR_DEFINE_AUTO( deathmatch, "0", FCVAR_LATCH, "deathmatch mode in multiplayer game" ); +CVAR_DEFINE_AUTO( coop, "0", FCVAR_LATCH, "cooperative mode in multiplayer game" ); CVAR_DEFINE_AUTO( teamplay, "0", 0, "team mode in multiplayer game" ); CVAR_DEFINE_AUTO( skill, "1", 0, "skill level in singleplayer game" ); CVAR_DEFINE_AUTO( temp1, "0", 0, "temporary cvar that used by some mods" ); @@ -418,7 +418,7 @@ void SV_CheckTimeouts( void ) { if( !NET_IsLocalAddress( cl->netchan.remote_address )) { - SV_BroadcastPrintf( NULL, PRINT_HIGH, "%s timed out\n", cl->name ); + SV_BroadcastPrintf( NULL, "%s timed out\n", cl->name ); SV_DropClient( cl ); cl->state = cs_free; // don't bother with zombie state } @@ -825,14 +825,13 @@ void SV_FinalMessage( char *message, qboolean reconnect ) MSG_Init( &msg, "FinalMessage", msg_buf, sizeof( msg_buf )); MSG_BeginServerCmd( &msg, svc_print ); - MSG_WriteByte( &msg, PRINT_HIGH ); MSG_WriteString( &msg, va( "%s\n", message )); if( reconnect ) { MSG_BeginServerCmd( &msg, svc_changing ); - if( sv.loadgame || svs.maxclients > 1 || sv.changelevel ) + if( svs.maxclients > 1 || sv.changelevel ) MSG_WriteOneBit( &msg, 1 ); // changelevel else MSG_WriteOneBit( &msg, 0 ); } @@ -852,6 +851,29 @@ void SV_FinalMessage( char *message, qboolean reconnect ) Netchan_Transmit( &cl->netchan, MSG_GetNumBytesWritten( &msg ), MSG_GetData( &msg )); } +void SV_FreeClients( void ) +{ + if( svs.maxclients != 0 ) + { + // free server static data + if( svs.clients ) + { + Z_Free( svs.clients ); + svs.clients = NULL; + } + + if( svs.packet_entities ) + { + Z_Free( svs.packet_entities ); + svs.packet_entities = NULL; + svs.num_client_entities = 0; + svs.next_client_entities = 0; + } + + svs.maxclients = 0; + } +} + /* ================ SV_Shutdown @@ -863,7 +885,7 @@ before Sys_Quit or Sys_Error void SV_Shutdown( qboolean reconnect ) { // already freed - if( !SV_Active( )) return; + if( !SV_Initialized( )) return; // rcon will be disconnected SV_EndRedirect(); @@ -882,31 +904,18 @@ void SV_Shutdown( qboolean reconnect ) // free current level memset( &sv, 0, sizeof( sv )); - Host_SetServerState( sv.state ); + Host_SetServerState( ss_dead ); + + SV_FreeClients(); Log_Printf( "Server shutdown\n" ); Log_Close(); - // free server static data - if( svs.clients ) - { - Z_Free( svs.clients ); - svs.clients = NULL; - } - if( svs.baselines ) { Z_Free( svs.baselines ); svs.baselines = NULL; } - if( svs.packet_entities ) - { - Z_Free( svs.packet_entities ); - svs.packet_entities = NULL; - svs.num_client_entities = 0; - svs.next_client_entities = 0; - } - svs.initialized = false; } \ No newline at end of file diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index 96aea153..9d58e9de 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -80,7 +80,7 @@ void SV_CheckAllEnts( void ) nextcheck = Sys_DoubleTime() + 5.0; // check edicts errors - for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) + for( i = svs.maxclients + 1; i < svgame.numEntities; i++ ) { e = EDICT_NUM( i ); @@ -896,7 +896,7 @@ static edict_t *SV_PushMove( edict_t *pusher, float movetime ) sv_pushed_t *p, *pushed_p; edict_t *check; - if( svgame.globals->changelevel || VectorIsNull( pusher->v.velocity )) + if( VectorIsNull( pusher->v.velocity )) { pusher->v.ltime += movetime; return NULL; @@ -1014,7 +1014,7 @@ static edict_t *SV_PushRotate( edict_t *pusher, float movetime ) vec3_t org, org2, temp; edict_t *check; - if( svgame.globals->changelevel || VectorIsNull( pusher->v.avelocity )) + if( VectorIsNull( pusher->v.avelocity )) { pusher->v.ltime += movetime; return NULL; @@ -1800,7 +1800,7 @@ void SV_Physics( void ) if( !SV_IsValidEdict( ent )) continue; - if( i > 0 && i <= svgame.globals->maxClients ) + if( i > 0 && i <= svs.maxclients ) continue; SV_Physics_Entity( ent ); diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index da490133..2303c9d4 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -32,6 +32,9 @@ void SV_ClearPhysEnts( void ) qboolean SV_PlayerIsFrozen( edict_t *pClient ) { + if( sv.background ) + return true; // always freeze client on background + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) return false; diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 3a73cf1b..cdfadc97 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -1060,7 +1060,7 @@ SV_SaveClientState write out the list of premanent decals for this level ============= */ -void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level ) +void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int changelevel ) { string name; file_t *pFile; @@ -1094,7 +1094,7 @@ void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level ) // g-cont. add space for studiodecals if present decalList = (decallist_t *)Z_Malloc( sizeof( decallist_t ) * MAX_RENDER_DECALS * 2 ); - decalCount = R_CreateDecalList( decalList, svgame.globals->changelevel ); + decalCount = R_CreateDecalList( decalList ); // DECALS SECTION sections.offsets[LUMP_DECALS_OFFSET] = FS_Tell( pFile ); @@ -1175,7 +1175,7 @@ void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level ) } // DYNAMIC SOUNDS SECTION (don't go across transition) - if( !svgame.globals->changelevel && ( soundCount = S_GetCurrentDynamicSounds( soundInfo, MAX_CHANNELS )) != 0 ) + if( !changelevel && ( soundCount = S_GetCurrentDynamicSounds( soundInfo, MAX_CHANNELS )) != 0 ) { sections.offsets[LUMP_SOUNDS_OFFSET] = FS_Tell( pFile ); FS_Write( pFile, &soundCount, sizeof( int )); @@ -1205,7 +1205,7 @@ void SV_SaveClientState( SAVERESTOREDATA *pSaveData, const char *level ) } // BACKGROUND MUSIC SECTION (don't go across transition) - if( !svgame.globals->changelevel && S_StreamGetCurrentState( curtrack, looptrack, &position )) + if( !changelevel && S_StreamGetCurrentState( curtrack, looptrack, &position )) { byte nameSize; @@ -1440,7 +1440,7 @@ SV_SaveGameState save current game state ============= */ -SAVERESTOREDATA *SV_SaveGameState( void ) +SAVERESTOREDATA *SV_SaveGameState( int changelevel ) { SaveFileSectionsInfo_t sectionsInfo; SaveFileSections_t sections; @@ -1528,12 +1528,12 @@ SAVERESTOREDATA *SV_SaveGameState( void ) SV_EntityPatchWrite( pSaveData, sv.name ); - SV_SaveClientState( pSaveData, sv.name ); + SV_SaveClientState( pSaveData, sv.name, changelevel ); return pSaveData; } -int SV_LoadGameState( char const *level, qboolean createPlayers ) +int SV_LoadGameState( char const *level, qboolean changelevel ) { SAVE_HEADER header; SAVERESTOREDATA *pSaveData; @@ -1578,7 +1578,7 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) // create entity list if( svgame.physFuncs.pfnCreateEntitiesInRestoreList != NULL ) { - svgame.physFuncs.pfnCreateEntitiesInRestoreList( pSaveData, createPlayers ); + svgame.physFuncs.pfnCreateEntitiesInRestoreList( pSaveData ); } else { @@ -1586,7 +1586,7 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) { pEntInfo = &pSaveData->pTable[i]; - if( pEntInfo->classname != 0 && pEntInfo->size && !( pEntInfo->flags & FENTTABLE_REMOVED )) + if( pEntInfo->classname != 0 && pEntInfo->size && !FBitSet( pEntInfo->flags, FENTTABLE_REMOVED )) { if( pEntInfo->id == 0 ) // worldspawn { @@ -1597,11 +1597,11 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) SV_InitEdict( pent ); pent = SV_CreateNamedEntity( pent, pEntInfo->classname ); } - else if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svgame.globals->maxClients + 1 )) + else if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svs.maxclients + 1 )) { edict_t *ed; - if(!( pEntInfo->flags & FENTTABLE_PLAYER )) + if( !FBitSet( pEntInfo->flags, FENTTABLE_PLAYER )) { MsgDev( D_WARN, "ENTITY IS NOT A PLAYER: %d\n", i ); Assert( 0 ); @@ -1609,9 +1609,9 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) ed = EDICT_NUM( pEntInfo->id ); - if( ed && createPlayers ) + if( ed != NULL ) { - Assert( ed->free == false ); + ASSERT( ed->free == false ); // create the player pent = SV_CreateNamedEntity( ed, pEntInfo->classname ); } @@ -1653,7 +1653,7 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) pent = pSaveData->pTable[bound( 0, (word)header.viewentity, pSaveData->tableCount )].pent; // don't go camera across the levels - if( SV_IsValidEdict( pent ) && !svgame.globals->changelevel ) + if( SV_IsValidEdict( pent ) && !changelevel ) sv.viewentity = NUM_FOR_EDICT( pent ); else sv.viewentity = 0; @@ -1670,6 +1670,21 @@ int SV_LoadGameState( char const *level, qboolean createPlayers ) return 1; } +/* +============= +SV_ClearGameState + +clear current game state +============= +*/ +void SV_ClearGameState( void ) +{ + SV_ClearSaveDir(); + + if( svgame.dllFuncs.pfnResetGlobalState != NULL ) + svgame.dllFuncs.pfnResetGlobalState(); +} + // ripped out from the hl.dll edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname ) { @@ -1715,7 +1730,7 @@ int SV_CreateEntityTransitionList( SAVERESTOREDATA *pSaveData, int levelMask ) active = (pEntInfo->flags & levelMask) ? 1 : 0; // spawn players - if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svgame.globals->maxClients + 1 )) + if(( pEntInfo->id > 0 ) && ( pEntInfo->id < svs.maxclients + 1 )) { edict_t *ed = EDICT_NUM( pEntInfo->id ); @@ -1942,11 +1957,9 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char svgame.globals->changelevel = true; // save the current level's state - pSaveData = SV_SaveGameState(); - sv.loadgame = true; + pSaveData = SV_SaveGameState( true ); } - SV_InactivateClients (); SV_DeactivateServer (); if( !SV_SpawnServer( level, startspot, false )) @@ -1954,20 +1967,24 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char if( loadfromsavedgame ) { - // Finish saving gamestate + // finish saving gamestate SV_SaveFinish( pSaveData ); - svgame.globals->changelevel = true; - SV_LevelInit( level, oldlevel, startspot, true ); - sv.paused = true; // pause until all clients connect - sv.loadgame = true; + if( !SV_LoadGameState( level, true )) + SV_SpawnEntities( level, SV_EntityScript( )); + SV_LoadAdjacentEnts( oldlevel, startspot ); + sv.loadgame = sv.paused = true; // pause until all clients connect + + if( sv_newunit.value ) + SV_ClearSaveDir(); + SV_ActivateServer( false ); } else { - SV_LevelInit( level, NULL, NULL, false ); + // classic quake changelevel + SV_SpawnEntities( level, SV_EntityScript( )); + SV_ActivateServer( true ); } - - SV_ActivateServer (); } int SV_SaveGameSlot( const char *pSaveName, const char *pSaveComment ) @@ -1979,7 +1996,7 @@ int SV_SaveGameSlot( const char *pSaveName, const char *pSaveComment ) int i, tag, tokenSize; file_t *pFile; - pSaveData = SV_SaveGameState(); + pSaveData = SV_SaveGameState( false ); if( !pSaveData ) return 0; SV_SaveFinish( pSaveData ); @@ -2127,9 +2144,7 @@ qboolean SV_LoadGame( const char *pPath ) if( !FS_FileExists( pPath, true )) return false; - SV_ClearSaveDir(); SV_InitGameProgs(); - if( !svgame.hInstance ) return false; @@ -2137,6 +2152,8 @@ qboolean SV_LoadGame( const char *pPath ) if( pFile ) { + SV_ClearGameState(); + if( SV_SaveReadHeader( pFile, &gameHeader )) { SV_DirectoryExtract( pFile, gameHeader.mapCount );