18
18
*/
19
19
20
20
#include " networkbasetypes.pb.h"
21
+ #include " gameevents.pb.h"
21
22
22
23
#include < stdio.h>
23
24
#include " multiaddonmanager.h"
24
25
#include " module.h"
25
26
#include " utils/plat.h"
26
27
#include " networksystem/inetworkserializer.h"
27
28
#include " networksystem/inetworkmessages.h"
29
+ #include " igameeventsystem.h"
28
30
#include " serversideclient.h"
29
31
#include " funchook.h"
30
32
#include " filesystem.h"
@@ -43,7 +45,7 @@ void Message(const char *msg, ...)
43
45
char buf[1024 ] = {};
44
46
V_vsnprintf (buf, sizeof (buf) - 1 , msg, args);
45
47
46
- ConColorMsg ( Color (0 , 255 , 200 ), " [MultiAddonManager] %s" , buf);
48
+ LoggingSystem_Log ( 0 , LS_MESSAGE, Color (0 , 255 , 200 ), " [MultiAddonManager] %s" , buf);
47
49
48
50
va_end (args);
49
51
}
@@ -104,21 +106,24 @@ HostStateRequest_t g_pfnHostStateRequest = nullptr;
104
106
funchook_t *g_pSendNetMessageHook = nullptr ;
105
107
funchook_t *g_pHostStateRequestHook = nullptr ;
106
108
107
- int g_iSendNetMessage;
108
-
109
109
class GameSessionConfiguration_t { };
110
110
111
111
SH_DECL_HOOK0_void (IServerGameDLL, GameServerSteamAPIActivated, SH_NOATTRIB, 0 );
112
112
SH_DECL_HOOK3_void (INetworkServerService, StartupServer, SH_NOATTRIB, 0 , const GameSessionConfiguration_t &, ISource2WorldSession *, const char *);
113
113
SH_DECL_HOOK6 (IServerGameClients, ClientConnect, SH_NOATTRIB, 0 , bool , CPlayerSlot, const char *, uint64, const char *, bool , CBufferString *);
114
114
SH_DECL_HOOK3_void (IServerGameDLL, GameFrame, SH_NOATTRIB, 0 , bool , bool , bool );
115
+ SH_DECL_HOOK8_void (IGameEventSystem, PostEventAbstract, SH_NOATTRIB, 0 , CSplitScreenSlot, bool , int , const uint64 *,
116
+ INetworkMessageInternal *, const CNetMessage *, unsigned long , NetChannelBufType_t);
117
+ SH_DECL_HOOK2 (IGameEventManager2, LoadEventsFromFile, SH_NOATTRIB, 0 , int , const char *, bool );
115
118
116
119
#ifdef PLATFORM_WINDOWS
117
120
constexpr int g_iSendNetMessageOffset = 15 ;
118
121
#else
119
122
constexpr int g_iSendNetMessageOffset = 16 ;
120
123
#endif
121
124
125
+ int g_iLoadEventsFromFileId = -1 ;
126
+
122
127
struct ClientJoinInfo_t
123
128
{
124
129
uint64 steamid;
@@ -127,12 +132,14 @@ struct ClientJoinInfo_t
127
132
};
128
133
129
134
CUtlVector<ClientJoinInfo_t> g_ClientsPendingAddon; // List of clients who are still downloading addons
130
- std::set <uint64> g_ClientsWithAddons; // List of clients who already downloaded everything so they don't get reconnects on mapchange/rejoin
135
+ std::unordered_set <uint64> g_ClientsWithAddons; // List of clients who already downloaded everything so they don't get reconnects on mapchange/rejoin
131
136
132
137
MultiAddonManager g_MultiAddonManager;
133
138
INetworkGameServer *g_pNetworkGameServer = nullptr ;
134
139
CSteamGameServerAPIContext g_SteamAPI;
135
140
CGlobalVars *gpGlobals = nullptr ;
141
+ IGameEventSystem *g_pGameEventSystem = nullptr ;
142
+ IGameEventManager2 *g_pGameEventManager = nullptr ;
136
143
137
144
// Interface to other plugins
138
145
CAddonManagerInterface g_AddonManagerInterface;
@@ -148,12 +155,14 @@ bool MultiAddonManager::Load(PluginId id, ISmmAPI *ismm, char *error, size_t max
148
155
GET_V_IFACE_ANY (GetServerFactory, g_pSource2Server, ISource2Server, SOURCE2SERVER_INTERFACE_VERSION);
149
156
GET_V_IFACE_ANY (GetEngineFactory, g_pNetworkServerService, INetworkServerService, NETWORKSERVERSERVICE_INTERFACE_VERSION);
150
157
GET_V_IFACE_ANY (GetEngineFactory, g_pNetworkMessages, INetworkMessages, NETWORKMESSAGES_INTERFACE_VERSION);
158
+ GET_V_IFACE_ANY (GetEngineFactory, g_pGameEventSystem, IGameEventSystem, GAMEEVENTSYSTEM_INTERFACE_VERSION);
151
159
GET_V_IFACE_ANY (GetFileSystemFactory, g_pFullFileSystem, IFileSystem, FILESYSTEM_INTERFACE_VERSION);
152
160
153
161
// Required to get the IMetamodListener events
154
162
g_SMAPI->AddListener ( this , this );
155
163
156
164
CModule engineModule (ROOTBIN, " engine2" );
165
+ CModule serverModule (GAMEBIN, " server" );
157
166
158
167
// "Discarding pending request '%s, %u'\n"
159
168
#ifdef PLATFORM_WINDOWS
@@ -181,6 +190,7 @@ bool MultiAddonManager::Load(PluginId id, ISmmAPI *ismm, char *error, size_t max
181
190
funchook_prepare (g_pHostStateRequestHook, (void **)&g_pfnHostStateRequest, (void *)Hook_HostStateRequest);
182
191
funchook_install (g_pHostStateRequestHook, 0 );
183
192
193
+ // We're using funchook even though it's a virtual function because it can be called on a different thread and SourceHook isn't thread-safe
184
194
void **pServerSideClientVTable = (void **)engineModule.FindVirtualTable (" CServerSideClient" );
185
195
g_pfnSendNetMessage = (SendNetMessage_t)pServerSideClientVTable[g_iSendNetMessageOffset];
186
196
@@ -192,6 +202,14 @@ bool MultiAddonManager::Load(PluginId id, ISmmAPI *ismm, char *error, size_t max
192
202
SH_ADD_HOOK (INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER (this , &MultiAddonManager::Hook_StartupServer), true );
193
203
SH_ADD_HOOK (IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER (this , &MultiAddonManager::Hook_ClientConnect), false );
194
204
SH_ADD_HOOK (IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER (this , &MultiAddonManager::Hook_GameFrame), true );
205
+ SH_ADD_HOOK (IGameEventSystem, PostEventAbstract, g_pGameEventSystem, SH_MEMBER (this , &MultiAddonManager::Hook_PostEvent), false );
206
+
207
+ auto pCGameEventManagerVTable = (IGameEventManager2*)serverModule.FindVirtualTable (" CGameEventManager" );
208
+
209
+ if (!pCGameEventManagerVTable)
210
+ return false ;
211
+
212
+ g_iLoadEventsFromFileId = SH_ADD_DVPHOOK (IGameEventManager2, LoadEventsFromFile, pCGameEventManagerVTable, SH_MEMBER (this , &MultiAddonManager::Hook_LoadEventsFromFile), false );
195
213
196
214
if (late)
197
215
{
@@ -218,7 +236,8 @@ bool MultiAddonManager::Unload(char *error, size_t maxlen)
218
236
SH_REMOVE_HOOK (INetworkServerService, StartupServer, g_pNetworkServerService, SH_MEMBER (this , &MultiAddonManager::Hook_StartupServer), true );
219
237
SH_REMOVE_HOOK (IServerGameClients, ClientConnect, g_pSource2GameClients, SH_MEMBER (this , &MultiAddonManager::Hook_ClientConnect), false );
220
238
SH_REMOVE_HOOK (IServerGameDLL, GameFrame, g_pSource2Server, SH_MEMBER (this , &MultiAddonManager::Hook_GameFrame), true );
221
- SH_REMOVE_HOOK_ID (g_iSendNetMessage);
239
+ SH_REMOVE_HOOK (IGameEventSystem, PostEventAbstract, g_pGameEventSystem, SH_MEMBER (this , &MultiAddonManager::Hook_PostEvent), false );
240
+ SH_REMOVE_HOOK_ID (g_iLoadEventsFromFileId);
222
241
223
242
if (g_pHostStateRequestHook)
224
243
{
@@ -791,6 +810,42 @@ void MultiAddonManager::Hook_GameFrame(bool simulating, bool bFirstTick, bool bL
791
810
}
792
811
}
793
812
813
+ bool g_bBlockDisconnectMsgs = false ;
814
+ FAKE_BOOL_CVAR (mm_block_disconnect_messages, " Whether to block \" loop shutdown\" disconnect messages" , g_bBlockDisconnectMsgs, false , false );
815
+
816
+ void MultiAddonManager::Hook_PostEvent (CSplitScreenSlot nSlot, bool bLocalOnly, int nClientCount, const uint64 *clients,
817
+ INetworkMessageInternal *pEvent, const CNetMessage *pData, unsigned long nSize, NetChannelBufType_t bufType)
818
+ {
819
+ NetMessageInfo_t *info = pEvent->GetNetMessageInfo ();
820
+
821
+ if (g_bBlockDisconnectMsgs && info->m_MessageId == GE_Source1LegacyGameEvent)
822
+ {
823
+ auto pMsg = pData->ToPB <CMsgSource1LegacyGameEvent>();
824
+
825
+ static int sDisconnectId = g_pGameEventManager->LookupEventId (" player_disconnect" );
826
+
827
+ if (pMsg->eventid () == sDisconnectId )
828
+ {
829
+ IGameEvent *pEvent = g_pGameEventManager->UnserializeEvent (*pMsg);
830
+
831
+ // This will prevent "loop shutdown" messages in the chat when clients reconnect
832
+ // As far as we're aware, there are no other cases where this reason is used
833
+ if (pEvent->GetInt (" reason" ) == NETWORK_DISCONNECT_LOOPSHUTDOWN)
834
+ *(uint64*)clients = 0 ;
835
+ }
836
+ }
837
+
838
+ RETURN_META (MRES_IGNORED);
839
+ }
840
+
841
+ int MultiAddonManager::Hook_LoadEventsFromFile (const char *filename, bool bSearchAll)
842
+ {
843
+ if (!g_pGameEventManager)
844
+ g_pGameEventManager = META_IFACEPTR (IGameEventManager2);
845
+
846
+ RETURN_META_VALUE (MRES_IGNORED, 0 );
847
+ }
848
+
794
849
const char *MultiAddonManager::GetLicense ()
795
850
{
796
851
return " GPL v3 License" ;
0 commit comments