Added more infos to debug out hitboxes

This commit is contained in:
Kamay Xutax 2024-09-03 12:45:53 +02:00
parent ece2f9a81f
commit f4ded28fe0
8 changed files with 283 additions and 53 deletions

View file

@ -149,24 +149,8 @@ const unsigned int FCLIENTANIM_SEQUENCE_CYCLE = 0x00000001;
static CUtlVector< clientanimating_t > g_ClientSideAnimationList; static CUtlVector< clientanimating_t > g_ClientSideAnimationList;
void RecvProxy_Sequence( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
// Have the regular proxy store the data.
RecvProxy_Int32ToInt32( pData, pStruct, pOut );
C_BaseAnimating *pAnimating = (C_BaseAnimating *)pStruct;
if ( !pAnimating )
return;
pAnimating->SetReceivedSequence();
// render bounds may have changed
pAnimating->UpdateVisibility();
}
IMPLEMENT_CLIENTCLASS_DT(C_BaseAnimating, DT_BaseAnimating, CBaseAnimating) IMPLEMENT_CLIENTCLASS_DT(C_BaseAnimating, DT_BaseAnimating, CBaseAnimating)
RecvPropInt(RECVINFO(m_nSequence), 0, RecvProxy_Sequence), RecvPropInt(RECVINFO(m_nSequence)),
RecvPropInt(RECVINFO(m_nForceBone)), RecvPropInt(RECVINFO(m_nForceBone)),
RecvPropVector(RECVINFO(m_vecForce)), RecvPropVector(RECVINFO(m_vecForce)),
RecvPropInt(RECVINFO(m_nSkin)), RecvPropInt(RECVINFO(m_nSkin)),

View file

@ -521,9 +521,12 @@ private:
bool m_bWasFrozen; bool m_bWasFrozen;
int m_flPhysics; int m_flPhysics;
public:
int m_nTickBase; int m_nTickBase;
int m_nFinalPredictedTick; int m_nFinalPredictedTick;
public:
EHANDLE m_pCurrentVguiScreen; EHANDLE m_pCurrentVguiScreen;
bool m_bFiredWeapon; bool m_bFiredWeapon;

View file

@ -16,6 +16,7 @@
#include "interpolatedvar.h" #include "interpolatedvar.h"
#include "mathlib/vector.h" #include "mathlib/vector.h"
#include "shareddefs.h" #include "shareddefs.h"
#include "strtools.h"
#include "studio.h" #include "studio.h"
#include "util_shared.h" #include "util_shared.h"
#include "view.h" #include "view.h"
@ -2145,10 +2146,14 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
if ( player && !player->IsLocalPlayer() ) if ( player && !player->IsLocalPlayer() )
{ {
const auto nAttackerTickBase = event->GetInt( "tickbase" );
const auto numhitboxes = event->GetInt( "num_hitboxes" ); const auto numhitboxes = event->GetInt( "num_hitboxes" );
QAngle angles[MAXSTUDIOBONES]; QAngle angles[MAXSTUDIOBONES];
Vector positions[MAXSTUDIOBONES]; Vector positions[MAXSTUDIOBONES];
Assert( numhitboxes == player->GetModelPtr()->numbones() );
for ( int i = 0; i < numhitboxes; i++ ) for ( int i = 0; i < numhitboxes; i++ )
{ {
char buffer[256]; char buffer[256];
@ -2175,11 +2180,12 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
// Let's see what the client thinks to check if there's any problems with hitboxes // Let's see what the client thinks to check if there's any problems with hitboxes
float flBackupPoseParams[MAXSTUDIOPOSEPARAM]; float flBackupPoseParams[MAXSTUDIOPOSEPARAM];
float flBackupBoneControllers[MAXSTUDIOBONECTRLS]; float flBackupBoneControllers[MAXSTUDIOBONECTRLS];
C_AnimationLayer backupAnimLayers[C_BaseAnimatingOverlay::MAX_OVERLAYS]; C_AnimationLayer backupAnimLayers[C_BaseAnimatingOverlay::MAX_OVERLAYS];
Vector vecBackupPosition = player->GetAbsOrigin(); Vector vecBackupPosition = player->GetAbsOrigin();
QAngle angBackupAngles = player->GetRenderAngles(); QAngle angBackupAngles = player->GetRenderAngles();
auto flOldCycle = GetCycle(); auto flOldCycle = player->GetCycle();
auto iOldSequence = GetSequence(); auto iOldSequence = player->GetSequence();
for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ ) for ( int i = 0; i < MAXSTUDIOPOSEPARAM; i++ )
{ {
@ -2191,13 +2197,13 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
flBackupBoneControllers[i] = player->m_flEncodedController[i]; flBackupBoneControllers[i] = player->m_flEncodedController[i];
} }
for ( int i = 0; i < GetNumAnimOverlays(); i++ ) for ( int i = 0; i < player->GetNumAnimOverlays(); i++ )
{ {
backupAnimLayers[i] = *player->GetAnimOverlay(i); backupAnimLayers[i] = *player->GetAnimOverlay(i);
} }
player->SetSequence( event->GetInt( "sequence" ) ); player->m_nSequence = event->GetInt( "sequence" );
player->SetCycle( event->GetFloat( "cycle" ) ); player->m_flCycle = event->GetFloat( "cycle" );
player->SetAbsOrigin( Vector( event->GetFloat( "position_x" ), player->SetAbsOrigin( Vector( event->GetFloat( "position_x" ),
event->GetFloat( "position_y" ), event->GetFloat( "position_y" ),
event->GetFloat( "position_z" ) ) ); event->GetFloat( "position_z" ) ) );
@ -2207,7 +2213,7 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
event->GetFloat( "angle_z" ) ); event->GetFloat( "angle_z" ) );
const auto numposeparams = event->GetInt( "num_poseparams" ); const auto numposeparams = event->GetInt( "num_poseparams" );
Assert( numposeparams == pStudioHdr->GetNumPoseParameters() ); Assert( numposeparams == player->GetModelPtr()->GetNumPoseParameters() );
for ( int i = 0; i < numposeparams; i++ ) for ( int i = 0; i < numposeparams; i++ )
{ {
@ -2218,7 +2224,7 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
} }
const auto numbonecontrollers = event->GetInt( "num_bonecontrollers" ); const auto numbonecontrollers = event->GetInt( "num_bonecontrollers" );
Assert( numbonecontrollers == pStudioHdr->GetNumBoneControllers() ); Assert( numbonecontrollers == player->GetModelPtr()->GetNumBoneControllers() );
for ( int i = 0; i < numbonecontrollers; i++ ) for ( int i = 0; i < numbonecontrollers; i++ )
{ {
@ -2252,17 +2258,168 @@ void C_CSPlayer::FireGameEvent( IGameEvent* event )
animOverlay->m_fFlags = event->GetInt( buffer ); animOverlay->m_fFlags = event->GetInt( buffer );
} }
player->PushAllowBoneAccess( true, false, "Lag compensation context" ); // Let's see if anything wrong has happened, print some infos.
HitboxRecord* pRecord = nullptr;
for ( int i = 0; i < MAX_HISTORY_HITBOX_RECORDS; i++ )
{
pRecord = m_HitboxTrack[player->index].Get( i );
if ( pRecord && ( pRecord->m_nAttackerTickBase == nAttackerTickBase ) )
{
break;
}
}
if ( pRecord )
{
// Let's check what went wrong.
int pos = 0;
auto newOrigin = player->GetAbsOrigin();
if ( pRecord->m_vecAbsOrigin != newOrigin )
{
char buffer[256];
V_sprintf_safe( buffer,
"pos: %f != %f, %f != %f, %f != %f",
newOrigin.x,
pRecord->m_vecAbsOrigin.x,
newOrigin.y,
pRecord->m_vecAbsOrigin.y,
newOrigin.z,
pRecord->m_vecAbsOrigin.z );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
if ( pRecord->m_angRenderAngles != player->m_angRenderAngles )
{
char buffer[256];
V_sprintf_safe( buffer,
"angles: %f != %f, %f != %f, %f != %f",
player->m_angRenderAngles.x,
pRecord->m_angRenderAngles.x,
player->m_angRenderAngles.y,
pRecord->m_angRenderAngles.y,
player->m_angRenderAngles.z,
pRecord->m_angRenderAngles.z );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
if ( pRecord->m_flCycle != player->m_flCycle )
{
char buffer[256];
V_sprintf_safe( buffer, "cycle: %f != %f", player->m_flCycle, pRecord->m_flCycle );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
if ( pRecord->m_nSequence != player->m_nSequence )
{
char buffer[256];
V_sprintf_safe( buffer,
"sequence: %s(%i) != %s(%i)",
player->GetSequenceName( player->m_nSequence ),
player->m_nSequence,
player->GetSequenceName( pRecord->m_nSequence ),
pRecord->m_nSequence );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
auto mdl = player->GetModelPtr();
for ( int i = 0; i < mdl->GetNumPoseParameters(); i++ )
{
if ( pRecord->m_flPoseParameters[i] != player->m_flPoseParameter[i] )
{
char buffer[256];
V_sprintf_safe( buffer,
"pose parameter %s (%i): %f != %f",
mdl->pPoseParameter( i ).pszName(),
i,
player->m_flPoseParameter[i],
pRecord->m_flPoseParameters[i] );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
}
for ( int i = 0; i < mdl->GetNumBoneControllers(); i++ )
{
if ( pRecord->m_flEncodedControllers[i] != player->m_flEncodedController[i] )
{
char buffer[256];
V_sprintf_safe( buffer,
"bone controller %i (%i): %f != %f",
mdl->pBonecontroller( i )->bone,
i,
player->m_flEncodedController[i],
pRecord->m_flEncodedControllers[i] );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
}
for ( int i = 0; i < player->GetNumAnimOverlays(); i++ )
{
auto animOverlay = player->GetAnimOverlay( i );
if ( animOverlay->m_flCycle != pRecord->m_AnimationLayer[i].m_flCycle
|| animOverlay->m_flWeight != pRecord->m_AnimationLayer[i].m_flWeight
|| animOverlay->m_nSequence != pRecord->m_AnimationLayer[i].m_nSequence
|| animOverlay->m_nOrder != pRecord->m_AnimationLayer[i].m_nOrder
|| animOverlay->m_fFlags != pRecord->m_AnimationLayer[i].m_fFlags )
{
char buffer[256];
V_sprintf_safe( buffer,
"anim overlay %i: sequence: %s != %s ( %i != %i ), cycle: %f != %f, "
"weight: %f != %f, order %i != %i, flags: %i != %i",
i,
GetSequenceName( animOverlay->m_nSequence ),
GetSequenceName( pRecord->m_AnimationLayer[i].m_nSequence ),
animOverlay->m_nSequence,
pRecord->m_AnimationLayer[i].m_nSequence,
animOverlay->m_flCycle,
pRecord->m_AnimationLayer[i].m_flCycle,
animOverlay->m_flWeight,
pRecord->m_AnimationLayer[i].m_flWeight,
animOverlay->m_nOrder,
pRecord->m_AnimationLayer[i].m_nOrder,
animOverlay->m_fFlags,
pRecord->m_AnimationLayer[i].m_fFlags );
NDebugOverlay::EntityTextAtPosition( pRecord->m_vecAbsOrigin, pos, buffer, flDuration );
pos++;
}
}
}
else
{
DevMsg( "Could not get record info for player %s (%i) with tickbase %i\n",
player->GetPlayerName(),
player->index,
nAttackerTickBase );
}
player->PushAllowBoneAccess( true, false, "Lag compensation context" );
// Be sure we setup the bones again. // Be sure we setup the bones again.
player->InvalidateBoneCache(); player->InvalidateBoneCache();
player->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime ); player->SetupBones( NULL, -1, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
player->DrawClientHitboxes( cl_debug_duration.GetFloat(), false ); player->DrawClientHitboxes( flDuration, false );
player->PopBoneAccess( "Lag compensation context" ); player->PopBoneAccess( "Lag compensation context" );
// Set back original stuff. // Set back original stuff.
player->SetSequence( iOldSequence ); player->m_nSequence = iOldSequence;
player->SetCycle( flOldCycle ); player->m_flCycle = flOldCycle;
player->SetAbsOrigin( vecBackupPosition ); player->SetAbsOrigin( vecBackupPosition );
player->m_angRenderAngles = angBackupAngles; player->m_angRenderAngles = angBackupAngles;

View file

@ -10,7 +10,6 @@
#pragma once #pragma once
#endif #endif
#include "cs_playeranimstate.h" #include "cs_playeranimstate.h"
#include "c_baseplayer.h" #include "c_baseplayer.h"
#include "cs_shareddefs.h" #include "cs_shareddefs.h"
@ -31,8 +30,6 @@ public:
int m_iAttachmentPoint; // Which attachment point on the player model this guy is on. int m_iAttachmentPoint; // Which attachment point on the player model this guy is on.
}; };
class C_CSPlayer : public C_BasePlayer, public ICSPlayerAnimStateHelpers class C_CSPlayer : public C_BasePlayer, public ICSPlayerAnimStateHelpers
{ {
public: public:
@ -328,8 +325,10 @@ private:
bool m_bHasHelmet; bool m_bHasHelmet;
int m_iClass; int m_iClass;
int m_ArmorValue; int m_ArmorValue;
public:
QAngle m_angEyeAngles; QAngle m_angEyeAngles;
QAngle m_angRenderAngles; QAngle m_angRenderAngles;
private:
bool m_bHasDefuser; bool m_bHasDefuser;
float m_fNextThinkPushAway; float m_fNextThinkPushAway;
@ -394,6 +393,35 @@ private:
float duration); float duration);
C_CSPlayer( const C_CSPlayer& ); C_CSPlayer( const C_CSPlayer& );
public:
// Around 256 bullets, should be way enough to debug out what happned...
static constexpr auto MAX_HISTORY_HITBOX_RECORDS = 256;
struct AnimLayerRecord
{
int m_nSequence;
float m_flCycle;
float m_flWeight;
int m_nOrder;
int m_fFlags;
};
struct HitboxRecord
{
int m_nAttackerTickBase;
Vector m_vecAbsOrigin;
QAngle m_angRenderAngles;
float m_flSimulationTime;
int m_nSequence;
float m_flCycle;
float m_flAnimTime;
AnimLayerRecord m_AnimationLayer[MAX_LAYER_RECORDS];
float m_flPoseParameters[MAXSTUDIOPOSEPARAM];
float m_flEncodedControllers[MAXSTUDIOBONECTRLS];
};
CUtlCircularBuffer< HitboxRecord, MAX_HISTORY_HITBOX_RECORDS > m_HitboxTrack[MAX_PLAYERS + 1];
}; };
C_CSPlayer* GetLocalOrInEyeCSPlayer( void ); C_CSPlayer* GetLocalOrInEyeCSPlayer( void );

View file

@ -683,8 +683,9 @@ void CBasePlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs,
int CBasePlayer::UpdateTransmitState() int CBasePlayer::UpdateTransmitState()
{ {
// TODO_ENHANCED: this fucks up animations.
// always call ShouldTransmit() for players // always call ShouldTransmit() for players
return SetTransmitState( FL_EDICT_FULLCHECK ); return SetTransmitState( FL_EDICT_ALWAYS );
} }
int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo ) int CBasePlayer::ShouldTransmit( const CCheckTransmitInfo *pInfo )

View file

@ -159,7 +159,7 @@ struct LagRecord
// If it can't get there, leave the player where he is. // If it can't get there, leave the player where he is.
// //
ConVar sv_unlag_debug( "sv_unlag_debug", "0", FCVAR_GAMEDLL | FCVAR_DEVELOPMENTONLY ); ConVar sv_unlag_debug( "sv_unlag_debug", "0" );
float g_flFractionScale = 0.95; float g_flFractionScale = 0.95;
@ -426,6 +426,8 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* c
// get track history of this player // get track history of this player
auto track = &m_EntityTrack[pl_index]; auto track = &m_EntityTrack[pl_index];
bool foundSim = false;
bool foundAnim = false;
for ( int i = 0; i < MAX_TICKS_SAVED; i++ ) for ( int i = 0; i < MAX_TICKS_SAVED; i++ )
{ {
@ -443,11 +445,13 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* c
if ( flTargetSimTime == recordSim->m_flSimulationTime ) if ( flTargetSimTime == recordSim->m_flSimulationTime )
{ {
foundSim = true;
break; break;
} }
if ( recordSim->m_flSimulationTime < flTargetSimTime ) if ( recordSim->m_flSimulationTime < flTargetSimTime )
{ {
foundSim = true;
prevRecordSim = track->Get( i - 1 ); prevRecordSim = track->Get( i - 1 );
break; break;
} }
@ -464,6 +468,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* c
if ( recordAnim->m_flAnimTime == flTargetAnimTime ) if ( recordAnim->m_flAnimTime == flTargetAnimTime )
{ {
foundAnim = true;
break; break;
} }
} }
@ -471,7 +476,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* c
Assert( recordAnim ); Assert( recordAnim );
Assert( recordSim ); Assert( recordSim );
if ( !recordSim || !recordAnim ) if ( !foundAnim || !foundSim )
{ {
if ( sv_unlag_debug.GetBool() ) if ( sv_unlag_debug.GetBool() )
{ {
@ -489,7 +494,7 @@ void CLagCompensationManager::BacktrackPlayer( CBasePlayer* pPlayer, CUserCmd* c
// so interpolate between these two records; // so interpolate between these two records;
Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime ); Assert( prevRecordSim->m_flSimulationTime > recordSim->m_flSimulationTime );
Assert( flTargetLerpSimTime < prevRecordSim->m_flSimulationTime ); Assert( flTargetSimTime < prevRecordSim->m_flSimulationTime );
// calc fraction between both records // calc fraction between both records
fracSim = float( ( double( flTargetSimTime ) - double( recordSim->m_flSimulationTime ) ) fracSim = float( ( double( flTargetSimTime ) - double( recordSim->m_flSimulationTime ) )

View file

@ -7,6 +7,7 @@
#include "cbase.h" #include "cbase.h"
#include "const.h" #include "const.h"
#include "debugoverlay_shared.h" #include "debugoverlay_shared.h"
#include "shareddefs.h"
#include "strtools.h" #include "strtools.h"
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
#include "player.h" #include "player.h"
@ -526,16 +527,16 @@ void CCSPlayer::FireBullet(
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
auto WritePlayerHitboxEvent = [&]( CBasePlayer* lagPlayer ) auto WritePlayerHitboxEvent = [&]( CBasePlayer* lagPlayer )
{ {
if ( IsBot() )
{
return;
}
IGameEvent* event = gameeventmanager->CreateEvent( "bullet_player_hitboxes" ); IGameEvent* event = gameeventmanager->CreateEvent( "bullet_player_hitboxes" );
if ( event ) if ( event )
{ {
event->SetInt( "userid", GetUserID() ); event->SetInt( "userid", GetUserID() );
event->SetInt( "player_index", lagPlayer->entindex() ); event->SetInt( "player_index", lagPlayer->entindex() );
event->SetInt( "tickbase", TIME_TO_TICKS( GetTimeBase() ) );
event->SetInt( "bullet", iBullet );
event->SetFloat( "simtime", lagPlayer->GetSimulationTime() );
event->SetFloat( "animtime", lagPlayer->GetAnimTime() );
Vector positions[MAXSTUDIOBONES]; Vector positions[MAXSTUDIOBONES];
QAngle angles[MAXSTUDIOBONES]; QAngle angles[MAXSTUDIOBONES];
@ -718,6 +719,8 @@ void CCSPlayer::FireBullet(
event->SetFloat( "dst_y", tr.endpos.y ); event->SetFloat( "dst_y", tr.endpos.y );
event->SetFloat( "dst_z", tr.endpos.z ); event->SetFloat( "dst_z", tr.endpos.z );
event->SetFloat( "radius", flBulletRadius ); event->SetFloat( "radius", flBulletRadius );
event->SetInt( "tickbase", TIME_TO_TICKS( GetTimeBase() ) );
event->SetInt( "bullet", iBullet );
gameeventmanager->FireEvent( event ); gameeventmanager->FireEvent( event );
} }
#endif #endif

View file

@ -9,6 +9,7 @@
#include "convar.h" #include "convar.h"
#include "mathlib/vector.h" #include "mathlib/vector.h"
#include "usercmd.h" #include "usercmd.h"
#include "util_shared.h"
#include "weapon_csbase.h" #include "weapon_csbase.h"
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
@ -322,6 +323,54 @@ void FX_FireBullets(
y1[iBullet] = fRadius1 * sinf(fTheta1); y1[iBullet] = fRadius1 * sinf(fTheta1);
} }
#ifdef CLIENT_DLL
static ConVarRef cl_showfirebullethitboxes("cl_showfirebullethitboxes");
static ConVarRef cl_showimpacts( "cl_showimpacts" );
if ( playerCmd && !playerCmd->hasbeenpredicted && ( cl_showfirebullethitboxes.GetBool() || cl_showimpacts.GetBool() ) )
{
for ( int i = 1; i <= gpGlobals->maxClients; i++ )
{
auto lagPlayer = ( C_CSPlayer* )UTIL_PlayerByIndex( i );
if ( !lagPlayer )
{
continue;
}
C_CSPlayer::HitboxRecord record;
record.m_vecAbsOrigin = lagPlayer->GetRenderOrigin();
record.m_angRenderAngles = lagPlayer->m_angRenderAngles;
record.m_nAttackerTickBase = pPlayer->m_nTickBase;
record.m_flSimulationTime = lagPlayer->GetSimulationTime();
record.m_flAnimTime = lagPlayer->GetAnimTime();
record.m_flCycle = lagPlayer->GetCycle();
record.m_nSequence = lagPlayer->GetSequence();
lagPlayer->GetPoseParameters( lagPlayer->GetModelPtr(), record.m_flPoseParameters );
lagPlayer->GetBoneControllers( record.m_flEncodedControllers );
for ( int i = 0; i < lagPlayer->GetNumAnimOverlays(); i++ )
{
CAnimationLayer* layer = lagPlayer->GetAnimOverlay( i );
if ( layer )
{
record.m_AnimationLayer[i].m_flCycle = layer->m_flCycle;
record.m_AnimationLayer[i].m_nOrder = layer->m_nOrder;
record.m_AnimationLayer[i].m_nSequence = layer->m_nSequence;
record.m_AnimationLayer[i].m_flWeight = layer->m_flWeight;
record.m_AnimationLayer[i].m_fFlags = layer->m_fFlags;
}
}
pPlayer->m_HitboxTrack[lagPlayer->index].Push( record );
}
}
#endif
for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ ) for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
{ {
#ifdef CLIENT_DLL #ifdef CLIENT_DLL