358 lines
10 KiB
SourcePawn
358 lines
10 KiB
SourcePawn
#include <sourcemod>
|
|
#include <PlayerManager>
|
|
#include <sdktools>
|
|
#include <basecomm>
|
|
#include <ccc>
|
|
#include <clientprefs>
|
|
#include <ripext>
|
|
#tryinclude <zombiereloaded>
|
|
#include <selfmute>
|
|
#include <connect>
|
|
#include <voice>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
Handle g_hCheckMutes = null;
|
|
Handle g_hCookieTorchMuted = null;
|
|
|
|
bool g_bWebclient[MAXPLAYERS + 1];
|
|
bool g_bReRunWebClientName;
|
|
bool g_bSkipInfection = false;
|
|
|
|
char g_sLastWebclientIp[64];
|
|
|
|
int g_iHttpFileCounter;
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "NoSteam CELT Voice override",
|
|
author = "jenz",
|
|
version = "1.5"
|
|
};
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
Format(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), "");
|
|
g_hCheckMutes = CreateTimer(2.0, check_mutes, _, TIMER_REPEAT);
|
|
g_hCookieTorchMuted = RegClientCookie("torch_muted", "is torch muted", CookieAccess_Protected);
|
|
}
|
|
|
|
public void OnPluginEnd()
|
|
{
|
|
if (g_hCheckMutes != null)
|
|
{
|
|
delete g_hCheckMutes;
|
|
}
|
|
}
|
|
|
|
public void set_nosteam_listen_override(int client)
|
|
{
|
|
for (int j = 1; j <= MaxClients; j++)
|
|
{
|
|
if (!IsValidClient(j) || client == j || IsFakeClient(j))
|
|
continue;
|
|
if (PM_IsPlayerSteam(j))
|
|
{
|
|
//the nosteam client is not allowed to hear the steam client because broadcastvoicedata would ear rape them.
|
|
//LogMessage("%N has muted %N", client, j);
|
|
SetListenOverride(client, j, Listen_No);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Action check_mutes(Handle timer, any data)
|
|
{
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (!IsValidClient(i) || IsFakeClient(i))
|
|
{
|
|
continue;
|
|
}
|
|
if (!PM_IsPlayerSteam(i))
|
|
{
|
|
set_nosteam_listen_override(i);
|
|
}
|
|
else
|
|
{
|
|
//maybe this is a fix? maybe not.
|
|
clear_steam_listen_override(i);
|
|
}
|
|
for (int j = 1; j <= MaxClients; j++)
|
|
{
|
|
if (!IsValidClient(j) || i == j || IsFakeClient(j))
|
|
{
|
|
continue;
|
|
}
|
|
bool isMuted = BaseComm_IsClientMuted(j);
|
|
if (!isMuted)
|
|
{
|
|
isMuted = SelfMute_IsIgnoring(i, j);
|
|
}
|
|
//PrintToChatAll("i: %N. j: %N. isMuted: %i", i, j, isMuted);
|
|
ClientMutedOtherClient(i, j, isMuted);
|
|
}
|
|
if (g_bReRunWebClientName && g_bWebclient[i])
|
|
{
|
|
char dummy[64];
|
|
OnClientAuthorized(i, dummy);
|
|
g_bReRunWebClientName = false;
|
|
}
|
|
}
|
|
|
|
|
|
//sourceTV special case, index 0.
|
|
for (int j = 1; j <= MaxClients; j++)
|
|
{
|
|
if (!IsValidClient(j) || IsFakeClient(j))
|
|
{
|
|
continue;
|
|
}
|
|
//yeah its monkey work but server can probably handle it fine.
|
|
char sBuffer[2];
|
|
GetClientCookie(j, g_hCookieTorchMuted, sBuffer, sizeof(sBuffer));
|
|
//PrintToChatAll("sBuffer: %s. j: %N", sBuffer, j);
|
|
if(sBuffer[0] != '\0')
|
|
{
|
|
ClientMutedOtherClient(j, 0, true);
|
|
}
|
|
else
|
|
{
|
|
ClientMutedOtherClient(j, 0, false);
|
|
}
|
|
}
|
|
|
|
//check if webclient has new IP every 2 seconds through endpoint.
|
|
PollWebclientCurrentIp();
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public void clear_steam_listen_override(int client)
|
|
{
|
|
for (int j = 1; j <= MaxClients; j++)
|
|
{
|
|
if (!IsValidClient(j) || client == j || IsFakeClient(j))
|
|
continue;
|
|
if (PM_IsPlayerSteam(j) && !BaseComm_IsClientMuted(j) && !SelfMute_IsIgnoring(client, j))
|
|
{
|
|
SetListenOverride(client, j, Listen_Default);
|
|
}
|
|
}
|
|
}
|
|
|
|
//AI slop
|
|
public void SendWebclientNameToSignaling(const char[] webclientName)
|
|
{
|
|
char url[256];
|
|
// Adjust port/path to match your signaling servers actual endpoint
|
|
FormatEx(url, sizeof(url), "http://127.0.0.1:3000/webclient-name");
|
|
|
|
HTTPRequest hRequest = new HTTPRequest(url);
|
|
|
|
// Send the name as a POST body parameter
|
|
hRequest.AppendFormParam("name", "%s", webclientName);
|
|
hRequest.PostForm(OnWebclientNamePosted);
|
|
}
|
|
|
|
public void OnWebclientNamePosted(HTTPResponse response, any value)
|
|
{
|
|
// Fire-and-forget: we dont care about the response. ripext closes the
|
|
// request handle for us once this callback returns.
|
|
}
|
|
|
|
public void OnMapStart()
|
|
{
|
|
g_bReRunWebClientName = true;
|
|
}
|
|
|
|
public void OnClientDisconnect(int client)
|
|
{
|
|
g_bWebclient[client] = false;
|
|
}
|
|
|
|
public void OnClientPostAdminCheck(int client)
|
|
{
|
|
g_bWebclient[client] = false;
|
|
if (IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
char sIP[32];
|
|
GetClientIP(client, sIP, sizeof(sIP));
|
|
|
|
char allowed_ips[128];
|
|
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
|
if (sv_set_steam_id_ips != null)
|
|
{
|
|
sv_set_steam_id_ips.GetString(allowed_ips, sizeof(allowed_ips));
|
|
if (StrEqual(sIP, allowed_ips))
|
|
{
|
|
g_bWebclient[client] = true;
|
|
g_bSkipInfection = GetConVarInt(FindConVar("zr_infect_mzombie_ratio")) > 10 ? true : false;
|
|
|
|
//this works fine with one webclient only existing.
|
|
char webclientName[64];
|
|
GetConVarString(FindConVar("sv_webclient_name"), webclientName, sizeof(webclientName));
|
|
SetClientName(client, webclientName);
|
|
SendWebclientNameToSignaling(webclientName);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnClientAuthorized(int client, const char[] auth)
|
|
{
|
|
if (IsFakeClient(client))
|
|
{
|
|
return;
|
|
}
|
|
|
|
SetClientNoSteam(client, false);
|
|
if (!PM_IsPlayerSteam(client))
|
|
{
|
|
SetClientNoSteam(client, true);
|
|
//LogMessage("here in onauthorized client: %N", client);
|
|
//setting it too early does not work, so some delay needed.
|
|
CreateTimer(10.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
CreateTimer(20.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
CreateTimer(30.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
CreateTimer(40.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
CreateTimer(50.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
CreateTimer(60.0, Timer_SendVoiceInit, GetClientSerial(client));
|
|
}
|
|
}
|
|
|
|
public Action CCC_OnChatMessage(int client, int author, const char[] message)
|
|
{
|
|
if (!g_bWebclient[client])
|
|
{
|
|
return Plugin_Continue;
|
|
}
|
|
int index = FindCharInString(message, '"', false);
|
|
int index1 = FindCharInString(message, ';', false);
|
|
if (index != -1 || index1 != -1)
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
#if defined _zr_included
|
|
public Action ZR_OnClientMotherZombieEligible(int client)
|
|
{
|
|
if (g_bWebclient[client] && g_bSkipInfection)
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
return Plugin_Continue;
|
|
}
|
|
#endif
|
|
|
|
public Action Timer_SendVoiceInit(Handle timer, int Serial)
|
|
{
|
|
int client;
|
|
if ((client = GetClientFromSerial(Serial)) == 0)
|
|
{
|
|
return Plugin_Handled;
|
|
}
|
|
if (IsValidClient(client) && !PM_IsPlayerSteam(client))
|
|
{
|
|
SendCeltVoiceInit(client);
|
|
}
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
/**
|
|
* Polls the signaling server for the current webclient controller's real IP.
|
|
* If the IP has changed since the last
|
|
* check and a webclient player is currently connected, forces them to
|
|
* reconnect with the new password via ClientCommand.
|
|
*
|
|
* ripext's HTTPResponse only exposes the body as decoded JSON, unlike
|
|
* SteamWorks' GetHTTPResponseBodyCallback which handed back a plain char[].
|
|
* Rather than change the signaling server's response format, we use
|
|
* HTTPRequest.DownloadFile() to write the raw body to a temp file and read
|
|
* it back as plain text, keeping the endpoint untouched.
|
|
**/
|
|
public void PollWebclientCurrentIp()
|
|
{
|
|
g_iHttpFileCounter++;
|
|
|
|
char sPath[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sPath, sizeof(sPath), "data/nosteamcelt_ip_%d.tmp", g_iHttpFileCounter);
|
|
|
|
HTTPRequest hRequest = new HTTPRequest("http://127.0.0.1:3000/webclient-current-ip");
|
|
hRequest.DownloadFile(sPath, OnWebclientCurrentIpDownloaded, g_iHttpFileCounter);
|
|
}
|
|
|
|
public void OnWebclientCurrentIpDownloaded(HTTPStatus status, any value)
|
|
{
|
|
char sPath[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, sPath, sizeof(sPath), "data/nosteamcelt_ip_%d.tmp", value);
|
|
|
|
if (status != HTTPStatus_OK)
|
|
{
|
|
DeleteFile(sPath);
|
|
return;
|
|
}
|
|
|
|
char newIp[64];
|
|
newIp[0] = '\0';
|
|
|
|
File hFile = OpenFile(sPath, "r");
|
|
if (hFile != null)
|
|
{
|
|
hFile.ReadLine(newIp, sizeof(newIp));
|
|
delete hFile;
|
|
}
|
|
DeleteFile(sPath);
|
|
|
|
TrimString(newIp);
|
|
|
|
if (newIp[0] == '\0')
|
|
return;
|
|
|
|
if (StrEqual(newIp, g_sLastWebclientIp))
|
|
return;
|
|
|
|
for (int i = 1; i <= MaxClients; i++)
|
|
{
|
|
if (IsValidClient(i) && g_bWebclient[i])
|
|
{
|
|
strcopy(g_sLastWebclientIp, sizeof(g_sLastWebclientIp), newIp);
|
|
LogMessage("Forcing webclient reconnect for client %N with new IP: %s", i, newIp);
|
|
ClientCommand(i, "retry");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public EConnect OnClientPreConnectEx(const char[] sName, char sPassword[255], const char[] sIP, const char[] sSteam32ID, char sRejectReason[255])
|
|
{
|
|
char allowedIp[64];
|
|
ConVar sv_set_steam_id_ips = FindConVar("sv_set_steam_id_ips");
|
|
if (sv_set_steam_id_ips != null)
|
|
{
|
|
sv_set_steam_id_ips.GetString(allowedIp, sizeof(allowedIp));
|
|
//this is to handle that the connect extension receives the password parameter for the webclient as its real connecting IP.
|
|
//works both for the client command retry and for the case that somebody presses the connect button in the clients Menu as the same password
|
|
//is applied again.
|
|
if (StrEqual(sIP, allowedIp) && strlen(g_sLastWebclientIp) > 0)
|
|
{
|
|
strcopy(sPassword, 255, g_sLastWebclientIp);
|
|
LogMessage("Backfilled empty password for webclient with cached IP: %s", g_sLastWebclientIp);
|
|
}
|
|
}
|
|
// ET_LowEvent takes the LOWEST value returned across all plugins hooking this forward.
|
|
//this plugin returns 1 (Accept), meanwhile the reserved slot plugin still might return 0 (reject) or -1 (async) to overrule the decision from this return value.
|
|
return k_OnClientPreConnectEx_Accept;
|
|
}
|
|
|
|
stock bool IsValidClient(int client)
|
|
{
|
|
if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
|
|
return true;
|
|
return false;
|
|
}
|
|
|