[Hypovolemia] Help With Prediction

Grab your favourite IDE and tinker with the innards of game engines

[Hypovolemia] Help With Prediction

Postby IBeMad on Sun Jan 22, 2012 9:18 pm

Hi all,

This is IBeMad from the Hypovolemia Mod Team (http://hypovolemia-mod.net). As a programmer, I'm currently tasked with fixing some of the glitches with the shotguns when there is lag. For some reason, it appears as if they're not being predicted properly. We're using the Alien Swarm SDK.

Our CPP file from the spas12
Code: Select all
#include "cbase.h"
#include "hyp_weapon_SPAS12.h"
#include "in_buttons.h"

#ifdef CLIENT_DLL
#include "c_asw_player.h"
#include "c_asw_weapon.h"
#include "c_asw_marine.h"
#include "c_asw_marine_resource.h"
#include "prediction.h"
#define CASW_Marine C_ASW_Marine
#define CASW_Marine_Resource C_ASW_Marine_Resource
#else
#include "asw_marine.h"
#include "asw_player.h"
#include "asw_weapon.h"
#include "npcevent.h"
#include "shot_manipulator.h"
#include "asw_grenade_prifle.h"
#include "asw_fail_advice.h"
#include "effect_dispatch_data.h"
#include "asw_marine_resource.h"
#include "te_effect_dispatch.h"
#endif
#include "asw_marine_skills.h"
#include "asw_weapon_parse.h"
#include "particle_parse.h"
#include "shot_manipulator.h"
#include "asw_weapon_ammo_bag_shared.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

acttable_t   CHYP_Weapon_SPAS12::m_acttable[] =
{
   { ACT_MP_STAND_IDLE,            ACT_MP_STAND_AIM_MG,            false },
   { ACT_MP_STAND_AIM_IDLE,         ACT_MP_STAND_AIM_BAR,            false },
   { ACT_MP_CROUCH_IDLE,            ACT_MP_CROUCH_AIM_BAR,            false },
   { ACT_MP_SPRINT,               ACT_MP_SPRINT_IDLE_BAR,            false },
   { ACT_MP_RUN,                  ACT_MP_RUN_AIM_MG,               false },
   { ACT_MP_WALK,                  ACT_MP_WALK_AIM_BAR,            false },
   { ACT_MP_CROUCHWALK,            ACT_MP_CROUCHWALK_AIM_BAR,         false },
   { ACT_MP_JUMP,                  ACT_MP_JUMP,                   false },
   { ACT_MP_ATTACK_STAND_PRIMARYFIRE,   ACT_MP_PRIMARYATTACK_BAR,         false },
   { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE,   ACT_MP_PRIMARYATTACK_BAR,         false },
   { ACT_MP_RELOAD_STAND,            ACT_MP_RELOAD_BAR,                 false },
   { ACT_MP_RELOAD_CROUCH,            ACT_MP_RELOAD_CROUCH_BAR,         false },
};

IMPLEMENT_ACTTABLE(CHYP_Weapon_SPAS12);

IMPLEMENT_NETWORKCLASS_ALIASED( HYP_Weapon_SPAS12, DT_HYP_Weapon_SPAS12 )

   BEGIN_NETWORK_TABLE( CHYP_Weapon_SPAS12, DT_HYP_Weapon_SPAS12 )
#ifdef CLIENT_DLL
   // recvprops
#else
   // sendprops
#endif
   END_NETWORK_TABLE()

   BEGIN_PREDICTION_DATA( CHYP_Weapon_SPAS12 )

   END_PREDICTION_DATA()

   LINK_ENTITY_TO_CLASS( hyp_weapon_SPAS12, CHYP_Weapon_SPAS12 );
PRECACHE_WEAPON_REGISTER(hyp_weapon_SPAS12);

#ifndef CLIENT_DLL
extern ConVar asw_debug_marine_damage;
extern ConVar asw_DebugAutoAim;
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CHYP_Weapon_SPAS12 )

   END_DATADESC()

#endif /* not client */

   extern ConVar asw_weapon_max_shooting_distance;
extern ConVar asw_weapon_force_scale;

CHYP_Weapon_SPAS12::CHYP_Weapon_SPAS12()
{
   m_iChamberState = HYP_CHAMBER_EMPTY;
}


CHYP_Weapon_SPAS12::~CHYP_Weapon_SPAS12()
{

}

void CHYP_Weapon_SPAS12::Precache()
{
   // precache the weapon model here?   
   PrecacheScriptSound("ASW_Weapon_PRifle.StunGrenadeExplosion");   
   PrecacheScriptSound("ASW_PRifle.ReloadA");
   PrecacheScriptSound("ASW_PRifle.ReloadB");
   PrecacheScriptSound("ASW_PRifle.ReloadC");
   PrecacheModel( "swarm/effects/electrostunbeam.vmt" );
   PrecacheModel( "swarm/effects/bluemuzzle_nocull.vmt" );
   PrecacheModel( "effects/bluelaser2.vmt" );

   BaseClass::Precache();
}


float CHYP_Weapon_SPAS12::GetWeaponDamage()
{
   return ((m_iActionType == HYP_SEMI_AUTO) ? GetWeaponInfo()->m_fDamageSecondaryBase : GetWeaponInfo()->m_flBaseDamage);
}

//------------------------------------
// Determining what buttons are pressed and based on those results do the correct corresponding actions (primaryattack, action, reload)
//------------------------------------
void CHYP_Weapon_SPAS12::ItemPostFrame()
{
   // New owner instead of player
   CASW_Marine* pOwner = GetMarine();
   if (!pOwner)
      return;

   bool bThisActive = ( pOwner && pOwner->GetActiveWeapon() == this );

   bool bAttack1, bAttack2, bReload, bOldReload, bOldAttack1;
   GetButtons(bAttack1, bAttack2, bReload, bOldReload, bOldAttack1 );
   GetButtonsOverride(bAttack1, bAttack2, bReload, bOldReload, bOldAttack1 );

   // check for clearing our weapon switching bool
   if (m_bSwitchingWeapons && gpGlobals->curtime > m_flNextPrimaryAttack)
      m_bSwitchingWeapons = false;

   // check for clearing our firing bool from reloading
   if (m_bInReload && gpGlobals->curtime > m_fReloadClearFiringTime)
      ClearIsFiring();

   // delayed shot on firing weapon?
   if ( m_bShotDelayed && gpGlobals->curtime > m_flDelayedFire )
      DelayedAttack();

   // some reload checking going on?
   if ( UsesClipsForAmmo1() )
      CheckReload();

   bool bFired = false;
   if ( bThisActive )
   {
      //Track the duration of the fire
      //FIXME: What if we're calling ItemBusyFrame?
      m_fFireDuration = (bAttack1 ? ( m_fFireDuration + gpGlobals->frametime ) : 0.0f);

      if (bAttack2 && (m_flNextSecondaryAttack <= gpGlobals->curtime) && gpGlobals->curtime > pOwner->m_fFFGuardTime)
      {
         if ( SecondaryAttackUsesPrimaryAmmo() )
         {
            if ( !IsMeleeWeapon() && 
               (( UsesClipsForAmmo1() && !(this->PrimaryAmmoLoaded())) || ( !UsesClipsForAmmo1() && pOwner->GetAmmoCount(m_iPrimaryAmmoType)<=0 )) )
            {
               HandleFireOnEmpty();
            }
            else
            {
               bFired = true;
               SecondaryAttack();
            }
         }
         else if ( UsesSecondaryAmmo() && 
            ( ( UsesClipsForAmmo2() && m_iClip2 <= 0 ) ||
            ( !UsesClipsForAmmo2() && pOwner->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) ) )
         {
            if ( m_flNextEmptySoundTime < gpGlobals->curtime )
            {
               if (!IsSecondaryAttackDisabled())
                  WeaponSound( EMPTY );
               m_flNextSecondaryAttack = m_flNextEmptySoundTime = gpGlobals->curtime + 0.5;
            }
         }      
         else
         {
            bFired = true;
            SecondaryAttack();
         }
      }

      if (m_iActionType == HYP_SEMI_AUTO)
      {
         if (m_bAutoEmptyChamber && m_fAutoEmptyChamber <= gpGlobals->curtime)
         {
            m_bAutoEmptyChamber = false;
            m_iChamberState     = HYP_CHAMBER_OPEN;         // no shell [empty or loaded]
         }
         else if (m_bAutoRechamber && m_flNextPrimaryAttack <= gpGlobals->curtime && m_iChamberState != HYP_CHAMBER_LOADED)
         {
            m_bAutoRechamber = false;
            if (m_iClip1 > 0)
            {
               m_iChamberState = HYP_CHAMBER_LOADED;
#ifdef CLIENT_DLL
                  m_iClip1--;
                  DevWarning("[CLIENT] Predicting ammo %i.\n", m_iClip1);
#else
               m_iClip1--;
               DevWarning("[SERVER] Ammo currently %i.\n", m_iClip1);
#endif
            }
            else
            {
               m_iChamberState = HYP_CHAMBER_EMPTY;
            }
         }
      }
      if ( !bFired && !bOldAttack1 && bAttack1 && (m_flNextPrimaryAttack <= gpGlobals->curtime) && (m_fNextAction <= gpGlobals->curtime) && gpGlobals->curtime > pOwner->m_fFFGuardTime)
      {
         if ( m_iChamberState != HYP_CHAMBER_LOADED )
         {
            HandleFireOnEmpty();
         }
         else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
         {
            // This weapon doesn't fire underwater
            WeaponSound(EMPTY);
            m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
            return;
         }
         else if (m_iChamberState == HYP_CHAMBER_LOADED)
         {
            //NOTENOTE: There is a bug with this code with regards to the way machine guns catch the leading edge trigger
            //         on the player hitting the attack key.  It relies on the gun catching that case in the same frame.
            //         However, because the player can also be doing a secondary attack, the edge trigger may be missed.
            //         We really need to hold onto the edge trigger and only clear the condition when the gun has fired its
            //         first shot.  Right now that's too much of an architecture change -- jdw
            PrimaryAttack_SPAS();
            m_flNextPrimaryAttack = gpGlobals->curtime + GetWeaponInfo()->m_flFireRate;
         }
      }
   }

   if (!bAttack1)   // clear our firing var if we don't have the attack button held down (not totally accurate since firing could continue for some time after pulling the trigger, but it's good enough for our purposes)
   {
      m_bIsFiring = false;      // NOTE: Only want to clear primary fire IsFiring bool here (i.e. don't call ClearIsFiring as that'll clear secondary fire too, in subclasses that have it)
      if ( bOldAttack1 )
      {
         OnStoppedFiring();
      }
   }

   // -----------------------
   //  Reload pressed / Clip Empty
   // -----------------------
   if ( bReload && UsesClipsForAmmo1())   
   {
      if ( m_bInReload )
      {
         // todo: check for a fast reload
         //Msg("Check for fast reload\n");
      }
      else
      {
         // reload when reload is pressed, or if no buttons are down and weapon is empty.
         Reload();
         m_fFireDuration = 0.0f;
      }
   }

   // -----------------------
   //  Action buttons down
   // -----------------------
   // the variables
   bool bOldAction = false, bCurrentAction = false;

   // get the button states
   if (pOwner->IsInhabited() && pOwner->GetCommander())
   {
      bCurrentAction = !!(pOwner->GetCommander()->m_nButtons & IN_ACTION);
      bOldAction = !!(pOwner->m_nOldButtons & IN_ACTION);
   }

   // command the action if button is just pressed
   if (((!bOldAction && bCurrentAction) || m_bIsTargetActionLoading) && m_fNextAction < gpGlobals->curtime)
      DoActionMechanism();

   // -----------------------
   //  No buttons down
   // -----------------------
   if (!(bAttack1 || bAttack2 || bReload || bCurrentAction))
   {
      // no fire buttons down or reloading
      if ( !ReloadOrSwitchWeapons() && ( m_bInReload == false ) )
      {
         WeaponIdle();
      }
   }
}

void CHYP_Weapon_SPAS12::PrimaryAttack_SPAS()
{
   CASW_Player *pPlayer = GetCommander();
   CASW_Marine *pMarine = GetMarine();
   if ( !pMarine )
      return;

   if (m_iChamberState != HYP_CHAMBER_LOADED)
      return;

   m_bIsFiring = true;
   // MUST call sound before removing a round from the clip of a CMachineGun
   WeaponSound(SINGLE);

   // tell the marine to tell its weapon to draw the muzzle flash
   pMarine->DoMuzzleFlash();

   // sets the animation on the weapon model itself
   SendWeaponAnim( GetPrimaryAttackActivity() );

#ifdef GAME_DLL   // check for turning on lag compensation
   if (pPlayer && pMarine->IsInhabited())
   {
      CASW_Lag_Compensation::RequestLagCompensation( pPlayer, pPlayer->GetCurrentUserCommand() );
   }
#endif

   FireBulletsInfo_t info;
   info.m_vecSrc = pMarine->Weapon_ShootPosition( );
   if ( pPlayer && pMarine->IsInhabited() )
   {
      info.m_vecDirShooting = pPlayer->GetAutoaimVectorForMarine(pMarine, GetAutoAimAmount(), GetVerticalAdjustOnlyAutoAimAmount());   // 45 degrees = 0.707106781187
   }
   else
   {
#ifdef CLIENT_DLL
      Msg("Error, clientside firing of a weapon that's being controlled by an AI marine\n");
#else
      info.m_vecDirShooting = pMarine->GetActualShootTrajectory( info.m_vecSrc );
#endif
   }

   info.m_flDistance = asw_weapon_max_shooting_distance.GetFloat();
   info.m_iAmmoType = m_iPrimaryAmmoType;
   info.m_iTracerFreq = 1;  // asw tracer test everytime
   info.m_flDamageForceScale = asw_weapon_force_scale.GetFloat();

   // determine the new spread
   info.m_iShots = GetWeaponInfo()->m_iQuantityPerShot;
   info.m_vecSpread = pMarine->GetActiveWeapon()->GetBulletSpread();

   CShotManipulator Manipulator( info.m_vecDirShooting );
   Vector vecDir;
   vecDir = Manipulator.ApplySpread( pMarine->GetActiveWeapon()->GetBulletSpread() );
   info.m_vecDirShooting = vecDir;

#ifdef GAME_DLL
   switch(pPlayer ? pPlayer->m_iDebugMovementState : -1)
#else
   switch(pPlayer ? pPlayer->GetDebugMovementState() : -1)
#endif      
   {
   case 0: //Standing
      info.m_vecSpread = ConvertSpread(GetWeaponInfo()->m_fFixedSpread_Hipshoot);
      break;
   case 1: // jumping
      info.m_vecSpread = ConvertSpread(GetWeaponInfo()->m_fFixedSpread_Jumping);
      break;
   case 2: // running Backwards
   case 3: // running
      info.m_vecSpread = ConvertSpread(GetWeaponInfo()->m_fFixedSpread_Hipshoot);
      break;
   case 4: // walking
   case 5: // croutching
      info.m_vecSpread = ConvertSpread(GetWeaponInfo()->m_fFixedSpread_Aiming);
      break;
   }

   info.m_flDamage = GetWeaponDamage();
   pMarine->FireBullets( info );

   // increment shooting stats
#ifndef CLIENT_DLL
   if (pMarine && pMarine->GetMarineResource())
   {
      pMarine->GetMarineResource()->UsedWeapon(this, info.m_iShots);
      pMarine->OnWeaponFired( this, info.m_iShots );
   }
#endif

   //#ifndef CLIENT_DLL
   pPlayer->ApplyRecoilSpread();
   //#endif

   if (m_iActionType == HYP_SEMI_AUTO)
   {
      m_fAutoEmptyChamber =  gpGlobals->curtime + GetWeaponInfo()->m_flFireRate/2;
      m_bAutoEmptyChamber = true;
      m_bAutoRechamber = true;
   }

   m_iChamberState = HYP_CHAMBER_SPENT;

#ifdef CLIENT_DLL
   SetViewRecoil();
#endif
}

void CHYP_Weapon_SPAS12::FinishReload( void )
{
   CASW_Marine *pOwner = GetMarine();

   if (pOwner)
   {
      // primary ammo reload
      m_iClip1++;
      pOwner->SetAmmoCount(pOwner->GetAmmoCount(m_iPrimaryAmmoType)-1, m_iPrimaryAmmoType);//;m_iAmmo[m_iPrimaryAmmoType]

      if ( m_bReloadsSingly )
      {
         m_bInReload = false;
      }

#ifdef GAME_DLL
      if ( !m_bFastReloadSuccess )
      {
         pOwner->m_nFastReloadsInARow = 0;
      }
#endif
      m_bFastReloadSuccess = false;
      m_bFastReloadFailure = false;
   }
}
#ifdef CLIENT_DLL
const char* CHYP_Weapon_SPAS12::GetPartialReloadSound(int iPart)
{
   switch (iPart)
   {
   case 1: return "ASW_PRifle.ReloadB"; break;
   case 2: return "ASW_PRifle.ReloadC"; break;
   default: break;
   };
   return "ASW_PRifle.ReloadA";
}

void CHYP_Weapon_SPAS12::ASWReloadSound(int iType)
{
   if (iType == 0)
      WeaponSound(SPECIAL2);
}

const char* CHYP_Weapon_SPAS12::GetMuzzleEffectName()
{
   // get muzzle
   float iState = RandomFloat();

   if (iState <= MUZZLE_DEFORM_PERCENT)
      return    MUZZLE_DEFORM_FXNAME;   //10% chance of getting here
   else if (iState <= MUZZLE_BRIGHT_PERCENT)
      return    MUZZLE_BRIGHT_FXNAME;   //30% chance of getting here
   else
      return MUZZLE_WEAK_FXNAME;
}

const char* CHYP_Weapon_SPAS12::GetTracerEffectName()
{
   return TRACER_FXNAME;
}
#endif


And our h file...

Code: Select all
#ifndef _INCLUDED_HYP_WEAPON_SPAS12_H
#define _INCLUDED_HYP_WEAPON_SPAS12_H
#pragma once

#ifdef CLIENT_DLL
   #define CHYP_Weapon_SPAS12 C_HYP_Weapon_SPAS12
   #define CHYP_Weapon_Shotgun C_HYP_Weapon_Shotgun

   #define MUZZLE_BRIGHT_FXNAME "muzzle_spas12_bright"
   #define MUZZLE_WEAK_FXNAME "muzzle_spas12_weak"
   #define MUZZLE_DEFORM_FXNAME "muzzle_spas12_deform"
   #define TRACER_FXNAME "tracer_762"
#else
   #include "npc_combine.h"
#endif

#define MUZZLE_BRIGHT_PERCENT 0.40f   //Is actually 30%, even though it says .4
#define MUZZLE_DEFORM_PERCENT 0.10f

#include "hyp_weapon_shotgun.h"

class CHYP_Weapon_SPAS12 : public CHYP_Weapon_Shotgun
{
public:
   DECLARE_CLASS( CHYP_Weapon_SPAS12, CHYP_Weapon_Shotgun );
   DECLARE_NETWORKCLASS();
   DECLARE_PREDICTABLE();
   DECLARE_ACTTABLE();

   CHYP_Weapon_SPAS12();
   virtual ~CHYP_Weapon_SPAS12();
   void Precache();
   
   virtual bool IsSecondaryAttackDisabled() { return false; }

   virtual float GetWeaponDamage();
   #ifdef GAME_DLL
      DECLARE_DATADESC();
      virtual const char* GetPickupClass() { return "weapon_SPAS12"; }
   #else
      virtual bool HasSecondaryExplosive( void ) const { return false; }
      virtual const char* GetPartialReloadSound(int iPart);

      virtual const char* GetTracerEffectName();
      virtual const char* GetMuzzleEffectName();
      void ASWReloadSound(int iType);
   #endif

   virtual void ItemPostFrame();
   virtual void PrimaryAttack_SPAS();
   virtual void FinishReload();

   // Classification
   virtual Class_T      Classify( void ) { return (Class_T) CLASS_HYP_SPAS12; }
};


#endif /* _INCLUDED_HYP_WEAPON_SPAS12_H */


Of particular interest is around line 200 in the cpp, which looks like this:

Code: Select all
if (m_iClip1 > 0)
            {
               m_iChamberState = HYP_CHAMBER_LOADED;
#ifdef CLIENT_DLL
                  m_iClip1--;
                  DevWarning("[CLIENT] Predicting ammo %i.\n", m_iClip1);
#else
               m_iClip1--;
               DevWarning("[SERVER] Ammo currently %i.\n", m_iClip1);
#endif
            }


This is what I see in the console after firing with net_fakelag 50:

Code: Select all
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[CLIENT] Predicting ammo 7.
[SERVER] Ammo currently 7.
[CLIENT] Predicting ammo 6.
[CLIENT] Predicting ammo 5.
[CLIENT] Predicting ammo 4.
[CLIENT] Predicting ammo 3.
[CLIENT] Predicting ammo 3.
[CLIENT] Predicting ammo 2.
[CLIENT] Predicting ammo 1.
[CLIENT] Predicting ammo 1.
[CLIENT] Predicting ammo 0.


All I have to say is, wtf? How can I go about fixing this? The page on prediction in the valve developer wiki is very vague on how this whole prediction system works.

Thanks,
IBeMad
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Tue Jan 24, 2012 4:58 pm

Bump... I really need some help with this!
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Thu Jan 26, 2012 5:19 pm

Anyone?
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby zombie@computer on Thu Jan 26, 2012 5:36 pm

I know only very little about prediction and nothing at all about the AS sdk...
When you are up to your neck in shit, keep your head up high
zombie@computer
Forum Goer Elite™
Forum Goer Elite™
 
Joined: Fri Dec 31, 2004 5:58 pm
Location: Lent, Netherlands

Re: [Hypovolemia] Help With Prediction

Postby Gary on Thu Jan 26, 2012 5:44 pm

If it doesn't actually affect anything in-game... you could always:

Put a "//" right before "DevWarning("[CLIENT] Predicting ammo %i.\n", m_iClip1);"

I would write down the error though, in case of a future problem that might of been caused by this.
Have a question related to modding or something I posted? Something that needs staff attention? I haven't been active lately, but feel free to PM me or message me on Steam(link below)

User avatar
Gary
Interlopers Staff
Interlopers Staff
 
Joined: Wed Dec 16, 2009 12:40 am
Location: USA, FL

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Thu Jan 26, 2012 6:22 pm

Well the problem isn't those messages... I just put them in to help me figure this out. When you see those messages, the HUD also displays these changes, which looks very weird.
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby stoopdapoop on Sat Feb 11, 2012 9:04 pm

I've never actually worked with prediction myself, I'm reading up now, and it seems that you haven't really done any of the steps mentioned in the "implementation" section

https://developer.valvesoftware.com/wiki/Prediction

the constructor doesn't have "SetPredictionEligible(true);"

m_iClip1 doesn't even appear to be a networked variable, How you gunna predict a non-networked variable?

you haven't selected a prediction table. It doesn't seem like you've really done anything in the prediction department. Am I missing something?

The example on the wiki compiles fine, and I'm guessing the steps mentioned in the implementation section are complete, but there's no way to till till you try.

And the reason why your client variable is decrementing faster than your server one is just because your client is running at a much higher framerate than your server's tick rate.
I'm Brown
Image
User avatar
stoopdapoop
Veteran
Veteran
 
Joined: Sun Aug 21, 2005 2:14 am
Location: Ann Arbor, MI

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Sat Feb 11, 2012 9:56 pm

m_iClip1 is networked in another class that this one inherits from (CBaseCombatWeapon). Setpredictioneligable(true) was done in that base class too, and just to make sure I tried it again the constructor of this class; still no dice. What do you mean by "I havent selected a prediction table"?
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby stoopdapoop on Sun Feb 12, 2012 5:26 am

ah, it hadn't occurred to me that part of the implementation might have been in the base.

can I see the baseclass too?

and I phrased the thing about the prediction table poorly. What I meant is that you hadn't defined any predicted variables inside of the prediction table. Between these lines of...

Code: Select all
BEGIN_PREDICTION_DATA( CHYP_Weapon_SPAS12 )

   END_PREDICTION_DATA()


there should be a definition for at least one predicted field that includes the variable, the type of the variable, and the "prediction behavior" for lack of a better term.

Check out the lines above the "CTestWeapon::ShouldPredict()" function for an example.
I'm Brown
Image
User avatar
stoopdapoop
Veteran
Veteran
 
Joined: Sun Aug 21, 2005 2:14 am
Location: Ann Arbor, MI

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Sun Feb 12, 2012 6:57 am

Baseclass (hyp_weapon_shotgun)

CPP file:
Code: Select all
#include "cbase.h"
#include "hyp_weapon_shotgun.h"
#include "in_buttons.h"

#ifdef CLIENT_DLL
#include "c_asw_player.h"
#include "c_asw_weapon.h"
#include "c_asw_marine.h"
#include "c_asw_marine_resource.h"
#include "prediction.h"
#define CASW_Marine C_ASW_Marine
#define CASW_Marine_Resource C_ASW_Marine_Resource
#else
#include "asw_marine.h"
#include "asw_player.h"
#include "asw_weapon.h"
#include "npcevent.h"
#include "shot_manipulator.h"
#include "asw_grenade_prifle.h"
#include "asw_fail_advice.h"
#include "effect_dispatch_data.h"
#include "asw_marine_resource.h"
#include "te_effect_dispatch.h"
#endif
#include "asw_marine_skills.h"
#include "asw_weapon_parse.h"
#include "particle_parse.h"
#include "asw_weapon_ammo_bag_shared.h"


// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

IMPLEMENT_NETWORKCLASS_ALIASED( HYP_Weapon_Shotgun, DT_HYP_Weapon_Shotgun )

   BEGIN_NETWORK_TABLE( CHYP_Weapon_Shotgun, DT_HYP_Weapon_Shotgun )
#ifdef CLIENT_DLL
   // recvprops
   RecvPropBool   ( RECVINFO( m_bIsTargetActionLoading ) ),
   RecvPropInt      ( RECVINFO( m_iActionType ) ),
   RecvPropInt      ( RECVINFO( m_iChamberState )),
   RecvPropFloat   ( RECVINFO( m_fNextAction )),
   RecvPropBool   ( RECVINFO(m_bAutoRechamber) ),
   RecvPropBool   ( RECVINFO(m_bAutoEmptyChamber) ),
   RecvPropFloat   ( RECVINFO(m_fAutoEmptyChamber) ),
#else
   // sendprops
   SendPropBool   ( SENDINFO(m_bIsTargetActionLoading) ),
   SendPropInt      ( SENDINFO(m_iActionType) ),
   SendPropInt      ( SENDINFO(m_iChamberState ) ),
   SendPropFloat   ( SENDINFO(m_fNextAction),   0,   SPROP_NOSCALE ),   
   SendPropBool   ( SENDINFO(m_bAutoRechamber) ),
   SendPropBool   ( SENDINFO(m_bAutoEmptyChamber) ),
   SendPropFloat   ( SENDINFO(m_fAutoEmptyChamber) ),
#endif
   END_NETWORK_TABLE()

#ifdef CLIENT_DLL
   BEGIN_PREDICTION_DATA( CHYP_Weapon_Shotgun )
   DEFINE_PRED_FIELD_TOL( m_fNextAction, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
   DEFINE_PRED_FIELD( m_iActionType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
   DEFINE_PRED_FIELD( m_iChamberState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
   DEFINE_PRED_FIELD( m_bIsTargetActionLoading, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
   DEFINE_PRED_FIELD( m_bAutoRechamber, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
   DEFINE_PRED_FIELD( m_bAutoEmptyChamber, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
   DEFINE_PRED_FIELD_TOL( m_fAutoEmptyChamber, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
   END_PREDICTION_DATA()

   bool CHYP_Weapon_Shotgun::ShouldPredict()
{
   if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
      return true;
   else
      return BaseClass::ShouldPredict();
}

#endif

LINK_ENTITY_TO_CLASS( hyp_weapon_shotgun, CHYP_Weapon_Shotgun );
PRECACHE_WEAPON_REGISTER(hyp_weapon_shotgun);

#ifndef CLIENT_DLL
extern ConVar asw_debug_marine_damage;
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CHYP_Weapon_Shotgun )

   END_DATADESC()

#endif /* not client */

   CHYP_Weapon_Shotgun::CHYP_Weapon_Shotgun()
{
   SetPredictionEligible(true);
   m_iChamberState = HYP_CHAMBER_EMPTY;
   m_iActionType = HYP_MANUAL;
   m_bIsTargetActionLoading = false;
}

CHYP_Weapon_Shotgun::~CHYP_Weapon_Shotgun()
{

}

float CHYP_Weapon_Shotgun::GetFireRate()
{
   return GetWeaponInfo()->m_flFireRate;
}

void CHYP_Weapon_Shotgun::SecondaryAttack()
{
   if (IsSecondaryAttackDisabled())
      return;

   CASW_Player *pPlayer = GetCommander();
   if (!pPlayer)
      return;

   CASW_Marine *pMarine = GetMarine();

   if (!pMarine)
      return;

   if(m_iActionType == HYP_SEMI_AUTO)
      m_iActionType = HYP_MANUAL;
   else
      m_iActionType = HYP_SEMI_AUTO;

   if (m_iActionType != HYP_SEMI_AUTO){
      m_fAutoEmptyChamber =  -1;
      m_bAutoEmptyChamber = false;
      m_bAutoRechamber = false;
   }

   //Must have ammo
   bool bUsesSecondary = UsesSecondaryAmmo();
   bool bUsesClips = UsesClipsForAmmo2();
   int iAmmoCount = pMarine->GetAmmoCount(m_iSecondaryAmmoType);
   bool bInWater = ( pMarine->GetWaterLevel() == 3 );
   if ( (bUsesSecondary && 
      (   ( bUsesClips && m_iClip1 <= 0) ||
      ( !bUsesClips && iAmmoCount<=0 )
      ) )
      || bInWater || m_bInReload )
   {
      //SendWeaponAnim( ACT_VM_DRYFIRE );
      //BaseClass::WeaponSound( EMPTY );
      m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
      return;
   }

   //BaseClass::WeaponSound( SPECIAL1 );

   Vector vecSrc = pMarine->Weapon_ShootPosition();
   Vector   vecThrow;   
   vecThrow = pPlayer->GetAutoaimVectorForMarine(pMarine, GetAutoAimAmount(), GetVerticalAdjustOnlyAutoAimAmount());   // 45 degrees = 0.707106781187
   VectorScale( vecThrow, 1000.0f, vecThrow );

   SendWeaponAnim( ACT_VM_SECONDARYATTACK );
   if ( bUsesClips )
   {
      m_iClip2 -= 1;
   }
   else
   {
      pMarine->RemoveAmmo( 1, m_iSecondaryAmmoType );
   }

   // Can shoot again immediately
   m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;

   // Can blow up after a short delay (so have time to release mouse button)
   m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
}

//------------------------------------
//
//------------------------------------
void CHYP_Weapon_Shotgun::DoActionMechanism()
{
   if ( m_iChamberState != HYP_CHAMBER_OPEN )
   {
      if( !m_bIsTargetActionLoading )
      {
         // Start emptying the chamber
         m_fNextAction = gpGlobals->curtime + GetWeaponInfo()->m_fActionPullTime;
         m_flNextPrimaryAttack = MAX(m_flNextPrimaryAttack, gpGlobals->curtime + GetWeaponInfo()->m_fActionPullTime);
         m_bIsTargetActionLoading = true;
         WeaponSound(PUMPACTION_PULL);
      }
      else
      {
         // Empty the chamber
         m_iChamberState = HYP_CHAMBER_OPEN;
         m_bIsTargetActionLoading = false;
      }
   }
   else
   {
      if (!m_bIsTargetActionLoading)
      {
         // Close the chamber
         m_fNextAction = gpGlobals->curtime + GetWeaponInfo()->m_fActionPushTime;
         m_flNextPrimaryAttack = MAX(m_flNextPrimaryAttack, gpGlobals->curtime + GetWeaponInfo()->m_fActionPushTime);
         m_bIsTargetActionLoading = true;
         WeaponSound(PUMPACTION_PUSH);
      }
      else
      {
         m_bIsTargetActionLoading = false;

         if (m_iClip1 > 0)
         {
            // Load the chamber
            m_iChamberState = HYP_CHAMBER_LOADED;
#ifdef CLIENT_DLL
            if (!prediction->InPrediction() || prediction->IsFirstTimePredicted())
            {
               m_iClip1--;
               //DevWarning("[CLIENT] Predicting ammo %i.\n", m_iClip1);
            }
#else
            m_iClip1--;
            //DevWarning("[SERVER] Ammo currently %i.\n", m_iClip1);
#endif
         }
         else
            // No bullet to load
            m_iChamberState = HYP_CHAMBER_EMPTY;
      }
   }
}

int CHYP_Weapon_Shotgun::GetChamberedState()
{
   return m_iChamberState;
}

#ifdef GAME_DLL
void   CHYP_Weapon_Shotgun::DoImpactEffect( trace_t &tr, int nDamageType )
{
   CEffectData data;

   data.m_vOrigin = tr.endpos + ( tr.plane.normal * 1.0f );
   data.m_vNormal = tr.plane.normal;

   DispatchEffect( "AR2Impact", data );

   BaseClass::DoImpactEffect( tr, nDamageType );
}
void CHYP_Weapon_Shotgun::Precache()
{
   // precache the weapon model here?   
   PrecacheScriptSound("ASW_Rifle.ReloadA");
   PrecacheScriptSound("ASW_Rifle.ReloadB");
   PrecacheScriptSound("ASW_Rifle.ReloadC");

   UTIL_PrecacheOther( "env_entity_dissolver" );
   BaseClass::Precache();
}
#endif


h file
Code: Select all
#ifndef _INCLUDED_HYP_WEAPON_SHOTGUN_H
#define _INCLUDED_HYP_WEAPON_SHOTGUN_H
#pragma once

#ifdef CLIENT_DLL
   #define CHYP_Weapon_Shotgun C_HYP_Weapon_Shotgun
   #include "c_asw_weapon.h"
#else
   #include "npc_combine.h"
   #include "asw_weapon.h"
#endif
#include "asw_shareddefs.h"

class CHYP_Weapon_Shotgun : public CASW_Weapon
{
public:
   DECLARE_CLASS( CHYP_Weapon_Shotgun, CASW_Weapon );
   DECLARE_NETWORKCLASS();
#ifdef CLIENT_DLL
   DECLARE_PREDICTABLE();
   virtual bool ShouldPredict();
#endif

   CHYP_Weapon_Shotgun();
   virtual ~CHYP_Weapon_Shotgun();

   // shared functions
   virtual const float GetAutoAimAmount() { return 0.0f;}
   virtual const float GetAutoAimRadiusScale() { return 0.0f;}
   virtual void SecondaryAttack( void );// disable +attck2
   virtual bool IsSecondaryAttackDisabled() { return true; }
   float   GetFireRate( void );

   virtual bool IsPredicted() const { return true; }

   virtual void DoActionMechanism();
   //virtual void ItemPostFrame();
   //virtual void PrimaryAttack();
   CNetworkVar(float, m_fNextAction);
   CNetworkVar(bool, m_bIsTargetActionLoading);
   CNetworkVar(int, m_iChamberState);
   CNetworkVar(HYP_Weapon_Action_Type, m_iActionType);
   int GetChamberedState();

#ifdef GAME_DLL
   DECLARE_DATADESC();
   void Precache();
   const char *GetTracerType( void ) { return "ASWTracer"; }
   int      CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; }
   void   DoImpactEffect( trace_t &tr, int nDamageType );
#else
   virtual const char* GetTracerEffectName() { return "tracer_hk416"; }   // particle effect name
   virtual const char* GetMuzzleEffectName() { return "muzzle_hk416"; }   // muzzle_rifle particle effect name
   virtual bool HasSecondaryExplosive( void ) const { return false; }
#endif
   
   CNetworkVar(bool, m_bAutoRechamber);
   CNetworkVar(bool, m_bAutoEmptyChamber);
   CNetworkVar(float, m_fAutoEmptyChamber);

   // Classification
   virtual Class_T      Classify( void ) { return (Class_T) CLASS_HYP_SHOTGUN; }
};


#endif /* _INCLUDED_HYP_WEAPON_SHOTGUN_H */
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby stoopdapoop on Sun Feb 12, 2012 5:20 pm

I don't see a networked m_iClip1 or a prediction declaration for it.

I see other predicted networked variables, but not the one you're having problems with.

Is it declared in that class's base class?
I'm Brown
Image
User avatar
stoopdapoop
Veteran
Veteran
 
Joined: Sun Aug 21, 2005 2:14 am
Location: Ann Arbor, MI

Re: [Hypovolemia] Help With Prediction

Postby IBeMad on Sun Feb 12, 2012 5:43 pm

CBaseCombatWeaponShared

haha it was 107,185 chars... apparently the max is 60k

CPP file
http://pastebin.com/N8N21XFH

H file
http://pastebin.com/Qj1iHAu8
IBeMad
Member
Member
 
Joined: Sun Jan 22, 2012 5:18 pm

Re: [Hypovolemia] Help With Prediction

Postby stoopdapoop on Sun Feb 12, 2012 6:42 pm

Ok, so m_iClip1 appears to be properly declared for prediction.

My guess is that it's a logic error. This may not be the problem, but why do you decrement the clip in itempostframe? it seems strange to make ammo depletion a framerate dependent action.

What happens if you move...

Code: Select all
#ifdef CLIENT_DLL
                  m_iClip1--;
                  DevWarning("[CLIENT] Predicting ammo %i.\n", m_iClip1);
#else
               m_iClip1--;
               DevWarning("[SERVER] Ammo currently %i.\n", m_iClip1);
#endif


out of ItemPostFrame and move it into PrimaryAttackSpas() right after...


Code: Select all
   info.m_flDamage = GetWeaponDamage();
   pMarine->FireBullets( info );
I'm Brown
Image
User avatar
stoopdapoop
Veteran
Veteran
 
Joined: Sun Aug 21, 2005 2:14 am
Location: Ann Arbor, MI

Re: [Hypovolemia] Help With Prediction

Postby MrTwoVideoCards on Sun Feb 12, 2012 7:40 pm

If I recall, prediction is totally broken in ASK SDK skeleton, if that is perhaps what you are using?
User avatar
MrTwoVideoCards
Monothetic
 
Joined: Thu Aug 02, 2007 11:18 am
Location: IN YOUR SOUL

Re: [Hypovolemia] Help With Prediction

Postby stoopdapoop on Sun Feb 12, 2012 8:03 pm

MrTwoVideoCards wrote:If I recall, prediction is totally broken in ASK SDK skeleton, if that is perhaps what you are using?



That doesn't sound right. Was it recently broken? Not having prediction would make input lag unbearable when playing online.
I'm Brown
Image
User avatar
stoopdapoop
Veteran
Veteran
 
Joined: Sun Aug 21, 2005 2:14 am
Location: Ann Arbor, MI
Next

Return to Programming

Who is online

Users browsing this forum: No registered users