#include #include #include #include #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(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); }