Files
projects-jenz/fastdl-rotator/scripting/fastdl_rotater.sp
T

203 lines
7.6 KiB
SourcePawn

#include <sourcemod>
#include <dhooks>
#include <ripext>
#include <sdktools>
#pragma semicolon 1
#pragma newdecls required
GameData gamedatafile; //Handle to the gamedata
char originalConVar[1024]; //original sv_downloadurl value
char g_cBackupURLS[2][1024];
char g_cIPaddresses[MAXPLAYERS + 1][256];
StringMap g_SteamIDRotations;
bool g_bDoesIndexHaveBZ2[sizeof(g_cBackupURLS)]; //must have same size as g_cBackupURLS
bool g_bRotated[MAXPLAYERS + 1];
ConVar downloadurl; // sv_downloadurl
public Plugin myinfo =
{
name = "Fastdownload rotater",
description = "Rotates fastdl used for clients when missing map errors happen",
author = "jenz",
url = "",
version = "1.0"
};
public void OnPluginStart()
{
g_SteamIDRotations = new StringMap();
ConVar cvar;
HookConVarChange((cvar = CreateConVar("sm_first_backup_fastdl_url", "https://uk-fastdl.unloze.com/css_ze/", "the first backup fastdl URL")), Cvar_backupurl1);
cvar.GetString(g_cBackupURLS[0], sizeof(g_cBackupURLS[]));
delete cvar;
ConVar cvar1;
HookConVarChange((cvar1 = CreateConVar("sm_second_backup_fastdl_url", "https://uk-fastdl.unloze.com/css_ze/", "the second backup fastdl URL")), Cvar_backupurl2);
cvar1.GetString(g_cBackupURLS[1], sizeof(g_cBackupURLS[]));
delete cvar1;
//we only use one backup fastdl but you could potentially have several.
downloadurl = FindConVar("sv_downloadurl"); //Save original downloadurl, so we can send it to clients who we cant locate
downloadurl.GetString(originalConVar, sizeof(originalConVar));
gamedatafile = LoadGameConfigFile("fastdl_rotater.games");
if(gamedatafile == null)
SetFailState("Cannot load fastdl_rotater.games.txt! Make sure you have it installed!");
Handle detourBuildConVarMessage = DHookCreateDetour(Address_Null, CallConv_CDECL, ReturnType_Void, ThisPointer_Ignore);
if(detourBuildConVarMessage == null)
SetFailState("Failed to create detour for Host_BuildConVarUpdateMessage!");
if(!DHookSetFromConf(detourBuildConVarMessage, gamedatafile, SDKConf_Signature, "Host_BuildConVarUpdateMessage"))
SetFailState("Failed to load Host_BuildConVarUpdateMessage signature from gamedata!");
DHookAddParam(detourBuildConVarMessage, HookParamType_Unknown);
DHookAddParam(detourBuildConVarMessage, HookParamType_Int);
DHookAddParam(detourBuildConVarMessage, HookParamType_Bool);
if(!DHookEnableDetour(detourBuildConVarMessage, false, buildConVarMessageDetCallback_Pre))
SetFailState("Failed to detour Host_BuildConVarUpdateMessage PreHook!");
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
OnMapStart();
}
public Action Event_PlayerDisconnect(Handle event, const char[] name, bool dontBroadcast)
{
int client = GetClientOfUserId(GetEventInt(event, "userid"));
char reason[256];
GetEventString(event, "reason", reason, sizeof(reason));
//requires net_disconnect_reason 1
if (StrContains(reason, "Map is missing") != -1)
{
GetClientIP(client, g_cIPaddresses[client], sizeof(g_cIPaddresses[]));
int rotated = -1;
g_SteamIDRotations.GetValue(g_cIPaddresses[client], rotated);
rotated++;
g_SteamIDRotations.SetValue(g_cIPaddresses[client], rotated, true);
}
g_bRotated[client] = false;
return Plugin_Continue;
}
public void OnMapStart()
{
//http request to the backup fastdl urls to verify if they have the bz2 files.
char sRequest[512];
char map[256];
GetCurrentMap(map, sizeof(map));
for (int i = 0; i < sizeof(g_cBackupURLS);i++)
{
g_bDoesIndexHaveBZ2[i] = false; //resetting to false until the http header finished
FormatEx(sRequest, sizeof(sRequest), "%s%s%s%s", g_cBackupURLS[i], "maps/", map, ".bsp.bz2");
HTTPRequest hRequest = new HTTPRequest(sRequest);
// ripext has no HEAD verb (SteamWorks' k_EHTTPMethodHEAD has no
// equivalent native here). Ask for just the first byte via a Range
// header on a GET instead, so we still avoid downloading the whole
// (potentially large) .bsp.bz2 just to check whether it exists.
// A server that honors Range answers 206/200 without the full body;
// one that doesn't just falls back to sending the whole file.
hRequest.SetHeader("Range", "bytes=0-0");
hRequest.Get(OnTransferComplete, i);
}
}
//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public void OnTransferComplete(HTTPResponse response, any url_index)
{
if (response.Status == HTTPStatus_OK || response.Status == HTTPStatus_PartialContent)
{
g_bDoesIndexHaveBZ2[url_index] = true;
}
else
{
g_bDoesIndexHaveBZ2[url_index] = false;
}
}
public void OnMapEnd()
{
for (int i = 0; i <= MaxClients; i++)
{
Format(g_cIPaddresses[i], sizeof(g_cIPaddresses[]), "");
}
g_SteamIDRotations = new StringMap();
}
public void Cvar_backupurl1(ConVar convar, const char[] oldValue, const char[] newValue)
{
convar.SetString(newValue);
}
public void Cvar_backupurl2(ConVar convar, const char[] oldValue, const char[] newValue)
{
convar.SetString(newValue);
}
public void OnConfigsExecuted()
{
downloadurl = FindConVar("sv_downloadurl");
downloadurl.GetString(originalConVar, sizeof(originalConVar));
}
public MRESReturn buildConVarMessageDetCallback_Pre(Handle hParams) //Second callback
{
for (int client = 1; client <= MaxClients; client++)
{
if (IsClientConnected(client))
{
//LogMessage("client %N in buildConVarMessageDetCallback_Pre", client);
char clientIPAddress[64];
GetClientIP(client, clientIPAddress, sizeof(clientIPAddress));
for (int i = 0; i < sizeof(g_cIPaddresses); i++)
{
if (StrEqual(g_cIPaddresses[i], clientIPAddress) && !g_bRotated[client])
{
g_bRotated[client] = true; //its only from disconnecting that the clients needs to be rotated again by matching IP.
int rotated = -1;
g_SteamIDRotations.GetValue(clientIPAddress, rotated);
//0 = ovh uk unloze. 1 = ovh uk unloze. 2 = default sv_downloadurl again. so not overwriting anything
rotated = rotated % 3;
//the bz2 file does not exist on the backup urls so rotate to the next working one.
while (rotated < sizeof(g_cBackupURLS) && !g_bDoesIndexHaveBZ2[rotated])
{
rotated++;
}
if (rotated < sizeof(g_cBackupURLS))
{
char map[256];
GetCurrentMap(map, sizeof(map));
g_SteamIDRotations.SetValue(clientIPAddress, rotated, true); //adapting in case of increment.
LogMessage("client %N had missing map with default sv_downloadurl on %s. using %s instead.", client, map, g_cBackupURLS[rotated]);
setConVarValue(g_cBackupURLS[rotated]);
}
break;
}
}
}
}
return MRES_Ignored;
}
void setConVarValue(char[] value) //Sets the actual ConVar value
{
int oldflags = GetConVarFlags(downloadurl);
SetConVarFlags(downloadurl, oldflags &~ FCVAR_REPLICATED);
SetConVarString(downloadurl, value, true, false);
SetConVarFlags(downloadurl, oldflags|FCVAR_REPLICATED);
}