Test
This commit is contained in:
@@ -15,6 +15,12 @@
|
|||||||
"library" "engine"
|
"library" "engine"
|
||||||
"linux" "@_ZN11CBaseClient12SendSnapshotEP12CClientFrame"
|
"linux" "@_ZN11CBaseClient12SendSnapshotEP12CClientFrame"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"CBaseServer__WriteTempEntities"
|
||||||
|
{
|
||||||
|
"library" "engine"
|
||||||
|
"linux" "@_ZN11CBaseServer17WriteTempEntitiesEP11CBaseClientP14CFrameSnapshotS3_R8bf_writei"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-401
@@ -34,179 +34,16 @@
|
|||||||
#include "CDetour/detours.h"
|
#include "CDetour/detours.h"
|
||||||
#include <sourcehook.h>
|
#include <sourcehook.h>
|
||||||
#include <iclient.h>
|
#include <iclient.h>
|
||||||
|
#include <iserver.h>
|
||||||
#include <igameevents.h>
|
#include <igameevents.h>
|
||||||
#include <iplayerinfo.h>
|
#include <iplayerinfo.h>
|
||||||
#include <smartptr.h>
|
|
||||||
#include <platform.h>
|
|
||||||
#include <inetchannel.h>
|
|
||||||
#include <vprof.h>
|
|
||||||
#include <soundinfo.h>
|
|
||||||
|
|
||||||
// // Custom
|
class CFrameSnapshot;
|
||||||
// #include <netmessages.h>
|
class CClientFrame;
|
||||||
|
|
||||||
#define NET_MAX_PAYLOAD 288000 // largest message we can send in bytes
|
|
||||||
|
|
||||||
class SVC_Sounds
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void SetReliable( bool state);
|
|
||||||
virtual bool WriteToBuffer( bf_write &buffer ) ; // returns true if writing was OK
|
|
||||||
public:
|
|
||||||
|
|
||||||
bool m_bReliableSound;
|
|
||||||
int m_nNumSounds;
|
|
||||||
int m_nLength;
|
|
||||||
bf_read m_DataIn;
|
|
||||||
bf_write m_DataOut;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ConVar sv_netspike_on_reliable_snapshot_overflow;
|
|
||||||
|
|
||||||
extern float host_frametime_unbounded;
|
|
||||||
extern float host_frametime_stddeviation;
|
|
||||||
|
|
||||||
class NET_Tick
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NET_Tick( int tick, float hostFrametime, float hostFrametime_stddeviation )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
bool WriteToBuffer( bf_write &buffer );
|
|
||||||
};
|
|
||||||
|
|
||||||
class CLocalNetworkBackdoor;
|
|
||||||
|
|
||||||
// The client will set this if it decides to use the fast path.
|
|
||||||
extern CLocalNetworkBackdoor *g_pLocalNetworkBackdoor;
|
|
||||||
|
|
||||||
#define TRACE_PACKET( text )
|
|
||||||
#define tmZoneFiltered(...)
|
|
||||||
#define TMZF_NONE 0x0000
|
|
||||||
|
|
||||||
typedef char *HTELEMETRY;
|
|
||||||
|
|
||||||
struct TelemetryData
|
|
||||||
{
|
|
||||||
HTELEMETRY tmContext[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
PLATFORM_INTERFACE TelemetryData g_Telemetry;
|
|
||||||
|
|
||||||
#define TELEMETRY_LEVEL0 g_Telemetry.tmContext[0] // high level tmZone()
|
|
||||||
#define TELEMETRY_LEVEL1 g_Telemetry.tmContext[1] // lower level tmZone(), tmZoneFiltered()
|
|
||||||
#define TELEMETRY_LEVEL2 g_Telemetry.tmContext[2] // VPROF_0
|
|
||||||
#define TELEMETRY_LEVEL3 g_Telemetry.tmContext[3] // VPROF_1
|
|
||||||
#define TELEMETRY_LEVEL4 g_Telemetry.tmContext[4] // VPROF_2
|
|
||||||
#define TELEMETRY_LEVEL5 g_Telemetry.tmContext[5] // VPROF_3
|
|
||||||
#define TELEMETRY_LEVEL6 g_Telemetry.tmContext[6] // VPROF_4
|
|
||||||
|
|
||||||
class CFrameSnapshot
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void AddReference();
|
|
||||||
void ReleaseReference();
|
|
||||||
};
|
|
||||||
|
|
||||||
class CClientFrame
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CFrameSnapshot* GetSnapshot();
|
|
||||||
public:
|
|
||||||
int tick_count; // server tick of this snapshot
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Index of snapshot entry that stores the entities that were active and the serial numbers
|
|
||||||
// for the frame number this packed entity corresponds to
|
|
||||||
// m_pSnapshot MUST be private to force using SetSnapshot(), see reference counters
|
|
||||||
CFrameSnapshot *m_pSnapshot;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CBaseServer;
|
|
||||||
|
|
||||||
class CBaseClient : public IGameEventListener2, public IClient
|
class CBaseClient : public IGameEventListener2, public IClient
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
bool IsTracing();
|
|
||||||
void TraceNetworkData( bf_write &msg, PRINTF_FORMAT_STRING char const *fmt, ... );
|
|
||||||
void TraceNetworkMsg( int nBits, PRINTF_FORMAT_STRING char const *fmt, ... );
|
|
||||||
|
|
||||||
virtual CClientFrame *GetDeltaFrame( int nTick );
|
|
||||||
virtual void WriteGameSounds(bf_write &buf);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void OnRequestFullUpdate();
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Array index in svs.clients:
|
|
||||||
int m_nClientSlot;
|
|
||||||
// entity index of this client (different from clientSlot+1 in HLTV and Replay mode):
|
|
||||||
int m_nEntityIndex;
|
|
||||||
|
|
||||||
CBaseServer *m_Server; // pointer to server object
|
|
||||||
bool m_bIsHLTV; // if this a HLTV proxy ?
|
|
||||||
|
|
||||||
bool m_bIsReplay; // if this is a Replay proxy ?
|
|
||||||
|
|
||||||
//===== NETWORK ============
|
|
||||||
INetChannel *m_NetChannel; // The client's net connection.
|
|
||||||
int m_nSignonState; // connection state
|
|
||||||
int m_nDeltaTick; // -1 = no compression. This is where the server is creating the
|
|
||||||
// compressed info from.
|
|
||||||
int m_nStringTableAckTick; // Highest tick acked for string tables (usually m_nDeltaTick, except when it's -1)
|
|
||||||
int m_nSignonTick; // tick the client got his signon data
|
|
||||||
CSmartPtr<CFrameSnapshot,CRefCountAccessorLongName> m_pLastSnapshot; // last send snapshot
|
|
||||||
|
|
||||||
|
|
||||||
// This is used when we send out a nodelta packet to put the client in a state where we wait
|
|
||||||
// until we get an ack from them on this packet.
|
|
||||||
// This is for 3 reasons:
|
|
||||||
// 1. A client requesting a nodelta packet means they're screwed so no point in deluging them with data.
|
|
||||||
// Better to send the uncompressed data at a slow rate until we hear back from them (if at all).
|
|
||||||
// 2. Since the nodelta packet deletes all client entities, we can't ever delta from a packet previous to it.
|
|
||||||
// 3. It can eat up a lot of CPU on the server to keep building nodelta packets while waiting for
|
|
||||||
// a client to get back on its feet.
|
|
||||||
int m_nForceWaitForTick;
|
|
||||||
|
|
||||||
bool m_bFakePlayer; // JAC: This client is a fake player controlled by the game DLL
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
SNAPSHOT_SCRATCH_BUFFER_SIZE = 160000,
|
|
||||||
};
|
|
||||||
|
|
||||||
unsigned int m_SnapshotScratchBuffer[ SNAPSHOT_SCRATCH_BUFFER_SIZE / 4 ];
|
|
||||||
|
|
||||||
public:
|
|
||||||
void StartTrace( bf_write &msg );
|
|
||||||
void EndTrace( bf_write &msg );
|
|
||||||
|
|
||||||
int m_iTracing; // 0 = not active, 1 = active for this frame, 2 = forced active
|
|
||||||
};
|
|
||||||
|
|
||||||
class CNetworkStringTableContainer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void WriteUpdateMessage( CBaseClient *client, int tick_ack, bf_write &buf );
|
|
||||||
};
|
|
||||||
|
|
||||||
class CBaseServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void WriteDeltaEntities( CBaseClient *client, CClientFrame *to, CClientFrame *from, bf_write &pBuf );
|
|
||||||
virtual void WriteTempEntities( CBaseClient *client, CFrameSnapshot *to, CFrameSnapshot *from, bf_write &pBuf, int nMaxEnts );
|
|
||||||
virtual bool IsMultiplayer( void );
|
|
||||||
public:
|
|
||||||
CNetworkStringTableContainer *m_StringTables;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CGameClient: public CBaseClient
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CUtlVector<SoundInfo_t> m_Sounds; // game sounds
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SSF g_SSF; /**< Global singleton for extension's main interface */
|
SSF g_SSF; /**< Global singleton for extension's main interface */
|
||||||
@@ -217,258 +54,46 @@ IGameConfig *g_pGameConf = NULL;
|
|||||||
CGlobalVars *gpGlobals = NULL;
|
CGlobalVars *gpGlobals = NULL;
|
||||||
|
|
||||||
CDetour *g_Detour_CBaseClient__SendSnapshot = NULL;
|
CDetour *g_Detour_CBaseClient__SendSnapshot = NULL;
|
||||||
|
CDetour *g_Detour_CBaseServer__WriteTempEntities = NULL;
|
||||||
|
|
||||||
ConVar *g_SvSSFLog = CreateConVar("sv_ssf_log", "0", FCVAR_NOTIFY, "Log ssf debug print statements.");
|
ConVar *g_SvSSFLog = CreateConVar("sv_ssf_log", "0", FCVAR_NOTIFY, "Log ssf debug print statements.");
|
||||||
ConVar *g_sv_multiplayer_maxtempentities = CreateConVar("sv_multiplayer_maxtempentities", "32");
|
ConVar *g_sv_multiplayer_maxtempentities = CreateConVar("sv_multiplayer_maxtempentities", "32");
|
||||||
ConVar *g_sv_multiplayer_maxsounds = CreateConVar("sv_multiplayer_sounds", "20");
|
|
||||||
ConVar *g_sv_sound_discardextraunreliable = CreateConVar( "sv_sound_discardextraunreliable", "1" );
|
|
||||||
|
|
||||||
int Custom_CGameClient_FillSoundsMessage(CGameClient *pGameClient, SVC_Sounds &msg, int nMaxSounds)
|
// ConVar *g_sv_multiplayer_maxsounds = CreateConVar("sv_multiplayer_sounds", "20");
|
||||||
|
// ConVar *g_sv_sound_discardextraunreliable = CreateConVar( "sv_sound_discardextraunreliable", "1" );
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER5(CBaseServer__WriteTempEntities, void, CBaseClient *, client, CFrameSnapshot *, pCurrentSnapshot, CFrameSnapshot *, pLastSnapshot, bf_write, &buf, int, ev_max)
|
||||||
{
|
{
|
||||||
int i, count = pGameClient->m_Sounds.Count();
|
if (!client->IsHLTV() && !client->IsReplay())
|
||||||
|
|
||||||
// Discard events if we have too many to signal with 8 bits
|
|
||||||
if ( count > nMaxSounds )
|
|
||||||
count = nMaxSounds;
|
|
||||||
|
|
||||||
// Nothing to send
|
|
||||||
if ( !count )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
SoundInfo_t defaultSound;
|
|
||||||
SoundInfo_t *pDeltaSound = &defaultSound;
|
|
||||||
|
|
||||||
msg.m_nNumSounds = count;
|
|
||||||
msg.m_bReliableSound = false;
|
|
||||||
|
|
||||||
Assert( msg.m_DataOut.GetNumBitsLeft() > 0 );
|
|
||||||
|
|
||||||
for ( i = 0 ; i < count; i++ )
|
|
||||||
{
|
{
|
||||||
SoundInfo_t &sound = pGameClient->m_Sounds[ i ];
|
if (g_SvSSFLog->GetBool())
|
||||||
sound.WriteDelta( pDeltaSound, msg.m_DataOut );
|
|
||||||
pDeltaSound = &pGameClient->m_Sounds[ i ];
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove added events from list
|
|
||||||
if ( g_sv_sound_discardextraunreliable->GetBool() )
|
|
||||||
{
|
|
||||||
if ( pGameClient->m_Sounds.Count() != count )
|
|
||||||
{
|
{
|
||||||
DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , pGameClient->m_Sounds.Count() - count, pGameClient->GetClientName() );
|
g_pSM->LogMessage(myself, "SSF:CBaseServer__WriteTempEntities maxentities before: %d", ev_max);
|
||||||
}
|
|
||||||
pGameClient->m_Sounds.RemoveAll();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int remove = pGameClient->m_Sounds.Count() - ( count + nMaxSounds );
|
|
||||||
if ( remove > 0 )
|
|
||||||
{
|
|
||||||
DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , remove, pGameClient->GetClientName() );
|
|
||||||
count+= remove;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( count > 0 )
|
// send all unreliable temp entities between last and current frame
|
||||||
|
// send max 64 events in multi player, 255 in SP
|
||||||
|
ev_max = client->GetServer()->IsMultiplayer() ? g_sv_multiplayer_maxtempentities->GetInt() : 255;
|
||||||
|
|
||||||
|
if (g_SvSSFLog->GetBool())
|
||||||
{
|
{
|
||||||
pGameClient->m_Sounds.RemoveMultiple( 0, count );
|
g_pSM->LogMessage(myself, "SSF:CBaseServer__WriteTempEntities maxentities after: %d", ev_max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert( pGameClient->m_Sounds.Count() <= nMaxSounds ); // keep ev_max temp ent for next update
|
if (g_SvSSFLog->GetBool())
|
||||||
|
|
||||||
return msg.m_nNumSounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Custom_CGameClient_WriteGameSounds(CGameClient *pGameClient, bf_write &buf, int nMaxSounds)
|
|
||||||
{
|
|
||||||
if ( pGameClient->m_Sounds.Count() <= 0 )
|
|
||||||
return;
|
|
||||||
|
|
||||||
char data[NET_MAX_PAYLOAD];
|
|
||||||
SVC_Sounds msg;
|
|
||||||
msg.m_DataOut.StartWriting( data, sizeof(data) );
|
|
||||||
|
|
||||||
msg.SetReliable( false );
|
|
||||||
int nSoundCount = Custom_CGameClient_FillSoundsMessage( pGameClient, msg, nMaxSounds );
|
|
||||||
msg.WriteToBuffer( buf );
|
|
||||||
|
|
||||||
if ( pGameClient->IsTracing() )
|
|
||||||
{
|
{
|
||||||
pGameClient->TraceNetworkData( buf, "Sounds [count=%d]", nSoundCount );
|
g_pSM->LogMessage(myself, "SSF:CBaseServer__WriteTempEntities maxentities: %d", ev_max);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Custom_CBaseClient_SendSnapshot(CBaseClient *pBaseClient, CClientFrame *pFrame)
|
|
||||||
{
|
|
||||||
// never send the same snapshot twice
|
|
||||||
if ( pBaseClient->m_pLastSnapshot == pFrame->GetSnapshot() )
|
|
||||||
{
|
|
||||||
pBaseClient->m_NetChannel->Transmit();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we send a full snapshot (no delta-compression) before, wait until client
|
DETOUR_MEMBER_CALL(CBaseServer__WriteTempEntities)(client, pCurrentSnapshot, pLastSnapshot, buf, ev_max);
|
||||||
// received and acknowledge that update. don't spam client with full updates
|
|
||||||
if ( pBaseClient->m_nForceWaitForTick > 0 )
|
|
||||||
{
|
|
||||||
// just continue transmitting reliable data
|
|
||||||
pBaseClient->m_NetChannel->Transmit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VPROF_BUDGET( "SendSnapshot", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
|
||||||
// tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
|
|
||||||
|
|
||||||
bf_write msg( "CBaseClient::SendSnapshot", pBaseClient->m_SnapshotScratchBuffer, sizeof( pBaseClient->m_SnapshotScratchBuffer ) );
|
|
||||||
|
|
||||||
TRACE_PACKET( ( "SendSnapshot(%d)\n", pFrame->tick_count ) );
|
|
||||||
|
|
||||||
// now create client snapshot packet
|
|
||||||
CClientFrame * deltaFrame = pBaseClient->m_nDeltaTick < 0 ? NULL : pBaseClient->GetDeltaFrame( pBaseClient->m_nDeltaTick ); // NULL if delta_tick is not found
|
|
||||||
if ( !deltaFrame )
|
|
||||||
{
|
|
||||||
// We need to send a full update and reset the instanced baselines
|
|
||||||
pBaseClient->OnRequestFullUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pBaseClient->IsTracing() )
|
|
||||||
{
|
|
||||||
pBaseClient->StartTrace( msg );
|
|
||||||
}
|
|
||||||
|
|
||||||
// send tick time
|
|
||||||
NET_Tick tickmsg( pFrame->tick_count, host_frametime_unbounded, host_frametime_stddeviation );
|
|
||||||
|
|
||||||
if ( !tickmsg.WriteToBuffer( msg ) )
|
|
||||||
{
|
|
||||||
pBaseClient->Disconnect( "ERROR! Couldnt write snapshot to buffer" );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( pBaseClient->IsTracing() )
|
|
||||||
{
|
|
||||||
pBaseClient->TraceNetworkData( msg, "NET_Tick" );
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef SHARED_NET_STRING_TABLES
|
|
||||||
// in LocalNetworkBackdoor mode we updated the stringtables already in SV_ComputeClientPacks()
|
|
||||||
if ( !g_pLocalNetworkBackdoor )
|
|
||||||
{
|
|
||||||
// Update shared client/server string tables. Must be done before sending entities
|
|
||||||
pBaseClient->m_Server->m_StringTables->WriteUpdateMessage( pBaseClient, pBaseClient->GetMaxAckTickCount(), msg );
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int nDeltaStartBit = 0;
|
|
||||||
if ( pBaseClient->IsTracing() )
|
|
||||||
{
|
|
||||||
nDeltaStartBit = msg.GetNumBitsWritten();
|
|
||||||
}
|
|
||||||
|
|
||||||
// send entity update, delta compressed if deltaFrame != NULL
|
|
||||||
pBaseClient->m_Server->WriteDeltaEntities( pBaseClient, pFrame, deltaFrame, msg );
|
|
||||||
|
|
||||||
if ( pBaseClient->IsTracing() )
|
|
||||||
{
|
|
||||||
int nBits = msg.GetNumBitsWritten() - nDeltaStartBit;
|
|
||||||
pBaseClient->TraceNetworkMsg( nBits, "Total Delta" );
|
|
||||||
}
|
|
||||||
|
|
||||||
// send all unreliable temp entities between last and current frame
|
|
||||||
// send max 64 events in multi player, 255 in SP
|
|
||||||
int nMaxTempEnts = pBaseClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxtempentities->GetInt() : 255;
|
|
||||||
pBaseClient->m_Server->WriteTempEntities( pBaseClient, pFrame->GetSnapshot(), pBaseClient->m_pLastSnapshot.GetObject(), msg, nMaxTempEnts );
|
|
||||||
|
|
||||||
if ( pBaseClient->IsTracing() )
|
|
||||||
{
|
|
||||||
pBaseClient->TraceNetworkData( msg, "Temp Entities" );
|
|
||||||
}
|
|
||||||
|
|
||||||
int nMaxSounds = pBaseClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
|
||||||
Custom_CGameClient_WriteGameSounds( (CGameClient *)pBaseClient, msg, nMaxSounds );
|
|
||||||
|
|
||||||
// write message to packet and check for overflow
|
|
||||||
if ( msg.IsOverflowed() )
|
|
||||||
{
|
|
||||||
if ( !deltaFrame )
|
|
||||||
{
|
|
||||||
// if this is a reliable snapshot, drop the client
|
|
||||||
pBaseClient->Disconnect( "ERROR! Reliable snaphsot overflow." );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// unreliable snapshots may be dropped
|
|
||||||
ConMsg ("WARNING: msg overflowed for %s\n", pBaseClient->GetClientName());
|
|
||||||
msg.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember this snapshot
|
|
||||||
pBaseClient->m_pLastSnapshot = pFrame->GetSnapshot();
|
|
||||||
|
|
||||||
// Don't send the datagram to fakeplayers unless sv_stressbots is on (which will make m_NetChannel non-null).
|
|
||||||
if ( pBaseClient->m_bFakePlayer && !pBaseClient->m_NetChannel )
|
|
||||||
{
|
|
||||||
pBaseClient->m_nDeltaTick = pFrame->tick_count;
|
|
||||||
pBaseClient->m_nStringTableAckTick = pBaseClient->m_nDeltaTick;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool bSendOK;
|
|
||||||
|
|
||||||
// is this is a full entity update (no delta) ?
|
|
||||||
if ( !deltaFrame )
|
|
||||||
{
|
|
||||||
VPROF_BUDGET( "SendSnapshot Transmit Full", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
|
||||||
|
|
||||||
// transmit snapshot as reliable data chunk
|
|
||||||
bSendOK = pBaseClient->m_NetChannel->SendData( msg );
|
|
||||||
bSendOK = bSendOK && pBaseClient->m_NetChannel->Transmit();
|
|
||||||
|
|
||||||
// remember this tickcount we send the reliable snapshot
|
|
||||||
// so we can continue sending other updates if this has been acknowledged
|
|
||||||
pBaseClient->m_nForceWaitForTick = pFrame->tick_count;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VPROF_BUDGET( "SendSnapshot Transmit Delta", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
|
||||||
|
|
||||||
// just send it as unreliable snapshot
|
|
||||||
bSendOK = pBaseClient->m_NetChannel->SendDatagram( &msg ) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !bSendOK )
|
|
||||||
{
|
|
||||||
pBaseClient->Disconnect( "ERROR! Couldn't send snapshot." );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DETOUR_DECL_MEMBER1(Custom_CBaseClient_FillSoundsMessage, int, SVC_Sounds, &msg)
|
|
||||||
{
|
|
||||||
CGameClient *pGameClient = (CGameClient *)this;
|
|
||||||
|
|
||||||
int nMaxSounds = pGameClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
|
||||||
int nResult = Custom_CGameClient_FillSoundsMessage((CGameClient *)this, msg, nMaxSounds);
|
|
||||||
RETURN_META_VALUE(MRES_SUPERCEDE, nResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
DETOUR_DECL_MEMBER1(CGameClient__WriteGameSounds, void, bf_write, &buf)
|
|
||||||
{
|
|
||||||
CGameClient *pGameClient = (CGameClient *)this;
|
|
||||||
|
|
||||||
int nMaxSounds = pGameClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
|
||||||
Custom_CGameClient_WriteGameSounds((CGameClient *)this, buf, nMaxSounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DETOUR_DECL_MEMBER1(CBaseClient__SendSnapshot, void, CClientFrame *, pFrame)
|
DETOUR_DECL_MEMBER1(CBaseClient__SendSnapshot, void, CClientFrame *, pFrame)
|
||||||
{
|
{
|
||||||
CBaseClient *pBaseClient = (CBaseClient *)this;
|
CBaseClient *pBaseClient = (CBaseClient *)this;
|
||||||
|
|
||||||
Custom_CBaseClient_SendSnapshot(pBaseClient, pFrame);
|
DETOUR_MEMBER_CALL(CBaseClient__SendSnapshot)(pFrame);
|
||||||
|
|
||||||
// return DETOUR_MEMBER_CALL(CBaseClient__SendSnapshot)(pFrame);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SSF::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
bool SSF::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
||||||
@@ -479,11 +104,6 @@ bool SSF::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late
|
|||||||
|
|
||||||
ConVar_Register(0, this);
|
ConVar_Register(0, this);
|
||||||
|
|
||||||
if (g_SvSSFLog->GetBool())
|
|
||||||
{
|
|
||||||
g_pSM->LogMessage(myself, "SSF:Inside SendSnapshot");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -509,6 +129,14 @@ bool SSF::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
|||||||
}
|
}
|
||||||
g_Detour_CBaseClient__SendSnapshot->EnableDetour();
|
g_Detour_CBaseClient__SendSnapshot->EnableDetour();
|
||||||
|
|
||||||
|
g_Detour_CBaseServer__WriteTempEntities = DETOUR_CREATE_MEMBER(CBaseServer__WriteTempEntities, "CBaseServer__WriteTempEntities");
|
||||||
|
if(!g_Detour_CBaseServer__WriteTempEntities)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Failed to detour CBaseServer__WriteTempEntities.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_Detour_CBaseServer__WriteTempEntities->EnableDetour();
|
||||||
|
|
||||||
AutoExecConfig(g_pCVar, true);
|
AutoExecConfig(g_pCVar, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -522,6 +150,12 @@ void SSF::SDK_OnUnload()
|
|||||||
g_Detour_CBaseClient__SendSnapshot = NULL;
|
g_Detour_CBaseClient__SendSnapshot = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(g_Detour_CBaseServer__WriteTempEntities)
|
||||||
|
{
|
||||||
|
g_Detour_CBaseServer__WriteTempEntities->Destroy();
|
||||||
|
g_Detour_CBaseServer__WriteTempEntities = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
gameconfs->CloseGameConfigFile(g_pGameConf);
|
gameconfs->CloseGameConfigFile(g_pGameConf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,536 @@
|
|||||||
|
/**
|
||||||
|
* vim: set ts=4 :
|
||||||
|
* =============================================================================
|
||||||
|
* SourceMod Sample Extension
|
||||||
|
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||||
|
* =============================================================================
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||||
|
* Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||||
|
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||||
|
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||||
|
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||||
|
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||||
|
* this exception to all derivative works. AlliedModders LLC defines further
|
||||||
|
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||||
|
* or <http://www.sourcemod.net/license.php>.
|
||||||
|
*
|
||||||
|
* Version: $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "extension.h"
|
||||||
|
#include "extensionHelper.h"
|
||||||
|
#include "CDetour/detours.h"
|
||||||
|
#include <sourcehook.h>
|
||||||
|
#include <iclient.h>
|
||||||
|
#include <igameevents.h>
|
||||||
|
#include <iplayerinfo.h>
|
||||||
|
#include <smartptr.h>
|
||||||
|
#include <platform.h>
|
||||||
|
#include <inetchannel.h>
|
||||||
|
#include <vprof.h>
|
||||||
|
#include <soundinfo.h>
|
||||||
|
|
||||||
|
// // Custom
|
||||||
|
// #include <netmessages.h>
|
||||||
|
|
||||||
|
#define NET_MAX_PAYLOAD 288000 // largest message we can send in bytes
|
||||||
|
|
||||||
|
class SVC_Sounds
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void SetReliable( bool state);
|
||||||
|
virtual bool WriteToBuffer( bf_write &buffer ) ; // returns true if writing was OK
|
||||||
|
public:
|
||||||
|
|
||||||
|
bool m_bReliableSound;
|
||||||
|
int m_nNumSounds;
|
||||||
|
int m_nLength;
|
||||||
|
bf_read m_DataIn;
|
||||||
|
bf_write m_DataOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ConVar sv_netspike_on_reliable_snapshot_overflow;
|
||||||
|
|
||||||
|
extern float host_frametime_unbounded;
|
||||||
|
extern float host_frametime_stddeviation;
|
||||||
|
|
||||||
|
class NET_Tick
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NET_Tick( int tick, float hostFrametime, float hostFrametime_stddeviation )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
bool WriteToBuffer( bf_write &buffer );
|
||||||
|
};
|
||||||
|
|
||||||
|
class CLocalNetworkBackdoor;
|
||||||
|
|
||||||
|
// The client will set this if it decides to use the fast path.
|
||||||
|
extern CLocalNetworkBackdoor *g_pLocalNetworkBackdoor;
|
||||||
|
|
||||||
|
#define TRACE_PACKET( text )
|
||||||
|
#define tmZoneFiltered(...)
|
||||||
|
#define TMZF_NONE 0x0000
|
||||||
|
|
||||||
|
typedef char *HTELEMETRY;
|
||||||
|
|
||||||
|
struct TelemetryData
|
||||||
|
{
|
||||||
|
HTELEMETRY tmContext[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
PLATFORM_INTERFACE TelemetryData g_Telemetry;
|
||||||
|
|
||||||
|
#define TELEMETRY_LEVEL0 g_Telemetry.tmContext[0] // high level tmZone()
|
||||||
|
#define TELEMETRY_LEVEL1 g_Telemetry.tmContext[1] // lower level tmZone(), tmZoneFiltered()
|
||||||
|
#define TELEMETRY_LEVEL2 g_Telemetry.tmContext[2] // VPROF_0
|
||||||
|
#define TELEMETRY_LEVEL3 g_Telemetry.tmContext[3] // VPROF_1
|
||||||
|
#define TELEMETRY_LEVEL4 g_Telemetry.tmContext[4] // VPROF_2
|
||||||
|
#define TELEMETRY_LEVEL5 g_Telemetry.tmContext[5] // VPROF_3
|
||||||
|
#define TELEMETRY_LEVEL6 g_Telemetry.tmContext[6] // VPROF_4
|
||||||
|
|
||||||
|
class CFrameSnapshot
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void AddReference();
|
||||||
|
void ReleaseReference();
|
||||||
|
};
|
||||||
|
|
||||||
|
class CClientFrame
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CFrameSnapshot* GetSnapshot();
|
||||||
|
public:
|
||||||
|
int tick_count; // server tick of this snapshot
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Index of snapshot entry that stores the entities that were active and the serial numbers
|
||||||
|
// for the frame number this packed entity corresponds to
|
||||||
|
// m_pSnapshot MUST be private to force using SetSnapshot(), see reference counters
|
||||||
|
CFrameSnapshot *m_pSnapshot;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CBaseServer;
|
||||||
|
|
||||||
|
class CBaseClient : public IGameEventListener2, public IClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool IsTracing();
|
||||||
|
void TraceNetworkData( bf_write &msg, PRINTF_FORMAT_STRING char const *fmt, ... );
|
||||||
|
void TraceNetworkMsg( int nBits, PRINTF_FORMAT_STRING char const *fmt, ... );
|
||||||
|
|
||||||
|
virtual CClientFrame *GetDeltaFrame( int nTick );
|
||||||
|
virtual void WriteGameSounds(bf_write &buf);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void OnRequestFullUpdate();
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Array index in svs.clients:
|
||||||
|
int m_nClientSlot;
|
||||||
|
// entity index of this client (different from clientSlot+1 in HLTV and Replay mode):
|
||||||
|
int m_nEntityIndex;
|
||||||
|
|
||||||
|
CBaseServer *m_Server; // pointer to server object
|
||||||
|
bool m_bIsHLTV; // if this a HLTV proxy ?
|
||||||
|
|
||||||
|
bool m_bIsReplay; // if this is a Replay proxy ?
|
||||||
|
|
||||||
|
//===== NETWORK ============
|
||||||
|
INetChannel *m_NetChannel; // The client's net connection.
|
||||||
|
int m_nSignonState; // connection state
|
||||||
|
int m_nDeltaTick; // -1 = no compression. This is where the server is creating the
|
||||||
|
// compressed info from.
|
||||||
|
int m_nStringTableAckTick; // Highest tick acked for string tables (usually m_nDeltaTick, except when it's -1)
|
||||||
|
int m_nSignonTick; // tick the client got his signon data
|
||||||
|
CSmartPtr<CFrameSnapshot,CRefCountAccessorLongName> m_pLastSnapshot; // last send snapshot
|
||||||
|
|
||||||
|
|
||||||
|
// This is used when we send out a nodelta packet to put the client in a state where we wait
|
||||||
|
// until we get an ack from them on this packet.
|
||||||
|
// This is for 3 reasons:
|
||||||
|
// 1. A client requesting a nodelta packet means they're screwed so no point in deluging them with data.
|
||||||
|
// Better to send the uncompressed data at a slow rate until we hear back from them (if at all).
|
||||||
|
// 2. Since the nodelta packet deletes all client entities, we can't ever delta from a packet previous to it.
|
||||||
|
// 3. It can eat up a lot of CPU on the server to keep building nodelta packets while waiting for
|
||||||
|
// a client to get back on its feet.
|
||||||
|
int m_nForceWaitForTick;
|
||||||
|
|
||||||
|
bool m_bFakePlayer; // JAC: This client is a fake player controlled by the game DLL
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
SNAPSHOT_SCRATCH_BUFFER_SIZE = 160000,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int m_SnapshotScratchBuffer[ SNAPSHOT_SCRATCH_BUFFER_SIZE / 4 ];
|
||||||
|
|
||||||
|
public:
|
||||||
|
void StartTrace( bf_write &msg );
|
||||||
|
void EndTrace( bf_write &msg );
|
||||||
|
|
||||||
|
int m_iTracing; // 0 = not active, 1 = active for this frame, 2 = forced active
|
||||||
|
};
|
||||||
|
|
||||||
|
class CNetworkStringTableContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void WriteUpdateMessage( CBaseClient *client, int tick_ack, bf_write &buf );
|
||||||
|
};
|
||||||
|
|
||||||
|
class CBaseServer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void WriteDeltaEntities( CBaseClient *client, CClientFrame *to, CClientFrame *from, bf_write &pBuf );
|
||||||
|
virtual void WriteTempEntities( CBaseClient *client, CFrameSnapshot *to, CFrameSnapshot *from, bf_write &pBuf, int nMaxEnts );
|
||||||
|
virtual bool IsMultiplayer( void );
|
||||||
|
public:
|
||||||
|
CNetworkStringTableContainer *m_StringTables;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CGameClient: public CBaseClient
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CUtlVector<SoundInfo_t> m_Sounds; // game sounds
|
||||||
|
};
|
||||||
|
|
||||||
|
SSF g_SSF; /**< Global singleton for extension's main interface */
|
||||||
|
|
||||||
|
SMEXT_LINK(&g_SSF);
|
||||||
|
|
||||||
|
IGameConfig *g_pGameConf = NULL;
|
||||||
|
CGlobalVars *gpGlobals = NULL;
|
||||||
|
|
||||||
|
CDetour *g_Detour_CBaseClient__SendSnapshot = NULL;
|
||||||
|
|
||||||
|
ConVar *g_SvSSFLog = CreateConVar("sv_ssf_log", "0", FCVAR_NOTIFY, "Log ssf debug print statements.");
|
||||||
|
ConVar *g_sv_multiplayer_maxtempentities = CreateConVar("sv_multiplayer_maxtempentities", "32");
|
||||||
|
ConVar *g_sv_multiplayer_maxsounds = CreateConVar("sv_multiplayer_sounds", "20");
|
||||||
|
ConVar *g_sv_sound_discardextraunreliable = CreateConVar( "sv_sound_discardextraunreliable", "1" );
|
||||||
|
|
||||||
|
int Custom_CGameClient_FillSoundsMessage(CGameClient *pGameClient, SVC_Sounds &msg, int nMaxSounds)
|
||||||
|
{
|
||||||
|
int i, count = pGameClient->m_Sounds.Count();
|
||||||
|
|
||||||
|
// Discard events if we have too many to signal with 8 bits
|
||||||
|
if ( count > nMaxSounds )
|
||||||
|
count = nMaxSounds;
|
||||||
|
|
||||||
|
// Nothing to send
|
||||||
|
if ( !count )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
SoundInfo_t defaultSound;
|
||||||
|
SoundInfo_t *pDeltaSound = &defaultSound;
|
||||||
|
|
||||||
|
msg.m_nNumSounds = count;
|
||||||
|
msg.m_bReliableSound = false;
|
||||||
|
|
||||||
|
Assert( msg.m_DataOut.GetNumBitsLeft() > 0 );
|
||||||
|
|
||||||
|
for ( i = 0 ; i < count; i++ )
|
||||||
|
{
|
||||||
|
SoundInfo_t &sound = pGameClient->m_Sounds[ i ];
|
||||||
|
sound.WriteDelta( pDeltaSound, msg.m_DataOut );
|
||||||
|
pDeltaSound = &pGameClient->m_Sounds[ i ];
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove added events from list
|
||||||
|
if ( g_sv_sound_discardextraunreliable->GetBool() )
|
||||||
|
{
|
||||||
|
if ( pGameClient->m_Sounds.Count() != count )
|
||||||
|
{
|
||||||
|
DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , pGameClient->m_Sounds.Count() - count, pGameClient->GetClientName() );
|
||||||
|
}
|
||||||
|
pGameClient->m_Sounds.RemoveAll();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int remove = pGameClient->m_Sounds.Count() - ( count + nMaxSounds );
|
||||||
|
if ( remove > 0 )
|
||||||
|
{
|
||||||
|
DevMsg( 2, "Warning! Dropped %i unreliable sounds for client %s.\n" , remove, pGameClient->GetClientName() );
|
||||||
|
count+= remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( count > 0 )
|
||||||
|
{
|
||||||
|
pGameClient->m_Sounds.RemoveMultiple( 0, count );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert( pGameClient->m_Sounds.Count() <= nMaxSounds ); // keep ev_max temp ent for next update
|
||||||
|
|
||||||
|
return msg.m_nNumSounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Custom_CGameClient_WriteGameSounds(CGameClient *pGameClient, bf_write &buf, int nMaxSounds)
|
||||||
|
{
|
||||||
|
if ( pGameClient->m_Sounds.Count() <= 0 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
char data[NET_MAX_PAYLOAD];
|
||||||
|
SVC_Sounds msg;
|
||||||
|
msg.m_DataOut.StartWriting( data, sizeof(data) );
|
||||||
|
|
||||||
|
msg.SetReliable( false );
|
||||||
|
int nSoundCount = Custom_CGameClient_FillSoundsMessage( pGameClient, msg, nMaxSounds );
|
||||||
|
msg.WriteToBuffer( buf );
|
||||||
|
|
||||||
|
if ( pGameClient->IsTracing() )
|
||||||
|
{
|
||||||
|
pGameClient->TraceNetworkData( buf, "Sounds [count=%d]", nSoundCount );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Custom_CBaseClient_SendSnapshot(CBaseClient *pBaseClient, CClientFrame *pFrame)
|
||||||
|
{
|
||||||
|
// never send the same snapshot twice
|
||||||
|
if ( pBaseClient->m_pLastSnapshot == pFrame->GetSnapshot() )
|
||||||
|
{
|
||||||
|
pBaseClient->m_NetChannel->Transmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we send a full snapshot (no delta-compression) before, wait until client
|
||||||
|
// received and acknowledge that update. don't spam client with full updates
|
||||||
|
if ( pBaseClient->m_nForceWaitForTick > 0 )
|
||||||
|
{
|
||||||
|
// just continue transmitting reliable data
|
||||||
|
pBaseClient->m_NetChannel->Transmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VPROF_BUDGET( "SendSnapshot", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||||
|
// tmZoneFiltered( TELEMETRY_LEVEL0, 50, TMZF_NONE, "%s", __FUNCTION__ );
|
||||||
|
|
||||||
|
bf_write msg( "CBaseClient::SendSnapshot", pBaseClient->m_SnapshotScratchBuffer, sizeof( pBaseClient->m_SnapshotScratchBuffer ) );
|
||||||
|
|
||||||
|
TRACE_PACKET( ( "SendSnapshot(%d)\n", pFrame->tick_count ) );
|
||||||
|
|
||||||
|
// now create client snapshot packet
|
||||||
|
CClientFrame * deltaFrame = pBaseClient->m_nDeltaTick < 0 ? NULL : pBaseClient->GetDeltaFrame( pBaseClient->m_nDeltaTick ); // NULL if delta_tick is not found
|
||||||
|
if ( !deltaFrame )
|
||||||
|
{
|
||||||
|
// We need to send a full update and reset the instanced baselines
|
||||||
|
pBaseClient->OnRequestFullUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pBaseClient->IsTracing() )
|
||||||
|
{
|
||||||
|
pBaseClient->StartTrace( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
// send tick time
|
||||||
|
NET_Tick tickmsg( pFrame->tick_count, host_frametime_unbounded, host_frametime_stddeviation );
|
||||||
|
|
||||||
|
if ( !tickmsg.WriteToBuffer( msg ) )
|
||||||
|
{
|
||||||
|
pBaseClient->Disconnect( "ERROR! Couldnt write snapshot to buffer" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pBaseClient->IsTracing() )
|
||||||
|
{
|
||||||
|
pBaseClient->TraceNetworkData( msg, "NET_Tick" );
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SHARED_NET_STRING_TABLES
|
||||||
|
// in LocalNetworkBackdoor mode we updated the stringtables already in SV_ComputeClientPacks()
|
||||||
|
if ( !g_pLocalNetworkBackdoor )
|
||||||
|
{
|
||||||
|
// Update shared client/server string tables. Must be done before sending entities
|
||||||
|
pBaseClient->m_Server->m_StringTables->WriteUpdateMessage( pBaseClient, pBaseClient->GetMaxAckTickCount(), msg );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int nDeltaStartBit = 0;
|
||||||
|
if ( pBaseClient->IsTracing() )
|
||||||
|
{
|
||||||
|
nDeltaStartBit = msg.GetNumBitsWritten();
|
||||||
|
}
|
||||||
|
|
||||||
|
// send entity update, delta compressed if deltaFrame != NULL
|
||||||
|
pBaseClient->m_Server->WriteDeltaEntities( pBaseClient, pFrame, deltaFrame, msg );
|
||||||
|
|
||||||
|
if ( pBaseClient->IsTracing() )
|
||||||
|
{
|
||||||
|
int nBits = msg.GetNumBitsWritten() - nDeltaStartBit;
|
||||||
|
pBaseClient->TraceNetworkMsg( nBits, "Total Delta" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// send all unreliable temp entities between last and current frame
|
||||||
|
// send max 64 events in multi player, 255 in SP
|
||||||
|
int nMaxTempEnts = pBaseClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxtempentities->GetInt() : 255;
|
||||||
|
pBaseClient->m_Server->WriteTempEntities( pBaseClient, pFrame->GetSnapshot(), pBaseClient->m_pLastSnapshot.GetObject(), msg, nMaxTempEnts );
|
||||||
|
|
||||||
|
if ( pBaseClient->IsTracing() )
|
||||||
|
{
|
||||||
|
pBaseClient->TraceNetworkData( msg, "Temp Entities" );
|
||||||
|
}
|
||||||
|
|
||||||
|
int nMaxSounds = pBaseClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
||||||
|
Custom_CGameClient_WriteGameSounds( (CGameClient *)pBaseClient, msg, nMaxSounds );
|
||||||
|
|
||||||
|
// write message to packet and check for overflow
|
||||||
|
if ( msg.IsOverflowed() )
|
||||||
|
{
|
||||||
|
if ( !deltaFrame )
|
||||||
|
{
|
||||||
|
// if this is a reliable snapshot, drop the client
|
||||||
|
pBaseClient->Disconnect( "ERROR! Reliable snaphsot overflow." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// unreliable snapshots may be dropped
|
||||||
|
ConMsg ("WARNING: msg overflowed for %s\n", pBaseClient->GetClientName());
|
||||||
|
msg.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember this snapshot
|
||||||
|
pBaseClient->m_pLastSnapshot = pFrame->GetSnapshot();
|
||||||
|
|
||||||
|
// Don't send the datagram to fakeplayers unless sv_stressbots is on (which will make m_NetChannel non-null).
|
||||||
|
if ( pBaseClient->m_bFakePlayer && !pBaseClient->m_NetChannel )
|
||||||
|
{
|
||||||
|
pBaseClient->m_nDeltaTick = pFrame->tick_count;
|
||||||
|
pBaseClient->m_nStringTableAckTick = pBaseClient->m_nDeltaTick;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bSendOK;
|
||||||
|
|
||||||
|
// is this is a full entity update (no delta) ?
|
||||||
|
if ( !deltaFrame )
|
||||||
|
{
|
||||||
|
VPROF_BUDGET( "SendSnapshot Transmit Full", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||||
|
|
||||||
|
// transmit snapshot as reliable data chunk
|
||||||
|
bSendOK = pBaseClient->m_NetChannel->SendData( msg );
|
||||||
|
bSendOK = bSendOK && pBaseClient->m_NetChannel->Transmit();
|
||||||
|
|
||||||
|
// remember this tickcount we send the reliable snapshot
|
||||||
|
// so we can continue sending other updates if this has been acknowledged
|
||||||
|
pBaseClient->m_nForceWaitForTick = pFrame->tick_count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VPROF_BUDGET( "SendSnapshot Transmit Delta", VPROF_BUDGETGROUP_OTHER_NETWORKING );
|
||||||
|
|
||||||
|
// just send it as unreliable snapshot
|
||||||
|
bSendOK = pBaseClient->m_NetChannel->SendDatagram( &msg ) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !bSendOK )
|
||||||
|
{
|
||||||
|
pBaseClient->Disconnect( "ERROR! Couldn't send snapshot." );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(Custom_CBaseClient_FillSoundsMessage, int, SVC_Sounds, &msg)
|
||||||
|
{
|
||||||
|
CGameClient *pGameClient = (CGameClient *)this;
|
||||||
|
|
||||||
|
int nMaxSounds = pGameClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
||||||
|
int nResult = Custom_CGameClient_FillSoundsMessage((CGameClient *)this, msg, nMaxSounds);
|
||||||
|
RETURN_META_VALUE(MRES_SUPERCEDE, nResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(CGameClient__WriteGameSounds, void, bf_write, &buf)
|
||||||
|
{
|
||||||
|
CGameClient *pGameClient = (CGameClient *)this;
|
||||||
|
|
||||||
|
int nMaxSounds = pGameClient->m_Server->IsMultiplayer() ? g_sv_multiplayer_maxsounds->GetInt() : 255;
|
||||||
|
Custom_CGameClient_WriteGameSounds((CGameClient *)this, buf, nMaxSounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
DETOUR_DECL_MEMBER1(CBaseClient__SendSnapshot, void, CClientFrame *, pFrame)
|
||||||
|
{
|
||||||
|
CBaseClient *pBaseClient = (CBaseClient *)this;
|
||||||
|
|
||||||
|
Custom_CBaseClient_SendSnapshot(pBaseClient, pFrame);
|
||||||
|
|
||||||
|
// return DETOUR_MEMBER_CALL(CBaseClient__SendSnapshot)(pFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSF::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
|
||||||
|
{
|
||||||
|
GET_V_IFACE_CURRENT(GetEngineFactory, g_pCVar, ICvar, CVAR_INTERFACE_VERSION);
|
||||||
|
|
||||||
|
gpGlobals = ismm->GetCGlobals();
|
||||||
|
|
||||||
|
ConVar_Register(0, this);
|
||||||
|
|
||||||
|
if (g_SvSSFLog->GetBool())
|
||||||
|
{
|
||||||
|
g_pSM->LogMessage(myself, "SSF:Inside SendSnapshot");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSF::SDK_OnLoad(char *error, size_t maxlen, bool late)
|
||||||
|
{
|
||||||
|
char conf_error[255] = "";
|
||||||
|
if(!gameconfs->LoadGameConfigFile("ssf.games", &g_pGameConf, conf_error, sizeof(conf_error)))
|
||||||
|
{
|
||||||
|
if(conf_error[0])
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Could not read ssf.games.txt: %s\n", conf_error);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CDetourManager::Init(g_pSM->GetScriptingEngine(), g_pGameConf);
|
||||||
|
|
||||||
|
g_Detour_CBaseClient__SendSnapshot = DETOUR_CREATE_MEMBER(CBaseClient__SendSnapshot, "CBaseClient__SendSnapshot");
|
||||||
|
if(!g_Detour_CBaseClient__SendSnapshot)
|
||||||
|
{
|
||||||
|
snprintf(error, maxlen, "Failed to detour CBaseClient__SendSnapshot.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
g_Detour_CBaseClient__SendSnapshot->EnableDetour();
|
||||||
|
|
||||||
|
AutoExecConfig(g_pCVar, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSF::SDK_OnUnload()
|
||||||
|
{
|
||||||
|
if(g_Detour_CBaseClient__SendSnapshot)
|
||||||
|
{
|
||||||
|
g_Detour_CBaseClient__SendSnapshot->Destroy();
|
||||||
|
g_Detour_CBaseClient__SendSnapshot = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameconfs->CloseGameConfigFile(g_pGameConf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSF::SDK_OnAllLoaded()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SSF::RegisterConCommandBase(ConCommandBase *pVar)
|
||||||
|
{
|
||||||
|
/* Always call META_REGCVAR instead of going through the engine. */
|
||||||
|
return META_REGCVAR(pVar);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user