initial release of fastdl-rotator. in case of missing map the client gets a different fastdl url
This commit is contained in:
parent
c960e3fc15
commit
d3fe6ec4dc
31
fastdl-rotator/gamedata/fastdl_rotater.games.txt
Normal file
31
fastdl-rotator/gamedata/fastdl_rotater.games.txt
Normal file
@ -0,0 +1,31 @@
|
||||
"Games"
|
||||
{
|
||||
"cstrike"
|
||||
{
|
||||
"Signatures"
|
||||
{
|
||||
"CBaseClient::SendServerInfo"
|
||||
{
|
||||
"library" "engine"
|
||||
"linux" "@_ZN11CBaseClient14SendServerInfoEv"
|
||||
}
|
||||
"Host_BuildConVarUpdateMessage"
|
||||
{
|
||||
"library" "engine"
|
||||
"linux" "@_Z29Host_BuildConVarUpdateMessageP13NET_SetConVarib"
|
||||
}
|
||||
"CBaseClientState::Disconnect"
|
||||
{
|
||||
"library" "engine"
|
||||
"linux" "@_ZN16CBaseClientState10DisconnectEPKcb"
|
||||
}
|
||||
}
|
||||
"Offsets"
|
||||
{
|
||||
"CBaseClient::GetPlayerSlot"
|
||||
{
|
||||
"linux" "3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
217
fastdl-rotator/scripting/fastdl_rotater.sp
Normal file
217
fastdl-rotator/scripting/fastdl_rotater.sp
Normal file
@ -0,0 +1,217 @@
|
||||
#include <sourcemod>
|
||||
#include <dhooks>
|
||||
#include <SteamWorks>
|
||||
#include <sdktools>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
|
||||
Handle hPlayerSlot = INVALID_HANDLE;
|
||||
GameData gamedatafile; //Handle to the gamedata
|
||||
|
||||
char originalConVar[1024]; //original sv_downloadurl value
|
||||
char g_cBackupURLS[2][1024];
|
||||
char g_cIPaddresses[MAXPLAYERS + 1][256];
|
||||
int g_icurrentClient;
|
||||
StringMap g_SteamIDRotations;
|
||||
bool g_bDoesIndexHaveBZ2[sizeof(g_cBackupURLS)]; //must have same size as g_cBackupURLS
|
||||
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://fastdl.nide.gg/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", "http://uk-fastdl.unloze.com/css_ze/", "the second backup fastdl URL")), Cvar_backupurl2);
|
||||
cvar1.GetString(g_cBackupURLS[1], sizeof(g_cBackupURLS[]));
|
||||
delete cvar1;
|
||||
|
||||
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!");
|
||||
|
||||
|
||||
Handle detourSendServerInfo = DHookCreateDetour(Address_Null, CallConv_THISCALL, ReturnType_Bool, ThisPointer_Address);
|
||||
if(detourSendServerInfo == null)
|
||||
SetFailState("Failed to create detour for CBaseClient::SendServerInfo!");
|
||||
|
||||
if(!DHookSetFromConf(detourSendServerInfo, gamedatafile, SDKConf_Signature, "CBaseClient::SendServerInfo"))
|
||||
SetFailState("Failed to load CBaseClient::SendServerInfo signature from gamedata!");
|
||||
|
||||
if(!DHookEnableDetour(detourSendServerInfo, false, sendServerInfoDetCallback_Pre))
|
||||
SetFailState("Failed to detour CBaseClient::SendServerInfo PreHook!");
|
||||
|
||||
HookEvent("player_disconnect", Event_PlayerDisconnect, EventHookMode_Pre);
|
||||
|
||||
StartPrepSDKCall(SDKCall_Raw);
|
||||
PrepSDKCall_SetFromConf(gamedatafile, SDKConf_Virtual, "CBaseClient::GetPlayerSlot");
|
||||
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
|
||||
hPlayerSlot = EndPrepSDKCall();
|
||||
|
||||
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);
|
||||
}
|
||||
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");
|
||||
|
||||
Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodHEAD, sRequest);
|
||||
if (!hRequest ||
|
||||
!SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) ||
|
||||
!SteamWorks_SetHTTPRequestContextValue(hRequest, i) ||
|
||||
!SteamWorks_SendHTTPRequest(hRequest))
|
||||
{
|
||||
delete hRequest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Purpose:
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
public int OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int url_index)
|
||||
{
|
||||
if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
|
||||
{
|
||||
g_bDoesIndexHaveBZ2[url_index] = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_bDoesIndexHaveBZ2[url_index] = true;
|
||||
}
|
||||
delete hRequest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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(g_cBackupURLS[0]);
|
||||
}
|
||||
|
||||
public void Cvar_backupurl2(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
{
|
||||
convar.SetString(g_cBackupURLS[1]);
|
||||
}
|
||||
|
||||
public void OnConfigsExecuted()
|
||||
{
|
||||
downloadurl = FindConVar("sv_downloadurl");
|
||||
downloadurl.GetString(originalConVar, sizeof(originalConVar));
|
||||
}
|
||||
|
||||
public MRESReturn sendServerInfoDetCallback_Pre(Address pointer, Handle hReturn, Handle hParams) //First callback
|
||||
{
|
||||
int client;
|
||||
client = view_as<int>(SDKCall(hPlayerSlot, pointer)) + 1; //we just use linux anyways, no reason to check OS.
|
||||
g_icurrentClient = client;
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
public MRESReturn buildConVarMessageDetCallback_Pre(Handle hParams) //Second callback
|
||||
{
|
||||
if ((g_icurrentClient == 0 || g_icurrentClient > MaxClients)) return MRES_Ignored;
|
||||
|
||||
char clientIPAddress[64]; //IP of the connecting client
|
||||
GetClientIP(g_icurrentClient, clientIPAddress, sizeof(clientIPAddress));
|
||||
|
||||
for (int i = 0; i < sizeof(g_cIPaddresses); i++)
|
||||
{
|
||||
if (StrEqual(g_cIPaddresses[i], clientIPAddress))
|
||||
{
|
||||
int rotated = -1;
|
||||
g_SteamIDRotations.GetValue(clientIPAddress, rotated);
|
||||
|
||||
//0 = hetzner nide. 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. for example hetzner nide might not have but ovh uk has.
|
||||
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 cloudflare on %s. using %s instead.", g_icurrentClient, 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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user