From 64609ae4754bfa8a29b52fea3041710ef4eaba9b Mon Sep 17 00:00:00 2001 From: g-cont Date: Mon, 26 Feb 2018 00:00:00 +0300 Subject: [PATCH] 26 Feb 2018 --- engine/client/cl_frame.c | 2 +- engine/client/cl_game.c | 36 +++- engine/client/cl_gameui.c | 19 +- engine/client/cl_main.c | 42 ++-- engine/client/cl_parse.c | 41 ++-- engine/client/cl_pmove.c | 6 +- engine/client/cl_tent.c | 164 ++++++++-------- engine/client/cl_view.c | 6 +- engine/client/client.h | 5 +- engine/client/gl_beams.c | 30 +-- engine/client/gl_decals.c | 6 +- engine/client/gl_rmain.c | 2 +- engine/client/gl_rsurf.c | 8 +- engine/client/gl_studio.c | 24 ++- engine/common/com_strings.h | 31 +++ engine/common/common.c | 16 +- engine/common/common.h | 53 ++++- engine/common/console.c | 5 +- engine/common/host.c | 44 ++--- engine/common/host_cmd.c | 23 --- engine/common/host_state.c | 210 ++++++++++++++++++++ engine/common/input.c | 16 +- engine/common/library.c | 2 +- engine/common/mod_bmodel.c | 10 +- engine/common/mod_local.h | 26 ++- engine/common/mod_studio.c | 4 +- engine/common/model.c | 325 ++++++++++++------------------ engine/common/sys_con.c | 6 +- engine/common/sys_win.c | 27 ++- engine/common/world.c | 114 ----------- engine/common/world.h | 1 - engine/engine.dsp | 6 +- engine/server/server.h | 10 +- engine/server/sv_client.c | 25 +-- engine/server/sv_cmds.c | 153 ++++++--------- engine/server/sv_frame.c | 18 +- engine/server/sv_game.c | 61 +++--- engine/server/sv_init.c | 381 ++++++++++++++---------------------- engine/server/sv_phys.c | 10 +- engine/server/sv_pmove.c | 7 +- engine/server/sv_save.c | 70 +++---- engine/server/sv_world.c | 152 ++++++++++++-- 42 files changed, 1130 insertions(+), 1067 deletions(-) create mode 100644 engine/common/com_strings.h delete mode 100644 engine/common/host_cmd.c create mode 100644 engine/common/host_state.c diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 02b1ec5c..10d8e38b 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -267,7 +267,7 @@ void CL_ProcessEntityUpdate( cl_entity_t *ent ) { qboolean parametric; - ent->model = Mod_Handle( ent->curstate.modelindex ); + ent->model = CL_ModelHandle( ent->curstate.modelindex ); ent->index = ent->curstate.number; // g-cont. make sure what it's no broke XashXT physics diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 473e4e3e..4d01a740 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -114,6 +114,20 @@ cl_entity_t *CL_GetEntityByIndex( int index ) return CL_EDICT_NUM( index ); } +/* +================ +CL_ModelHandle + +get model handle by index +================ +*/ +model_t *CL_ModelHandle( int modelindex ) +{ + if( modelindex < 0 || modelindex >= MAX_MODELS ) + return NULL; + return cl.models[modelindex]; +} + /* ==================== CL_IsThirdPerson @@ -2217,9 +2231,12 @@ int CL_FindModelIndex( const char *m ) if( !m || !m[0] ) return 0; - for( i = 1; i < MAX_MODELS && cl.model_precache[i][0]; i++ ) + for( i = 1; i < cl.nummodels; i++ ) { - if( !Q_stricmp( cl.model_precache[i], m )) + if( !cl.models[i] ) + continue; + + if( !Q_stricmp( cl.models[i]->name, m )) return i; } @@ -2431,13 +2448,16 @@ CL_LoadModel */ model_t *CL_LoadModel( const char *modelname, int *index ) { - int idx; + int i; - idx = CL_FindModelIndex( modelname ); - if( !idx ) return NULL; - if( index ) *index = idx; - - return Mod_Handle( idx ); + if( index ) *index = -1; + + if(( i = CL_FindModelIndex( modelname )) == 0 ) + return NULL; + + if( index ) *index = i; + + return CL_ModelHandle( i ); } int CL_AddEntity( int entityType, cl_entity_t *pEnt ) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 9408e592..411b78b8 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -656,9 +656,8 @@ for drawing playermodel previews */ static void pfnSetPlayerModel( cl_entity_t *ent, const char *path ) { - Mod_RegisterModel( path, MAX_MODELS - 1 ); - ent->curstate.modelindex = MAX_MODELS - 1; - ent->model = Mod_Handle( MAX_MODELS - 1 ); + ent->model = Mod_ForName( path, false, false ); + ent->curstate.modelindex = MAX_MODELS; // unreachable index } /* @@ -864,18 +863,6 @@ static void pfnChangeInstance( const char *newInstance, const char *szFinalMessa Host_NewInstance( newInstance, szFinalMessage ); } -/* -========= -pfnHostNewGame - -========= -*/ -static void pfnHostNewGame( const char *mapName ) -{ - if( !mapName || !*mapName ) return; - Host_NewGame( mapName, false ); -} - /* ========= pfnHostEndGame @@ -885,7 +872,7 @@ pfnHostEndGame static void pfnHostEndGame( const char *szFinalMessage ) { if( !szFinalMessage ) szFinalMessage = ""; - Host_EndGame( szFinalMessage ); + Host_EndGame( true, szFinalMessage ); } /* diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 56a50c98..2bc01aa0 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1306,18 +1306,18 @@ void CL_Disconnect( void ) void CL_Disconnect_f( void ) { if( Host_IsLocalClient( )) - Host_EndGame( "disconnected from server\n" ); + Host_EndGame( true, "disconnected from server\n" ); else CL_Disconnect(); } void CL_Crashed( void ) { // already freed - if( host.state == HOST_CRASHED ) return; + if( host.status == HOST_CRASHED ) return; if( host.type != HOST_NORMAL ) return; if( !cls.initialized ) return; - host.state = HOST_CRASHED; + host.status = HOST_CRASHED; CL_Stop_f(); // stop any demos @@ -1704,31 +1704,26 @@ Call before entering a new level, or after changing dlls */ void CL_PrepVideo( void ) { - string name, mapname; - int i, mdlcount; + int i; - if( !cl.model_precache[1][0] ) + if( !cl.models[WORLD_INDEX] ) return; // no map loaded Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar MsgDev( D_NOTE, "CL_PrepVideo: %s\n", clgame.mapname ); // let the render dll load the map - Q_strncpy( mapname, cl.model_precache[1], MAX_STRING ); - Mod_LoadWorld( mapname, NS_CLIENT ); - cl.worldmodel = Mod_Handle( 1 ); // get world pointer + world.loading = true; + cl.worldmodel = Mod_LoadModel( cl.models[WORLD_INDEX], true ); + world.loading = false; Cvar_SetValue( "scr_loading", 25.0f ); SCR_UpdateScreen(); - for( i = 0, mdlcount = 0; i < MAX_MODELS && cl.model_precache[i+1][0]; i++ ) - mdlcount++; // total num models - - for( i = 0; i < MAX_MODELS && cl.model_precache[i+1][0]; i++ ) + for( i = WORLD_INDEX; i < cl.nummodels - 1; i++ ) { - Q_strncpy( name, cl.model_precache[i+1], MAX_STRING ); - Mod_RegisterModel( name, i+1 ); - Cvar_SetValue( "scr_loading", scr_loading->value + 75.0f / mdlcount ); + Mod_LoadModel( cl.models[i+1], false ); + Cvar_SetValue( "scr_loading", scr_loading->value + 75.0f / cl.nummodels ); if( cl_allow_levelshots->value || host.developer > 3 || cl.background ) SCR_UpdateScreen(); } @@ -2185,7 +2180,7 @@ void CL_Precache_f( void ) CL_PrepVideo(); MSG_BeginClientCmd( &cls.netchan.message, clc_stringcmd ); - MSG_WriteString( &cls.netchan.message, va( "begin %i\n", spawncount )); + MSG_WriteString( &cls.netchan.message, va( "spawn %i\n", spawncount )); } qboolean CL_PrecacheResources( void ) @@ -2229,12 +2224,19 @@ qboolean CL_PrecacheResources( void ) case t_skin: break; case t_model: + if( pResource->nIndex >= cl.nummodels ) + { + cl.nummodels = pResource->nIndex + 1; + cl.nummodels = Q_min( cl.nummodels, MAX_MODELS ); + } + if( pResource->szFileName[0] != '*' ) { - Q_strncpy( cl.model_precache[pResource->nIndex], pResource->szFileName, sizeof( cl.model_precache[0] )); - Mod_RegisterModel( pResource->szFileName, pResource->nIndex ); + if( pResource->nIndex == WORLD_INDEX ) + cl.models[pResource->nIndex] = Mod_LoadWorld( pResource->szFileName, true ); + else cl.models[pResource->nIndex] = Mod_ForName( pResource->szFileName, false, true ); - if( !Mod_Handle( pResource->nIndex )) + if( cl.models[pResource->nIndex] == NULL ) { if( pResource->ucFlags != 0 ) MsgDev( D_WARN, "model %s not found and not available\n", pResource->szFileName ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 3e73c96d..f8b79bd4 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -559,7 +559,7 @@ void CL_ParseStaticEntity( sizebuf_t *msg ) // setup the new static entity VectorCopy( ent->curstate.origin, ent->origin ); VectorCopy( ent->curstate.angles, ent->angles ); - ent->model = Mod_Handle( state.modelindex ); + ent->model = CL_ModelHandle( state.modelindex ); ent->curstate.framerate = 1.0f; CL_ResetLatchedVars( ent, true ); @@ -1267,19 +1267,24 @@ prceache model from server */ void CL_PrecacheModel( sizebuf_t *msg ) { - int modelIndex; + const char *s; + int i; - modelIndex = MSG_ReadUBitLong( msg, MAX_MODEL_BITS ); + i = MSG_ReadUBitLong( msg, MAX_MODEL_BITS ); + s = MSG_ReadString( msg ); - if( modelIndex < 0 || modelIndex >= MAX_MODELS ) - Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex ); + if( i < 0 || i >= MAX_MODELS ) + Host_Error( "CL_PrecacheModel: bad modelindex %i\n", i ); - Q_strncpy( cl.model_precache[modelIndex], MSG_ReadString( msg ), sizeof( cl.model_precache[0] )); + if( i == WORLD_INDEX ) + cl.models[i] = Mod_LoadWorld( s, false ); + else cl.models[i] = Mod_FindName( s, false ); - // when we loading map all resources is precached sequentially - if( !cl.video_prepped ) return; - - Mod_RegisterModel( cl.model_precache[modelIndex], modelIndex ); + if( i >= cl.nummodels ) + { + cl.nummodels = i + 1; + cl.nummodels = Q_min( cl.nummodels, MAX_MODELS ); + } } /* @@ -1370,12 +1375,9 @@ void CL_PrecacheBSPModels( const char *pfilename ) if( p->type == t_model && p->szFileName[0] == '*' ) { - Q_strncpy( cl.model_precache[p->nIndex], p->szFileName, sizeof( cl.model_precache[0] )); + cl.models[p->nIndex] = Mod_ForName( p->szFileName, false, false ); - // when we loading map all resources is precached sequentially - if( cl.video_prepped ) Mod_RegisterModel( cl.model_precache[p->nIndex], p->nIndex ); - - if( !Mod_Handle( p->nIndex )) + if( cl.models[p->nIndex] == NULL ) { MsgDev( D_ERROR, "model %s not found\n", p->szFileName ); @@ -1481,12 +1483,11 @@ void CL_RegisterResources ( sizebuf_t *msg ) CL_SendConsistencyInfo( msg ); // All done precaching. - cl.worldmodel = Mod_Handle( 1 ); // get world pointer - cl.video_prepped = true; - cl.audio_prepped = true; + cl.worldmodel = CL_ModelHandle( 1 ); // get world pointer if( cl.worldmodel && cl.maxclients > 0 ) { + ASSERT( clgame.entities != NULL ); clgame.entities->model = cl.worldmodel; CL_PrecacheBSPModels( cl.worldmodel->name ); @@ -1499,6 +1500,8 @@ void CL_RegisterResources ( sizebuf_t *msg ) // invalidate all decal indexes memset( cl.decal_index, 0, sizeof( cl.decal_index )); + cl.video_prepped = true; + cl.audio_prepped = true; CL_ClearWorld (); @@ -1942,7 +1945,7 @@ void CL_ParseStudioDecal( sizebuf_t *msg ) cl_entity_t *ent = CL_GetEntityByIndex( entityIndex ); if( ent && !ent->model && modelIndex != 0 ) - ent->model = Mod_Handle( modelIndex ); + ent->model = CL_ModelHandle( modelIndex ); clgame.drawFuncs.R_StudioDecalShoot( decalTexture, ent, start, pos, flags, &state ); } diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 40561efa..fc7f2c57 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -329,7 +329,7 @@ void CL_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_ static void CL_CopyEntityToPhysEnt( physent_t *pe, entity_state_t *state, qboolean visent ) { - model_t *mod = Mod_Handle( state->modelindex ); + model_t *mod = CL_ModelHandle( state->modelindex ); pe->player = 0; @@ -359,7 +359,7 @@ static void CL_CopyEntityToPhysEnt( physent_t *pe, entity_state_t *state, qboole } else { - if( pe->solid != SOLID_BSP && Mod_GetType( state->modelindex ) == mod_studio ) + if( pe->solid != SOLID_BSP && ( mod != NULL ) && ( mod->type == mod_studio )) pe->studiomodel = mod; else pe->model = mod; } @@ -432,7 +432,7 @@ void CL_AddLinksToPmove( frame_t *frame ) if( !state->modelindex ) continue; - model = Mod_Handle( state->modelindex ); + model = CL_ModelHandle( state->modelindex ); if( !model ) continue; if(( state->owner != 0 ) && ( state->owner == cl.playernum + 1 )) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index e85cde58..1d14a191 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -257,7 +257,7 @@ void CL_PrepareTEnt( TEMPENTITY *pTemp, model_t *pmodel ) pTemp->flags = FTENT_NONE; pTemp->die = cl.time + 0.75f; - if( pmodel ) frameCount = Mod_FrameCount( pmodel ); + if( pmodel ) frameCount = pmodel->numframes; else pTemp->flags |= FTENT_NOMODEL; pTemp->entity.curstate.modelindex = modelIndex; @@ -580,25 +580,24 @@ Create a fizz effect void R_FizzEffect( cl_entity_t *pent, int modelIndex, int density ) { TEMPENTITY *pTemp; - int i, width, depth, count, frameCount; + int i, width, depth, count; float angle, maxHeight, speed; float xspeed, yspeed, zspeed; - vec3_t origin, mins, maxs; + vec3_t origin; + model_t *mod; - if( !pent || Mod_GetType( modelIndex ) == mod_bad ) + if( !pent || pent->curstate.modelindex <= 0 ) return; - if( pent->curstate.modelindex <= 0 ) + if(( mod = CL_ModelHandle( pent->curstate.modelindex )) == NULL ) return; count = density + 1; density = count * 3 + 6; + maxHeight = mod->maxs[2] - mod->mins[2]; + width = mod->maxs[0] - mod->mins[0]; + depth = mod->maxs[1] - mod->mins[1]; - Mod_GetBounds( pent->curstate.modelindex, mins, maxs ); - - maxHeight = maxs[2] - mins[2]; - width = maxs[0] - mins[0]; - depth = maxs[1] - mins[1]; speed = ( pent->curstate.rendercolor.r<<8 | pent->curstate.rendercolor.g ); if( pent->curstate.rendercolor.b ) speed = -speed; @@ -609,14 +608,12 @@ void R_FizzEffect( cl_entity_t *pent, int modelIndex, int density ) xspeed *= speed; yspeed *= speed; - Mod_GetFrames( modelIndex, &frameCount ); - for( i = 0; i < count; i++ ) { - origin[0] = mins[0] + COM_RandomLong( 0, width - 1 ); - origin[1] = mins[1] + COM_RandomLong( 0, depth - 1 ); - origin[2] = mins[2]; - pTemp = CL_TempEntAlloc( origin, Mod_Handle( modelIndex )); + origin[0] = mod->mins[0] + COM_RandomLong( 0, width - 1 ); + origin[1] = mod->mins[1] + COM_RandomLong( 0, depth - 1 ); + origin[2] = mod->mins[2]; + pTemp = CL_TempEntAlloc( origin, mod ); if ( !pTemp ) return; @@ -628,7 +625,7 @@ void R_FizzEffect( cl_entity_t *pent, int modelIndex, int density ) zspeed = COM_RandomLong( 80, 140 ); VectorSet( pTemp->entity.baseline.origin, xspeed, yspeed, zspeed ); pTemp->die = cl.time + ( maxHeight / zspeed ) - 0.1f; - pTemp->entity.curstate.frame = COM_RandomLong( 0, frameCount - 1 ); + pTemp->entity.curstate.frame = COM_RandomLong( 0, pTemp->frameMax ); // Set sprite scale pTemp->entity.curstate.scale = 1.0f / COM_RandomFloat( 2.0f, 5.0f ); pTemp->entity.curstate.rendermode = kRenderTransAlpha; @@ -646,22 +643,21 @@ Create bubbles void R_Bubbles( const vec3_t mins, const vec3_t maxs, float height, int modelIndex, int count, float speed ) { TEMPENTITY *pTemp; - int i, frameCount; float sine, cosine; float angle, zspeed; vec3_t origin; + model_t *mod; + int i; - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( mod = CL_ModelHandle( modelIndex )) == NULL ) return; - Mod_GetFrames( modelIndex, &frameCount ); - for ( i = 0; i < count; i++ ) { origin[0] = COM_RandomLong( mins[0], maxs[0] ); origin[1] = COM_RandomLong( mins[1], maxs[1] ); origin[2] = COM_RandomLong( mins[2], maxs[2] ); - pTemp = CL_TempEntAlloc( origin, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( origin, mod ); if( !pTemp ) return; pTemp->flags |= FTENT_SINEWAVE; @@ -674,7 +670,7 @@ void R_Bubbles( const vec3_t mins, const vec3_t maxs, float height, int modelInd zspeed = COM_RandomLong( 80, 140 ); VectorSet( pTemp->entity.baseline.origin, speed * cosine, speed * sine, zspeed ); pTemp->die = cl.time + ((height - (origin[2] - mins[2])) / zspeed) - 0.1f; - pTemp->entity.curstate.frame = COM_RandomLong( 0, frameCount - 1 ); + pTemp->entity.curstate.frame = COM_RandomLong( 0, pTemp->frameMax ); // Set sprite scale pTemp->entity.curstate.scale = 1.0f / COM_RandomFloat( 2.0f, 5.0f ); @@ -693,21 +689,20 @@ Create bubble trail void R_BubbleTrail( const vec3_t start, const vec3_t end, float height, int modelIndex, int count, float speed ) { TEMPENTITY *pTemp; - int i, frameCount; float sine, cosine, zspeed; float dist, angle; vec3_t origin; + model_t *mod; + int i; - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( mod = CL_ModelHandle( modelIndex )) == NULL ) return; - Mod_GetFrames( modelIndex, &frameCount ); - for( i = 0; i < count; i++ ) { dist = COM_RandomFloat( 0, 1.0 ); VectorLerp( start, dist, end, origin ); - pTemp = CL_TempEntAlloc( origin, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( origin, mod ); if( !pTemp ) return; pTemp->flags |= FTENT_SINEWAVE; @@ -720,7 +715,7 @@ void R_BubbleTrail( const vec3_t start, const vec3_t end, float height, int mode zspeed = COM_RandomLong( 80, 140 ); VectorSet( pTemp->entity.baseline.origin, speed * cosine, speed * sine, zspeed ); pTemp->die = cl.time + ((height - (origin[2] - start[2])) / zspeed) - 0.1f; - pTemp->entity.curstate.frame = COM_RandomLong( 0, frameCount - 1 ); + pTemp->entity.curstate.frame = COM_RandomLong( 0, pTemp->frameMax ); // Set sprite scale pTemp->entity.curstate.scale = 1.0f / COM_RandomFloat( 2.0f, 5.0f ); @@ -741,6 +736,7 @@ void R_AttachTentToPlayer( int client, int modelIndex, float zoffset, float life TEMPENTITY *pTemp; vec3_t position; cl_entity_t *pClient; + model_t *pModel; if( client <= 0 || client > cl.maxclients ) { @@ -753,10 +749,13 @@ void R_AttachTentToPlayer( int client, int modelIndex, float zoffset, float life if( !pClient || pClient->curstate.messagenum != cl.parsecount ) return; + if(( pModel = CL_ModelHandle( modelIndex )) == NULL ) + return; + VectorCopy( pClient->origin, position ); position[2] += zoffset; - pTemp = CL_TempEntAllocHigh( position, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAllocHigh( position, pModel ); if( !pTemp ) return; pTemp->entity.curstate.renderfx = kRenderFxNoDissipation; @@ -770,7 +769,7 @@ void R_AttachTentToPlayer( int client, int modelIndex, float zoffset, float life pTemp->flags |= FTENT_PLYRATTACHMENT|FTENT_PERSIST; // is the model a sprite? - if( Mod_GetType( pTemp->entity.curstate.modelindex ) == mod_sprite ) + if( pModel->type == mod_sprite ) { pTemp->flags |= FTENT_SPRANIMATE|FTENT_SPRANIMATELOOP; pTemp->entity.curstate.framerate = 10; @@ -918,10 +917,11 @@ and some blood drops. This is high-priority tent */ void R_BloodSprite( const vec3_t org, int colorIndex, int modelIndex, int modelIndex2, float size ) { - TEMPENTITY *pTemp; + model_t *pModel, *pModel2; int impactindex; int spatterindex; int i, splatter; + TEMPENTITY *pTemp; vec3_t pos; colorIndex += COM_RandomLong( 1, 3 ); @@ -929,13 +929,13 @@ void R_BloodSprite( const vec3_t org, int colorIndex, int modelIndex, int modelI spatterindex = colorIndex - 1; // validate the model first - if( modelIndex && ( Mod_GetType( modelIndex ) != mod_bad )) + if(( pModel = CL_ModelHandle( modelIndex )) != NULL ) { VectorCopy( org, pos ); pos[2] += COM_RandomFloat( 2.0f, 4.0f ); // make offset from ground (snarks issues) // large, single blood sprite is a high-priority tent - if(( pTemp = CL_TempEntAllocHigh( pos, Mod_Handle( modelIndex ))) != NULL ) + if(( pTemp = CL_TempEntAllocHigh( pos, pModel )) != NULL ) { pTemp->entity.curstate.rendermode = kRenderTransTexture; pTemp->entity.curstate.renderfx = kRenderFxClampMinScale; @@ -955,14 +955,14 @@ void R_BloodSprite( const vec3_t org, int colorIndex, int modelIndex, int modelI } // validate the model first - if( modelIndex2 && ( Mod_GetType( modelIndex2 ) != mod_bad )) + if(( pModel2 = CL_ModelHandle( modelIndex2 )) != NULL ) { splatter = size + ( COM_RandomLong( 1, 8 ) + COM_RandomLong( 1, 8 )); for( i = 0; i < splatter; i++ ) { // create blood drips - if(( pTemp = CL_TempEntAlloc( org, Mod_Handle( modelIndex2 ))) != NULL ) + if(( pTemp = CL_TempEntAlloc( org, pModel2 )) != NULL ) { pTemp->entity.curstate.rendermode = kRenderTransTexture; pTemp->entity.curstate.renderfx = kRenderFxClampMinScale; @@ -1004,15 +1004,15 @@ Create a shards void R_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t dir, float random, float life, int count, int modelIndex, char flags ) { TEMPENTITY *pTemp; + model_t *pmodel; char type; int i, j; - if( !modelIndex ) return; - type = flags & BREAK_TYPEMASK; - - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) return; + type = flags & BREAK_TYPEMASK; + if( count == 0 ) { // assume surface (not volume) @@ -1039,15 +1039,15 @@ void R_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t dir, float if( j == 32 ) continue; // a piece completely stuck in the wall, ignore it - pTemp = CL_TempEntAlloc( vecSpot, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( vecSpot, pmodel ); if( !pTemp ) return; // keep track of break_type, so we know how to play sound on collision pTemp->hitSound = type; - if( Mod_GetType( modelIndex ) == mod_sprite ) + if( pmodel->type == mod_sprite ) pTemp->entity.curstate.frame = COM_RandomLong( 0, pTemp->frameMax ); - else if( Mod_GetType( modelIndex ) == mod_studio ) + else if( pmodel->type == mod_studio ) pTemp->entity.curstate.body = COM_RandomLong( 0, pTemp->frameMax ); pTemp->flags |= FTENT_COLLIDEWORLD | FTENT_FADEOUT | FTENT_SLOWGRAVITY; @@ -1093,8 +1093,12 @@ TEMPENTITY *R_TempModel( const vec3_t pos, const vec3_t dir, const vec3_t angles { // alloc a new tempent TEMPENTITY *pTemp; + model_t *pmodel; - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) + return NULL; + + pTemp = CL_TempEntAlloc( pos, pmodel ); if( !pTemp ) return NULL; pTemp->flags = (FTENT_COLLIDEWORLD|FTENT_GRAVITY); @@ -1120,7 +1124,7 @@ TEMPENTITY *R_TempModel( const vec3_t pos, const vec3_t dir, const vec3_t angles break; } - if( Mod_GetType( modelIndex ) == mod_sprite ) + if( pmodel->type == mod_sprite ) pTemp->entity.curstate.frame = COM_RandomLong( 0, pTemp->frameMax ); else pTemp->entity.curstate.body = COM_RandomLong( 0, pTemp->frameMax ); @@ -1139,18 +1143,19 @@ Create an animated sprite TEMPENTITY *R_DefaultSprite( const vec3_t pos, int spriteIndex, float framerate ) { TEMPENTITY *pTemp; + model_t *psprite; // don't spawn while paused if( cl.time == cl.oldtime ) return NULL; - if( !spriteIndex || Mod_GetType( spriteIndex ) != mod_sprite ) + if(( psprite = CL_ModelHandle( spriteIndex )) == NULL || psprite->type != mod_sprite ) { MsgDev( D_INFO, "No Sprite %d!\n", spriteIndex ); return NULL; } - pTemp = CL_TempEntAlloc( pos, Mod_Handle( spriteIndex )); + pTemp = CL_TempEntAlloc( pos, psprite ); if( !pTemp ) return NULL; pTemp->entity.curstate.scale = 1.0f; @@ -1199,17 +1204,15 @@ Create an animated moving sprite TEMPENTITY *R_TempSprite( vec3_t pos, const vec3_t dir, float scale, int modelIndex, int rendermode, int renderfx, float a, float life, int flags ) { TEMPENTITY *pTemp; + model_t *pmodel; - if( !modelIndex ) - return NULL; - - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) { MsgDev( D_ERROR, "No model %d!\n", modelIndex ); return NULL; } - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( pos, pmodel ); if( !pTemp ) return NULL; pTemp->entity.curstate.framerate = 10; @@ -1307,22 +1310,23 @@ void R_Spray( const vec3_t pos, const vec3_t dir, int modelIndex, int count, int TEMPENTITY *pTemp; float noise; float znoise; + model_t *pmodel; int i; + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) + { + MsgDev( D_INFO, "No model %d!\n", modelIndex ); + return; + } + noise = (float)spread / 100.0f; // more vertical displacement znoise = Q_min( 1.0f, noise * 1.5f ); - if( Mod_GetType( modelIndex ) == mod_bad ) - { - MsgDev( D_INFO, "No model %d!\n", modelIndex ); - return; - } - for( i = 0; i < count; i++ ) { - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( pos, pmodel ); if( !pTemp ) return; pTemp->entity.curstate.rendermode = rendermode; @@ -1384,9 +1388,10 @@ void R_Sprite_Trail( int type, vec3_t start, vec3_t end, int modelIndex, int cou { TEMPENTITY *pTemp; vec3_t delta, dir; + model_t *pmodel; int i; - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) return; VectorSubtract( end, start, delta ); @@ -1402,7 +1407,7 @@ void R_Sprite_Trail( int type, vec3_t start, vec3_t end, int modelIndex, int cou if( i == 0 ) VectorCopy( start, pos ); else VectorMA( start, ( i / ( count - 1.0f )), delta, pos ); - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( pos, pmodel ); if( !pTemp ) return; pTemp->flags = (FTENT_COLLIDEWORLD|FTENT_SPRCYCLE|FTENT_FADEOUT|FTENT_SLOWGRAVITY); @@ -1434,21 +1439,14 @@ Create a funnel effect with custom sprite void R_FunnelSprite( const vec3_t org, int modelIndex, int reverse ) { TEMPENTITY *pTemp; - model_t *model; vec3_t dir, dest; float dist, vel; + model_t *pmodel; int i, j; - if( !modelIndex ) + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) { - MsgDev( D_ERROR, "no modelindex for funnel!\n" ); - return; - } - - model = Mod_Handle( modelIndex ); - if( !model ) - { - MsgDev( D_ERROR, "No model %d!\n", modelIndex ); + MsgDev( D_ERROR, "no model %d!\n", modelIndex ); return; } @@ -1456,7 +1454,7 @@ void R_FunnelSprite( const vec3_t org, int modelIndex, int reverse ) { for( j = -8; j < 8; j++ ) { - pTemp = CL_TempEntAlloc( org, model ); + pTemp = CL_TempEntAlloc( org, pmodel ); if( !pTemp ) return; dest[0] = (i * 32.0f) + org[0]; @@ -1535,22 +1533,25 @@ Create an projectile entity */ void R_Projectile( const vec3_t origin, const vec3_t velocity, int modelIndex, int life, int owner, void (*hitcallback)( TEMPENTITY*, pmtrace_t* )) { - // alloc a new tempent TEMPENTITY *pTemp; + model_t *pmodel; vec3_t dir; - pTemp = CL_TempEntAllocHigh( origin, Mod_Handle( modelIndex )); + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) + return; + + pTemp = CL_TempEntAllocHigh( origin, pmodel ); if( !pTemp ) return; VectorCopy( velocity, pTemp->entity.baseline.origin ); - if( Mod_GetType( modelIndex ) == mod_sprite ) + if( pmodel->type == mod_sprite ) { - pTemp->flags |= FTENT_SPRANIMATE; + SetBits( pTemp->flags, FTENT_SPRANIMATE ); if( pTemp->frameMax < 10 ) { - pTemp->flags |= FTENT_SPRANIMATE | FTENT_SPRANIMATELOOP; + SetBits( pTemp->flags, FTENT_SPRANIMATE|FTENT_SPRANIMATELOOP ); pTemp->entity.curstate.framerate = 10; } else @@ -1587,7 +1588,7 @@ void R_TempSphereModel( const vec3_t pos, float speed, float life, int count, in // create temp models for( i = 0; i < count; i++ ) { - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( pos, CL_ModelHandle( modelIndex )); if( !pTemp ) return; pTemp->entity.curstate.body = COM_RandomLong( 0, pTemp->frameMax ); @@ -1705,7 +1706,7 @@ void R_PlayerSprites( int client, int modelIndex, int count, int size ) position[1] += COM_RandomFloat( -10.0f, 10.0f ); position[2] += COM_RandomFloat( -20.0f, 36.0f ); - pTemp = CL_TempEntAlloc( position, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( position, CL_ModelHandle( modelIndex )); if( !pTemp ) return; VectorSubtract( pTemp->entity.origin, pEnt->origin, pTemp->tentOffset ); @@ -1750,11 +1751,12 @@ Makes a field of fire void R_FireField( float *org, int radius, int modelIndex, int count, int flags, float life ) { TEMPENTITY *pTemp; + model_t *pmodel; float time; vec3_t pos; int i; - if( Mod_GetType( modelIndex ) == mod_bad ) + if(( pmodel = CL_ModelHandle( modelIndex )) == NULL ) return; for( i = 0; i < count; i++ ) @@ -1766,7 +1768,7 @@ void R_FireField( float *org, int radius, int modelIndex, int count, int flags, if( !FBitSet( flags, TEFIRE_FLAG_PLANAR )) pos[2] += COM_RandomFloat( -radius, radius ); - pTemp = CL_TempEntAlloc( pos, Mod_Handle( modelIndex )); + pTemp = CL_TempEntAlloc( pos, pmodel ); if( !pTemp ) return; if( FBitSet( flags, TEFIRE_FLAG_ALPHA )) diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 1a556574..0689eccd 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -89,7 +89,7 @@ void V_SetupViewModel( void ) view->curstate.colormap = (info->topcolor & 0xFFFF)|((info->bottomcolor << 8) & 0xFFFF); view->curstate.number = cl.playernum + 1; view->index = cl.playernum + 1; - view->model = Mod_Handle( cl.local.viewmodel ); + view->model = CL_ModelHandle( cl.local.viewmodel ); view->curstate.modelindex = cl.local.viewmodel; view->curstate.sequence = cl.local.weaponsequence; view->curstate.rendermode = kRenderNormal; @@ -271,10 +271,10 @@ qboolean V_PreRender( void ) if( !glw_state.initialized ) return false; - if( host.state == HOST_NOFOCUS ) + if( host.status == HOST_NOFOCUS ) return false; - if( host.state == HOST_SLEEP ) + if( host.status == HOST_SLEEP ) return false; // if the screen is disabled (loading plaque is up) diff --git a/engine/client/client.h b/engine/client/client.h index 93551ef5..02b8c45c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -267,10 +267,11 @@ typedef struct entity_state_t instanced_baseline[MAX_CUSTOM_BASELINES]; int instanced_baseline_count; - char model_precache[MAX_MODELS][MAX_QPATH]; char sound_precache[MAX_SOUNDS][MAX_QPATH]; char event_precache[MAX_EVENTS][MAX_QPATH]; lightstyle_t lightstyles[MAX_LIGHTSTYLES]; + model_t *models[MAX_MODELS+1]; // precached models (plus one slot for menu preview) + int nummodels; consistency_t consistency_list[MAX_MODELS]; int num_consistency; @@ -807,10 +808,12 @@ void CL_TextMessageParse( byte *pMemFile, int fileSize ); client_textmessage_t *CL_TextMessageGet( const char *pName ); int pfnDecalIndexFromName( const char *szDecalName ); int pfnIndexFromTrace( struct pmtrace_s *pTrace ); +model_t *CL_ModelHandle( int modelindex ); void NetAPI_CancelAllRequests( void ); int CL_FindModelIndex( const char *m ); cl_entity_t *CL_GetLocalPlayer( void ); model_t *CL_LoadClientSprite( const char *filename ); +model_t *CL_LoadModel( const char *modelname, int *index ); HSPRITE pfnSPR_Load( const char *szPicName ); HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ); void PicAdjustSize( float *x, float *y, float *w, float *h ); diff --git a/engine/client/gl_beams.c b/engine/client/gl_beams.c index 101e31c9..4c9cf64e 100644 --- a/engine/client/gl_beams.c +++ b/engine/client/gl_beams.c @@ -164,7 +164,7 @@ passed through this */ void R_BeamSetup( BEAM *pbeam, vec3_t start, vec3_t end, int modelIndex, float life, float width, float amplitude, float brightness, float speed ) { - model_t *sprite = Mod_Handle( modelIndex ); + model_t *sprite = CL_ModelHandle( modelIndex ); if( !sprite ) return; @@ -172,7 +172,7 @@ void R_BeamSetup( BEAM *pbeam, vec3_t start, vec3_t end, int modelIndex, float l pbeam->modelIndex = modelIndex; pbeam->frame = 0; pbeam->frameRate = 0; - pbeam->frameCount = Mod_FrameCount( sprite ); + pbeam->frameCount = sprite->numframes; VectorCopy( start, pbeam->source ); VectorCopy( end, pbeam->target ); @@ -1065,21 +1065,19 @@ Update beam vars and draw it */ void R_BeamDraw( BEAM *pbeam, float frametime ) { - model_t *sprite; + model_t *model; vec3_t delta; + model = CL_ModelHandle( pbeam->modelIndex ); SetBits( pbeam->flags, FBEAM_ISACTIVE ); - if( Mod_GetType( pbeam->modelIndex ) != mod_sprite ) + if( !model || model->type != mod_sprite ) { pbeam->flags &= ~FBEAM_ISACTIVE; // force to ignore pbeam->die = cl.time; return; } - sprite = Mod_Handle( pbeam->modelIndex ); - if( !sprite ) return; - // update frequency pbeam->freq += frametime; @@ -1182,7 +1180,7 @@ void R_BeamDraw( BEAM *pbeam, float frametime ) TriRenderMode( FBitSet( pbeam->flags, FBEAM_SOLID ) ? kRenderNormal : kRenderTransAdd ); - if( !TriSpriteTexture( sprite, (int)(pbeam->frame + pbeam->frameRate * cl.time) % pbeam->frameCount )) + if( !TriSpriteTexture( model, (int)(pbeam->frame + pbeam->frameRate * cl.time) % pbeam->frameCount )) { ClearBits( pbeam->flags, FBEAM_ISACTIVE ); return; @@ -1590,9 +1588,12 @@ BEAM *R_BeamEnts( int startEnt, int endEnt, int modelIndex, float life, float wi { cl_entity_t *start, *end; BEAM *pbeam; + model_t *mod; + + mod = CL_ModelHandle( modelIndex ); // need a valid model. - if( Mod_GetType( modelIndex ) != mod_sprite ) + if( !mod || mod->type != mod_sprite ) return NULL; start = R_BeamGetEntity( startEnt ); @@ -1960,9 +1961,10 @@ void CL_ReadLineFile_f( void ) char *afile, *pfile; vec3_t p1, p2; int count, modelIndex; - char filename[64]; + char filename[MAX_QPATH]; + model_t *model; string token; - + Q_snprintf( filename, sizeof( filename ), "maps/%s.lin", clgame.mapname ); afile = FS_LoadFile( filename, NULL, false ); @@ -1976,7 +1978,7 @@ void CL_ReadLineFile_f( void ) count = 0; pfile = afile; - modelIndex = CL_FindModelIndex( "sprites/laserbeam.spr" ); + model = CL_LoadModel( "sprites/laserbeam.spr", &modelIndex ); while( 1 ) { @@ -2017,8 +2019,8 @@ void CL_ReadLineFile_f( void ) if( !R_BeamPoints( p1, p2, modelIndex, 99999, 2, 0, 255, 0, 0, 0, 255.0f, 0.0f, 0.0f )) { - if( Mod_GetType( modelIndex ) != mod_sprite ) - MsgDev( D_ERROR, "CL_ReadLineFile: failed to load sprites/laserbeam.spr!\n" ); + if( !model || model->type != mod_sprite ) + MsgDev( D_ERROR, "CL_ReadLineFile: failed to load \"sprites/laserbeam.spr\"!\n" ); else MsgDev( D_ERROR, "CL_ReadLineFile: not enough free beams!\n" ); break; } diff --git a/engine/client/gl_decals.c b/engine/client/gl_decals.c index 277fea2e..aff200bc 100644 --- a/engine/client/gl_decals.c +++ b/engine/client/gl_decals.c @@ -749,12 +749,12 @@ void R_DecalShoot( int textureIndex, int entityIndex, int modelIndex, vec3_t pos { ent = CL_GetEntityByIndex( entityIndex ); - if( modelIndex > 0 ) model = Mod_Handle( modelIndex ); - else if( ent != NULL ) model = Mod_Handle( ent->curstate.modelindex ); + if( modelIndex > 0 ) model = CL_ModelHandle( modelIndex ); + else if( ent != NULL ) model = CL_ModelHandle( ent->curstate.modelindex ); else return; } else if( modelIndex > 0 ) - model = Mod_Handle( modelIndex ); + model = CL_ModelHandle( modelIndex ); else model = cl.worldmodel; if( !model ) return; diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 13236593..75bf4574 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1460,7 +1460,7 @@ static render_api_t gRenderAPI = pfnFileBufferCRC32, COM_CompareFileTime, Host_Error, - Mod_Handle, + CL_ModelHandle, pfnTime, Cvar_Set, S_FadeMusicVolume, diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 6a80d692..e59ad989 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -2058,9 +2058,9 @@ void GL_RebuildLightmaps( void ) LM_InitBlock(); - for( i = 1; i < MAX_MODELS; i++ ) + for( i = 1; i < cl.nummodels; i++ ) { - if(( m = Mod_Handle( i )) == NULL ) + if(( m = CL_ModelHandle( i )) == NULL ) continue; if( m->name[0] == '*' || m->type != mod_brush ) @@ -2121,9 +2121,9 @@ void GL_BuildLightmaps( void ) LM_InitBlock(); - for( i = 1; i < MAX_MODELS; i++ ) + for( i = 1; i < cl.nummodels; i++ ) { - if(( m = Mod_Handle( i )) == NULL ) + if(( m = CL_ModelHandle( i )) == NULL ) continue; if( m->name[0] == '*' || m->type != mod_brush ) diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index a1a5ae1c..e5134ddb 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -439,6 +439,17 @@ static player_info_t *pfnPlayerInfo( int index ) return &cl.players[index]; } +/* +=============== +pfnMod_ForName + +=============== +*/ +static model_t *pfnMod_ForName( const char *model, int crash ) +{ + return Mod_ForName( model, crash, false ); +} + /* =============== pfnGetPlayerState @@ -2180,7 +2191,7 @@ void R_StudioRenderShadow( int iSprite, float *p1, float *p2, float *p3, float * if( !p1 || !p2 || !p3 || !p4 ) return; - if( TriSpriteTexture( Mod_Handle( iSprite ), 0 )) + if( TriSpriteTexture( CL_ModelHandle( iSprite ), 0 )) { TriRenderMode( kRenderTransAlpha ); TriColor4f( 0.0f, 0.0f, 0.0f, 1.0f ); @@ -2747,7 +2758,8 @@ static model_t *R_StudioSetupPlayerModel( int index ) state = &cl.player_models[index]; - if(( host.developer || !Host_IsLocalGame( )) && info->model[0] ) + // g-cont: force for "dev-mode", non-local games and menu preview + if(( host.developer || !Host_IsLocalGame( ) || !RI.drawWorld ) && info->model[0] ) { if( Q_strcmp( state->name, info->model )) { @@ -2757,7 +2769,7 @@ static model_t *R_StudioSetupPlayerModel( int index ) Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); if( FS_FileExists( state->modelname, false )) - state->model = Mod_ForName( state->modelname, false ); + state->model = Mod_ForName( state->modelname, false, true ); else state->model = NULL; if( !state->model ) @@ -3452,7 +3464,7 @@ static int R_StudioDrawPlayer( int flags, entity_state_t *pplayer ) if( pplayer->weaponmodel ) { cl_entity_t saveent = *RI.currententity; - model_t *pweaponmodel = Mod_Handle( pplayer->weaponmodel ); + model_t *pweaponmodel = CL_ModelHandle( pplayer->weaponmodel ); m_pStudioHeader = (studiohdr_t *)Mod_StudioExtradata( pweaponmodel ); @@ -4003,9 +4015,9 @@ static engine_studio_api_t gStudioAPI = Mod_Calloc, Mod_CacheCheck, Mod_LoadCacheFile, - Mod_ForName, + pfnMod_ForName, Mod_StudioExtradata, - Mod_Handle, + CL_ModelHandle, pfnGetCurrentEntity, pfnPlayerInfo, R_StudioGetPlayerState, diff --git a/engine/common/com_strings.h b/engine/common/com_strings.h new file mode 100644 index 00000000..c5fe4743 --- /dev/null +++ b/engine/common/com_strings.h @@ -0,0 +1,31 @@ +/* +com_strings.h - all paths to external resources that hardcoded into engine +Copyright (C) 2018 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. +*/ + +#ifndef COM_STRINGS_H +#define COM_STRINGS_H + +// end game final default message +#define DEFAULT_ENDGAME_MESSAGE "The End" + +// path to the hash-pak that contain custom player decals +#define CUSTOM_RES_PATH "custom.hpk" + +// path to default playermodel in GoldSrc +#define DEFAULT_PLAYER_PATH_HALFLIFE "models/player.mdl" + +// path to default playermodel in Quake +#define DEFAULT_PLAYER_PATH_QUAKE "progs/player.mdl" + +#endif//COM_STRINGS_H \ No newline at end of file diff --git a/engine/common/common.c b/engine/common/common.c index 99ba27b5..817aca7f 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -312,6 +312,19 @@ qboolean COM_ParseVector( char **pfile, float *v, size_t size ) return false; } +/* +============= +COM_CheckString + +============= +*/ +int COM_CheckString( const char *string ) +{ + if( !string || (byte)*string <= ' ' ) + return 0; + return 1; +} + /* ============= COM_FileSize @@ -673,7 +686,6 @@ void pfnGetModelBounds( model_t *mod, float *mins, float *maxs ) } else { - MsgDev( D_ERROR, "Mod_GetBounds: NULL model\n" ); if( mins ) VectorClear( mins ); if( maxs ) VectorClear( maxs ); } @@ -881,7 +893,7 @@ qboolean COM_IsSafeFileToDownload( const char *filename ) const char *ext; int i; - if( !filename ) + if( !COM_CheckString( filename )) return false; if( !Q_strncmp( filename, "!MD5", 4 )) diff --git a/engine/common/common.h b/engine/common/common.h index dc4278c6..4edfa03b 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -105,6 +105,7 @@ typedef enum #include "system.h" #include "com_model.h" +#include "com_strings.h" #include "crtlib.h" #include "cvar.h" @@ -127,8 +128,6 @@ typedef enum #define CIN_MAIN 0 #define CIN_LOGO 1 -#define CUSTOM_RES_PATH "custom.hpk" - #define MAX_NUM_ARGVS 128 // config strings are a general means of communication from @@ -150,6 +149,7 @@ typedef enum #define GI SI.GameInfo #define FS_Gamedir() SI.GameInfo->gamedir #define FS_Title() SI.GameInfo->title +#define GameState (&host.game) #ifdef _DEBUG void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage ); @@ -162,6 +162,7 @@ extern convar_t *gl_vsync; extern convar_t *scr_loading; extern convar_t *scr_download; extern convar_t *cmd_scripting; +extern convar_t *sv_maxclients; extern convar_t *cl_allow_levelshots; extern convar_t *vid_displayfrequency; extern convar_t *host_limitlocal; @@ -243,7 +244,27 @@ typedef enum HOST_SLEEP, // sleeped by different reason, e.g. minimize window HOST_NOFOCUS, // same as HOST_FRAME, but disable mouse HOST_CRASHED // an exception handler called -} host_state; +} host_status_t; + +typedef enum +{ + STATE_RUNFRAME = 0, + STATE_LOAD_LEVEL, + STATE_LOAD_GAME, + STATE_CHANGELEVEL, + STATE_GAME_SHUTDOWN, +} host_state_t; + +typedef struct +{ + host_state_t curstate; + host_state_t nextstate; + char levelName[MAX_QPATH]; + char landmarkName[MAX_QPATH]; + qboolean backgroundMap; + qboolean loadGame; + qboolean newGame; // unload the server.dll before start a new map +} game_status_t; typedef enum { @@ -309,7 +330,8 @@ typedef struct host_parm_s HANDLE hMutex; LPTOP_LEVEL_EXCEPTION_FILTER oldFilter; - host_state state; // global host state + host_status_t status; // global host state + game_status_t game; // game manager uint type; // running at jmp_buf abortframe; // abort current frame dword errorframe; // to prevent multiple host error @@ -404,6 +426,7 @@ void COM_NormalizeAngles( vec3_t angles ); int COM_FileSize( const char *filename ); void COM_FixSlashes( char *pname ); void COM_FreeFile( void *buffer ); +int COM_CheckString( const char *string ); int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare ); search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ); file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ); @@ -652,8 +675,7 @@ void Host_SetServerState( int state ); int Host_ServerState( void ); int Host_CompareFileTime( long ft1, long ft2 ); void Host_NewInstance( const char *name, const char *finalmsg ); -qboolean Host_NewGame( const char *mapName, qboolean loadGame ); -void Host_EndGame( const char *message, ... ); +void Host_EndGame( qboolean abort, const char *message, ... ); void Host_AbortCurrentFrame( void ); void Host_RestartAmbientSounds( void ); void Host_RestartDecals( void ); @@ -667,9 +689,20 @@ void Host_ShutdownServer( void ); void Host_Print( const char *txt ); void Host_Error( const char *error, ... ); void Host_PrintEngineFeatures( void ); +void Host_Frame( float time ); void Host_InitDecals( void ); void Host_Credits( void ); +// +// host_state.c +// +void COM_InitHostState( void ); +void COM_NewGame( char const *pMapName ); +void COM_LoadLevel( char const *pMapName, qboolean background ); +void COM_LoadGame( char const *pSaveFileName ); +void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName ); +void COM_Frame( float time ); + /* ============================================================== @@ -840,6 +873,7 @@ int flags, struct modelstate_s *state ); 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 ); @@ -850,6 +884,8 @@ 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 ); @@ -868,10 +904,13 @@ 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_ClearSaveDir( void ); void SV_InitGameProgs( void ); void SV_FreeGameProgs( void ); -void SV_ForceError( void ); void CL_WriteMessageHistory( void ); void CL_SendCmd( void ); void CL_Disconnect( void ); diff --git a/engine/common/console.c b/engine/common/console.c index 7d73fbe8..76904982 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -2083,8 +2083,9 @@ void Con_DrawConsole( void ) Key_SetKeyDest( key_console ); } break; - case ca_connected: case ca_connecting: + case ca_connected: + case ca_validate: // force to show console always for -dev 3 and higher if( con.vislines ) { @@ -2174,7 +2175,7 @@ void Con_RunConsole( void ) } else con.showlines = 0; // none visible - if( cls.state == ca_connecting || cls.state == ca_connected || cl.first_frame ) + if( cls.state == ca_connecting || cls.state == ca_connected || cls.state == ca_validate || cl.first_frame ) host.realframetime = 0.000001f; // don't accumulate frametime lines_per_frame = fabs( scr_conspeed->value ) * host.realframetime; diff --git a/engine/common/host.c b/engine/common/host.c index 58ec73ca..9bc6e588 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -90,26 +90,12 @@ void Host_PrintEngineFeatures( void ) MsgDev( D_REPORT, "^3EXT:^7 runnung server at constant fps\n" ); } -/* -================ -Host_NewGame -================ -*/ -qboolean Host_NewGame( const char *mapName, qboolean loadGame ) -{ - qboolean iRet; - - iRet = SV_NewGame( mapName, loadGame ); - - return iRet; -} - /* ================ Host_EndGame ================ */ -void Host_EndGame( const char *message, ... ) +void Host_EndGame( qboolean abort, const char *message, ... ) { va_list argptr; static char string[MAX_SYSPATH]; @@ -132,9 +118,9 @@ void Host_EndGame( const char *message, ... ) CL_ClearEdicts (); // release all models - Mod_ClearAll( true ); + Mod_FreeAll(); - Host_AbortCurrentFrame (); + if( abort ) Host_AbortCurrentFrame (); } /* @@ -173,13 +159,13 @@ void Host_CheckSleep( void ) } else { - if( host.state == HOST_NOFOCUS ) + if( host.status == HOST_NOFOCUS ) { if( Host_ServerState() && CL_IsInGame( )) Sys_Sleep( 1 ); // listenserver else Sys_Sleep( 20 ); // sleep 20 ms otherwise } - else if( host.state == HOST_SLEEP ) + else if( host.status == HOST_SLEEP ) { // completely sleep in minimized state Sys_Sleep( 20 ); @@ -687,7 +673,7 @@ void Host_Error( const char *error, ... ) } // host is shutting down. don't invoke infinite loop - if( host.state == HOST_SHUTDOWN ) return; + if( host.status == HOST_SHUTDOWN ) return; if( recursive ) { @@ -711,7 +697,7 @@ void Host_Error( const char *error, ... ) CL_ClearEdicts (); // release all models - Mod_ClearAll( false ); + Mod_FreeAll(); recursive = false; Host_AbortCurrentFrame(); @@ -733,12 +719,6 @@ void Sys_Error_f( void ) Sys_Error( "%s\n", error ); } -void Net_Error_f( void ) -{ - Q_strncpy( host.finalmsg, Cmd_Argv( 1 ), sizeof( host.finalmsg )); - SV_ForceError(); -} - /* ================= Host_Crash_f @@ -777,7 +757,7 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) host.oldFilter = SetUnhandledExceptionFilter( Sys_Crash ); host.hInst = GetModuleHandle( NULL ); host.change_game = bChangeGame; - host.state = HOST_INIT; // initialzation started + host.status = HOST_INIT; // initialzation started host.developer = host.old_developer = 0; host.config_executed = false; @@ -886,6 +866,9 @@ void Host_InitCommon( const char *hostname, qboolean bChangeGame ) // get default screen res VID_InitDefaultResolution(); + // init host state machine + COM_InitHostState(); + // startup cmds and cvars subsystem Cmd_Init(); Cvar_Init(); @@ -947,7 +930,6 @@ int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func Cmd_AddCommand ( "sys_error", Sys_Error_f, "just throw a fatal error to test shutdown procedures"); Cmd_AddCommand ( "host_error", Host_Error_f, "just throw a host error to test shutdown procedures"); Cmd_AddCommand ( "crash", Host_Crash_f, "a way to force a bus error for development reasons"); - Cmd_AddCommand ( "net_error", Net_Error_f, "send network bad message from random place"); } host_maxfps = Cvar_Get( "fps_max", "72", FCVAR_ARCHIVE, "host fps upper limit" ); @@ -1024,7 +1006,7 @@ int EXPORT Host_Main( const char *progname, int bChangeGame, pfnChangeGame func while( !host.crashed ) { newtime = Sys_DoubleTime (); - Host_Frame( newtime - oldtime ); + COM_Frame( newtime - oldtime ); oldtime = newtime; } @@ -1042,7 +1024,7 @@ void EXPORT Host_Shutdown( void ) if( host.shutdown_issued ) return; host.shutdown_issued = true; - if( host.state != HOST_ERR_FATAL ) host.state = HOST_SHUTDOWN; // prepare host to normal shutdown + 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 )); if( host.type == HOST_NORMAL ) diff --git a/engine/common/host_cmd.c b/engine/common/host_cmd.c deleted file mode 100644 index 288d8403..00000000 --- a/engine/common/host_cmd.c +++ /dev/null @@ -1,23 +0,0 @@ -/* -host_cmd.c - dedicated and normal host -Copyright (C) 2017 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. -*/ - -#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 \ No newline at end of file diff --git a/engine/common/host_state.c b/engine/common/host_state.c new file mode 100644 index 00000000..cc392199 --- /dev/null +++ b/engine/common/host_state.c @@ -0,0 +1,210 @@ +/* +host_cmd.c - dedicated and normal host +Copyright (C) 2017 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. +*/ + +#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 ) +{ + if( clearNext ) + GameState->nextstate = newState; + GameState->curstate = newState; +} + +static void HostState_SetNextState( host_state_t nextState ) +{ + ASSERT( GameState->curstate == STATE_RUNFRAME ); + GameState->nextstate = nextState; +} + +void COM_NewGame( char const *pMapName ) +{ + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); + HostState_SetNextState( STATE_LOAD_LEVEL ); + + GameState->backgroundMap = false; + GameState->landmarkName[0] = 0; + GameState->newGame = true; +} + +void COM_LoadLevel( char const *pMapName, qboolean background ) +{ + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); + HostState_SetNextState( STATE_LOAD_LEVEL ); + + GameState->backgroundMap = background; + GameState->landmarkName[0] = 0; + GameState->newGame = false; +} + +void COM_LoadGame( char const *pMapName ) +{ + Q_strncpy( GameState->levelName, pMapName, sizeof( GameState->levelName )); + HostState_SetNextState( STATE_LOAD_GAME ); + GameState->backgroundMap = false; + GameState->newGame = false; + GameState->loadGame = true; +} + +void COM_ChangeLevel( char const *pNewLevel, char const *pLandmarkName ) +{ + Q_strncpy( GameState->levelName, pNewLevel, sizeof( GameState->levelName )); + + if( COM_CheckString( pLandmarkName )) + { + Q_strncpy( GameState->landmarkName, pLandmarkName, sizeof( GameState->landmarkName )); + GameState->loadGame = true; + } + else + { + GameState->landmarkName[0] = 0; + GameState->loadGame = false; + } + + HostState_SetNextState( STATE_CHANGELEVEL ); + GameState->newGame = false; +} + +void HostState_LoadLevel( 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(); + } + + switch( GameState->nextstate ) + { + case STATE_LOAD_GAME: + case STATE_LOAD_LEVEL: + HostState_SetState( GameState->nextstate, true ); + break; + default: + HostState_SetState( STATE_RUNFRAME, true ); + break; + } +} + +void HostState_Run( float time ) +{ + // engine main frame + Host_Frame( time ); + + switch( GameState->nextstate ) + { + case STATE_RUNFRAME: + break; + case STATE_LOAD_GAME: + case STATE_LOAD_LEVEL: + SCR_BeginLoadingPlaque( GameState->backgroundMap ); + // intentionally fallthrough + case STATE_GAME_SHUTDOWN: + HostState_SetState( STATE_GAME_SHUTDOWN, false ); + break; + case STATE_CHANGELEVEL: + SCR_BeginLoadingPlaque( false ); + HostState_SetState( GameState->nextstate, true ); + break; + default: + HostState_SetState( STATE_RUNFRAME, true ); + break; + } +} + +void COM_Frame( float time ) +{ + int loopCount = 0; + + while( 1 ) + { + int oldState = GameState->curstate; + + // execute the current state (and transition to the next state if not in HS_RUN) + switch( GameState->curstate ) + { + case STATE_LOAD_LEVEL: + HostState_LoadLevel(); + break; + case STATE_LOAD_GAME: + HostState_LoadGame(); + break; + case STATE_CHANGELEVEL: + HostState_ChangeLevel(); + break; + case STATE_RUNFRAME: + HostState_Run( time ); + break; + case STATE_GAME_SHUTDOWN: + HostState_ShutdownGame(); + break; + } + + if( oldState == STATE_RUNFRAME ) + break; + + if(( GameState->curstate == oldState ) || ( ++loopCount > 8 )) + Sys_Error( "state infinity loop!\n" ); + } +} \ No newline at end of file diff --git a/engine/common/input.c b/engine/common/input.c index f328be6a..26f67f07 100644 --- a/engine/common/input.c +++ b/engine/common/input.c @@ -382,7 +382,7 @@ void Host_InputFrame( void ) if( !in_mouseinitialized ) return; - if( host.state != HOST_FRAME ) + if( host.status != HOST_FRAME ) { IN_DeactivateMouse(); return; @@ -451,21 +451,21 @@ LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ) Sys_Quit(); break; case WM_ACTIVATE: - if( host.state == HOST_SHUTDOWN ) + if( host.status == HOST_SHUTDOWN ) break; // no need to activate if( HIWORD( wParam )) - host.state = HOST_SLEEP; + host.status = HOST_SLEEP; else if( LOWORD( wParam ) == WA_INACTIVE ) - host.state = HOST_NOFOCUS; - else host.state = HOST_FRAME; - fActivate = (host.state == HOST_FRAME) ? true : false; + host.status = HOST_NOFOCUS; + else host.status = HOST_FRAME; + fActivate = (host.status == HOST_FRAME) ? true : false; wnd_caption = GetSystemMetrics( SM_CYCAPTION ) + WND_BORDER; S_Activate( fActivate, host.hWnd ); IN_ActivateMouse( fActivate ); Key_ClearStates(); - if( host.state == HOST_FRAME ) + if( host.status == HOST_FRAME ) { SetForegroundWindow( hWnd ); ShowWindow( hWnd, SW_RESTORE ); @@ -512,7 +512,7 @@ LONG IN_WndProc( HWND hWnd, UINT uMsg, UINT wParam, LONG lParam ) break; case WM_SYSCOMMAND: // never turn screensaver while Xash is active - if( wParam == SC_SCREENSAVE && host.state != HOST_SLEEP ) + if( wParam == SC_SCREENSAVE && host.status != HOST_SLEEP ) return 0; break; case WM_SYSKEYDOWN: diff --git a/engine/common/library.c b/engine/common/library.c index 1b02dd1d..0065e1ae 100644 --- a/engine/common/library.c +++ b/engine/common/library.c @@ -833,7 +833,7 @@ void Com_FreeLibrary( void *hInstance ) if( !hInst || !hInst->hInstance ) return; // already freed - if( host.state == HOST_CRASHED ) + if( host.status == HOST_CRASHED ) { // we need to hold down all modules, while MSVC can find error MsgDev( D_NOTE, "Sys_FreeLibrary: hold %s for debugging\n", hInst->dllName ); diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index dd28e48b..e9aae678 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -524,7 +524,7 @@ byte *Mod_GetPVSForPoint( const vec3_t p ) mnode_t *node; mleaf_t *leaf = NULL; - Assert( worldmodel != NULL ); + ASSERT( worldmodel != NULL ); node = worldmodel->nodes; @@ -589,9 +589,12 @@ within radius pixels of the given point. */ int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ) { - mleaf_t *leaf = Mod_PointInLeaf( org, worldmodel->nodes ); int bytes = world.visbytes; + mleaf_t *leaf = NULL; + ASSERT( worldmodel != NULL ); + + leaf = Mod_PointInLeaf( org, worldmodel->nodes ); bytes = Q_min( bytes, visbytes ); // enable full visibility for some reasons @@ -1856,7 +1859,8 @@ static void Mod_LoadTextures( dbspmodel_t *bmod ) // if texture is completely missed if( !tx->gl_texturenum ) { - MsgDev( D_ERROR, "couldn't load %s.mip\n", mt->name ); + if( host.type != HOST_DEDICATED ) + MsgDev( D_ERROR, "couldn't load %s.mip\n", mt->name ); tx->gl_texturenum = tr.defaultTexture; } diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index f5e7a5db..cbbe5895 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -53,6 +53,8 @@ GNU General Public License for more details. #define FATPVS_RADIUS 8.0f // FatPVS use radius smaller than the FatPHS #define FATPHS_RADIUS 16.0f +#define WORLD_INDEX (1) // world index is always 1 + // model flags (stored in model_t->flags) #define MODEL_CONVEYOR BIT( 0 ) #define MODEL_HAS_ORIGIN BIT( 1 ) @@ -89,10 +91,9 @@ typedef struct } model_info_t; // values for model_t's needload -#define NL_PRESENT 0 +#define NL_UNREFERENCED 0 // this model can be freed after sequence precaching is done #define NL_NEEDS_LOADED 1 -#define NL_UNREFERENCED 2 // this model can be freed after sequence precaching is done -#define NL_CLIENT 3 // client-side models are static +#define NL_PRESENT 2 typedef struct { @@ -131,27 +132,23 @@ extern convar_t *r_wadtextures; // model.c // void Mod_Init( void ); -void Mod_ClearAll( qboolean keep_playermodel ); +void Mod_FreeAll( void ); void Mod_Shutdown( void ); void Mod_ClearUserData( void ); -void Mod_GetBounds( int handle, vec3_t mins, vec3_t maxs ); -void Mod_GetFrames( int handle, int *numFrames ); -void Mod_LoadWorld( const char *name, netsrc_t type ); -int Mod_FrameCount( model_t *mod ); +model_t *Mod_LoadWorld( const char *name, qboolean preload ); void *Mod_Calloc( int number, size_t size ); void *Mod_CacheCheck( struct cache_user_s *c ); void Mod_LoadCacheFile( const char *path, struct cache_user_s *cu ); void *Mod_AliasExtradata( model_t *mod ); void *Mod_StudioExtradata( model_t *mod ); -model_t *Mod_FindName( const char *name, qboolean create ); -model_t *Mod_LoadModel( model_t *mod, qboolean world ); -model_t *Mod_ForName( const char *name, qboolean world ); -qboolean Mod_RegisterModel( const char *name, int index ); +model_t *Mod_FindName( const char *name, qboolean trackCRC ); +model_t *Mod_LoadModel( model_t *mod, qboolean crash ); +model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC ); qboolean Mod_ValidateCRC( const char *name, CRC32_t crc ); void Mod_NeedCRC( const char *name, qboolean needCRC ); -modtype_t Mod_GetType( int handle ); -model_t *Mod_Handle( int handle ); +void Mod_PurgeStudioCache( void ); void Mod_FreeUnused( void ); +void Mod_ClearAll( void ); // // mod_bmodel.c @@ -187,5 +184,6 @@ void R_StudioCalcBonePosition( int frame, float s, void *pbone, void *panim, vec void *R_StudioGetAnim( void *m_pStudioHeader, void *m_pSubModel, void *pseqdesc ); void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean ignore_sequences ); int Mod_HitgroupForStudioHull( int index ); +void Mod_ClearStudioCache( void ); #endif//MOD_LOCAL_H \ No newline at end of file diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index fe1c6699..7799e8b2 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -509,7 +509,7 @@ void Mod_StudioGetAttachment( const edict_t *e, int iAtt, float *origin, float * vec3_t angles2; model_t *mod; - mod = Mod_Handle( e->v.modelindex ); + mod = SV_ModelHandle( e->v.modelindex ); mod_studiohdr = (studiohdr_t *)Mod_StudioExtradata( mod ); if( !mod_studiohdr ) return; @@ -558,7 +558,7 @@ void Mod_GetBonePosition( const edict_t *e, int iBone, float *origin, float *ang { model_t *mod; - mod = Mod_Handle( e->v.modelindex ); + mod = SV_ModelHandle( e->v.modelindex ); mod_studiohdr = (studiohdr_t *)Mod_StudioExtradata( mod ); if( !mod_studiohdr ) return; diff --git a/engine/common/model.c b/engine/common/model.c index 3c57aec5..0014384b 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -25,10 +25,9 @@ GNU General Public License for more details. #include "client.h" #include "server.h" // LUMP_ error codes -static model_t *com_models[MAX_MODELS]; // shared replacement modeltable -static model_info_t cm_modinfo[MAX_MODELS]; -static model_t cm_models[MAX_MODELS]; -static int cm_nummodels = 0; +static model_info_t mod_crcinfo[MAX_MODELS]; +static model_t mod_known[MAX_MODELS]; +static int mod_numknown = 0; byte *com_studiocache; // cache for submodels convar_t *mod_studiocache; convar_t *r_wadtextures; @@ -54,7 +53,7 @@ static void Mod_Modellist_f( void ) Msg( "\n" ); Msg( "-----------------------------------\n" ); - for( i = nummodels = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) + for( i = nummodels = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) { if( !mod->name[0] ) continue; // free slot @@ -75,8 +74,8 @@ Mod_FreeUserData */ static void Mod_FreeUserData( model_t *mod ) { - // already freed? - if( !mod || !mod->name[0] ) + // ignore submodels and freed models + if( !mod->name[0] || mod->name[0] == '*' ) return; if( host.type == HOST_DEDICATED ) @@ -108,6 +107,7 @@ 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 @@ -126,6 +126,8 @@ static void Mod_FreeModel( model_t *mod ) Mod_UnloadAliasModel( mod ); break; } + + memset( mod, 0, sizeof( *mod )); } /* @@ -148,36 +150,59 @@ void Mod_Init( void ) Mod_InitStudioHull (); } -void Mod_ClearAll( qboolean keep_playermodel ) +/* +================ +Mod_FreeAll +================ +*/ +void Mod_FreeAll( void ) { - model_t *plr = com_models[MAX_MODELS-1]; model_t *mod; int i; - for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) - { - if( keep_playermodel && mod == plr ) - continue; - + for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) Mod_FreeModel( mod ); - memset( mod, 0, sizeof( *mod )); - } - - // g-cont. may be just leave unchanged? - if( !keep_playermodel ) cm_nummodels = 0; + mod_numknown = 0; } +/* +================ +Mod_ClearAll + +clear all models as unreferenced +but don't touch the real data +================ +*/ +void Mod_ClearAll( void ) +{ + model_t *mod; + int i; + + for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) + mod->needload = NL_UNREFERENCED; +} + +/* +================ +Mod_ClearUserData +================ +*/ void Mod_ClearUserData( void ) { int i; - for( i = 0; i < cm_nummodels; i++ ) - Mod_FreeUserData( &cm_models[i] ); + for( i = 0; i < mod_numknown; i++ ) + Mod_FreeUserData( &mod_known[i] ); } +/* +================ +Mod_Shutdown +================ +*/ void Mod_Shutdown( void ) { - Mod_ClearAll( false ); + Mod_FreeAll(); Mem_FreePool( &com_studiocache ); } @@ -194,48 +219,46 @@ Mod_FindName ================== */ -model_t *Mod_FindName( const char *filename, qboolean create ) +model_t *Mod_FindName( const char *filename, qboolean trackCRC ) { + char modname[MAX_QPATH]; model_t *mod; - char name[64]; int i; - if( !filename || !filename[0] ) + if( !COM_CheckString( filename )) return NULL; - if( *filename == '!' ) filename++; - Q_strncpy( name, filename, sizeof( name )); - COM_FixSlashes( name ); - + Q_strncpy( modname, filename, sizeof( modname )); + // search the currently loaded models - for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) + for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) { - if( !mod->name[0] ) continue; - if( !Q_stricmp( mod->name, name )) + if( !Q_stricmp( mod->name, modname )) { - // prolonge registration - mod->needload = world.load_sequence; + if( mod->mempool ) + mod->needload = NL_PRESENT; + else mod->needload = NL_NEEDS_LOADED; + return mod; } } - if( !create ) return NULL; - // find a free model slot spot - for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) + for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) if( !mod->name[0] ) break; // this is a valid spot - if( i == cm_nummodels ) + if( i == mod_numknown ) { - if( cm_nummodels == MAX_MODELS ) + if( mod_numknown == MAX_MODELS ) Host_Error( "Mod_ForName: MAX_MODELS limit exceeded\n" ); - cm_nummodels++; + mod_numknown++; } // copy name, so model loader can find model file - Q_strncpy( mod->name, name, sizeof( mod->name )); - cm_modinfo[i].initialCRC = 0; - cm_modinfo[i].flags = 0; + Q_strncpy( mod->name, modname, sizeof( mod->name )); + if( trackCRC ) mod_crcinfo[i].flags = FCRC_SHOULD_CHECKSUM; + else mod_crcinfo[i].flags = 0; + mod_crcinfo[i].initialCRC = 0; return mod; } @@ -264,7 +287,10 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) // check if already loaded (or inline bmodel) if( mod->mempool || mod->name[0] == '*' ) + { + mod->needload = NL_PRESENT; return mod; + } // store modelname to show error Q_strncpy( tempname, mod->name, sizeof( tempname )); @@ -341,7 +367,8 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) } } - p = &cm_modinfo[mod - cm_models]; + p = &mod_crcinfo[mod - mod_known]; + mod->needload = NL_PRESENT; if( FBitSet( p->flags, FCRC_SHOULD_CHECKSUM )) { @@ -374,14 +401,37 @@ Mod_ForName Loads in a model for the given name ================== */ -model_t *Mod_ForName( const char *name, qboolean crash ) +model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC ) { - model_t *mod; - - mod = Mod_FindName( name, true ); + model_t *mod = Mod_FindName( name, trackCRC ); return Mod_LoadModel( mod, crash ); } +/* +================== +Mod_PurgeStudioCache + +free studio cache on change level +================== +*/ +void Mod_PurgeStudioCache( void ) +{ + int i; + + // we should release all the world submodels + // and clear studio sequences + for( i = 1; i < mod_numknown; i++ ) + { + if( mod_known[i].type == mod_studio ) + mod_known[i].submodels = NULL; + if( mod_known[i].name[0] == '*' ) + Mod_FreeModel( &mod_known[i] ); + } + + Mem_EmptyPool( com_studiocache ); + Mod_ClearStudioCache(); +} + /* ================== Mod_LoadWorld @@ -389,37 +439,32 @@ Mod_LoadWorld Loads in the map and all submodels ================== */ -void Mod_LoadWorld( const char *name, netsrc_t source ) +model_t *Mod_LoadWorld( const char *name, qboolean preload ) { - int i; + model_t *pworld; - // invalidate representation table - if( source == NS_SERVER || !Host_ServerState()) - memset( com_models, 0, sizeof( com_models )); + // already loaded? + if( !Q_stricmp( mod_known->name, name )) + return mod_known; - if( !Q_stricmp( cm_models[0].name, name )) - { - // map already loaded - com_models[1] = cm_models; - return; - } + // free sequence files on studiomodels + Mod_PurgeStudioCache(); - // clear all studio submodels on restart - for( i = 1; i < cm_nummodels; i++ ) - { - if( cm_models[i].type == mod_studio ) - cm_models[i].submodels = NULL; - } + // release previois map + Mod_FreeModel( mod_known ); // world is stuck on slot #0 always - // purge all submodels - Mod_FreeModel( &cm_models[0] ); // unload the previois map - Mem_EmptyPool( com_studiocache ); // purge studio cache - world.load_sequence++; // now all models are invalid + world.load_sequence++; // now all models are invalid + Mod_ClearAll(); // load the newmap world.loading = true; - com_models[1] = Mod_ForName( name, true ); + pworld = Mod_FindName( name, false ); + if( preload ) Mod_LoadModel( pworld, true ); world.loading = false; + + ASSERT( pworld == mod_known ); + + return pworld; } /* @@ -434,10 +479,11 @@ void Mod_FreeUnused( void ) model_t *mod; int i; - for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) + Msg( "Mod_FreeUnused()\n" ); + + for( i = 0, mod = mod_known; i < mod_numknown; i++, mod++ ) { - if( !mod->name[0] ) continue; - if( mod->needload != world.load_sequence ) + if( mod->needload == NL_UNREFERENCED ) Mod_FreeModel( mod ); } } @@ -449,80 +495,6 @@ void Mod_FreeUnused( void ) =============================================================================== */ -/* -=================== -Mod_GetType -=================== -*/ -modtype_t Mod_GetType( int handle ) -{ - model_t *mod = Mod_Handle( handle ); - - if( !mod ) return mod_bad; - return mod->type; -} - -/* -=================== -Mod_GetFrames -=================== -*/ -void Mod_GetFrames( int handle, int *numFrames ) -{ - model_t *mod = Mod_Handle( handle ); - - if( !numFrames ) return; - if( !mod ) - { - *numFrames = 1; - return; - } - - if( mod->type == mod_brush ) - *numFrames = 2; // regular and alternate animation - else *numFrames = mod->numframes; - if( *numFrames < 1 ) *numFrames = 1; -} - -/* -=================== -Mod_FrameCount - -model_t as input -=================== -*/ -int Mod_FrameCount( model_t *mod ) -{ - if( !mod ) return 1; - - return mod->numframes; -} - -/* -=================== -Mod_GetBounds -=================== -*/ -void Mod_GetBounds( int handle, vec3_t mins, vec3_t maxs ) -{ - model_t *cmod; - - if( handle <= 0 ) return; - cmod = Mod_Handle( handle ); - - if( cmod ) - { - if( mins ) VectorCopy( cmod->mins, mins ); - if( maxs ) VectorCopy( cmod->maxs, maxs ); - } - else - { - MsgDev( D_ERROR, "Mod_GetBounds: NULL model %i\n", handle ); - if( mins ) VectorClear( mins ); - if( maxs ) VectorClear( maxs ); - } -} - /* =============== Mod_Calloc @@ -559,52 +531,25 @@ Mod_LoadCacheFile */ void Mod_LoadCacheFile( const char *filename, cache_user_t *cu ) { + char modname[MAX_QPATH]; + size_t size; byte *buf; - string name; - size_t i, j, size; Assert( cu != NULL ); - if( !filename || !filename[0] ) return; + if( !COM_CheckString( filename )) + return; - // eliminate '!' symbol (i'm doesn't know what this doing) - for( i = j = 0; i < Q_strlen( filename ); i++ ) - { - if( filename[i] == '!' ) continue; - else if( filename[i] == '\\' ) name[j] = '/'; - else name[j] = Q_tolower( filename[i] ); - j++; - } - name[j] = '\0'; + Q_strncpy( modname, filename, sizeof( modname )); + COM_FixSlashes( modname ); - buf = FS_LoadFile( name, &size, false ); + buf = FS_LoadFile( modname, &size, false ); if( !buf || !size ) Host_Error( "LoadCacheFile: ^1can't load %s^7\n", filename ); cu->data = Mem_Alloc( com_studiocache, size ); memcpy( cu->data, buf, size ); Mem_Free( buf ); } -/* -=================== -Mod_RegisterModel - -register model with shared index -=================== -*/ -qboolean Mod_RegisterModel( const char *name, int index ) -{ - model_t *mod; - - if( index < 0 || index > MAX_MODELS ) - return false; - - // this array used for acess to servermodels - mod = Mod_ForName( name, false ); - com_models[index] = mod; - - return ( mod != NULL ); -} - /* =============== Mod_AliasExtradata @@ -631,20 +576,6 @@ void *Mod_StudioExtradata( model_t *mod ) return NULL; } -/* -================== -Mod_Handle - -================== -*/ -model_t *Mod_Handle( int handle ) -{ - if( handle < 0 || handle >= MAX_MODELS ) - return NULL; - - return com_models[handle]; -} - /* ================== Mod_ValidateCRC @@ -657,7 +588,7 @@ qboolean Mod_ValidateCRC( const char *name, CRC32_t crc ) model_t *mod; mod = Mod_FindName( name, true ); - p = &cm_modinfo[mod - cm_models]; + p = &mod_crcinfo[mod - mod_known]; if( !FBitSet( p->flags, FCRC_CHECKSUM_DONE )) return true; @@ -678,7 +609,7 @@ void Mod_NeedCRC( const char *name, qboolean needCRC ) model_info_t *p; mod = Mod_FindName( name, true ); - p = &cm_modinfo[mod - cm_models]; + p = &mod_crcinfo[mod - mod_known]; if( needCRC ) SetBits( p->flags, FCRC_SHOULD_CHECKSUM ); else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM ); diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 8aefbed9..c787e9e2 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -134,7 +134,7 @@ static long _stdcall Con_WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lP SetFocus( s_wcd.hwndInputLine ); break; case WM_CLOSE: - if( host.state == HOST_ERR_FATAL ) + if( host.status == HOST_ERR_FATAL ) { // send windows message PostQuitMessage( 0 ); @@ -197,7 +197,7 @@ long _stdcall Con_InputLineProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPa case WM_CHAR: if( Con_KeyEvent( wParam, true )) return 0; - if( wParam == 13 && host.state != HOST_ERR_FATAL ) + if( wParam == 13 && host.status != HOST_ERR_FATAL ) { GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer )); Q_strncat( s_wcd.consoleText, inputBuffer, sizeof( s_wcd.consoleText ) - Q_strlen( s_wcd.consoleText ) - 5 ); @@ -499,7 +499,7 @@ void Sys_CloseLog( void ) char event_name[64]; // continue logged - switch( host.state ) + switch( host.status ) { case HOST_CRASHED: Q_strncpy( event_name, "crashed", sizeof( event_name )); diff --git a/engine/common/sys_win.c b/engine/common/sys_win.c index 27ee5ad2..32a5c5f4 100644 --- a/engine/common/sys_win.c +++ b/engine/common/sys_win.c @@ -422,7 +422,7 @@ qboolean Sys_FreeLibrary( dll_info_t *dll ) if( !dll || !dll->link ) return false; - if( host.state == HOST_CRASHED ) + if( host.status == HOST_CRASHED ) { // we need to hold down all modules, while MSVC can find error MsgDev( D_NOTE, "Sys_FreeLibrary: hold %s for debugging\n", dll->name ); @@ -466,7 +466,7 @@ void Sys_WaitForQuit( void ) long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) { // save config - if( host.state != HOST_CRASHED ) + if( host.status != HOST_CRASHED ) { // check to avoid recursive call error_on_exit = true; @@ -474,20 +474,17 @@ long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) if( host.type == HOST_NORMAL ) CL_Crashed(); // tell client about crash - else host.state = HOST_CRASHED; - - Msg( "Sys_Crash: call %p at address %p\n", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode ); - - if( host.developer <= 0 ) - { - // no reason to call debugger in release build - just exit - Sys_Quit(); - return EXCEPTION_CONTINUE_EXECUTION; - } + else host.status = HOST_CRASHED; + Msg( "unhandled exception: %p at address %p\n", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode ); +#ifdef NDEBUG + // no reason to call debugger in release build - just exit + Sys_Quit(); + return EXCEPTION_CONTINUE_EXECUTION; +#endif // all other states keep unchanged to let debugger find bug Con_DestroyConsole(); - } + } if( host.oldFilter ) return host.oldFilter( pInfo ); @@ -507,14 +504,14 @@ void Sys_Error( const char *error, ... ) va_list argptr; char text[MAX_SYSPATH]; - if( host.state == HOST_ERR_FATAL ) + if( host.status == HOST_ERR_FATAL ) return; // don't multiple executes // make sure what console received last message if( host.change_game ) Sys_Sleep( 200 ); error_on_exit = true; - host.state = HOST_ERR_FATAL; + host.status = HOST_ERR_FATAL; va_start( argptr, error ); Q_vsprintf( text, error, argptr ); va_end( argptr ); diff --git a/engine/common/world.c b/engine/common/world.c index ad1ba869..7ad13709 100644 --- a/engine/common/world.c +++ b/engine/common/world.c @@ -166,120 +166,6 @@ void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t m } } -/* -================== -World_PortalCSG - -a portal is flush with a world surface behind it. this causes problems. namely that we can't pass through the portal plane -if the bsp behind it prevents out origin from getting through. so if the trace was clipped and ended infront of the portal, -continue the trace to the edges of the portal cutout instead. -================== -*/ -void World_PortalCSG( edict_t *portal, const vec3_t trace_mins, const vec3_t trace_maxs, const vec3_t start, const vec3_t end, trace_t *trace ) -{ - vec4_t planes[6]; //far, near, right, left, up, down - int plane, k; - vec3_t worldpos; - float bestfrac; - int hitplane; - model_t *model; - float portalradius; - - // only run this code if we impacted on the portal's parent. - if( trace->fraction == 1.0f && !trace->startsolid ) - return; - - // decide which clipping hull to use, based on the size - model = Mod_Handle( portal->v.modelindex ); - - if( !model || model->type != mod_brush ) - return; - - // make sure we use a sane valid position. - if( trace->startsolid ) VectorCopy( start, worldpos ); - else VectorCopy( trace->endpos, worldpos ); - - // determine the csg area. normals should be facing in - AngleVectors( portal->v.angles, planes[1], planes[3], planes[5] ); - VectorNegate(planes[1], planes[0]); - VectorNegate(planes[3], planes[2]); - VectorNegate(planes[5], planes[4]); - - portalradius = model->radius * 0.5f; - planes[0][3] = DotProduct( portal->v.origin, planes[0] ) - (4.0f / 32.0f); - planes[1][3] = DotProduct( portal->v.origin, planes[1] ) - (4.0f / 32.0f); //an epsilon beyond the portal - planes[2][3] = DotProduct( portal->v.origin, planes[2] ) - portalradius; - planes[3][3] = DotProduct( portal->v.origin, planes[3] ) - portalradius; - planes[4][3] = DotProduct( portal->v.origin, planes[4] ) - portalradius; - planes[5][3] = DotProduct( portal->v.origin, planes[5] ) - portalradius; - - // if we're actually inside the csg region - for( plane = 0; plane < 6; plane++ ) - { - float d = DotProduct( worldpos, planes[plane] ); - vec3_t nearest; - - for( k = 0; k < 3; k++ ) - nearest[k] = (planes[plane][k]>=0) ? trace_maxs[k] : trace_mins[k]; - - // front plane gets further away with side - if( !plane ) - { - planes[plane][3] -= DotProduct( nearest, planes[plane] ); - } - else if( plane > 1 ) - { - // side planes get nearer with size - planes[plane][3] += 24; // DotProduct( nearest, planes[plane] ); - } - - if( d - planes[plane][3] >= 0 ) - continue; // endpos is inside - else return; // end is already outside - } - - // yup, we're inside, the trace shouldn't end where it actually did - bestfrac = 1; - hitplane = -1; - - for( plane = 0; plane < 6; plane++ ) - { - float ds = DotProduct( start, planes[plane] ) - planes[plane][3]; - float de = DotProduct( end, planes[plane] ) - planes[plane][3]; - float frac; - - if( ds >= 0 && de < 0 ) - { - frac = (ds) / (ds - de); - if( frac < bestfrac ) - { - if( frac < 0 ) - frac = 0; - bestfrac = frac; - hitplane = plane; - } - } - } - - trace->startsolid = trace->allsolid = false; - - // if we cross the front of the portal, don't shorten the trace, - // that will artificially clip us - if( hitplane == 0 && trace->fraction > bestfrac ) - return; - - // okay, elongate to clip to the portal hole properly. - VectorLerp( start, bestfrac, end, trace->endpos ); - trace->fraction = bestfrac; - - if( hitplane >= 0 ) - { - VectorCopy( planes[hitplane], trace->plane.normal ); - trace->plane.dist = planes[hitplane][3]; - if( hitplane == 1 ) trace->ent = portal; - } -} - /* ================== RankForContents diff --git a/engine/common/world.h b/engine/common/world.h index 86dec522..1eb86a20 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -49,7 +49,6 @@ void ClearLink( link_t *l ); // trace common void World_MoveBounds( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, vec3_t boxmins, vec3_t boxmaxs ); void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t maxs, vec3_t outmins, vec3_t outmaxs ); -void World_PortalCSG( edict_t *portal, const vec3_t trace_mins, const vec3_t trace_maxs, const vec3_t start, const vec3_t end, trace_t *trace ); trace_t World_CombineTraces( trace_t *cliptrace, trace_t *trace, edict_t *touch ); int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ); int RankForContents( int contents ); diff --git a/engine/engine.dsp b/engine/engine.dsp index 176cf8dd..809c6df8 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -151,6 +151,10 @@ SOURCE=.\client\cl_cmds.c # End Source File # Begin Source File +SOURCE=.\client\cl_custom.c +# End Source File +# Begin Source File + SOURCE=.\client\cl_demo.c # End Source File # Begin Source File @@ -327,7 +331,7 @@ SOURCE=.\common\host.c # End Source File # Begin Source File -SOURCE=.\common\host_cmd.c +SOURCE=.\common\host_state.c # End Source File # Begin Source File diff --git a/engine/server/server.h b/engine/server/server.h index 35f7893a..5f1f30f3 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -152,6 +152,7 @@ typedef struct server_s char files_precache[MAX_CUSTOM][MAX_QPATH]; char event_precache[MAX_EVENTS][MAX_QPATH]; byte model_precache_flags[MAX_MODELS]; + model_t *models[MAX_MODELS]; sv_static_entity_t static_entities[MAX_STATIC_ENTITIES]; int num_static_entities; @@ -188,7 +189,6 @@ typedef struct server_s model_t *worldmodel; // pointer to world qboolean simulating; - qboolean write_bad_message; // just for debug qboolean paused; } server_t; @@ -238,7 +238,6 @@ typedef struct sv_client_s double cmdtime; double ignorecmdtime; - int modelindex; // custom playermodel index int packet_loss; float latency; @@ -451,7 +450,6 @@ extern convar_t *sv_check_errors; extern convar_t *sv_reconnect_limit; extern convar_t *sv_lighting_modulate; extern convar_t *hostname; -extern convar_t *sv_maxclients; extern convar_t *sv_novis; extern convar_t *sv_hostmap; extern convar_t *sv_sendvelocity; @@ -487,11 +485,10 @@ void Master_Packet( void ); // // sv_init.c // -void SV_InitGame( void ); void SV_ActivateServer( void ); -void SV_DeactivateServer( void ); void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLandmarkName, qboolean loadGame ); -qboolean SV_SpawnServer( const char *server, const char *startspot ); +qboolean SV_SpawnServer( const char *server, const char *startspot, qboolean background ); +model_t *SV_ModelHandle( int modelindex ); // // sv_phys.c @@ -648,7 +645,6 @@ void SV_ServerLog_f( sv_client_t *cl ); void SV_ClearSaveDir( void ); void SV_SaveGame( const char *pName ); qboolean SV_LoadGame( const char *pName ); -void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start ); int SV_LoadGameState( char const *level, qboolean createPlayers ); void SV_LoadAdjacentEnts( const char *pOldLevel, const char *pLandmarkName ); const char *SV_GetLatestSave( void ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 6e73256f..dbc1e95d 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1782,26 +1782,6 @@ void SV_UserinfoChanged( sv_client_t *cl, const char *userinfo ) else cl->cl_updaterate = 0.0; } - if( svs.maxclients > 1 ) - { - const char *model = Info_ValueForKey( cl->userinfo, "model" ); - - // apply custom playermodel - if( Q_strlen( model ) && Q_stricmp( model, "player" )) - { - const char *path = va( "models/player/%s/%s.mdl", model, model ); - if( FS_FileExists( path, false )) - { - Mod_RegisterModel( path, SV_ModelIndex( path )); // register model - SV_SetModel( ent, path ); - cl->modelindex = ent->v.modelindex; - } - else cl->modelindex = 0; - } - else cl->modelindex = 0; - } - else cl->modelindex = 0; - // call prog code to allow overrides svgame.dllFuncs.pfnClientUserInfoChanged( cl->edict, cl->userinfo ); @@ -2248,6 +2228,7 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) usercmd_t cmds[CMD_BACKUP]; float packet_loss; edict_t *player; + model_t *model; player = cl->edict; @@ -2349,9 +2330,11 @@ static void SV_ParseClientMove( sv_client_t *cl, sizebuf_t *msg ) // the message probably arrived 1/2 through client's frame loop frame->ping_time -= ( cl->lastcmd.msec * 0.5f ) / 1000.0f; frame->ping_time = Q_max( 0.0f, frame->ping_time ); + model = SV_ModelHandle( player->v.modelindex ); - if( Mod_GetType( player->v.modelindex ) == mod_studio ) + if( model && model->type == mod_studio ) { + // g-cont. yes we using svgame.globals->time instead of sv.time if( player->v.animtime > svgame.globals->time + sv.frametime ) player->v.animtime = svgame.globals->time + sv.frametime; } diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index e150ef44..2e3a0148 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -170,6 +170,46 @@ qboolean SV_SetPlayer( void ) return false; } +/* +================== +SV_ValidateMap + +check map for typically errors +================== +*/ +qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn ) +{ + char *spawn_entity; + int flags; + + // determine spawn entity classname + if( !check_spawn || svs.maxclients == 1 ) + spawn_entity = GI->sp_entity; + else spawn_entity = GI->mp_entity; + + flags = SV_MapIsValid( pMapName, spawn_entity, NULL ); + + if( FBitSet( flags, MAP_INVALID_VERSION )) + { + MsgDev( D_ERROR, "map %s is invalid or not supported\n", pMapName ); + return false; + } + + if( !FBitSet( flags, MAP_IS_EXIST )) + { + MsgDev( D_ERROR, "map %s doesn't exist\n", pMapName ); + return false; + } + + if( check_spawn && !FBitSet( flags, MAP_HAS_SPAWNPOINT )) + { + MsgDev( D_ERROR, "map %s doesn't have a valid spawnpoint\n", pMapName ); + return false; + } + + return true; +} + /* ================== SV_Map_f @@ -180,9 +220,7 @@ For development work */ void SV_Map_f( void ) { - char *spawn_entity; - string mapname; - int flags; + char mapname[MAX_QPATH]; if( Cmd_Argc() != 2 ) { @@ -193,49 +231,17 @@ void SV_Map_f( void ) // hold mapname to other place Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname )); FS_StripExtension( mapname ); - - // determine spawn entity classname - if( svs.maxclients == 1 ) - spawn_entity = GI->sp_entity; - else spawn_entity = GI->mp_entity; - flags = SV_MapIsValid( mapname, spawn_entity, NULL ); - - if( FBitSet( flags, MAP_INVALID_VERSION )) - { - MsgDev( D_ERROR, "map %s is invalid or not supported\n", mapname ); + if( !SV_ValidateMap( mapname, true )) return; - } - - if( !FBitSet( flags, MAP_IS_EXIST )) - { - MsgDev( D_ERROR, "map %s doesn't exist\n", mapname ); - return; - } - - if( !FBitSet( flags, MAP_HAS_SPAWNPOINT )) - { - MsgDev( D_ERROR, "map %s doesn't have a valid spawnpoint\n", mapname ); - return; - } // changing singleplayer to multiplayer or back. refresh the player count if( FBitSet( sv_maxclients->flags, FCVAR_CHANGED )) Host_ShutdownServer(); - SCR_BeginLoadingPlaque( false ); - - sv.changelevel = false; - sv.background = false; - sv.loadgame = false; // set right state - SV_ClearSaveDir (); // delete all temporary *.hl files - Cvar_DirectSet( sv_hostmap, mapname ); - SV_DeactivateServer(); - SV_SpawnServer( mapname, NULL ); - SV_LevelInit( mapname, NULL, NULL, false ); - SV_ActivateServer (); + COM_LoadLevel( mapname, false ); } /* @@ -247,8 +253,7 @@ Set background map (enable physics in menu) */ void SV_MapBackground_f( void ) { - string mapname; - int flags; + char mapname[MAX_QPATH]; if( Cmd_Argc() != 2 ) { @@ -266,42 +271,19 @@ void SV_MapBackground_f( void ) Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname )); FS_StripExtension( mapname ); - flags = SV_MapIsValid( mapname, GI->sp_entity, NULL ); - - if( FBitSet( flags, MAP_INVALID_VERSION )) - { - MsgDev( D_ERROR, "map %s is invalid or not supported\n", mapname ); + if( !SV_ValidateMap( mapname, false )) return; - } - if( !FBitSet( flags, MAP_IS_EXIST )) - { - MsgDev( D_ERROR, "map %s doesn't exist\n", mapname ); - return; - } - - // background maps allow without spawnpoints (just throw warning) - if( !FBitSet( flags, MAP_HAS_SPAWNPOINT )) - MsgDev( D_WARN, "map %s doesn't have a valid spawnpoint\n", mapname ); - Q_strncpy( host.finalmsg, "", MAX_STRING ); SV_Shutdown( true ); - NET_Config ( false ); // close network sockets - - sv.changelevel = false; - sv.background = true; - sv.loadgame = false; // set right state + NET_Config( false ); // close network sockets // reset all multiplayer cvars Cvar_FullSet( "maxplayers", "1", FCVAR_LATCH ); Cvar_SetValue( "deathmatch", 0 ); Cvar_SetValue( "coop", 0 ); - SCR_BeginLoadingPlaque( true ); - - SV_SpawnServer( mapname, NULL ); - SV_LevelInit( mapname, NULL, NULL, false ); - SV_ActivateServer (); + COM_LoadLevel( mapname, true ); } /* @@ -318,7 +300,7 @@ void SV_NewGame_f( void ) return; } - Host_NewGame( GI->startmap, false ); + COM_NewGame( GI->startmap ); } /* @@ -339,9 +321,9 @@ void SV_HazardCourse_f( void ) if( FS_FileExists( va( "media/%s.avi", GI->trainmap ), false )) { Cbuf_AddText( va( "wait; movie %s\n", GI->trainmap )); - Host_EndGame( "The End" ); + Host_EndGame( true, DEFAULT_ENDGAME_MESSAGE ); } - else Host_NewGame( GI->trainmap, false ); + else COM_NewGame( GI->trainmap ); } /* @@ -529,17 +511,11 @@ void SV_ChangeLevel_f( void ) return; } - SCR_BeginLoadingPlaque( false ); - if( sv.background ) - { - // just load map - Cbuf_AddText( va( "map %s\n", mapname )); - return; - } - - if( c == 2 ) SV_ChangeLevel( false, Cmd_Argv( 1 ), NULL ); - else SV_ChangeLevel( true, Cmd_Argv( 1 ), Cmd_Argv( 2 )); + COM_LoadLevel( mapname, false ); + else if( c == 2 ) + COM_ChangeLevel( Cmd_Argv( 1 ), NULL ); + else COM_ChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 )); } /* @@ -554,15 +530,7 @@ void SV_Restart_f( void ) if( sv.state != ss_active ) return; - // just sending console command - if( sv.background ) - { - Cbuf_AddText( va( "map_background %s\n", sv.name )); - } - else - { - Cbuf_AddText( va( "map %s\n", sv.name )); - } + COM_LoadLevel( sv.name, sv.background ); } /* @@ -574,15 +542,8 @@ continue from latest savedgame */ void SV_Reload_f( void ) { - const char *savefile; - - if( sv.state != ss_active || sv.background ) - return; - - savefile = SV_GetLatestSave(); - - if( savefile ) SV_LoadGame( savefile ); - else SV_NewGame( sv_hostmap->string, false ); + if( !SV_LoadGame( SV_GetLatestSave( ))) + COM_LoadLevel( sv_hostmap->string, false ); } /* diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 86b98a9d..f1b5edc9 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -117,12 +117,6 @@ static void SV_AddEntitiesToPacket( edict_t *pViewEnt, edict_t *pClient, client_ // to prevent adds it twice through portals SETVISBIT( ents->sended, e ); - if( netclient && netclient->modelindex ) - { - // update playermodel if this was changed - state->modelindex = netclient->modelindex; - } - if( SV_IsValidEdict( ent->v.aiment ) && FBitSet( ent->v.aiment->v.effects, EF_MERGE_VISIBILITY )) { if( cl != NULL && cl->num_viewents < MAX_VIEWENTS ) @@ -764,16 +758,6 @@ void SV_UpdateToReliableMessages( void ) } } - // 1% chanse for simulate random network bugs - if( sv.write_bad_message && COM_RandomLong( 0, 512 ) == 404 ) - { - // just for network debugging (send only for local client) - MSG_BeginServerCmd( &sv.reliable_datagram, svc_bad ); - MSG_WriteLong( &sv.reliable_datagram, COM_RandomLong( 1, 65536 )); // send some random data - MSG_WriteString( &sv.reliable_datagram, host.finalmsg ); // send final message - sv.write_bad_message = false; - } - // clear the server datagram if it overflowed. if( MSG_CheckOverflow( &sv.datagram )) { @@ -997,5 +981,7 @@ void SV_InactivateClients( void ) // clear netchan message (but keep other buffers) MSG_Clear( &cl->netchan.message ); + MSG_Clear( &cl->datagram ); + COM_ClearCustomizationList( &cl->customdata, false ); } } \ No newline at end of file diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 7084f0d6..d9b06046 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -105,7 +105,7 @@ void SV_SetMinMaxSize( edict_t *e, const float *min, const float *max, qboolean if( min[i] > max[i] ) { MsgDev( D_ERROR, "SV_SetMinMaxSize: %s backwards mins/maxs\n", SV_ClassName( e )); - SV_LinkEdict( e, false ); // just relink edict and exit + if( relink ) SV_LinkEdict( e, false ); // just relink edict and exit return; } } @@ -137,27 +137,29 @@ void SV_CopyTraceToGlobal( trace_t *trace ) void SV_SetModel( edict_t *ent, const char *name ) { - vec3_t mins = { 0.0f, 0.0f, 0.0f }; - vec3_t maxs = { 0.0f, 0.0f, 0.0f }; + model_t *mod; int i; - i = SV_ModelIndex( name ); - if( i == 0 ) return; + // check to see if model was properly precached + for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) + { + if( !Q_stricmp( sv.model_precache[i], name )) + break; + } + + if( i == MAX_MODELS ) + { + MsgDev( D_ERROR, "SetModel: %s not precached\n", name ); + return; + } ent->v.model = MAKE_STRING( sv.model_precache[i] ); ent->v.modelindex = i; + mod = sv.models[i]; - // studio models will be ignored here - switch( Mod_GetType( ent->v.modelindex )) - { - case mod_alias: - case mod_brush: - case mod_sprite: - Mod_GetBounds( ent->v.modelindex, mins, maxs ); - break; - } - - SV_SetMinMaxSize( ent, mins, maxs, true ); + // set the model size + if( mod ) SV_SetMinMaxSize( ent, mod->mins, mod->maxs, true ); + else SV_SetMinMaxSize( ent, vec3_origin, vec3_origin, true ); } float SV_AngleMod( float ideal, float current, float speed ) @@ -1022,7 +1024,7 @@ pfnPrecacheModel int pfnPrecacheModel( const char *s ) { qboolean optional = false; - int modelIndex; + int i; if( *s == '!' ) { @@ -1030,13 +1032,13 @@ int pfnPrecacheModel( const char *s ) *s++; } - modelIndex = SV_ModelIndex( s ); - Mod_RegisterModel( s, modelIndex ); + i = SV_ModelIndex( s ); + sv.models[i] = Mod_ForName( s, false, true ); if( !optional ) - SetBits( sv.model_precache_flags[modelIndex], RES_FATALIFMISSING ); + SetBits( sv.model_precache_flags[i], RES_FATALIFMISSING ); - return modelIndex; + return i; } /* @@ -1100,11 +1102,11 @@ pfnModelFrames */ int pfnModelFrames( int modelIndex ) { - int numFrames = 0; + model_t *pmodel = SV_ModelHandle( modelIndex ); - Mod_GetFrames( modelIndex, &numFrames ); - - return numFrames; + if( pmodel != NULL ) + return pmodel->numframes; + return 1; } /* @@ -1473,7 +1475,7 @@ edict_t* pfnFindClientInPVS( edict_t *pEdict ) if( !SV_ClientFromEdict( pClient, true )) return svgame.edicts; - mod = Mod_Handle( pEdict->v.modelindex ); + mod = SV_ModelHandle( pEdict->v.modelindex ); // portals & monitors // NOTE: this specific break "radiaton tick" in normal half-life. use only as feature @@ -2122,6 +2124,7 @@ pfnTraceModel static void pfnTraceModel( const float *v1, const float *v2, int hullNumber, edict_t *pent, TraceResult *ptr ) { float *mins, *maxs; + model_t *model; trace_t trace; if( !ptr ) return; @@ -2137,6 +2140,7 @@ static void pfnTraceModel( const float *v1, const float *v2, int hullNumber, edi mins = sv.worldmodel->hulls[hullNumber].clip_mins; maxs = sv.worldmodel->hulls[hullNumber].clip_maxs; + model = SV_ModelHandle( pent->v.modelindex ); if( pent->v.solid == SOLID_CUSTOM ) { @@ -2144,7 +2148,7 @@ static void pfnTraceModel( const float *v1, const float *v2, int hullNumber, edi // even if our callbacks is not initialized SV_CustomClipMoveToEntity( pent, v1, mins, maxs, v2, &trace ); } - else if( Mod_GetType( pent->v.modelindex ) == mod_brush ) + else if( model && model->type == mod_brush ) { int oldmovetype = pent->v.movetype; int oldsolid = pent->v.solid; @@ -3080,7 +3084,7 @@ static void *pfnGetModelPtr( edict_t *pEdict ) if( !SV_IsValidEdict( pEdict )) return NULL; - mod = Mod_Handle( pEdict->v.modelindex ); + mod = SV_ModelHandle( pEdict->v.modelindex ); return Mod_StudioExtradata( mod ); } @@ -4786,6 +4790,7 @@ void SV_UnloadProgs( void ) { SV_DeactivateServer (); Delta_Shutdown (); + Mod_ClearUserData (); Mem_FreePool( &svgame.stringspool ); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index a6cbccfa..6ad130d7 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -23,40 +23,6 @@ server_t sv; // local server server_static_t svs; // persistant server info svgame_static_t svgame; // persistant game info -const char *check_exts[7] = -{ - ".cfg", - ".lst", - ".exe", - ".vbs", - ".com", - ".bat", - ".dll", -}; - -/* -================ -SV_IsGenericFileAllowed - -some files is not allowed to be sended to client -================ -*/ -qboolean SV_IsGenericFileAllowed( const char *filename ) -{ - const char *ext = FS_FileExtension( filename ); - int i; - - for( i = 0; i < ARRAYSIZE( check_exts ); i++ ) - { - if( !Q_stricmp( ext, check_exts[i] )) - { - MsgDev( D_WARN, "Can't precache %s files: %s\n", check_exts[i], filename ); - return false; - } - } - return true; -} - /* ================ SV_ModelIndex @@ -69,7 +35,7 @@ int SV_ModelIndex( const char *filename ) char name[64]; int i; - if( !filename || !filename[0] ) + if( !COM_CheckString( filename )) return 0; Q_strncpy( name, filename, sizeof( name )); @@ -116,7 +82,7 @@ int SV_SoundIndex( const char *filename ) int i; // don't precache sentence names! - if( !filename || !filename[0] || filename[0] == '!' ) + if( !COM_CheckString( filename )) return 0; Q_strncpy( name, filename, sizeof( name )); @@ -162,7 +128,7 @@ int SV_EventIndex( const char *filename ) char name[64]; int i; - if( !filename || !filename[0] ) + if( !COM_CheckString( filename )) return 0; Q_strncpy( name, filename, sizeof( name )); @@ -206,7 +172,7 @@ int SV_GenericIndex( const char *filename ) char name[64]; int i; - if( !filename || !filename[0] ) + if( !COM_CheckString( filename )) return 0; Q_strncpy( name, filename, sizeof( name )); @@ -237,6 +203,20 @@ int SV_GenericIndex( const char *filename ) return i; } +/* +================ +SV_ModelHandle + +register unique model for a server and client +================ +*/ +model_t *SV_ModelHandle( int modelindex ) +{ + if( modelindex < 0 || modelindex >= MAX_MODELS ) + return NULL; + return sv.models[modelindex]; +} + void SV_AddResource( resourcetype_t type, const char *name, int size, byte flags, int index ) { resource_t *pResource = &sv.resources[sv.num_resources]; @@ -272,28 +252,7 @@ void SV_CreateGenericResources( void ) while( ( pfile = COM_ParseFile( pfile, token )) != NULL ) { - if( Q_strlen( token ) <= 0 ) - break; - - if ( Q_strstr( token, ".." ) ) - { - Con_Printf( "Can't precache resource with invalid relative path %s\n", token ); - continue; - } - - if ( Q_strstr( token, ":" ) ) - { - Con_Printf( "Can't precache resource with absolute path %s\n", token ); - continue; - } - - if ( Q_strstr( token, "\\" ) ) - { - Con_Printf( "Can't precache resource with invalid relative path %s\n", token ); - continue; - } - - if( !SV_IsGenericFileAllowed( token )) + if( !COM_IsSafeFileToDownload( token )) continue; MsgDev( D_REPORT, " %s\n", token ); @@ -427,7 +386,10 @@ void SV_CreateBaseline( void ) qboolean player; int entnum; - playermodel = SV_ModelIndex( "models/player.mdl" ); + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + playermodel = SV_ModelIndex( DEFAULT_PLAYER_PATH_QUAKE ); + else playermodel = SV_ModelIndex( DEFAULT_PLAYER_PATH_HALFLIFE ); + memset( &nullstate, 0, sizeof( nullstate )); for( entnum = 0; entnum < svgame.numEntities; entnum++ ) @@ -648,8 +610,7 @@ void SV_DeactivateServer( void ) if( !svs.initialized || sv.state == ss_dead ) return; - sv.state = ss_dead; - + svgame.globals->time = sv.time; svgame.dllFuncs.pfnServerDeactivate(); SV_FreeEdicts (); @@ -671,6 +632,12 @@ void SV_DeactivateServer( void ) svgame.numEntities = svgame.globals->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; } /* @@ -721,130 +688,6 @@ void SV_LevelInit( const char *pMapName, char const *pOldLevel, char const *pLan SV_FreeOldEntities (); } -/* -================ -SV_SpawnServer - -Change the server to a new map, taking all connected -clients along with it. -================ -*/ -qboolean SV_SpawnServer( const char *mapname, const char *startspot ) -{ - int i, current_skill; - qboolean loadgame, paused; - qboolean background, changelevel; - - // save state - loadgame = sv.loadgame; - background = sv.background; - 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 ) - return false; - - Log_Open(); - Log_Printf( "Loading map \"%s\"\n", mapname ); - Log_PrintServerVars(); - - svgame.globals->changelevel = false; // will be restored later if needed - svs.timestart = Sys_DoubleTime(); - svs.spawncount++; // any partially connected client will be restarted - - if( startspot ) - { - MsgDev( D_INFO, "Spawn Server: %s [%s]\n", mapname, startspot ); - } - else - { - MsgDev( D_INFO, "Spawn Server: %s\n", mapname ); - } - - sv.state = ss_dead; - Host_SetServerState( sv.state ); - memset( &sv, 0, sizeof( sv )); // wipe the entire per-level structure - - // restore state - sv.paused = paused; - sv.loadgame = loadgame; - 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 )); - MSG_Init( &sv.multicast, "Multicast", sv.multicast_buf, sizeof( sv.multicast_buf )); - MSG_Init( &sv.datagram, "Datagram", sv.datagram_buf, sizeof( sv.datagram_buf )); - 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 ) - { - // tell the game parts about background state - Cvar_FullSet( "sv_background", "1", FCVAR_READ_ONLY ); - Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); - } - else - { - Cvar_FullSet( "sv_background", "0", FCVAR_READ_ONLY ); - Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); - } - - // make sure what server name doesn't contain path and extension - FS_FileBase( mapname, sv.name ); - - if( startspot ) - Q_strncpy( sv.startspot, startspot, sizeof( sv.startspot )); - else sv.startspot[0] = '\0'; - - Q_snprintf( sv.model_precache[1], sizeof( sv.model_precache[0] ), "maps/%s.bsp", sv.name ); - SetBits( sv.model_precache_flags[1], RES_FATALIFMISSING ); - Mod_LoadWorld( sv.model_precache[1], NS_SERVER ); - sv.worldmodel = Mod_Handle( 1 ); // get world pointer - - CRC32_MapFile( &sv.worldmapCRC, sv.model_precache[1], svs.maxclients > 1 ); - - for( i = 1; i < sv.worldmodel->numsubmodels; i++ ) - { - SetBits( sv.model_precache_flags[i+1], RES_FATALIFMISSING ); - Q_sprintf( sv.model_precache[i+1], "*%i", i ); - Mod_RegisterModel( sv.model_precache[i+1], i+1 ); - } - - // precache and static commands can be issued during map initialization - sv.state = ss_loading; - - Host_SetServerState( sv.state ); - - // clear physics interaction links - SV_ClearWorld(); - - return true; -} - /* ============== SV_InitGame @@ -932,6 +775,127 @@ void SV_InitGame( void ) svs.initialized = true; } +/* +================ +SV_SpawnServer + +Change the server to a new map, taking all connected +clients along with it. +================ +*/ +qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean background ) +{ + int i, current_skill; + qboolean loadgame, paused; + qboolean changelevel; + + // 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 ) + return false; + + Log_Open(); + Log_Printf( "Loading map \"%s\"\n", mapname ); + Log_PrintServerVars(); + + svgame.globals->changelevel = false; // will be restored later if needed + svs.timestart = Sys_DoubleTime(); + svs.spawncount++; // any partially connected client will be restarted + + if( startspot ) + { + MsgDev( D_INFO, "Spawn Server: %s [%s]\n", mapname, startspot ); + } + else + { + MsgDev( D_INFO, "Spawn Server: %s\n", mapname ); + } + + sv.state = ss_dead; + Host_SetServerState( sv.state ); + memset( &sv, 0, sizeof( sv )); // wipe the entire per-level structure + + // restore state + sv.paused = paused; + sv.loadgame = loadgame; + 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 )); + MSG_Init( &sv.multicast, "Multicast", sv.multicast_buf, sizeof( sv.multicast_buf )); + MSG_Init( &sv.datagram, "Datagram", sv.datagram_buf, sizeof( sv.datagram_buf )); + 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 ) + { + // tell the game parts about background state + Cvar_FullSet( "sv_background", "1", FCVAR_READ_ONLY ); + Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); + } + else + { + Cvar_FullSet( "sv_background", "0", FCVAR_READ_ONLY ); + Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); + } + + // make sure what server name doesn't contain path and extension + FS_FileBase( mapname, sv.name ); + + if( startspot ) + Q_strncpy( sv.startspot, startspot, sizeof( sv.startspot )); + else sv.startspot[0] = '\0'; + + Q_snprintf( sv.model_precache[WORLD_INDEX], sizeof( sv.model_precache[0] ), "maps/%s.bsp", sv.name ); + SetBits( sv.model_precache_flags[WORLD_INDEX], RES_FATALIFMISSING ); + sv.worldmodel = sv.models[WORLD_INDEX] = Mod_LoadWorld( sv.model_precache[WORLD_INDEX], true ); + CRC32_MapFile( &sv.worldmapCRC, sv.model_precache[WORLD_INDEX], svs.maxclients > 1 ); + + for( i = WORLD_INDEX; i < sv.worldmodel->numsubmodels; i++ ) + { + Q_sprintf( sv.model_precache[i+1], "*%i", i ); + sv.models[i+1] = Mod_ForName( sv.model_precache[i+1], false, false ); + SetBits( sv.model_precache_flags[i+1], RES_FATALIFMISSING ); + } + + // precache and static commands can be issued during map initialization + sv.state = ss_loading; + + Host_SetServerState( sv.state ); + + // clear physics interaction links + SV_ClearWorld(); + + return true; +} + qboolean SV_Active( void ) { return svs.initialized; @@ -942,13 +906,6 @@ int SV_GetMaxClients( void ) return svs.maxclients; } -void SV_ForceError( void ) -{ - // this is only for singleplayer testing - if( svs.maxclients != 1 ) return; - sv.write_bad_message = true; -} - void SV_InitGameProgs( void ) { if( svgame.hInstance ) return; // already loaded @@ -963,40 +920,4 @@ void SV_FreeGameProgs( void ) // unload progs (free cvars and commands) SV_UnloadProgs(); -} - -qboolean SV_NewGame( const char *mapName, qboolean loadGame ) -{ - if( !loadGame ) - { - if( !SV_MapIsValid( mapName, GI->sp_entity, NULL )) - return false; - - SV_ClearSaveDir (); - SV_Shutdown( true ); - } - else - { - S_StopAllSounds (true); - SV_DeactivateServer (); - } - - sv.loadgame = loadGame; - sv.background = false; - sv.changelevel = false; - - SCR_BeginLoadingPlaque( false ); - - if( !SV_SpawnServer( mapName, NULL )) - return false; - - SV_LevelInit( mapName, NULL, NULL, loadGame ); - sv.loadgame = loadGame; - - SV_ActivateServer(); - - if( sv.state != ss_active ) - return false; - - return true; } \ No newline at end of file diff --git a/engine/server/sv_phys.c b/engine/server/sv_phys.c index bcda65af..96aea153 100644 --- a/engine/server/sv_phys.c +++ b/engine/server/sv_phys.c @@ -765,14 +765,18 @@ qboolean SV_AllowPushRotate( edict_t *ent ) { model_t *mod; - mod = Mod_Handle( ent->v.modelindex ); + mod = SV_ModelHandle( ent->v.modelindex ); + if( !mod || mod->type != mod_brush ) return true; if( !FBitSet( host.features, ENGINE_PHYSICS_PUSHER_EXT )) return false; - return (mod->flags & MODEL_HAS_ORIGIN) ? true : false; + if( FBitSet( mod->flags, MODEL_HAS_ORIGIN )) + return true; + + return false; } /* @@ -2012,7 +2016,7 @@ static server_physics_api_t gPhysicsAPI = SV_LinkEdict, SV_GetServerTime, SV_GetFrameTime, - Mod_Handle, + SV_ModelHandle, SV_GetHeadNode, SV_ServerState, Host_Error, diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 9a6f4121..da490133 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -58,7 +58,7 @@ void SV_ClipPMoveToEntity( physent_t *pe, const vec3_t start, vec3_t mins, vec3_ qboolean SV_CopyEdictToPhysEnt( physent_t *pe, edict_t *ed ) { - model_t *mod = Mod_Handle( ed->v.modelindex ); + model_t *mod = SV_ModelHandle( ed->v.modelindex ); if( !mod ) return false; pe->player = false; @@ -283,6 +283,7 @@ void SV_AddLaddersToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3 { link_t *l, *next; edict_t *check; + model_t *mod; physent_t *pe; // get water edicts @@ -294,8 +295,10 @@ void SV_AddLaddersToPmove( areanode_t *node, const vec3_t pmove_mins, const vec3 if( check->v.solid != SOLID_NOT || check->v.skin != CONTENTS_LADDER ) continue; + mod = SV_ModelHandle( check->v.modelindex ); + // only brushes can have special contents - if( Mod_GetType( check->v.modelindex ) != mod_brush ) + if( !mod || mod->type != mod_brush ) continue; if( !BoundsIntersect( pmove_mins, pmove_maxs, check->v.absmin, check->v.absmax )) diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 9c852b4f..3a73cf1b 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -1914,10 +1914,10 @@ SV_ChangeLevel */ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start ) { - string level; - string oldlevel; - string _startspot; - char *startspot; + char level[MAX_QPATH]; + char oldlevel[MAX_QPATH]; + char _startspot[MAX_QPATH]; + char *startspot = NULL; SAVERESTOREDATA *pSaveData = NULL; if( sv.state != ss_active ) @@ -1926,22 +1926,15 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char return; } - if( !start ) - { - startspot = NULL; - } - else + if( start ) { Q_strncpy( _startspot, start, MAX_STRING ); startspot = _startspot; } - // init network stuff Q_strncpy( level, mapname, MAX_STRING ); Q_strncpy( oldlevel, sv.name, MAX_STRING ); - sv.background = false; - sv.changelevel = true; // NOTE: this is used to indicate changelevel for classic Quake changelevel - // because demos wan't properly update clock on a new level while recording + sv.changelevel = true; if( loadfromsavedgame ) { @@ -1956,8 +1949,8 @@ void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char SV_InactivateClients (); SV_DeactivateServer (); - if( !SV_SpawnServer( level, startspot )) - return; + if( !SV_SpawnServer( level, startspot, false )) + return; // ??? if( loadfromsavedgame ) { @@ -2119,36 +2112,26 @@ int SV_SaveReadHeader( file_t *pFile, GAME_HEADER *pHeader ) qboolean SV_LoadGame( const char *pPath ) { - file_t *pFile; qboolean validload = false; GAME_HEADER gameHeader; + file_t *pFile; int flags; - string name; if( host.type == HOST_DEDICATED ) return false; - if( !pPath || !pPath[0] ) + if( !COM_CheckString( pPath )) return false; - FS_FileBase( pPath, name ); - // silently ignore if missed if( !FS_FileExists( pPath, true )) return false; - if( sv.background || svs.maxclients > 1 ) - SV_Shutdown( true ); - sv.background = false; - - SCR_BeginLoadingPlaque ( false ); - S_StopBackgroundTrack(); - - MsgDev( D_INFO, "Loading game from %s...\n", pPath ); SV_ClearSaveDir(); + SV_InitGameProgs(); - if( !svs.initialized ) SV_InitGame (); - if( !svs.initialized ) return false; + if( !svgame.hInstance ) + return false; pFile = FS_Open( pPath, "rb", true ); @@ -2176,20 +2159,20 @@ qboolean SV_LoadGame( const char *pPath ) validload = false; } } - else MsgDev( D_ERROR, "File not found or failed to open.\n" ); if( !validload ) { - Q_snprintf( host.finalmsg, MAX_STRING, "Couldn't load %s.sav\n", name ); - SV_Shutdown( false ); + MsgDev( D_ERROR, "Couldn't load %s\n", pPath ); return false; } + MsgDev( D_INFO, "Loading game from %s...\n", pPath ); Cvar_FullSet( "maxplayers", "1", FCVAR_LATCH ); Cvar_SetValue( "deathmatch", 0 ); Cvar_SetValue( "coop", 0 ); + COM_LoadGame( gameHeader.mapName ); - return Host_NewGame( gameHeader.mapName, true ); + return true; } /* @@ -2201,7 +2184,9 @@ void SV_SaveGetName( int lastnum, char *filename ) { int a, b, c; - if( !filename ) return; + if( !COM_CheckString( filename )) + return; + if( lastnum < 0 || lastnum > 999 ) { // bound @@ -2223,7 +2208,7 @@ void SV_SaveGame( const char *pName ) string savename; int n; - if( !pName || !*pName ) + if( !COM_CheckString( pName )) return; // can we save at this point? @@ -2233,7 +2218,7 @@ void SV_SaveGame( const char *pName ) if( !Q_stricmp( pName, "new" )) { // scan for a free filename - for( n = 0; n < 999; n++ ) + for( n = 0; n < 1000; n++ ) { SV_SaveGetName( n, savename ); @@ -2243,17 +2228,16 @@ void SV_SaveGame( const char *pName ) if( n == 1000 ) { - Msg( "^3ERROR: no free slots for savegame\n" ); + Msg( "^3ERROR^7: no free slots for savegame\n" ); return; } } else Q_strncpy( savename, pName, sizeof( savename )); - // HACKHACK: unload previous image from memory + // unload previous image from memory (it's will be overwritten) GL_FreeImage( va( "save/%s.bmp", savename )); comment[0] = '\0'; - SV_BuildSaveComment( comment, sizeof( comment )); SV_SaveGameSlot( savename, comment ); @@ -2282,9 +2266,9 @@ used for reload game after player death const char *SV_GetLatestSave( void ) { search_t *f = FS_Search( "save/*.sav", true, true ); // lookup only in gamedir - int i, found = 0; + char savename[MAX_QPATH]; long newest = 0, ft; - string savename; + int i, found = 0; if( !f ) return NULL; @@ -2295,7 +2279,7 @@ const char *SV_GetLatestSave( void ) // found a match? if( ft > 0 ) { - // should we use the matche? + // should we use the matched? if( !found || Host_CompareFileTime( newest, ft ) < 0 ) { newest = ft; diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index fecf8bb6..7e153aa0 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -126,7 +126,7 @@ qboolean SV_CheckSphereIntersection( edict_t *ent, const vec3_t start, const vec if( !FBitSet( ent->v.flags, FL_CLIENT|FL_FAKECLIENT )) return true; - if(( mod = Mod_Handle( ent->v.modelindex )) == NULL ) + if(( mod = SV_ModelHandle( ent->v.modelindex )) == NULL ) return true; if(( pstudiohdr = (studiohdr_t *)Mod_StudioExtradata( mod )) == NULL ) @@ -229,7 +229,7 @@ hull_t *SV_HullForBsp( edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_ } // decide which clipping hull to use, based on the size - model = Mod_Handle( ent->v.modelindex ); + model = SV_ModelHandle( ent->v.modelindex ); if( !model || model->type != mod_brush ) Host_Error( "Entity %i (%s) SOLID_BSP with a non bsp model %s\n", NUM_FOR_EDICT( ent ), SV_ClassName( ent ), STRING( ent->v.model )); @@ -329,7 +329,7 @@ hull_t *SV_HullForStudioModel( edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t of vec3_t size; model_t *mod; - if(( mod = Mod_Handle( ent->v.modelindex )) == NULL ) + if(( mod = SV_ModelHandle( ent->v.modelindex )) == NULL ) { *numhitboxes = 1; return SV_HullForEntity( ent, mins, maxs, offset ); @@ -505,6 +505,7 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node ) edict_t *touch; hull_t *hull; vec3_t test, offset; + model_t *mod; // touch linked edicts for( l = node->trigger_edicts.next; l != &node->trigger_edicts; l = next ) @@ -533,16 +534,16 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node ) if( !BoundsIntersect( ent->v.absmin, ent->v.absmax, touch->v.absmin, touch->v.absmax )) continue; - // check brush triggers accuracy - if( Mod_GetType( touch->v.modelindex ) == mod_brush ) - { - model_t *mod = Mod_Handle( touch->v.modelindex ); + mod = SV_ModelHandle( touch->v.modelindex ); + // check brush triggers accuracy + if( mod && mod->type == mod_brush ) + { // force to select bsp-hull hull = SV_HullForBsp( touch, ent->v.mins, ent->v.maxs, offset ); // support for rotational triggers - if(( mod->flags & MODEL_HAS_ORIGIN ) && !VectorIsNull( touch->v.angles )) + if( FBitSet( mod->flags, MODEL_HAS_ORIGIN ) && !VectorIsNull( touch->v.angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, touch->v.angles, offset, 1.0f ); @@ -561,7 +562,7 @@ void SV_TouchLinks( edict_t *ent, areanode_t *node ) } // never touch the triggers when "playersonly" is active - if( !( sv.hostflags & SVF_PLAYERSONLY )) + if( !FBitSet( sv.hostflags, SVF_PLAYERSONLY )) { svgame.globals->time = sv.time; svgame.dllFuncs.pfnTouch( touch, ent ); @@ -724,20 +725,20 @@ void SV_WaterLinks( const vec3_t origin, int *pCont, areanode_t *node ) continue; } + mod = SV_ModelHandle( touch->v.modelindex ); + // only brushes can have special contents - if( Mod_GetType( touch->v.modelindex ) != mod_brush ) + if( !mod || mod->type != mod_brush ) continue; if( !BoundsIntersect( origin, origin, touch->v.absmin, touch->v.absmax )) continue; - mod = Mod_Handle( touch->v.modelindex ); - // check water brushes accuracy hull = SV_HullForBsp( touch, vec3_origin, vec3_origin, offset ); // support for rotational water - if(( mod->flags & MODEL_HAS_ORIGIN ) && !VectorIsNull( touch->v.angles )) + if( FBitSet( mod->flags, MODEL_HAS_ORIGIN ) && !VectorIsNull( touch->v.angles )) { matrix4x4 matrix; Matrix4x4_CreateFromEntity( matrix, touch->v.angles, offset, 1.0f ); @@ -869,7 +870,7 @@ void SV_ClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t trace->fraction = 1.0f; trace->allsolid = 1; - model = Mod_Handle( ent->v.modelindex ); + model = SV_ModelHandle( ent->v.modelindex ); if( model && model->type == mod_studio ) { @@ -982,6 +983,120 @@ void SV_ClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t trace->ent = ent; } +/* +================== +SV_PortalCSG + +a portal is flush with a world surface behind it. this causes problems. namely that we can't pass through the portal plane +if the bsp behind it prevents out origin from getting through. so if the trace was clipped and ended infront of the portal, +continue the trace to the edges of the portal cutout instead. +================== +*/ +void SV_PortalCSG( edict_t *portal, const vec3_t trace_mins, const vec3_t trace_maxs, const vec3_t start, const vec3_t end, trace_t *trace ) +{ + vec4_t planes[6]; //far, near, right, left, up, down + int plane, k; + vec3_t worldpos; + float bestfrac; + int hitplane; + model_t *model; + float portalradius; + + // only run this code if we impacted on the portal's parent. + if( trace->fraction == 1.0f && !trace->startsolid ) + return; + + // decide which clipping hull to use, based on the size + model = SV_ModelHandle( portal->v.modelindex ); + + if( !model || model->type != mod_brush ) + return; + + // make sure we use a sane valid position. + if( trace->startsolid ) VectorCopy( start, worldpos ); + else VectorCopy( trace->endpos, worldpos ); + + // determine the csg area. normals should be facing in + AngleVectors( portal->v.angles, planes[1], planes[3], planes[5] ); + VectorNegate(planes[1], planes[0]); + VectorNegate(planes[3], planes[2]); + VectorNegate(planes[5], planes[4]); + + portalradius = model->radius * 0.5f; + planes[0][3] = DotProduct( portal->v.origin, planes[0] ) - (4.0f / 32.0f); + planes[1][3] = DotProduct( portal->v.origin, planes[1] ) - (4.0f / 32.0f); //an epsilon beyond the portal + planes[2][3] = DotProduct( portal->v.origin, planes[2] ) - portalradius; + planes[3][3] = DotProduct( portal->v.origin, planes[3] ) - portalradius; + planes[4][3] = DotProduct( portal->v.origin, planes[4] ) - portalradius; + planes[5][3] = DotProduct( portal->v.origin, planes[5] ) - portalradius; + + // if we're actually inside the csg region + for( plane = 0; plane < 6; plane++ ) + { + float d = DotProduct( worldpos, planes[plane] ); + vec3_t nearest; + + for( k = 0; k < 3; k++ ) + nearest[k] = (planes[plane][k]>=0) ? trace_maxs[k] : trace_mins[k]; + + // front plane gets further away with side + if( !plane ) + { + planes[plane][3] -= DotProduct( nearest, planes[plane] ); + } + else if( plane > 1 ) + { + // side planes get nearer with size + planes[plane][3] += 24; // DotProduct( nearest, planes[plane] ); + } + + if( d - planes[plane][3] >= 0 ) + continue; // endpos is inside + else return; // end is already outside + } + + // yup, we're inside, the trace shouldn't end where it actually did + bestfrac = 1; + hitplane = -1; + + for( plane = 0; plane < 6; plane++ ) + { + float ds = DotProduct( start, planes[plane] ) - planes[plane][3]; + float de = DotProduct( end, planes[plane] ) - planes[plane][3]; + float frac; + + if( ds >= 0 && de < 0 ) + { + frac = (ds) / (ds - de); + if( frac < bestfrac ) + { + if( frac < 0 ) + frac = 0; + bestfrac = frac; + hitplane = plane; + } + } + } + + trace->startsolid = trace->allsolid = false; + + // if we cross the front of the portal, don't shorten the trace, + // that will artificially clip us + if( hitplane == 0 && trace->fraction > bestfrac ) + return; + + // okay, elongate to clip to the portal hole properly. + VectorLerp( start, bestfrac, end, trace->endpos ); + trace->fraction = bestfrac; + + if( hitplane >= 0 ) + { + VectorCopy( planes[hitplane], trace->plane.normal ); + trace->plane.dist = planes[hitplane][3]; + if( hitplane == 1 ) trace->ent = portal; + } +} + /* ================== SV_CustomClipMoveToEntity @@ -1019,6 +1134,7 @@ generic clip function static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) { trace_t trace; + model_t *mod; if( touch->v.groupinfo != 0 && SV_IsValidEdict( clip->passedict ) && clip->passedict->v.groupinfo != 0 ) { @@ -1057,7 +1173,9 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) return true; } - if( Mod_GetType( touch->v.modelindex ) == mod_brush && clip->flags & FMOVE_IGNORE_GLASS ) + mod = SV_ModelHandle( touch->v.modelindex ); + + if( mod && mod->type == mod_brush && FBitSet( clip->flags, FMOVE_IGNORE_GLASS )) { // we ignore brushes with rendermode != kRenderNormal and without FL_WORLDBRUSH set if( touch->v.rendermode != kRenderNormal && !FBitSet( touch->v.flags, FL_WORLDBRUSH )) @@ -1098,7 +1216,7 @@ static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) // make sure we don't hit the world if we're inside the portal if( touch->v.solid == SOLID_PORTAL ) - World_PortalCSG( touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace ); + SV_PortalCSG( touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace ); if( touch->v.solid == SOLID_CUSTOM ) SV_CustomClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); @@ -1335,7 +1453,7 @@ msurface_t *SV_TraceSurface( edict_t *ent, const vec3_t start, const vec3_t end vec3_t start_l, end_l; vec3_t offset; - bmodel = Mod_Handle( ent->v.modelindex ); + bmodel = SV_ModelHandle( ent->v.modelindex ); if( !bmodel || bmodel->type != mod_brush ) return NULL;