185 lines
7.2 KiB
SourcePawn
185 lines
7.2 KiB
SourcePawn
#include <sourcemod>
|
|
#include <dhooks>
|
|
#include <sdktools>
|
|
#include <geoip>
|
|
|
|
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
GameData gamedatafile; //Handle to the gamedata
|
|
Handle hPlayerSlot = INVALID_HANDLE;
|
|
KeyValues nodeConfig; //main node file
|
|
|
|
char originalConVar[256]; //original sv_downloadurl value
|
|
char clientIPAddress[64]; //IP of the connecting client
|
|
|
|
int url_length = 512;
|
|
|
|
ConVar downloadurl; // sv_downloadurl
|
|
|
|
//unloze is just using geoip2, no reason to keep SypexGeo for us.
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = "Smart Fast Downloads",
|
|
description = "Routes clients to the closest Fast Download server available. removed IP lookup feature because its not useable with S3 objects",
|
|
author = "Nolo001, edits by jenz for unloze",
|
|
url = "original is at: https://github.com/Nolo001-Aha/sourcemod_smart_fastdownloads",
|
|
version = "1.2"
|
|
};
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
PrintToServer("[Smart Fast Downloads] Initializing...");
|
|
gamedatafile = LoadGameConfigFile("betterfastdl.games");
|
|
|
|
if(gamedatafile == null)
|
|
SetFailState("Cannot load betterfastdl.games.txt! Make sure you have it installed!");
|
|
|
|
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!");
|
|
|
|
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!");
|
|
|
|
if(!DHookEnableDetour(detourBuildConVarMessage, true, buildConVarMessageDetCallback_Post))
|
|
SetFailState("Failed to detour Host_BuildConVarUpdateMessage PostHook!");
|
|
|
|
StartPrepSDKCall(SDKCall_Raw);
|
|
PrepSDKCall_SetFromConf(gamedatafile, SDKConf_Virtual, "CBaseClient::GetPlayerSlot");
|
|
PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
|
|
hPlayerSlot = EndPrepSDKCall();
|
|
|
|
nodeConfig = new KeyValues("FastDL Settings"); //Load the main node config file
|
|
char config[PLATFORM_MAX_PATH];
|
|
BuildPath(Path_SM, config, sizeof(config), "configs/fastdlmanager.cfg");
|
|
nodeConfig.ImportFromFile(config);
|
|
nodeConfig.Rewind();
|
|
|
|
BuildPath(Path_SM, config, sizeof(config), "fastdl_debug.log");
|
|
|
|
AutoExecConfig(true, "SmartFastDownloads");
|
|
|
|
downloadurl = FindConVar("sv_downloadurl"); //Save original downloadurl, so we can send it to clients who we cant locate
|
|
downloadurl.GetString(originalConVar, sizeof(originalConVar));
|
|
}
|
|
|
|
public void OnConfigsExecuted()
|
|
{
|
|
downloadurl = FindConVar("sv_downloadurl");
|
|
downloadurl.GetString(originalConVar, sizeof(originalConVar));
|
|
}
|
|
|
|
public MRESReturn sendServerInfoDetCallback_Pre(Address pointer, Handle hReturn, Handle hParams) //First callback in chain, derive client and find their IP
|
|
{
|
|
int client;
|
|
client = view_as<int>(SDKCall(hPlayerSlot, pointer)) + 1; //we just use linux anyways, no reason to check OS.
|
|
GetClientIP(client, clientIPAddress, sizeof(clientIPAddress));
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
public MRESReturn buildConVarMessageDetCallback_Pre(Handle hParams) //Second callback in chain, call our main function and get a node link in response
|
|
{
|
|
char[] url = new char[url_length];
|
|
getLocationSettings(url);
|
|
setConVarValue(url);
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
void getLocationSettings(char[] link) //Main function
|
|
{
|
|
nodeConfig.Rewind();
|
|
float clientLongitude, clientLatitude;
|
|
if(nodeConfig.JumpToKey("Nodes", false))
|
|
{
|
|
char[] nodeURL = new char[url_length];
|
|
char[] finalurl = new char[url_length];
|
|
float distance, currentDistance; //1 - distance to the closest server, may change in iterations. 2 - distance between client and current iteration node
|
|
clientLatitude = GetLatitude(clientIPAddress); //clients coordinates
|
|
clientLongitude = GetLongitude(clientIPAddress);
|
|
|
|
//if look up of client failed just give them the default fastdl value instead.
|
|
if(clientLatitude == 0 || clientLongitude == 0)
|
|
{
|
|
//LogMessage("Failed distance calculation. Sending default values. Client(%f %f IP: %s).", clientLatitude, clientLongitude, clientIPAddress);
|
|
strcopy(link, url_length, "EMPTY");
|
|
return;
|
|
}
|
|
|
|
char section[64];
|
|
nodeConfig.GotoFirstSubKey(false);
|
|
do
|
|
{
|
|
float nodeLongitude, nodeLatitude;
|
|
nodeConfig.GetSectionName(section, sizeof(section));
|
|
nodeLatitude = nodeConfig.GetFloat("latitude");
|
|
nodeLongitude = nodeConfig.GetFloat("longitude");
|
|
currentDistance = GetDistance(nodeLatitude, nodeLongitude, clientLatitude, clientLongitude);
|
|
|
|
if((currentDistance < distance) || distance == 0)
|
|
{
|
|
nodeConfig.GetString("link", nodeURL, url_length, "EMPTY");
|
|
strcopy(finalurl, url_length, nodeURL);
|
|
distance = currentDistance;
|
|
}
|
|
}
|
|
while(nodeConfig.GotoNextKey(false));
|
|
strcopy(link, url_length, nodeURL);
|
|
//LogMessage("Sending final: Distance is %f. Client IP Address: %s. selected link is: %s", distance, clientIPAddress, link);
|
|
}
|
|
}
|
|
|
|
void setConVarValue(char[] value) //Sets the actual ConVar value
|
|
{
|
|
int oldflags = GetConVarFlags(downloadurl);
|
|
SetConVarFlags(downloadurl, oldflags &~ FCVAR_REPLICATED);
|
|
if (StrEqual(value, "EMPTY", false))
|
|
{
|
|
SetConVarString(downloadurl, originalConVar, true, false);
|
|
}
|
|
else
|
|
{
|
|
SetConVarString(downloadurl, value, true, false);
|
|
}
|
|
SetConVarFlags(downloadurl, oldflags|FCVAR_REPLICATED);
|
|
}
|
|
|
|
public MRESReturn buildConVarMessageDetCallback_Post(Handle hParams) //Reverts the ConVar to its original value
|
|
{
|
|
setConVarValue("EMPTY");
|
|
return MRES_Ignored;
|
|
}
|
|
|
|
float GetLatitude(char[] lookupIP)
|
|
{
|
|
return GeoipLatitude(lookupIP);
|
|
}
|
|
|
|
float GetLongitude(char[] lookupIP)
|
|
{
|
|
return GeoipLongitude(lookupIP);
|
|
}
|
|
|
|
float GetDistance(float nodeLatitude, float nodeLongitude, float clientLatitude, float clientLongitude)
|
|
{
|
|
return GeoipDistance(nodeLatitude, nodeLongitude, clientLatitude, clientLongitude);
|
|
}
|