General fixes and improved lag comp performance

This commit is contained in:
Kamay Xutax 2024-08-31 06:18:28 +02:00
parent 380f352adf
commit 11d345802e
17 changed files with 499 additions and 527 deletions

View file

@ -1948,7 +1948,8 @@ void C_BaseAnimating::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quat
return;
}
if (GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 )
// CS weapons have this sometimes ...
if ( GetSequence() >= hdr->GetNumSeq() || GetSequence() == -1 )
{
SetSequence( 0 );
}
@ -4928,7 +4929,8 @@ void C_BaseAnimating::Simulate()
DelayedInitModelEffects();
}
if ( gpGlobals->frametime != 0.0f )
// TODO_ENHANCED: check if there's other stuff like this! This can break lag compensation.
if ( gpGlobals->frametime != 0.0f && m_bClientSideAnimation )
{
DoAnimationEvents( GetModelPtr() );
}

View file

@ -534,6 +534,7 @@ protected:
float m_fadeMinDist;
float m_fadeMaxDist;
float m_flFadeScale;
bool m_bClientSideAnimation;
private:
@ -567,7 +568,6 @@ private:
float m_flOldEncodedController[MAXSTUDIOBONECTRLS];
// Clientside animation
bool m_bClientSideAnimation;
bool m_bLastClientSideFrameReset;
int m_nNewSequenceParity;

View file

@ -124,40 +124,6 @@ CAddonInfo g_AddonInfo[] =
{ "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" },
};
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class C_TEPlayerAnimEvent : public C_BaseTempEntity
{
public:
DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity );
DECLARE_CLIENTCLASS();
virtual void PostDataUpdate( DataUpdateType_t updateType )
{
// Create the effect.
C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
if ( pPlayer && !pPlayer->IsDormant() )
{
pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData );
}
}
public:
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent );
BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent )
RecvPropEHandle( RECVINFO( m_hPlayer ) ),
RecvPropInt( RECVINFO( m_iEvent ) ),
RecvPropInt( RECVINFO( m_nData ) )
END_RECV_TABLE()
BEGIN_PREDICTION_DATA( C_CSPlayer )
#ifdef CS_SHIELD_ENABLED
DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
@ -767,7 +733,7 @@ C_CSPlayer::C_CSPlayer() :
{
m_angEyeAngles.Init();
AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
// AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
m_iLastAddonBits = m_iAddonBits = 0;
m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;
@ -1689,6 +1655,11 @@ bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
void C_CSPlayer::UpdateClientSideAnimation()
{
if ( !m_bClientSideAnimation )
{
return;
}
// We do this in a different order than the base class.
// We need our cycle to be valid for when we call the playeranimstate update code,
// or else it'll synchronize the upper body anims with the wrong cycle.
@ -2074,6 +2045,8 @@ void C_CSPlayer::PlayReloadEffect()
void C_CSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
// do nothing ... let server doing its animations.
return;
}
void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
@ -2330,11 +2303,11 @@ void C_CSPlayer::Simulate( void )
}
}
BaseClass::Simulate();
BaseClass::Simulate();
if ((cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2) && !IsLocalPlayer())
{
DrawClientHitboxes(gpGlobals->frametime, true);
if ( ( cl_showhitboxes.GetInt() == 1 || cl_showhitboxes.GetInt() == 2 ) && !IsLocalPlayer() )
{
DrawClientHitboxes( gpGlobals->frametime, true );
}
}

View file

@ -1306,7 +1306,7 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
for ( int i = 0; i < MAX_EDICTS; i++ )
{
cmd->simulationdata[i].m_bEntityExists = false;
cmd->simulationdata[i].entityexists = false;
}
// Send interpolated simulation time for lag compensation, let it also auto-vectorize this.
@ -1319,9 +1319,9 @@ void CInput::CreateMove ( int sequence_number, float input_sample_frametime, boo
continue;
}
cmd->simulationdata[pEntity->index].m_flSimulationTime = pEntity->m_flInterpolatedSimulationTime;
cmd->simulationdata[pEntity->index].m_flAnimTime = pEntity->m_flSimulationTime;
cmd->simulationdata[pEntity->index].m_bEntityExists = true;
cmd->simulationdata[pEntity->index].lerp_time = pEntity->m_flInterpolatedSimulationTime;
cmd->simulationdata[pEntity->index].animated_sim_time = pEntity->m_flSimulationTime;
cmd->simulationdata[pEntity->index].entityexists = true;
}
#ifdef CSTRIKE_DLL

View file

@ -480,7 +480,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
if ( m_pIk )
{
CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask );
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik );
}
else
@ -490,7 +490,7 @@ void CBaseAnimatingOverlay::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Q
if ( pStudioHdr->numbonecontrollers() )
{
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() );
}
}

View file

@ -2805,8 +2805,12 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
CStudioHdr *pStudioHdr = GetModelPtr( );
Assert(pStudioHdr);
// Use this for lag compensation
float oldcurtime = gpGlobals->curtime;
gpGlobals->curtime = m_flSimulationTime;
CBoneCache *pcache = Studio_GetBoneCache( m_boneCacheHandle );
int boneMask = BONE_USED_BY_HITBOX | BONE_USED_BY_ATTACHMENT;
int boneMask = BONE_USED_BY_ANYTHING;
// TF queries these bones to position weapons when players are killed
#if defined( TF_DLL )
@ -2814,8 +2818,10 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
#endif
if ( pcache )
{
if ( pcache->IsValid( gpGlobals->curtime ) && (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid <= gpGlobals->curtime)
// Only validate for current time.
if ( (pcache->m_boneMask & boneMask) == boneMask && pcache->m_timeValid == gpGlobals->curtime)
{
gpGlobals->curtime = oldcurtime;
// Msg("%s:%s:%s (%x:%x:%8.4f) cache\n", GetClassname(), GetDebugName(), STRING(GetModelName()), boneMask, pcache->m_boneMask, pcache->m_timeValid );
// in memory and still valid, use it!
return pcache;
@ -2849,6 +2855,8 @@ CBoneCache *CBaseAnimating::GetBoneCache( void )
pcache = Studio_GetBoneCache( m_boneCacheHandle );
}
Assert(pcache);
gpGlobals->curtime = oldcurtime;
return pcache;
}
@ -3035,7 +3043,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( m_pIk )
{
CIKContext auto_ik;
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, 0, boneMask );
auto_ik.Init( pStudioHdr, GetAbsAngles(), GetAbsOrigin(), gpGlobals->curtime, m_iIKCounter, boneMask );
boneSetup.CalcAutoplaySequences( pos, q, gpGlobals->curtime, &auto_ik );
}
else
@ -3045,7 +3053,7 @@ void CBaseAnimating::GetSkeleton( CStudioHdr *pStudioHdr, Vector pos[], Quaterni
if ( pStudioHdr->numbonecontrollers() )
{
boneSetup.CalcBoneAdj( pos, q, GetEncodedControllerArray() );
boneSetup.CalcBoneAdj( pos, q, GetBoneControllerArray() );
}
}

View file

@ -325,7 +325,7 @@ public:
CBaseEntity *GetLightingOrigin();
const float* GetPoseParameterArray() { return m_flPoseParameter.Base(); }
const float *GetEncodedControllerArray() { return m_flEncodedController.Base(); }
const float *GetBoneControllerArray() { return m_flEncodedController.Base(); }
void BuildMatricesWithBoneMerge( const CStudioHdr *pStudioHdr, const QAngle& angles,
const Vector& origin, const Vector pos[MAXSTUDIOBONES],

View file

@ -240,45 +240,6 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll )
SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ),
END_SEND_TABLE()
// -------------------------------------------------------------------------------- //
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
// -------------------------------------------------------------------------------- //
class CTEPlayerAnimEvent : public CBaseTempEntity
{
public:
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
DECLARE_SERVERCLASS();
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
{
}
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
SendPropEHandle( SENDINFO( m_hPlayer ) ),
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nData ), 32 )
END_SEND_TABLE()
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
{
CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
g_TEPlayerAnimEvent.m_iEvent = event;
g_TEPlayerAnimEvent.m_nData = nData;
g_TEPlayerAnimEvent.Create( filter, 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Filters updates to a variable so that only non-local players see
// the changes. This is so we can send a low-res origin to non-local players
@ -6664,7 +6625,6 @@ void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
else
{
m_PlayerAnimState->DoAnimationEvent( event, nData );
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
}
}

View file

@ -782,7 +782,7 @@ protected:
void PushawayThink();
private:
public:
ICSPlayerAnimState *m_PlayerAnimState;

View file

@ -22,8 +22,9 @@ abstract_class ILagCompensationManager
public:
// Called during player movement to set up/restore after lag compensation
virtual void StartLagCompensation( CBasePlayer *player, CUserCmd *cmd ) = 0;
virtual void FinishLagCompensation(CBasePlayer * player) = 0;
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0;
virtual void FinishLagCompensation(CBasePlayer *player) = 0;
virtual void BacktrackPlayer( CBasePlayer *player, CUserCmd *cmd ) = 0;
virtual void TrackPlayerData( CBasePlayer *pPlayer ) = 0;
};
extern ILagCompensationManager *lagcompensation;

View file

@ -342,15 +342,15 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
return; // Don't process this command
}
gpGlobals->frametime = playerFrameTime;
gpGlobals->curtime = player->m_nTickBase * TICK_INTERVAL;
StartCommand( player, ucmd );
gpGlobals->frametime = playerFrameTime;
gpGlobals->curtime = player->m_nTickBase * TICK_INTERVAL;
g_pGameMovement->StartTrackPredictionErrors( player );
StartCommand( player, ucmd );
g_pGameMovement->StartTrackPredictionErrors( player );
// Prevent hacked clients from sending us invalid view angles to try to get leaf server code to crash
if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable(ucmd->viewangles) )
if ( !ucmd->viewangles.IsValid() || !IsEntityQAngleReasonable( ucmd->viewangles ) )
{
ucmd->viewangles = vec3_angle;
}
@ -363,9 +363,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{
// If no clipping and cheats enabled and noclipduring game enabled, then leave
// forwardmove and angles stuff in usercmd
if ( player->GetMoveType() == MOVETYPE_NOCLIP &&
sv_cheats->GetBool() &&
sv_noclipduringpause.GetBool() )
if ( player->GetMoveType() == MOVETYPE_NOCLIP && sv_cheats->GetBool() && sv_noclipduringpause.GetBool() )
{
gpGlobals->frametime = TICK_INTERVAL;
}
@ -385,7 +383,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
// Do weapon selection
if ( ucmd->weaponselect != 0 )
{
CBaseCombatWeapon *weapon = dynamic_cast< CBaseCombatWeapon * >( CBaseEntity::Instance( ucmd->weaponselect ) );
CBaseCombatWeapon* weapon = dynamic_cast< CBaseCombatWeapon* >( CBaseEntity::Instance( ucmd->weaponselect ) );
if ( weapon )
{
VPROF( "player->SelectItem()" );
@ -393,13 +391,14 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
}
}
IServerVehicle *pVehicle = player->GetVehicle();
IServerVehicle* pVehicle = player->GetVehicle();
// Latch in impulse.
if ( ucmd->impulse )
{
// Discard impulse commands unless the vehicle allows them.
// FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for example.
// FIXME: UsingStandardWeapons seems like a bad filter for this. The flashlight is an impulse command, for
// example.
if ( !pVehicle || player->UsingStandardWeaponsInVehicle() )
{
player->m_nImpulse = ucmd->impulse;
@ -420,7 +419,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
{
player->pl.v_angle = ucmd->viewangles;
}
else if( player->pl.fixangle == FIXANGLE_RELATIVE )
else if ( player->pl.fixangle == FIXANGLE_RELATIVE )
{
player->pl.v_angle = ucmd->viewangles + player->pl.anglechange;
}
@ -446,18 +445,18 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
VPROF( "pVehicle->ProcessMovement()" );
pVehicle->ProcessMovement( player, g_pMoveData );
}
// Copy output
FinishMove( player, ucmd, g_pMoveData );
// Let server invoke any needed impact functions
// Let server invoke any needed impact functions
VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" );
moveHelper->ProcessImpacts();
VPROF_SCOPE_END();
VPROF_SCOPE_END();
RunPostThink( player );
RunPostThink( player );
ServiceEventQueue( player );
ServiceEventQueue( player );
g_pGameMovement->FinishTrackPredictionErrors( player );
@ -467,5 +466,7 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper
if ( gpGlobals->frametime > 0 )
{
player->m_nTickBase++;
}
}
lagcompensation->TrackPlayerData( player );
}

File diff suppressed because it is too large Load diff

View file

@ -5,6 +5,7 @@
//=============================================================================//
#include "cbase.h"
#include "const.h"
#include "debugoverlay_shared.h"
#include "strtools.h"
#ifndef CLIENT_DLL
@ -38,7 +39,7 @@
#include "engine/ivdebugoverlay.h"
#include "obstacle_pushaway.h"
#include "props_shared.h"
#include <iostream>
ConVar weapon_accuracy_nospread( "weapon_accuracy_nospread", "0", FCVAR_REPLICATED );
#define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
@ -193,7 +194,7 @@ void UTIL_ClipTraceToPlayersHull(const Vector& vecAbsStart, const Vector& vecAbs
float smallestFraction = tr->fraction;
const float maxRange = 60.0f;
ray.Init( vecAbsStart, vecAbsEnd , mins, maxs );
ray.Init( vecAbsStart, vecAbsEnd, mins, maxs );
for ( int k = 1; k <= gpGlobals->maxClients; ++k )
{
@ -508,7 +509,8 @@ void CCSPlayer::FireBullet(
CBasePlayer *lastPlayerHit = NULL;
MDLCACHE_CRITICAL_SECTION();
bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE;
// Only show for one bullet.
bool shouldDebugHitboxesOnFire = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_FIRE && iBullet == 0;
bool shouldDebugHitboxesOnHit = m_pCurrentCommand->debug_hitboxes & CUserCmd::DEBUG_HITBOXES_ON_HIT;
// TODO_ENHANCED:
@ -526,7 +528,29 @@ void CCSPlayer::FireBullet(
{
CBasePlayer* lagPlayer = UTIL_PlayerByIndex(i);
if (lagPlayer && lagPlayer != this)
{
{
#ifdef CLIENT_DLL
if ( !m_pCurrentCommand->hasbeenpredicted )
{
std::string text = "client:\n";
auto angles = lagPlayer->GetRenderAngles();
text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n';
NDebugOverlay::EntityBounds( lagPlayer, 255, 0, 0, 32, 60 );
std::cout << text;
}
#else
std::string text = "server:\n";
auto angles = lagPlayer->GetAbsAngles();
text += "x: " + std::to_string(angles.x) + ", y: " + std::to_string(angles.y) + ", z: " + std::to_string(angles.z) + '\n';
NDebugOverlay::EntityBounds( lagPlayer, 0, 255, 0, 32, 60 );
std::cout << text;
#endif
#ifdef CLIENT_DLL
if (!m_pCurrentCommand->hasbeenpredicted)
{

View file

@ -108,7 +108,7 @@ bool CBaseCSGrenade::Deploy()
bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
{
m_bRedraw = false;
m_bPinPulled = false; // when this is holstered make sure the pin isnt pulled.
m_bPinPulled = false; // when this is holstered make sure the pin isn<EFBFBD>t pulled.
m_fThrowTime = 0;
#ifndef CLIENT_DLL
@ -222,10 +222,11 @@ void CBaseCSGrenade::ItemPostFrame()
return;
// If they let go of the fire button, they want to throw the grenade.
if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
{
#ifndef CLIENT_DLL
pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
#endif
StartGrenadeThrow();
MDLCACHE_CRITICAL_SECTION();

View file

@ -162,20 +162,20 @@ void WriteUsercmd( bf_write *buf, const CUserCmd *to, const CUserCmd *from )
// Write finally simulation data with entity index
for ( unsigned int i = 0; i <= highestEntityIndex; i++ )
{
if ( from->simulationdata[i].m_flSimulationTime != to->simulationdata[i].m_flSimulationTime )
if ( from->simulationdata[i].lerp_time != to->simulationdata[i].lerp_time )
{
buf->WriteOneBit( 1 );
buf->WriteBitFloat( to->simulationdata[i].m_flSimulationTime );
buf->WriteBitFloat( to->simulationdata[i].lerp_time );
}
else
{
buf->WriteOneBit( 0 );
}
if ( from->simulationdata[i].m_flAnimTime != to->simulationdata[i].m_flAnimTime )
if ( from->simulationdata[i].animated_sim_time != to->simulationdata[i].animated_sim_time )
{
buf->WriteOneBit( 1 );
buf->WriteBitFloat( to->simulationdata[i].m_flAnimTime );
buf->WriteBitFloat( to->simulationdata[i].animated_sim_time );
}
else
{
@ -309,12 +309,12 @@ void ReadUsercmd( bf_read *buf, CUserCmd *move, CUserCmd *from )
{
if (buf->ReadOneBit())
{
move->simulationdata[i].m_flSimulationTime = buf->ReadBitFloat();
move->simulationdata[i].lerp_time = buf->ReadBitFloat();
}
if (buf->ReadOneBit())
{
move->simulationdata[i].m_flAnimTime = buf->ReadBitFloat();
move->simulationdata[i].animated_sim_time = buf->ReadBitFloat();
}
}

View file

@ -30,7 +30,7 @@
#endif
#define MAX_LAYER_RECORDS (CBaseAnimatingOverlay::MAX_OVERLAYS)
#define MAX_POSE_PARAMETERS (CBaseAnimating::NUM_POSEPAREMETERS)
#define MAX_POSE_PARAMETERS (MAXSTUDIOPOSEPARAM)
#define MAX_ENCODED_CONTROLLERS (MAXSTUDIOBONECTRLS)
class bf_read;
@ -57,9 +57,9 @@ struct SimulationData
// TODO_ENHANCED:
// For now we send the last received update for animations.
// anim time is unreliable on low fps.
float m_flSimulationTime;
float m_flAnimTime;
bool m_bEntityExists;
float lerp_time;
float animated_sim_time;
bool entityexists;
};
class CEntityGroundContact

View file

@ -1531,5 +1531,106 @@ private:
char *m_szBuffer; // a copy of original string, with '\0' instead of separators
};
template < typename T, size_t N >
class CUtlCircularBuffer
{
public:
using pT = T*;
enum PushType
{
Filling,
Updating
};
public:
inline pT Get( const int wantedSlot = 0 )
{
const auto real_slot = _Slot( wantedSlot );
return ( real_slot == -1 ) ? NULL : &_buffer[real_slot];
}
public:
inline PushType Push( T&& element )
{
if ( _filled_history >= N )
{
if ( _index >= ( N - 1 ) )
{
_index = 0;
}
else
{
_index++;
}
_buffer[_index] = element;
return Updating;
}
_buffer[_filled_history] = element;
_index = _filled_history;
_filled_history++;
return Filling;
}
inline PushType Push( const T& element )
{
if ( _filled_history >= N )
{
if ( _index >= ( N - 1 ) )
{
_index = 0;
}
else
{
_index++;
}
_buffer[_index] = element;
return Updating;
}
_buffer[_filled_history] = element;
_index = _filled_history;
_filled_history++;
return Filling;
}
inline void Clear()
{
_index = 0;
_filled_history = 0;
}
private:
inline int _Slot( const int wantedSlot = 0 ) const
{
if ( wantedSlot >= _filled_history || wantedSlot < 0 || _filled_history <= 0 )
{
return -1;
}
if ( _filled_history >= N )
{
const int calc_slot = ( _index - wantedSlot );
return ( calc_slot < 0 ) ? calc_slot + N : calc_slot;
}
else
{
return ( _filled_history - 1 ) - wantedSlot;
}
}
private:
T _buffer[N];
int _filled_history {};
int _index {};
};
#endif // CCVECTOR_H