Added trigger teleport prediction

This commit is contained in:
Kamay Xutax 2024-08-23 18:24:10 +02:00
parent 186b8c046d
commit 340eeb7e03
10 changed files with 232 additions and 19 deletions

View file

@ -365,6 +365,12 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId )
END_RECV_TABLE() END_RECV_TABLE()
#endif #endif
void RecvProxy_Name(const CRecvProxyData *pData, void *pStruct, void *pOut)
{
C_BaseEntity *entity = (C_BaseEntity *) pStruct;
Q_strncpy( entity->m_iName, pData->m_Value.m_pString, MAX_PATH );
}
BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity) BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity)
RecvPropDataTable( "AnimTimeMustBeFirst", 0, 0, &REFERENCE_RECV_TABLE(DT_AnimTimeMustBeFirst) ), RecvPropDataTable( "AnimTimeMustBeFirst", 0, 0, &REFERENCE_RECV_TABLE(DT_AnimTimeMustBeFirst) ),
@ -398,7 +404,7 @@ BEGIN_RECV_TABLE_NOBASE(C_BaseEntity, DT_BaseEntity)
RecvPropInt( RECVINFO( m_iParentAttachment ) ), RecvPropInt( RECVINFO( m_iParentAttachment ) ),
// Receive the name // Receive the name
RecvPropString(RECVINFO(m_iName)), RecvPropString(RECVINFO(m_iName), NULL, RecvProxy_Name),
RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ),
RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ),

View file

@ -175,6 +175,7 @@ $Project
$File "css_enhanced\c_triggers_base.cpp" $File "css_enhanced\c_triggers_base.cpp"
$File "css_enhanced\c_triggers.cpp" $File "css_enhanced\c_triggers.cpp"
$File "css_enhanced\c_trigger_push.cpp" $File "css_enhanced\c_trigger_push.cpp"
$File "css_enhanced\c_trigger_teleport.cpp"
$File "css_enhanced\variant_t.cpp" $File "css_enhanced\variant_t.cpp"
$File "hl2\C_Func_Monitor.cpp" $File "hl2\C_Func_Monitor.cpp"
$File "geiger.cpp" $File "geiger.cpp"

View file

@ -1,5 +1,6 @@
#include "cbase.h" #include "cbase.h"
#include "datamap.h" #include "datamap.h"
#include "dt_recv.h"
#include "util_shared.h" #include "util_shared.h"
#include "c_trigger_push.h" #include "c_trigger_push.h"
@ -8,16 +9,14 @@
LINK_ENTITY_TO_CLASS( trigger_push, C_TriggerPush ); LINK_ENTITY_TO_CLASS( trigger_push, C_TriggerPush );
BEGIN_PREDICTION_DATA(C_TriggerPush)
DEFINE_PRED_FIELD(m_vecPushDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE),
DEFINE_PRED_FIELD(m_flAlternateTicksFix, FIELD_FLOAT, FTYPEDESC_INSENDTABLE),
DEFINE_PRED_FIELD(m_flPushSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE),
// DEFINE_PRED_ARRAY(m_hPredictedTouchingEntities, FIELD_EHANDLE, MAX_EDICTS, FTYPEDESC_PRIVATE),
// DEFINE_PRED_FIELD(m_iCountPredictedTouchingEntities, FIELD_INTEGER, FTYPEDESC_PRIVATE)
END_PREDICTION_DATA();
// Since this is called only during creation // Since this is called only during creation
// we allow a small margin for prediction errors here // we allow a small margin for prediction errors here
BEGIN_PREDICTION_DATA(C_TriggerPush)
// DEFINE_PRED_FIELD(m_vecPushDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE),
// DEFINE_PRED_FIELD(m_flAlternateTicksFix, FIELD_FLOAT, FTYPEDESC_INSENDTABLE),
// DEFINE_PRED_FIELD(m_flPushSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE),
END_PREDICTION_DATA();
IMPLEMENT_CLIENTCLASS_DT(C_TriggerPush, DT_TriggerPush, CTriggerPush) IMPLEMENT_CLIENTCLASS_DT(C_TriggerPush, DT_TriggerPush, CTriggerPush)
RecvPropVector(RECVINFO(m_vecPushDir)), RecvPropVector(RECVINFO(m_vecPushDir)),
RecvPropFloat(RECVINFO(m_flAlternateTicksFix)), RecvPropFloat(RECVINFO(m_flAlternateTicksFix)),

View file

@ -0,0 +1,152 @@
#include "cbase.h"
#include "c_baseplayer.h"
#include "datamap.h"
#include "dt_recv.h"
#include "entitylist_base.h"
#include "predictable_entity.h"
#include "util_shared.h"
#include "c_trigger_teleport.h"
#include "prediction.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
LINK_ENTITY_TO_CLASS(trigger_teleport, C_TriggerTeleport);
// TODO_ENHANCED: what to do if m_iLandmark changes?
BEGIN_PREDICTION_DATA(C_TriggerTeleport)
// DEFINE_PRED_FIELD(m_iLandmark, FIELD_STRING, FTYPEDESC_INSENDTABLE),
END_PREDICTION_DATA();
void RecvProxy_LandMark(const CRecvProxyData *pData, void *pStruct, void *pOut)
{
C_TriggerTeleport *entity = (C_TriggerTeleport *) pStruct;
Q_strncpy( entity->m_iLandmark, pData->m_Value.m_pString, MAX_PATH );
}
IMPLEMENT_CLIENTCLASS_DT(C_TriggerTeleport, DT_TriggerTeleport, CTriggerTeleport)
RecvPropString(RECVINFO(m_iLandmark), NULL, RecvProxy_LandMark)
END_RECV_TABLE();
void C_TriggerTeleport::Touch( CBaseEntity *pOther )
{
CBaseEntity *pentTarget = NULL;
if (!PassesTriggerFilters(pOther))
{
return;
}
// The activator and caller are the same
pentTarget = UTIL_FindEntityByName( pentTarget, m_target, NULL, pOther, pOther );
if (!pentTarget)
{
return;
}
//
// If a landmark was specified, offset the player relative to the landmark.
//
CBaseEntity *pentLandmark = NULL;
Vector vecLandmarkOffset(0, 0, 0);
// The activator and caller are the same
pentLandmark = UTIL_FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther );
if (pentLandmark)
{
vecLandmarkOffset = pOther->GetAbsOrigin() - pentLandmark->GetAbsOrigin();
}
pOther->SetGroundEntity( NULL );
Vector tmp = pentTarget->GetAbsOrigin();
if (!pentLandmark && pOther->IsPlayer())
{
// make origin adjustments in case the teleportee is a player. (origin in center, not at feet)
tmp.z -= pOther->WorldAlignMins().z;
}
//
// Only modify the toucher's angles and zero their velocity if no landmark was specified.
//
const QAngle *pAngles = NULL;
Vector *pVelocity = NULL;
#ifdef HL1_DLL
Vector vecZero(0,0,0);
#endif
if (!pentLandmark && !HasSpawnFlags(SF_TELEPORT_PRESERVE_ANGLES) )
{
pAngles = &pentTarget->GetAbsAngles();
#ifdef HL1_DLL
pVelocity = &vecZero;
#else
pVelocity = NULL; //BUGBUG - This does not set the player's velocity to zero!!!
#endif
}
tmp += vecLandmarkOffset;
UTIL_SetOrigin( pOther, tmp );
if (pAngles)
{
if (!pOther->IsPlayer())
{
pOther->SetLocalAngles( *pAngles );
}
else
{
if ((C_BasePlayer*)pOther == C_BasePlayer::GetLocalPlayer())
{
auto angles = *pAngles;
prediction->SetViewOrigin( tmp );
// This needs to be set only once!
if (prediction->IsFirstTimePredicted())
{
engine->SetViewAngles( angles );
}
}
}
}
}
// TODO_ENHANCED: point entity can change ? If yes should be predictable... ?
class C_PointEntity : public CBaseEntity
{
public:
DECLARE_CLASS( C_PointEntity, CBaseEntity );
DECLARE_NETWORKCLASS();
virtual void Spawn( void );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual bool KeyValue( const char *szKeyName, const char *szValue );
private:
};
IMPLEMENT_CLIENTCLASS_DT(C_PointEntity, DT_PointEntity, CPointEntity)
END_RECV_TABLE();
void C_PointEntity::Spawn( void )
{
SetSolid( SOLID_NONE );
// UTIL_SetSize(this, vec3_origin, vec3_origin);
}
bool C_PointEntity::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "mins" ) || FStrEq( szKeyName, "maxs" ) )
{
Warning("Warning! Can't specify mins/maxs for point entities! (%s)\n", GetClassname() );
return true;
}
return BaseClass::KeyValue( szKeyName, szValue );
}
LINK_ENTITY_TO_CLASS( info_teleport_destination, C_PointEntity );

View file

@ -0,0 +1,21 @@
#ifndef C_TRIGGERS_TELEPORT_H
#define C_TRIGGERS_TELEPORT_H
#include "c_triggers.h"
#include "predictable_entity.h"
constexpr int SF_TELEPORT_PRESERVE_ANGLES = 0x20; // Preserve angles even when a local landmark is not specified
class C_TriggerTeleport : public C_BaseTrigger
{
public:
DECLARE_CLASS( C_TriggerTeleport, C_BaseTrigger );
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
virtual void Touch( CBaseEntity *pOther );
char m_iLandmark[MAX_PATH];
};
#endif // C_TRIGGERS_TELEPORT_H

View file

@ -88,10 +88,11 @@ BEGIN_DATADESC( C_BaseTrigger )
END_DATADESC() END_DATADESC()
// TODO_ENHANCED: this should be predicted, but since we don't have yet proper spawn, m_target would be an empty string all the time.
BEGIN_PREDICTION_DATA(C_BaseTrigger) BEGIN_PREDICTION_DATA(C_BaseTrigger)
DEFINE_PRED_FIELD(m_bDisabled, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE), // DEFINE_PRED_FIELD(m_bDisabled, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE),
DEFINE_PRED_FIELD(m_target, FIELD_STRING, FTYPEDESC_INSENDTABLE), // DEFINE_PRED_FIELD(m_target, FIELD_STRING, FTYPEDESC_INSENDTABLE),
DEFINE_PRED_FIELD(m_iFilterName, FIELD_STRING, FTYPEDESC_INSENDTABLE), // DEFINE_PRED_FIELD(m_iFilterName, FIELD_STRING, FTYPEDESC_INSENDTABLE),
// DEFINE_PRED_ARRAY(m_hPredictedTouchingEntities, FIELD_EHANDLE, MAX_EDICTS, FTYPEDESC_PRIVATE), // DEFINE_PRED_ARRAY(m_hPredictedTouchingEntities, FIELD_EHANDLE, MAX_EDICTS, FTYPEDESC_PRIVATE),
// DEFINE_PRED_FIELD(m_iCountPredictedTouchingEntities, FIELD_INTEGER, FTYPEDESC_PRIVATE) // DEFINE_PRED_FIELD(m_iCountPredictedTouchingEntities, FIELD_INTEGER, FTYPEDESC_PRIVATE)
END_PREDICTION_DATA(); END_PREDICTION_DATA();
@ -107,6 +108,13 @@ void RecvProxy_FilterName(const CRecvProxyData *pData, void *pStruct, void *pOut
entity->m_hFilter = static_cast<C_BaseFilter *>(UTIL_FindEntityByName(entity->m_iFilterName)); entity->m_hFilter = static_cast<C_BaseFilter *>(UTIL_FindEntityByName(entity->m_iFilterName));
} }
void RecvProxy_Target(const CRecvProxyData *pData, void *pStruct, void *pOut)
{
C_BaseTrigger *entity = (C_BaseTrigger *) pStruct;
Q_strncpy( entity->m_target, pData->m_Value.m_pString, MAX_PATH );
}
// Incase server decides to change m_bDisabled // Incase server decides to change m_bDisabled
void RecvProxy_Disabled(const CRecvProxyData *pData, void *pStruct, void *pOut) void RecvProxy_Disabled(const CRecvProxyData *pData, void *pStruct, void *pOut)
{ {
@ -120,7 +128,7 @@ void RecvProxy_Disabled(const CRecvProxyData *pData, void *pStruct, void *pOut)
IMPLEMENT_CLIENTCLASS_DT(C_BaseTrigger, DT_BaseTrigger, CBaseTrigger) IMPLEMENT_CLIENTCLASS_DT(C_BaseTrigger, DT_BaseTrigger, CBaseTrigger)
RecvPropInt(RECVINFO(m_bDisabled), NULL, RecvProxy_Disabled), RecvPropInt(RECVINFO(m_bDisabled), NULL, RecvProxy_Disabled),
RecvPropString(RECVINFO(m_target)), RecvPropString(RECVINFO(m_target), NULL, RecvProxy_Target),
RecvPropString(RECVINFO(m_iFilterName), NULL, RecvProxy_FilterName), RecvPropString(RECVINFO(m_iFilterName), NULL, RecvProxy_FilterName),
END_RECV_TABLE(); END_RECV_TABLE();

View file

@ -250,7 +250,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity )
SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED), SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED),
// Send the name // Send the name
SendPropString (SENDINFO(m_iName)), SendPropStringT (SENDINFO(m_iName)),
SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ), SendPropInt (SENDINFO_NAME( m_MoveType, movetype ), MOVETYPE_MAX_BITS, SPROP_UNSIGNED ),
SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ), SendPropInt (SENDINFO_NAME( m_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ),
@ -3477,6 +3477,7 @@ int CBaseEntity::UpdateTransmitState()
return SetTransmitState( FL_EDICT_ALWAYS ); return SetTransmitState( FL_EDICT_ALWAYS );
} }
// TODO_ENHANCED: this needs to be always now since teleports are predicted.
// by default cull against PVS // by default cull against PVS
return SetTransmitState( FL_EDICT_PVSCHECK ); return SetTransmitState( FL_EDICT_PVSCHECK );
} }
@ -4066,7 +4067,8 @@ static void TeleportEntity( CBaseEntity *pSourceEntity, TeleportListEntry_t &ent
{ {
if ( newAngles ) if ( newAngles )
{ {
pTeleport->SetLocalAngles( *newAngles ); pTeleport->SetLocalAngles(*newAngles);
// TODO_ENHANCED: this isn't needed anymore. Should be set client side now.
if ( pTeleport->IsPlayer() ) if ( pTeleport->IsPlayer() )
{ {
CBasePlayer *pPlayer = (CBasePlayer *)pTeleport; CBasePlayer *pPlayer = (CBasePlayer *)pTeleport;

View file

@ -2673,10 +2673,12 @@ class CPointEntity : public CBaseEntity
{ {
public: public:
DECLARE_CLASS( CPointEntity, CBaseEntity ); DECLARE_CLASS( CPointEntity, CBaseEntity );
DECLARE_NETWORKCLASS();
void Spawn( void ); void Spawn( void );
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
virtual bool KeyValue( const char *szKeyName, const char *szValue ); virtual bool KeyValue( const char *szKeyName, const char *szValue );
virtual int UpdateTransmitState( void );
private: private:
}; };

View file

@ -22,6 +22,10 @@ void CPointEntity::Spawn( void )
// UTIL_SetSize(this, vec3_origin, vec3_origin); // UTIL_SetSize(this, vec3_origin, vec3_origin);
} }
int CPointEntity::UpdateTransmitState( void )
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
class CNullEntity : public CBaseEntity class CNullEntity : public CBaseEntity
{ {

View file

@ -9,6 +9,7 @@
#include "const.h" #include "const.h"
#include "dt_send.h" #include "dt_send.h"
#include "dt_utlvector_send.h" #include "dt_utlvector_send.h"
#include "networkvar.h"
#include "player.h" #include "player.h"
#include "predictable_entity.h" #include "predictable_entity.h"
#include "saverestore.h" #include "saverestore.h"
@ -2337,15 +2338,20 @@ class CTriggerTeleport : public CBaseTrigger
{ {
public: public:
DECLARE_CLASS( CTriggerTeleport, CBaseTrigger ); DECLARE_CLASS( CTriggerTeleport, CBaseTrigger );
DECLARE_NETWORKCLASS();
void Spawn( void ); void Spawn( void );
void Touch( CBaseEntity *pOther ); void Touch( CBaseEntity *pOther );
string_t m_iLandmark; CNetworkVar(string_t, m_iLandmark);
DECLARE_DATADESC(); DECLARE_DATADESC();
}; };
IMPLEMENT_SERVERCLASS_ST(CTriggerTeleport, DT_TriggerTeleport)
SendPropStringT(SENDINFO(m_iLandmark))
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport ); LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport );
BEGIN_DATADESC( CTriggerTeleport ) BEGIN_DATADESC( CTriggerTeleport )
@ -2394,7 +2400,7 @@ void CTriggerTeleport::Touch( CBaseEntity *pOther )
// //
CBaseEntity *pentLandmark = NULL; CBaseEntity *pentLandmark = NULL;
Vector vecLandmarkOffset(0, 0, 0); Vector vecLandmarkOffset(0, 0, 0);
if (m_iLandmark != NULL_STRING) if (m_iLandmark.Get() != NULL_STRING)
{ {
// The activator and caller are the same // The activator and caller are the same
pentLandmark = gEntList.FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther ); pentLandmark = gEntList.FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther );
@ -2435,10 +2441,22 @@ void CTriggerTeleport::Touch( CBaseEntity *pOther )
#endif #endif
} }
tmp += vecLandmarkOffset; tmp += vecLandmarkOffset;
pOther->Teleport( &tmp, pAngles, pVelocity );
auto player = dynamic_cast<CBasePlayer*>(pOther);
// TODO_ENHANCED: There's better way to do this with fixangle ...
// So we can enforce server angles ...
if (player)
{
pAngles = NULL;
}
pOther->Teleport(&tmp, pAngles, pVelocity);
} }
IMPLEMENT_SERVERCLASS_ST(CPointEntity, DT_PointEntity)
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity ); LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity );