26 Feb 2018

This commit is contained in:
g-cont 2018-02-26 00:00:00 +03:00 committed by Alibek Omarov
parent 209d2aabca
commit 64609ae475
42 changed files with 1130 additions and 1067 deletions

View File

@ -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

View File

@ -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 )

View File

@ -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 );
}
/*

View File

@ -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 );

View File

@ -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 );
}

View File

@ -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 ))

View File

@ -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 ))

View File

@ -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)

View File

@ -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 );

View File

@ -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;
}

View File

@ -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;

View File

@ -1460,7 +1460,7 @@ static render_api_t gRenderAPI =
pfnFileBufferCRC32,
COM_CompareFileTime,
Host_Error,
Mod_Handle,
CL_ModelHandle,
pfnTime,
Cvar_Set,
S_FadeMusicVolume,

View File

@ -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 )

View File

@ -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,

View File

@ -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

View File

@ -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 ))

View File

@ -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 );

View File

@ -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;

View File

@ -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 )

View File

@ -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

210
engine/common/host_state.c Normal file
View File

@ -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" );
}
}

View File

@ -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:

View File

@ -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 );

View File

@ -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;
}

View File

@ -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

View File

@ -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;

View File

@ -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 );

View File

@ -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 ));

View File

@ -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 );

View File

@ -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

View File

@ -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 );

View File

@ -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

View File

@ -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 );

View File

@ -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;
}

View File

@ -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 );
}
/*

View File

@ -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 );
}
}

View File

@ -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 );

View File

@ -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;
}

View File

@ -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,

View File

@ -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 ))

View File

@ -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;

View File

@ -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;