void CForwardManager::OnSpectatorPutInServer()
{
	void *pGameClient = META_IFACEPTR(void);
	if (!pGameClient)
		RETURN_META(MRES_IGNORED);

	// The IClient vtable is + 4 from the CBaseClient vtable due to multiple inheritance.
	IClient *client = (IClient *)((intptr_t)pGameClient + 4);

	m_SpectatorPutInServerFwd->PushCell(client->GetPlayerSlot() + 1);
	m_SpectatorPutInServerFwd->Execute();

	RETURN_META(MRES_IGNORED);
}
void CForwardManager::CallOnSpectatorChatMessage_Post(HLTVServerWrapper *server, const char *msg, const char *chatgroup)
{
	int clientIndex = 0;
	if (server)
	{
		IClient *client = server->GetLastChatClient();
		if (client)
			clientIndex = client->GetPlayerSlot() + 1;
	}

	m_SpectatorChatMessagePostFwd->PushCell(clientIndex);
	m_SpectatorChatMessagePostFwd->PushString(msg);
	m_SpectatorChatMessagePostFwd->PushString(chatgroup);
	m_SpectatorChatMessagePostFwd->Execute();
}
bool CForwardManager::CallOnSpectatorChatMessage(HLTVServerWrapper *server, char *msg, int msglen, char *chatgroup, int grouplen)
{
	int clientIndex = 0;
	if (server)
	{
		IClient *client = server->GetLastChatClient();
		if (client)
			clientIndex = client->GetPlayerSlot() + 1;
	}

	m_SpectatorChatMessageFwd->PushCell(clientIndex);
	m_SpectatorChatMessageFwd->PushStringEx(msg, msglen, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
	m_SpectatorChatMessageFwd->PushStringEx(chatgroup, grouplen, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);

	cell_t res = Pl_Continue;
	m_SpectatorChatMessageFwd->Execute(&res);
	if (res >= Pl_Handled)
		return true;
	return false;
}
IClient *CForwardManager::OnSpectatorConnect(netadr_t & address, int nProtocol, int iChallenge, int iClientChallenge, int nAuthProtocol, const char *pchName, const char *pchPassword, const char *pCookie, int cbCookie)
#endif
{
	if (!pCookie || (size_t)cbCookie < sizeof(uint64))
		RETURN_META_VALUE(MRES_IGNORED, nullptr);

#if SOURCE_ENGINE == SE_CSGO
	// CS:GO doesn't send the player name in pchName, but only in the client info convars.
	// Try to extract the name from the protobuf msg.
	char playerName[MAX_PLAYER_NAME_LENGTH];
	if (ExtractPlayerName(pSplitPlayerConnectVector, playerName, sizeof(playerName)))
		pchName = playerName;
#endif

	char ipString[16];
	V_snprintf(ipString, sizeof(ipString), "%u.%u.%u.%u", address.ip[0], address.ip[1], address.ip[2], address.ip[3]);
	V_strncpy(passwordBuffer, pchPassword, 255);

	// SourceTV doesn't validate steamids?!

	char rejectReason[255];

	m_SpectatorPreConnectFwd->PushString(pchName);
	m_SpectatorPreConnectFwd->PushStringEx(passwordBuffer, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
	m_SpectatorPreConnectFwd->PushString(ipString);
	m_SpectatorPreConnectFwd->PushStringEx(rejectReason, 255, SM_PARAM_STRING_UTF8 | SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);

	cell_t retVal = 1;
	m_SpectatorPreConnectFwd->Execute(&retVal);

	IServer *server = META_IFACEPTR(IServer);
	if (retVal == 0)
	{
		if (m_bHasRejectConnectionOffset)
		{
#if SOURCE_ENGINE == SE_CSGO || SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
			SH_MCALL(server, CHLTVServer_RejectConnection)(address, rejectReason);
#else
			SH_MCALL(server, CHLTVServer_RejectConnection)(address, iClientChallenge, rejectReason);
#endif
		}
		RETURN_META_VALUE(MRES_SUPERCEDE, nullptr);
	}

	// Call the original function.
#if SOURCE_ENGINE == SE_CSGO
	IClient *client = SH_MCALL(server, CHLTVServer_ConnectClient)(address, nProtocol, iChallenge, nAuthProtocol, pchName, passwordBuffer, pCookie, cbCookie, pSplitPlayerConnectVector, bUnknown, platform, pUnknown, iUnknown);
#elif SOURCE_ENGINE == SE_LEFT4DEAD || SOURCE_ENGINE == SE_LEFT4DEAD2
	IClient *client = SH_MCALL(server, CHLTVServer_ConnectClient)(address, nProtocol, iChallenge, nAuthProtocol, pchName, passwordBuffer, pCookie, cbCookie, pSplitPlayerConnectVector, bUnknown);
#else
	IClient *client = SH_MCALL(server, CHLTVServer_ConnectClient)(address, nProtocol, iChallenge, iClientChallenge, nAuthProtocol, pchName, passwordBuffer, pCookie, cbCookie);
#endif

	if (!client)
		RETURN_META_VALUE(MRES_SUPERCEDE, nullptr);

	HookClient(client);

	HLTVServerWrapper *wrapper = g_HLTVServers.GetWrapper(server);
	if (wrapper)
	{
		HLTVClientWrapper *clientWrapper = wrapper->GetClient(client->GetPlayerSlot() + 1);
		clientWrapper->Initialize(ipString, pchPassword, client);
	}

	m_SpectatorConnectedFwd->PushCell(client->GetPlayerSlot() + 1);
	m_SpectatorConnectedFwd->Execute();

	// Don't call the hooked function again, just return its value.
	RETURN_META_VALUE(MRES_SUPERCEDE, client);
}