diff --git a/cl_dll/entity.cpp b/cl_dll/entity.cpp index 6a209c86..923ec48d 100644 --- a/cl_dll/entity.cpp +++ b/cl_dll/entity.cpp @@ -19,6 +19,7 @@ #include "pm_defs.h" #include "pmtrace.h" #include "pm_shared.h" +#include "com_weapons.h" #define DLLEXPORT __declspec( dllexport ) @@ -27,6 +28,7 @@ void Game_AddObjects( void ); extern vec3_t v_origin; int g_iAlive = 1; +int g_iLaserDot = 0; extern "C" { @@ -89,6 +91,8 @@ void DLLEXPORT HUD_TxferLocalOverrides( struct entity_state_s *state, const stru { VectorCopy( client->origin, state->origin ); + g_iLaserDot = (client->flags & FL_LASERDOT) ? 1 : 0; + // Spectator state->iuser1 = client->iuser1; state->iuser2 = client->iuser2; @@ -504,6 +508,60 @@ void Beams( void ) } #endif +TEMPENTITY *m_pLaserSpot = NULL; + +void CL_UpdateLaserSpot( void ) +{ + cl_entity_t *player = gEngfuncs.GetLocalPlayer(); + + if( !player ) return; + + if(( g_iLaserDot && cl_lw->value ) && !m_pLaserSpot ) + { + // create laserspot + int m_iSpotModel = gEngfuncs.pEventAPI->EV_FindModelIndex( "sprites/laserdot.spr" ); + + m_pLaserSpot = gEngfuncs.pEfxAPI->R_TempSprite( Vector( 0, 0, 0), Vector( 0, 0, 0), 1.0, m_iSpotModel, kRenderGlow, kRenderFxNoDissipation, 1.0, 9999, FTENT_SPRCYCLE ); + if( !m_pLaserSpot ) return; + + m_pLaserSpot->entity.curstate.rendercolor.r = 200; + m_pLaserSpot->entity.curstate.rendercolor.g = 12; + m_pLaserSpot->entity.curstate.rendercolor.b = 12; +// gEngfuncs.Con_Printf( "CLaserSpot::Create()\n" ); + } + else if(( !g_iLaserDot || !cl_lw->value ) && m_pLaserSpot ) + { + // destroy laserspot +// gEngfuncs.Con_Printf( "CLaserSpot::Killed()\n" ); + m_pLaserSpot->die = 0.0f; + m_pLaserSpot = NULL; + return; + } + else if( !m_pLaserSpot ) + { + // inactive + return; + } + + assert( m_pLaserSpot != NULL ); + + Vector forward, vecSrc, vecEnd, origin, angles, view_ofs; + + gEngfuncs.GetViewAngles( (float *)angles ); + + AngleVectors( angles, forward, NULL, NULL ); + gEngfuncs.pEventAPI->EV_LocalPlayerViewheight( view_ofs ); + + vecSrc = player->origin + view_ofs; + vecEnd = vecSrc + forward * 8192.0f; + + pmtrace_t *trace = gEngfuncs.PM_TraceLine( vecSrc, vecEnd, PM_TRACELINE_ANYVISIBLE, 2, -1 ); + + // update laserspot endpos + m_pLaserSpot->entity.origin = trace->endpos; + m_pLaserSpot->die = gEngfuncs.GetClientTime() + 0.1f; +} + /* ========================= HUD_CreateEntities @@ -535,6 +593,8 @@ void DLLEXPORT HUD_CreateEntities( void ) #if defined( BEAM_TEST ) Beams(); #endif + // predictable laserdot + CL_UpdateLaserSpot(); // Add in any game specific objects Game_AddObjects(); diff --git a/common/const.h b/common/const.h index b1a27c9d..f16797e9 100644 --- a/common/const.h +++ b/common/const.h @@ -50,6 +50,7 @@ #define FL_ONTRAIN (1<<24) // Player is _controlling_ a train, so movement commands should be ignored on client during prediction. #define FL_WORLDBRUSH (1<<25) // Not moveable/removeable brush entity (really part of the world, but represented as an entity for transparency or something) #define FL_SPECTATOR (1<<26) // This client is a spectator, don't run touch functions, etc. +#define FL_LASERDOT (1<<27) // Predicted laser spot from rocket launcher #define FL_CUSTOMENTITY (1<<29) // This is a custom entity #define FL_KILLME (1<<30) // This entity is marked for death -- This allows the engine to kill ents at the appropriate time @@ -113,8 +114,6 @@ #define EF_LIGHT 64 // rocket flare glow sprite #define EF_NODRAW 128 // don't draw entity - - #define EF_WATERSIDES (1<<26) // Do not remove sides for func_water entity #define EF_FULLBRIGHT (1<<27) // Just get fullbright #define EF_NOSHADOW (1<<28) // ignore shadow for this entity diff --git a/common/event_api.h b/common/event_api.h index f9419ebd..535ce00c 100644 --- a/common/event_api.h +++ b/common/event_api.h @@ -44,8 +44,6 @@ typedef struct event_api_s void ( *EV_KillEvents )( int entnum, const char *eventname ); // Xash3D extension - unsigned short (*EV_IndexForEvent)( const char *name ); - const char *(*EV_EventForIndex)( unsigned short index ); void ( *EV_PlayerTraceExt )( float *start, float *end, int traceFlags, int (*pfnIgnore)( struct physent_s *pe ), struct pmtrace_s *tr ); const char *(*EV_SoundForIndex)( int index ); struct msurface_s *( *EV_TraceSurface )( int ground, float *vstart, float *vend ); diff --git a/common/render_api.h b/common/render_api.h index 46c13526..413d88e8 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -209,13 +209,13 @@ typedef struct render_api_s // Misc renderer functions void (*GL_DrawParticles)( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime ); - void (*EnvShot)( const float *vieworg, const char *name, qboolean skyshot, int shotsize ); // creates a cubemap or skybox into gfx\env folder + void (*EnvShot)( const float *vieworg, const char *name, qboolean skyshot, int shotsize ); // store skybox into gfx\env folder int (*SPR_LoadExt)( const char *szPicName, unsigned int texFlags ); // extended version of SPR_Load colorVec (*LightVec)( const float *start, const float *end, float *lightspot ); struct mstudiotex_s *( *StudioGetTexture )( struct cl_entity_s *e ); const struct ref_overview_s *( *GetOverviewParms )( void ); - void (*R_Reserved0)( void ); // for potential interface expansion without broken compatibility - void (*R_Reserved1)( void ); + const char *( *GetFileByIndex )( int fileindex ); + void (*R_Reserved1)( void ); // for potential interface expansion without broken compatibility void (*R_Reserved2)( void ); // static allocations @@ -245,8 +245,6 @@ typedef struct render_interface_s void (*GL_BuildLightmaps)( void ); // setup map bounds for ortho-projection when we in dev_overview mode void (*GL_OrthoBounds)( const float *mins, const float *maxs ); - // handle decals which hit mod_studio or mod_sprite - void (*R_StudioDecalShoot)( int decalTexture, struct cl_entity_s *ent, const float *start, const float *pos, int flags, modelstate_t *state ); // prepare studio decals for save int (*R_CreateStudioDecalList)( decallist_t *pList, int count ); // clear decals by engine request (e.g. for demo recording or vid_restart) diff --git a/dlls/rpg.cpp b/dlls/rpg.cpp index e8f60f00..27defbd5 100644 --- a/dlls/rpg.cpp +++ b/dlls/rpg.cpp @@ -47,13 +47,21 @@ LINK_ENTITY_TO_CLASS( laser_spot, CLaserSpot ); //========================================================= //========================================================= -CLaserSpot *CLaserSpot::CreateSpot( void ) +CLaserSpot *CLaserSpot::CreateSpot( edict_t *pOwner ) { CLaserSpot *pSpot = GetClassPtr( (CLaserSpot *)NULL ); pSpot->Spawn(); pSpot->pev->classname = MAKE_STRING("laser_spot"); + if( pOwner ) + { + // predictable laserspot + pSpot->pev->flags |= FL_SKIPLOCALHOST; + pOwner->v.flags |= FL_LASERDOT; + pSpot->pev->owner = pOwner; + } + return pSpot; } @@ -79,6 +87,9 @@ void CLaserSpot::Spawn( void ) void CLaserSpot::Suspend( float flSuspendTime ) { pev->effects |= EF_NODRAW; + + if( !FNullEnt( pev->owner )) + pev->owner->v.flags &= ~FL_LASERDOT; SetThink( Revive ); pev->nextthink = gpGlobals->time + flSuspendTime; @@ -91,9 +102,20 @@ void CLaserSpot::Revive( void ) { pev->effects &= ~EF_NODRAW; + if( !FNullEnt( pev->owner )) + pev->owner->v.flags |= FL_LASERDOT; + SetThink( NULL ); } +void CLaserSpot :: Killed( entvars_t *pevAttacker, int iGib ) +{ + // tell the owner about laserspot + if( !FNullEnt( pev->owner )) + pev->owner->v.flags &= ~FL_LASERDOT; + CBaseEntity :: Killed( pevAttacker, iGib ); +} + void CLaserSpot::Precache( void ) { PRECACHE_MODEL("sprites/laserdot.spr"); @@ -557,7 +579,7 @@ void CRpg::UpdateSpot( void ) { if (!m_pSpot) { - m_pSpot = CLaserSpot::CreateSpot(); + m_pSpot = CLaserSpot::CreateSpot( m_pPlayer->edict()); } UTIL_MakeVectors( m_pPlayer->pev->v_angle ); diff --git a/dlls/weapons.h b/dlls/weapons.h index aaa98247..92058e86 100644 --- a/dlls/weapons.h +++ b/dlls/weapons.h @@ -671,8 +671,8 @@ class CLaserSpot : public CBaseEntity public: void Suspend( float flSuspendTime ); void EXPORT Revive( void ); - - static CLaserSpot *CreateSpot( void ); + void Killed( entvars_t *pevAttacker, int iGib ); + static CLaserSpot *CreateSpot( edict_t *pOwner ); }; class CRpg : public CBasePlayerWeapon diff --git a/engine/client/cl_custom.c b/engine/client/cl_custom.c index 5c009f99..8ea78551 100644 --- a/engine/client/cl_custom.c +++ b/engine/client/cl_custom.c @@ -60,7 +60,8 @@ qboolean CL_CheckFile( sizebuf_t *msg, resource_t *pResource ) return true; } - if( FS_FileExists( filepath, false )) + // don't request downloads from local client it's silly + if( Host_IsLocalClient() || FS_FileExists( filepath, false )) return true; if( cls.demoplayback ) diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index 12846a05..3835228e 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -135,7 +135,7 @@ word CL_EventIndex( const char *name ) { int i; - if( !name || !name[0] ) + if( !COM_CheckString( name )) return 0; for( i = 1; i < MAX_EVENTS && cl.event_precache[i][0]; i++ ) @@ -146,20 +146,6 @@ word CL_EventIndex( const char *name ) return 0; } -/* -============= -CL_EventIndex - -============= -*/ -const char *CL_IndexEvent( word index ) -{ - if( index < 0 || index >= MAX_EVENTS ) - return 0; - - return cl.event_precache[index]; -} - /* ============= CL_RegisterEvent diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 4e1b5c27..9068da23 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -868,12 +868,6 @@ int CL_ParsePacketEntities( sizebuf_t *msg, qboolean delta ) // Clear loading plaque. CL_SignonReply (); } - else if( cls.state == ca_active ) - { - // g-cont: now stair climbing interpolation - // is prepared for a new level and ready to use - cl.first_frame = false; - } return playerbytes; } diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index d7607240..fa4fde8c 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3566,9 +3566,7 @@ static event_api_t gEventApi = pfnTraceTexture, pfnStopAllSounds, pfnKillEvents, - CL_EventIndex, // Xash3D added - CL_IndexEvent, - CL_PlayerTraceExt, + CL_PlayerTraceExt, // Xash3D added CL_SoundFromIndex, pfnTraceSurface, pfnGetMoveVars, diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ec119f58..2bcec09c 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -174,7 +174,7 @@ void CL_CheckClientState( void ) cls.state = ca_active; cls.changelevel = false; // changelevel is done cls.changedemo = false; // changedemo is done - cl.first_frame = true; + cl.first_frame = true; // first rendering frame SCR_MakeLevelShot(); // make levelshot if needs Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar @@ -183,7 +183,6 @@ void CL_CheckClientState( void ) Con_DPrintf( "client connected at %.2f sec\n", Sys_DoubleTime() - cls.timestart ); if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped ) SCR_EndLoadingPlaque(); // get rid of loading plaque - cl.first_frame = true; } } @@ -2344,6 +2343,8 @@ qboolean CL_PrecacheResources( void ) Q_strncpy( host.draw_decals[pRes->nIndex], pRes->szFileName, sizeof( host.draw_decals[0] )); break; case t_generic: + Q_strncpy( cl.files_precache[pRes->nIndex], pRes->szFileName, sizeof( cl.files_precache[0] )); + cl.numfiles = Q_max( cl.numfiles, pRes->nIndex + 1 ); break; case t_eventscript: Q_strncpy( cl.event_precache[pRes->nIndex], pRes->szFileName, sizeof( cl.event_precache[0] )); @@ -2359,6 +2360,7 @@ qboolean CL_PrecacheResources( void ) // make sure modelcount is in-range cl.nummodels = bound( 0, cl.nummodels, MAX_MODELS ); + cl.numfiles = bound( 0, cl.numfiles, MAX_CUSTOM ); S_EndRegistration(); return true; diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index e5d26119..dfcfabee 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -45,7 +45,7 @@ const char *svc_strings[svc_lastmsg+1] = "svc_updateuserinfo", "svc_deltatable", "svc_clientdata", - "svc_studiodecal", + "svc_fileindex", "svc_pings", "svc_particle", "svc_restoresound", @@ -1543,24 +1543,17 @@ prceache model from server */ void CL_PrecacheModel( sizebuf_t *msg ) { - const char *s; - int i; + int modelIndex; - i = MSG_ReadUBitLong( msg, MAX_MODEL_BITS ); - s = MSG_ReadString( msg ); + modelIndex = MSG_ReadUBitLong( msg, MAX_MODEL_BITS ); - if( i < 0 || i >= MAX_MODELS ) - Host_Error( "CL_PrecacheModel: bad modelindex %i\n", i ); + if( modelIndex < 0 || modelIndex >= MAX_MODELS ) + Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex ); - if( i == WORLD_INDEX ) - cl.models[i] = Mod_LoadWorld( s, false ); - else cl.models[i] = Mod_FindName( s, false ); + cl.models[modelIndex] = Mod_ForName( MSG_ReadString( msg ), false, false ); - if( i >= cl.nummodels ) - { - cl.nummodels = i + 1; - cl.nummodels = Q_min( cl.nummodels, MAX_MODELS ); - } + cl.nummodels = Q_max( cl.nummodels, modelIndex + 1 ); + cl.nummodels = bound( 0, cl.nummodels, MAX_MODELS ); } /* @@ -1609,6 +1602,28 @@ void CL_PrecacheEvent( sizebuf_t *msg ) CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex ); } +/* +================ +CL_PrecacheFile + +prceache generic resource from server +================ +*/ +void CL_PrecacheFile( sizebuf_t *msg ) +{ + int fileIndex; + + fileIndex = MSG_ReadUBitLong( msg, MAX_CUSTOM_BITS ); + + if( fileIndex < 0 || fileIndex >= MAX_CUSTOM ) + Host_Error( "CL_PrecacheFile: bad fileindex %i\n", fileIndex ); + + Q_strncpy( cl.files_precache[fileIndex], MSG_ReadString( msg ), sizeof( cl.files_precache[0] )); + + cl.numfiles = Q_max( cl.numfiles, fileIndex + 1 ); + cl.numfiles = bound( 0, cl.numfiles, MAX_CUSTOM ); +} + /* ================ CL_UpdateUserPings @@ -1969,53 +1984,6 @@ void CL_ParseDirector( sizebuf_t *msg ) clgame.dllFuncs.pfnDirectorMessage( iSize, pbuf ); } -/* -============== -CL_ParseStudioDecal - -Studio Decal message. Used by engine in case -we need save\restore decals -============== -*/ -void CL_ParseStudioDecal( sizebuf_t *msg ) -{ - modelstate_t state; - vec3_t start, pos; - int decalIndex, entityIndex; - int modelIndex = 0; - int i, flags; - - MSG_ReadVec3Coord( msg, pos ); - MSG_ReadVec3Coord( msg, start ); - decalIndex = MSG_ReadWord( msg ); - entityIndex = MSG_ReadWord( msg ); - flags = MSG_ReadByte( msg ); - - state.sequence = MSG_ReadShort( msg ); - state.frame = MSG_ReadShort( msg ); - state.blending[0] = MSG_ReadByte( msg ); - state.blending[1] = MSG_ReadByte( msg ); - for( i = 0; i < 4; i++ ) - state.controller[i] = MSG_ReadByte( msg ); - for( i = 0; i < 16; i++ ) - state.poseparam[i] = MSG_ReadByte( msg ); - modelIndex = MSG_ReadWord( msg ); - state.body = MSG_ReadByte( msg ); - state.skin = MSG_ReadByte( msg ); - state.scale = MSG_ReadWord( msg ); - - if( clgame.drawFuncs.R_StudioDecalShoot != NULL ) - { - int decalTexture = CL_DecalIndex( decalIndex ); - cl_entity_t *ent = CL_GetEntityByIndex( entityIndex ); - - if( ent && !ent->model && modelIndex != 0 ) - ent->model = CL_ModelHandle( modelIndex ); - - clgame.drawFuncs.R_StudioDecalShoot( decalTexture, ent, start, pos, flags, &state ); - } -} - /* ============== CL_ParseScreenShake @@ -2347,12 +2315,12 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) SCR_BeginLoadingPlaque( cl.background ); cls.changedemo = true; } + + CL_ClearState (); + CL_InitEdicts (); // re-arrange edicts } else MsgDev( D_INFO, "Server disconnected, reconnecting\n" ); - CL_ClearState (); - CL_InitEdicts (); // re-arrange edicts - if( cls.demoplayback ) { cl.background = (cls.demonum != -1) ? true : false; @@ -2403,8 +2371,8 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) CL_ParseClientData( msg ); cl.frames[cl.parsecountmod].graphdata.client += MSG_GetNumBytesRead( msg ) - bufStart; break; - case svc_studiodecal: - CL_ParseStudioDecal( msg ); + case svc_fileindex: + CL_PrecacheFile( msg ); break; case svc_pings: CL_UpdateUserPings( msg ); diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index b82528d1..b9a5537b 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -319,6 +319,9 @@ void SCR_BeginLoadingPlaque( qboolean is_background ) if( cls.state == ca_disconnected || cls.disable_screen ) return; // already set + if( cls.key_dest == key_console ) + return; + cls.draw_changelevel = !is_background; SCR_UpdateScreen(); cls.disable_screen = host.realtime; diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 18ed1736..82ef26db 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -141,6 +141,9 @@ void CL_AddClientResources( void ) char filepath[MAX_QPATH]; int i; + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + return; + // check sprites first for( i = 0; i < ARRAYSIZE( cl_default_sprites ); i++ ) { diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index b928263a..5357b8c7 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -143,7 +143,11 @@ void V_SetRefParams( ref_params_t *fd ) fd->demoplayback = cls.demoplayback; fd->hardware = 1; // OpenGL - if( cl.first_frame ) fd->smoothing = true; // NOTE: currently this used to prevent ugly un-duck effect while level is changed + if( cl.first_frame ) + { + cl.first_frame = false; // now can be unlocked + fd->smoothing = true; // NOTE: currently this used to prevent ugly un-duck effect while level is changed + } else fd->smoothing = cl.local.pushmsec; // enable smoothing in multiplayer by server request (AMX uses) // get pointers to movement vars and user cmd diff --git a/engine/client/client.h b/engine/client/client.h index 54b24f2e..03a5f54d 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -267,9 +267,11 @@ typedef struct char sound_precache[MAX_SOUNDS][MAX_QPATH]; char event_precache[MAX_EVENTS][MAX_QPATH]; + char files_precache[MAX_CUSTOM][MAX_QPATH]; lightstyle_t lightstyles[MAX_LIGHTSTYLES]; model_t *models[MAX_MODELS+1]; // precached models (plus sentinel slot) int nummodels; + int numfiles; consistency_t consistency_list[MAX_MODELS]; int num_consistency; @@ -776,9 +778,8 @@ void CL_QueueEvent( int flags, int index, float delay, event_args_t *args ); void CL_PlaybackEvent( int flags, const edict_t *pInvoker, word eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ); -word CL_EventIndex( const char *name ); -const char *CL_IndexEvent( word index ); void CL_ResetEvent( event_info_t *ei ); +word CL_EventIndex( const char *name ); void CL_FireEvents( void ); // diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index e8316e2f..50fa8161 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1402,6 +1402,19 @@ static uint pfnFileBufferCRC32( const void *buffer, const int length ) return modelCRC; } + +/* +============= +CL_GenericHandle + +============= +*/ +const char *CL_GenericHandle( int fileindex ) +{ + if( fileindex < 0 || fileindex >= MAX_CUSTOM ) + return 0; + return cl.files_precache[fileindex]; +} static render_api_t gRenderAPI = { @@ -1456,7 +1469,7 @@ static render_api_t gRenderAPI = R_LightVec, R_StudioGetTexture, GL_GetOverviewParms, - NULL, + CL_GenericHandle, NULL, NULL, R_Mem_Alloc, diff --git a/engine/common/common.c b/engine/common/common.c index acda1031..c95c9f93 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -147,6 +147,287 @@ long COM_RandomLong( long lLow, long lHigh ) return lLow + (n % x); } +/* +=============================================================================== + + LZSS Compression + +=============================================================================== +*/ +#define LZSS_ID (('S'<<24)|('S'<<16)|('Z'<<8)|('L')) +#define LZSS_LOOKSHIFT 4 +#define LZSS_WINDOW_SIZE 4096 +#define LZSS_LOOKAHEAD BIT( LZSS_LOOKSHIFT ) + + +typedef struct +{ + unsigned int id; + unsigned short size; +} lzss_header_t; + +// expected to be sixteen bytes +typedef struct lzss_node_s +{ + const byte *data; + struct lzss_node_s *prev; + struct lzss_node_s *next; + char pad[4]; +} lzss_node_t; + +typedef struct +{ + lzss_node_t *start; + lzss_node_t *end; +} lzss_list_t; + +typedef struct +{ + lzss_list_t *hash_table; + lzss_node_t *hash_node; + int window_size; +} lzss_state_t; + +qboolean LZSS_IsCompressed( const byte *source ) +{ + lzss_header_t *phdr = (lzss_header_t *)source; + + if( phdr && phdr->id == LZSS_ID ) + return true; + return false; +} + +uint LZSS_GetActualSize( const byte *source ) +{ + lzss_header_t *phdr = (lzss_header_t *)source; + + if( phdr && phdr->id == LZSS_ID ) + return phdr->size; + return 0; +} + +static void LZSS_BuildHash( lzss_state_t *state, const byte *source ) +{ + lzss_list_t *list; + lzss_node_t *node; + unsigned int targetindex = (uint)source & ( state->window_size - 1 ); + + node = &state->hash_node[targetindex]; + + if( node->data ) + { + list = &state->hash_table[*node->data]; + if( node->prev ) + { + list->end = node->prev; + node->prev->next = NULL; + } + else + { + list->start = NULL; + list->end = NULL; + } + } + + list = &state->hash_table[*source]; + node->data = source; + node->prev = NULL; + node->next = list->start; + if( list->start ) + list->start->prev = node; + else list->end = node; + list->start = node; +} + +byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_length, byte *pOutputBuf, uint *pOutputSize ) +{ + byte *pStart = pOutputBuf; // allocate the output buffer, compressed buffer is expected to be less, caller will free + byte *pEnd = pStart + input_length - sizeof( lzss_header_t ) - 8; // prevent compression failure + lzss_header_t *header = (lzss_header_t *)pStart; + byte *pOutput = pStart + sizeof( lzss_header_t ); + const byte *pEncodedPosition = NULL; + byte *pLookAhead = pInput; + byte *pWindow = pInput; + int i, putCmdByte = 0; + byte *pCmdByte = NULL; + + if( input_length <= sizeof( lzss_header_t ) + 8 ) + return NULL; + + // set LZSS header + header->id = LZSS_ID; + header->size = input_length; + + // create the compression work buffers, small enough (~64K) for stack + state->hash_table = (lzss_list_t *)_alloca( 256 * sizeof( lzss_list_t )); + memset( state->hash_table, 0, 256 * sizeof( lzss_list_t )); + state->hash_node = (lzss_node_t *)_alloca( state->window_size * sizeof( lzss_node_t )); + memset( state->hash_node, 0, state->window_size * sizeof( lzss_node_t )); + + while( input_length > 0 ) + { + int lookAheadLength = input_length < LZSS_LOOKAHEAD ? input_length : LZSS_LOOKAHEAD; + lzss_node_t *hash = state->hash_table[pLookAhead[0]].start; + int encoded_length = 0; + + pWindow = pLookAhead - state->window_size; + + if( pWindow < pInput ) + pWindow = pInput; + + if( !putCmdByte ) + { + pCmdByte = pOutput++; + *pCmdByte = 0; + } + + putCmdByte = ( putCmdByte + 1 ) & 0x07; + + while( hash != NULL ) + { + int length = lookAheadLength; + int match_length = 0; + + while( length-- && hash->data[match_length] == pLookAhead[match_length] ) + match_length++; + + if( match_length > encoded_length ) + { + encoded_length = match_length; + pEncodedPosition = hash->data; + } + + if( match_length == lookAheadLength ) + break; + + hash = hash->next; + } + + if ( encoded_length >= 3 ) + { + *pCmdByte = (*pCmdByte >> 1) | 0x80; + *pOutput++ = (( pLookAhead - pEncodedPosition - 1 ) >> LZSS_LOOKSHIFT ); + *pOutput++ = (( pLookAhead - pEncodedPosition - 1 ) << LZSS_LOOKSHIFT ) | ( encoded_length - 1 ); + } + else + { + *pCmdByte = ( *pCmdByte >> 1 ); + *pOutput++ = *pLookAhead; + encoded_length = 1; + } + + for( i = 0; i < encoded_length; i++ ) + { + LZSS_BuildHash( state, pLookAhead++ ); + } + + input_length -= encoded_length; + + if( pOutput >= pEnd ) + { + // compression is worse, abandon + return NULL; + } + } + + if( input_length != 0 ) + { + // unexpected failure + Assert( 0 ); + return NULL; + } + + if( !putCmdByte ) + { + pCmdByte = pOutput++; + *pCmdByte = 0x01; + } + else + { + *pCmdByte = (( *pCmdByte >> 1 ) | 0x80 ) >> ( 7 - putCmdByte ); + } + + // put two ints at end of buffer + *pOutput++ = 0; + *pOutput++ = 0; + + if( pOutputSize ) + *pOutputSize = pOutput - pStart; + + return pStart; +} + +byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize ) +{ + byte *pStart = (byte *)malloc( inputLength ); + byte *pFinal = NULL; + lzss_state_t state; + + memset( &state, 0, sizeof( state )); + state.window_size = LZSS_WINDOW_SIZE; + + pFinal = LZSS_CompressNoAlloc( &state, pInput, inputLength, pStart, pOutputSize ); + + if( !pFinal ) + { + free( pStart ); + return NULL; + } + + return pStart; +} + +uint LZSS_Decompress( const byte *pInput, byte *pOutput ) +{ + uint totalBytes = 0; + int getCmdByte = 0; + int cmdByte = 0; + uint actualSize = LZSS_GetActualSize( pInput ); + + if( !actualSize ) + return 0; + + pInput += sizeof( lzss_header_t ); + + while( 1 ) + { + if( !getCmdByte ) + cmdByte = *pInput++; + getCmdByte = ( getCmdByte + 1 ) & 0x07; + + if( cmdByte & 0x01 ) + { + int position = *pInput++ << LZSS_LOOKSHIFT; + int i, count; + byte *pSource; + + position |= ( *pInput >> LZSS_LOOKSHIFT ); + count = ( *pInput++ & 0x0F ) + 1; + + if( count == 1 ) + break; + + pSource = pOutput - position - 1; + for( i = 0; i < count; i++ ) + *pOutput++ = *pSource++; + totalBytes += count; + } + else + { + *pOutput++ = *pInput++; + totalBytes++; + } + cmdByte = cmdByte >> 1; + } + + if( totalBytes != actualSize ) + { + Assert( 0 ); + return 0; + } + return totalBytes; +} + /* ============ COM_FileBase diff --git a/engine/common/common.h b/engine/common/common.h index 64ac808b..e6bc8e18 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -866,7 +866,6 @@ struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); void SV_StartMusic( const char *curtrack, const char *looptrack, long position ); void SV_CreateDecal( struct sizebuf_s *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale ); -void SV_CreateStudioDecal( struct sizebuf_s *bf, const float *v0, const float *v1, int decal, int ent, int model, int flags, struct modelstate_s *s ); void Log_Printf( const char *fmt, ... ); struct sizebuf_s *SV_GetReliableDatagram( void ); void SV_BroadcastCommand( const char *fmt, ... ); @@ -945,6 +944,10 @@ void Cmd_AutoComplete( char *complete_string ); void COM_SetRandomSeed( long lSeed ); long COM_RandomLong( long lMin, long lMax ); float COM_RandomFloat( float fMin, float fMax ); +qboolean LZSS_IsCompressed( const byte *source ); +uint LZSS_GetActualSize( const byte *source ); +byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize ); +uint LZSS_Decompress( const byte *pInput, byte *pOutput ); const byte *GL_TextureData( unsigned int texnum ); void GL_FreeImage( const char *name ); void VID_InitDefaultResolution( void ); diff --git a/engine/common/console.c b/engine/common/console.c index e7245a91..5853f7cc 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -1008,6 +1008,7 @@ void Con_Print( const char *txt ) { static int cr_pending = 0; static char buf[MAX_PRINT_MSG]; + static qboolean inupdate; static int bufpos = 0; int c, mask = 0; @@ -1056,6 +1057,16 @@ void Con_Print( const char *txt ) break; } } + + if( cls.state != ca_disconnected && cls.state < ca_active && !cl.video_prepped && !cls.disable_screen ) + { + if( !inupdate ) + { + inupdate = true; + SCR_UpdateScreen (); + inupdate = false; + } + } } /* diff --git a/engine/common/host.c b/engine/common/host.c index e8ba1560..0d4debad 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -440,17 +440,9 @@ void Host_RestartDecals( void ) decalIndex = pfnDecalIndex( entry->name ); - // BSP and studio decals has different messages - if( entry->flags & FDECAL_STUDIO ) - { - // NOTE: studio decal trace start saved into impactPlaneNormal - SV_CreateStudioDecal( msg, entry->position, entry->impactPlaneNormal, decalIndex, entry->entityIndex, - modelIndex, entry->flags, &entry->studio_state ); - } - else - { + // studiodecals will be restored at game-side + if( !FBitSet( entry->flags, FDECAL_STUDIO )) SV_CreateDecal( msg, entry->position, decalIndex, entry->entityIndex, modelIndex, entry->flags, entry->scale ); - } } Z_Free( host.decalList ); diff --git a/engine/common/model.c b/engine/common/model.c index 70feb23c..a344d47a 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -106,7 +106,8 @@ static void Mod_FreeModel( model_t *mod ) if( !mod || !mod->name[0] ) return; - Mod_FreeUserData( mod ); + if( mod->name[0] != '*' ) + Mod_FreeUserData( mod ); // select the properly unloader switch( mod->type ) @@ -405,6 +406,9 @@ void Mod_PurgeStudioCache( void ) { int i; + // release previois map + Mod_FreeModel( mod_known ); // world is stuck on slot #0 always + // we should release all the world submodels // and clear studio sequences for( i = 1; i < mod_numknown; i++ ) @@ -438,9 +442,6 @@ model_t *Mod_LoadWorld( const char *name, qboolean preload ) // free sequence files on studiomodels Mod_PurgeStudioCache(); - // release previois map - Mod_FreeModel( mod_known ); // world is stuck on slot #0 always - // load the newmap world.loading = true; pworld = Mod_FindName( name, false ); diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 8e7337a1..35661da3 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1105,6 +1105,68 @@ void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to } } +/* +==================== +NET_BufferToBufferCompress + +generic fast compression +==================== +*/ +qboolean NET_BufferToBufferCompress( char *dest, uint *destLen, char *source, uint sourceLen ) +{ + uint uCompressedLen = 0; + byte *pbOut = NULL; + + memcpy( dest, source, sourceLen ); + pbOut = LZSS_Compress( source, sourceLen, &uCompressedLen ); + + if( pbOut && uCompressedLen > 0 && uCompressedLen <= *destLen ) + { + memcpy( dest, pbOut, uCompressedLen ); + *destLen = uCompressedLen; + free( pbOut ); + return true; + } + else + { + if( pbOut ) free( pbOut ); + memcpy( dest, source, sourceLen ); + *destLen = sourceLen; + return false; + } +} + +/* +==================== +NET_BufferToBufferDecompress + +generic fast decompression +==================== +*/ +qboolean NET_BufferToBufferDecompress( char *dest, uint *destLen, char *source, uint sourceLen ) +{ + if( LZSS_IsCompressed( source )) + { + uint uDecompressedLen = LZSS_GetActualSize( source ); + + if( uDecompressedLen <= *destLen ) + { + *destLen = LZSS_Decompress( source, dest ); + } + else + { + return false; + } + } + else + { + memcpy( dest, source, sourceLen ); + *destLen = sourceLen; + } + + return true; +} + /* ==================== NET_IPSocket diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 49e31c59..0ac35c4e 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -51,6 +51,8 @@ qboolean NET_StringToAdr( const char *string, netadr_t *adr ); qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ); qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ); qboolean NET_GetPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ); +qboolean NET_BufferToBufferCompress( char *dest, uint *destLen, char *source, uint sourceLen ); +qboolean NET_BufferToBufferDecompress( char *dest, uint *destLen, char *source, uint sourceLen ); void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ); void NET_ClearLagData( qboolean bClient, qboolean bServer ); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index ca30c10a..bfef640c 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -35,7 +35,7 @@ GNU General Public License for more details. #define svc_updateuserinfo 13 // [byte] playernum, [string] userinfo #define svc_deltatable 14 // [table header][...] #define svc_clientdata 15 // [...] -#define svc_studiodecal 16 // [float*3][float*3][short][short][byte] +#define svc_fileindex 16 // [index][filepath] #define svc_pings 17 // [bit][idx][ping][packet_loss] #define svc_particle 18 // [float*3][char*3][byte][byte] #define svc_restoresound 19 // @@ -125,7 +125,8 @@ GNU General Public License for more details. #define MAX_EDICTS_BYTES ((MAX_EDICTS + 7) / 8) #define LAST_EDICT (MAX_EDICTS - 1) -#define MAX_CUSTOM 1024 // max custom resources per level +#define MAX_CUSTOM_BITS 10 +#define MAX_CUSTOM (1<ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode ); -#ifdef NDEBUG - // no reason to call debugger in release build - just exit - Sys_Quit(); - return EXCEPTION_CONTINUE_EXECUTION; -#endif + + if( !host_developer.value ) + { + // for non-development mode + Sys_Quit(); + return EXCEPTION_CONTINUE_EXECUTION; + } + // all other states keep unchanged to let debugger find bug Con_DestroyConsole(); } diff --git a/engine/server/server.h b/engine/server/server.h index b398520c..81fde643 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -202,7 +202,6 @@ typedef struct server_s // statistics int ignored_static_ents; - int ignored_studio_decals; int ignored_world_decals; int static_ents_overflow; } server_t; diff --git a/engine/server/sv_custom.c b/engine/server/sv_custom.c index 564c4fc0..bc5fb069 100644 --- a/engine/server/sv_custom.c +++ b/engine/server/sv_custom.c @@ -542,7 +542,7 @@ void SV_SendResources( sv_client_t *cl, sizebuf_t *msg ) MSG_WriteLong( msg, svs.spawncount ); MSG_WriteLong( msg, 0 ); - if( sv_downloadurl.string && sv_downloadurl.string[0] && Q_strlen( sv_downloadurl.string ) < 256 ) + if( COM_CheckString( sv_downloadurl.string ) && Q_strlen( sv_downloadurl.string ) < 256 ) { MSG_BeginServerCmd( msg, svc_resourcelocation ); MSG_WriteString( msg, sv_downloadurl.string ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 33167ed9..b7df1ae0 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -490,57 +490,6 @@ void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int en MSG_WriteWord( msg, scale * 4096 ); } -/* -======================= -SV_CreateStudioDecal - -NOTE: static decals only accepted when game is loading -======================= -*/ -void SV_CreateStudioDecal( sizebuf_t *msg, const float *origin, const float *start, int decalIndex, int entityIndex, int modelIndex, int flags, modelstate_t *state ) -{ - int i; - - if( msg == &sv.signon && sv.state != ss_loading ) - return; - - // bad model or bad entity (e.g. changelevel) - if( !entityIndex || !modelIndex ) - return; - - Assert( origin != NULL ); - Assert( start != NULL ); - - // this can happens if serialized map contain 4096 static decals... - if( MSG_GetNumBytesLeft( msg ) < 50 ) - { - sv.ignored_studio_decals++; - return; - } - - // static decals are posters, it's always reliable - MSG_BeginServerCmd( msg, svc_studiodecal ); - MSG_WriteVec3Coord( msg, origin ); - MSG_WriteVec3Coord( msg, start ); - MSG_WriteWord( msg, decalIndex ); - MSG_WriteWord( msg, entityIndex ); - MSG_WriteByte( msg, flags ); - - // write model state - MSG_WriteShort( msg, state->sequence ); - MSG_WriteShort( msg, state->frame ); - MSG_WriteByte( msg, state->blending[0] ); - MSG_WriteByte( msg, state->blending[1] ); - for( i = 0; i < 4; i++ ) - MSG_WriteByte( msg, state->controller[i] ); - for( i = 0; i < 16; i++ ) - MSG_WriteByte( msg, state->poseparam[i] ); - MSG_WriteWord( msg, modelIndex ); - MSG_WriteByte( msg, state->body ); - MSG_WriteByte( msg, state->skin ); - MSG_WriteWord( msg, state->scale ); -} - /* ======================= SV_CreateStaticEntity @@ -3016,13 +2965,13 @@ edict_t *pfnPEntityOfEntIndex( int iEntIndex ) { edict_t *pEdict = EDICT_NUM( iEntIndex ); - if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + if( !iEntIndex || FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) return pEdict; // just get access to array if( SV_IsValidEdict( pEdict ) && pEdict->pvPrivateData ) return pEdict; - // g-cont: clients can be acessed even without private data! + // g-cont: world and clients can be acessed even without private data! if( SV_IsValidEdict( pEdict ) && SV_IsPlayerIndex( iEntIndex )) return pEdict; } diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index bef9fdcd..0b2e80c2 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -195,16 +195,17 @@ int SV_GenericIndex( const char *filename ) return 0; } - if( sv.state != ss_loading ) - { - // g-cont. can we downloading resources in-game ? need testing - Host_Error( "SV_PrecacheGeneric: ( %s ). Precache can only be done in spawn functions.", name ); - return 0; - } - // register new generic resource Q_strncpy( sv.files_precache[i], name, sizeof( sv.files_precache[i] )); + if( sv.state != ss_loading ) + { + // send the update to everyone + MSG_BeginServerCmd( &sv.reliable_datagram, svc_fileindex ); + MSG_WriteUBitLong( &sv.reliable_datagram, i, MAX_CUSTOM_BITS ); + MSG_WriteString( &sv.reliable_datagram, name ); + } + return i; } @@ -542,8 +543,8 @@ void SV_ActivateServer( int runPhysics ) if( sv.ignored_static_ents ) Con_Printf( S_WARN "%i static entities was rejected due buffer overflow\n", sv.ignored_static_ents ); - if( sv.ignored_studio_decals || sv.ignored_world_decals ) - Con_Printf( S_WARN "%i static decals was rejected due buffer overflow\n", sv.ignored_studio_decals + sv.ignored_world_decals ); + if( sv.ignored_world_decals ) + Con_Printf( S_WARN "%i static decals was rejected due buffer overflow\n", sv.ignored_world_decals ); if( svs.maxclients > 1 ) { diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index cfb41a70..a6af383b 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -447,15 +447,14 @@ void ReapplyDecal( SAVERESTOREDATA *pSaveData, decallist_t *entry, qboolean adja if( SV_RestoreCustomDecal( entry, pEdict, adjacent )) return; // decal was sucessfully restored at the game-side + // studio decals are handled at game-side + if( FBitSet( flags, FDECAL_STUDIO )) + return; + // NOTE: at this point all decal indexes is valid decalIndex = pfnDecalIndex( entry->name ); - if( FBitSet( flags, FDECAL_STUDIO )) - { - // NOTE: studio decal trace start saved into impactPlaneNormal - SV_CreateStudioDecal( &sv.signon, entry->position, entry->impactPlaneNormal, decalIndex, entityIndex, modelIndex, flags, &entry->studio_state ); - } - else if( adjacent && entityIndex != 0 && !SV_IsValidEdict( pEdict )) + if( adjacent && entityIndex != 0 && !SV_IsValidEdict( pEdict )) { vec3_t testspot, testend; trace_t tr;