From 340eeb7e0341fc11178d99fbfa3ab8a3f6ae0a28 Mon Sep 17 00:00:00 2001 From: Kamay Xutax Date: Fri, 23 Aug 2024 18:24:10 +0200 Subject: [PATCH] Added trigger teleport prediction --- game/client/c_baseentity.cpp | 8 +- game/client/client_base.vpc | 1 + game/client/css_enhanced/c_trigger_push.cpp | 15 +- .../css_enhanced/c_trigger_teleport.cpp | 152 ++++++++++++++++++ game/client/css_enhanced/c_trigger_teleport.h | 21 +++ game/client/css_enhanced/c_triggers_base.cpp | 16 +- game/server/baseentity.cpp | 6 +- game/server/baseentity.h | 2 + game/server/subs.cpp | 4 + game/server/triggers.cpp | 26 ++- 10 files changed, 232 insertions(+), 19 deletions(-) create mode 100644 game/client/css_enhanced/c_trigger_teleport.cpp create mode 100644 game/client/css_enhanced/c_trigger_teleport.h diff --git a/game/client/c_baseentity.cpp b/game/client/c_baseentity.cpp index 4ad229ca65..d4dc01d12b 100644 --- a/game/client/c_baseentity.cpp +++ b/game/client/c_baseentity.cpp @@ -365,6 +365,12 @@ BEGIN_RECV_TABLE_NOBASE( C_BaseEntity, DT_PredictableId ) END_RECV_TABLE() #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) 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 ) ), // Receive the name - RecvPropString(RECVINFO(m_iName)), + RecvPropString(RECVINFO(m_iName), NULL, RecvProxy_Name), RecvPropInt( "movetype", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveType ), RecvPropInt( "movecollide", 0, SIZEOF_IGNORE, 0, RecvProxy_MoveCollide ), diff --git a/game/client/client_base.vpc b/game/client/client_base.vpc index 9d43c1fc15..cdc9f58555 100644 --- a/game/client/client_base.vpc +++ b/game/client/client_base.vpc @@ -175,6 +175,7 @@ $Project $File "css_enhanced\c_triggers_base.cpp" $File "css_enhanced\c_triggers.cpp" $File "css_enhanced\c_trigger_push.cpp" + $File "css_enhanced\c_trigger_teleport.cpp" $File "css_enhanced\variant_t.cpp" $File "hl2\C_Func_Monitor.cpp" $File "geiger.cpp" diff --git a/game/client/css_enhanced/c_trigger_push.cpp b/game/client/css_enhanced/c_trigger_push.cpp index 30bd62429f..09b1fa4dc2 100644 --- a/game/client/css_enhanced/c_trigger_push.cpp +++ b/game/client/css_enhanced/c_trigger_push.cpp @@ -1,5 +1,6 @@ #include "cbase.h" #include "datamap.h" +#include "dt_recv.h" #include "util_shared.h" #include "c_trigger_push.h" @@ -8,16 +9,14 @@ 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 // 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) RecvPropVector(RECVINFO(m_vecPushDir)), RecvPropFloat(RECVINFO(m_flAlternateTicksFix)), diff --git a/game/client/css_enhanced/c_trigger_teleport.cpp b/game/client/css_enhanced/c_trigger_teleport.cpp new file mode 100644 index 0000000000..27480af06c --- /dev/null +++ b/game/client/css_enhanced/c_trigger_teleport.cpp @@ -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 ); \ No newline at end of file diff --git a/game/client/css_enhanced/c_trigger_teleport.h b/game/client/css_enhanced/c_trigger_teleport.h new file mode 100644 index 0000000000..02c53cd80b --- /dev/null +++ b/game/client/css_enhanced/c_trigger_teleport.h @@ -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 diff --git a/game/client/css_enhanced/c_triggers_base.cpp b/game/client/css_enhanced/c_triggers_base.cpp index f6e5e2217e..8265c1226d 100644 --- a/game/client/css_enhanced/c_triggers_base.cpp +++ b/game/client/css_enhanced/c_triggers_base.cpp @@ -88,10 +88,11 @@ BEGIN_DATADESC( C_BaseTrigger ) 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) - DEFINE_PRED_FIELD(m_bDisabled, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_FIELD(m_target, FIELD_STRING, FTYPEDESC_INSENDTABLE), - DEFINE_PRED_FIELD(m_iFilterName, FIELD_STRING, FTYPEDESC_INSENDTABLE), + // DEFINE_PRED_FIELD(m_bDisabled, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE), + // DEFINE_PRED_FIELD(m_target, 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_FIELD(m_iCountPredictedTouchingEntities, FIELD_INTEGER, FTYPEDESC_PRIVATE) END_PREDICTION_DATA(); @@ -107,6 +108,13 @@ void RecvProxy_FilterName(const CRecvProxyData *pData, void *pStruct, void *pOut entity->m_hFilter = static_cast(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 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) 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), END_RECV_TABLE(); diff --git a/game/server/baseentity.cpp b/game/server/baseentity.cpp index b7a6f885dd..0fac433a37 100644 --- a/game/server/baseentity.cpp +++ b/game/server/baseentity.cpp @@ -250,7 +250,7 @@ IMPLEMENT_SERVERCLASS_ST_NOBASE( CBaseEntity, DT_BaseEntity ) SendPropInt (SENDINFO(m_iParentAttachment), NUM_PARENTATTACHMENT_BITS, SPROP_UNSIGNED), // 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_MoveCollide, movecollide ), MOVECOLLIDE_MAX_BITS, SPROP_UNSIGNED ), @@ -3477,6 +3477,7 @@ int CBaseEntity::UpdateTransmitState() return SetTransmitState( FL_EDICT_ALWAYS ); } + // TODO_ENHANCED: this needs to be always now since teleports are predicted. // by default cull against PVS return SetTransmitState( FL_EDICT_PVSCHECK ); } @@ -4066,7 +4067,8 @@ static void TeleportEntity( CBaseEntity *pSourceEntity, TeleportListEntry_t &ent { if ( newAngles ) { - pTeleport->SetLocalAngles( *newAngles ); + pTeleport->SetLocalAngles(*newAngles); + // TODO_ENHANCED: this isn't needed anymore. Should be set client side now. if ( pTeleport->IsPlayer() ) { CBasePlayer *pPlayer = (CBasePlayer *)pTeleport; diff --git a/game/server/baseentity.h b/game/server/baseentity.h index 37d40ae97c..04d3861077 100644 --- a/game/server/baseentity.h +++ b/game/server/baseentity.h @@ -2673,10 +2673,12 @@ class CPointEntity : public CBaseEntity { public: DECLARE_CLASS( CPointEntity, CBaseEntity ); + DECLARE_NETWORKCLASS(); void Spawn( void ); virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; } virtual bool KeyValue( const char *szKeyName, const char *szValue ); + virtual int UpdateTransmitState( void ); private: }; diff --git a/game/server/subs.cpp b/game/server/subs.cpp index b8215b8a99..5b0359ad43 100644 --- a/game/server/subs.cpp +++ b/game/server/subs.cpp @@ -22,6 +22,10 @@ void CPointEntity::Spawn( void ) // UTIL_SetSize(this, vec3_origin, vec3_origin); } +int CPointEntity::UpdateTransmitState( void ) +{ + return SetTransmitState( FL_EDICT_ALWAYS ); +} class CNullEntity : public CBaseEntity { diff --git a/game/server/triggers.cpp b/game/server/triggers.cpp index ed5f78fde4..904c36b45d 100644 --- a/game/server/triggers.cpp +++ b/game/server/triggers.cpp @@ -9,6 +9,7 @@ #include "const.h" #include "dt_send.h" #include "dt_utlvector_send.h" +#include "networkvar.h" #include "player.h" #include "predictable_entity.h" #include "saverestore.h" @@ -2337,15 +2338,20 @@ class CTriggerTeleport : public CBaseTrigger { public: DECLARE_CLASS( CTriggerTeleport, CBaseTrigger ); + DECLARE_NETWORKCLASS(); void Spawn( void ); void Touch( CBaseEntity *pOther ); - string_t m_iLandmark; + CNetworkVar(string_t, m_iLandmark); DECLARE_DATADESC(); }; +IMPLEMENT_SERVERCLASS_ST(CTriggerTeleport, DT_TriggerTeleport) + SendPropStringT(SENDINFO(m_iLandmark)) +END_SEND_TABLE(); + LINK_ENTITY_TO_CLASS( trigger_teleport, CTriggerTeleport ); BEGIN_DATADESC( CTriggerTeleport ) @@ -2394,7 +2400,7 @@ void CTriggerTeleport::Touch( CBaseEntity *pOther ) // CBaseEntity *pentLandmark = NULL; Vector vecLandmarkOffset(0, 0, 0); - if (m_iLandmark != NULL_STRING) + if (m_iLandmark.Get() != NULL_STRING) { // The activator and caller are the same pentLandmark = gEntList.FindEntityByName(pentLandmark, m_iLandmark, NULL, pOther, pOther ); @@ -2435,10 +2441,22 @@ void CTriggerTeleport::Touch( CBaseEntity *pOther ) #endif } - tmp += vecLandmarkOffset; - pOther->Teleport( &tmp, pAngles, pVelocity ); + tmp += vecLandmarkOffset; + + auto player = dynamic_cast(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 );