317 lines
8.2 KiB
SourcePawn
317 lines
8.2 KiB
SourcePawn
#pragma semicolon 1
|
|
#pragma newdecls required
|
|
|
|
/* SM Includes */
|
|
#include <sourcemod>
|
|
#undef REQUIRE_EXTENSIONS
|
|
#include <cURL>
|
|
#include <socket>
|
|
#include <steamtools>
|
|
#include <SteamWorks>
|
|
#define REQUIRE_EXTENSIONS
|
|
|
|
/* Plugin Info */
|
|
#define PLUGIN_NAME "Updater"
|
|
#define PLUGIN_VERSION "1.2.2"
|
|
|
|
public Plugin myinfo =
|
|
{
|
|
name = PLUGIN_NAME,
|
|
author = "GoD-Tony",
|
|
description = "Automatically updates SourceMod plugins and files",
|
|
version = PLUGIN_VERSION,
|
|
url = "http://forums.alliedmods.net/showthread.php?t=169095"
|
|
};
|
|
|
|
/* Globals */
|
|
//#define DEBUG // This will enable verbose logging. Useful for developers testing their updates.
|
|
|
|
#define CURL_AVAILABLE() (GetFeatureStatus(FeatureType_Native, "curl_easy_init") == FeatureStatus_Available)
|
|
#define SOCKET_AVAILABLE() (GetFeatureStatus(FeatureType_Native, "SocketCreate") == FeatureStatus_Available)
|
|
#define STEAMTOOLS_AVAILABLE() (GetFeatureStatus(FeatureType_Native, "Steam_CreateHTTPRequest") == FeatureStatus_Available)
|
|
#define STEAMWORKS_AVAILABLE() (GetFeatureStatus(FeatureType_Native, "SteamWorks_WriteHTTPResponseBodyToFile") == FeatureStatus_Available)
|
|
|
|
#define EXTENSION_ERROR "This plugin requires one of the cURL, Socket, SteamTools, or SteamWorks extensions to function."
|
|
#define TEMP_FILE_EXT "temp" // All files are downloaded with this extension first.
|
|
#define MAX_URL_LENGTH 256
|
|
|
|
#define UPDATE_URL "http://godtony.mooo.com/updater/updater.txt"
|
|
|
|
enum UpdateStatus {
|
|
Status_Idle,
|
|
Status_Checking, // Checking for updates.
|
|
Status_Downloading, // Downloading an update.
|
|
Status_Updated, // Update is complete.
|
|
Status_Error, // An error occured while downloading.
|
|
};
|
|
|
|
bool g_bGetDownload;
|
|
bool g_bGetSource;
|
|
|
|
Handle g_hPluginPacks = INVALID_HANDLE;
|
|
Handle g_hDownloadQueue = INVALID_HANDLE;
|
|
Handle g_hRemoveQueue = INVALID_HANDLE;
|
|
bool g_bDownloading = false;
|
|
|
|
static Handle _hUpdateTimer = INVALID_HANDLE;
|
|
static float _fLastUpdate = 0.0;
|
|
static char _sDataPath[PLATFORM_MAX_PATH];
|
|
|
|
/* Core Includes */
|
|
#include "updater/plugins.sp"
|
|
#include "updater/filesys.sp"
|
|
#include "updater/download.sp"
|
|
#include "updater/api.sp"
|
|
|
|
/* Plugin Functions */
|
|
public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max)
|
|
{
|
|
// cURL
|
|
MarkNativeAsOptional("curl_OpenFile");
|
|
MarkNativeAsOptional("curl_slist");
|
|
MarkNativeAsOptional("curl_slist_append");
|
|
MarkNativeAsOptional("curl_easy_init");
|
|
MarkNativeAsOptional("curl_easy_setopt_int_array");
|
|
MarkNativeAsOptional("curl_easy_setopt_handle");
|
|
MarkNativeAsOptional("curl_easy_setopt_string");
|
|
MarkNativeAsOptional("curl_easy_perform_thread");
|
|
MarkNativeAsOptional("curl_easy_strerror");
|
|
|
|
// Socket
|
|
MarkNativeAsOptional("SocketCreate");
|
|
MarkNativeAsOptional("SocketSetArg");
|
|
MarkNativeAsOptional("SocketSetOption");
|
|
MarkNativeAsOptional("SocketConnect");
|
|
MarkNativeAsOptional("SocketSend");
|
|
|
|
// SteamTools
|
|
MarkNativeAsOptional("Steam_CreateHTTPRequest");
|
|
MarkNativeAsOptional("Steam_SetHTTPRequestHeaderValue");
|
|
MarkNativeAsOptional("Steam_SendHTTPRequest");
|
|
MarkNativeAsOptional("Steam_WriteHTTPResponseBody");
|
|
MarkNativeAsOptional("Steam_ReleaseHTTPRequest");
|
|
|
|
API_Init();
|
|
RegPluginLibrary("updater");
|
|
|
|
return APLRes_Success;
|
|
}
|
|
|
|
public void OnPluginStart()
|
|
{
|
|
if (!CURL_AVAILABLE() && !SOCKET_AVAILABLE() && !STEAMTOOLS_AVAILABLE() && !STEAMWORKS_AVAILABLE())
|
|
{
|
|
SetFailState(EXTENSION_ERROR);
|
|
}
|
|
|
|
LoadTranslations("common.phrases");
|
|
|
|
// Convars.
|
|
Handle hCvar = INVALID_HANDLE;
|
|
|
|
hCvar = CreateConVar("sm_updater_version", PLUGIN_VERSION, PLUGIN_NAME, 0|FCVAR_NOTIFY|FCVAR_DONTRECORD);
|
|
OnVersionChanged(hCvar, "", "");
|
|
HookConVarChange(hCvar, OnVersionChanged);
|
|
|
|
hCvar = CreateConVar("sm_updater", "2", "Determines update functionality. (1 = Notify, 2 = Download, 3 = Include source code)", 0, true, 1.0, true, 3.0);
|
|
OnSettingsChanged(hCvar, "", "");
|
|
HookConVarChange(hCvar, OnSettingsChanged);
|
|
|
|
// Commands.
|
|
RegAdminCmd("sm_updater_check", Command_Check, ADMFLAG_RCON, "Forces Updater to check for updates.");
|
|
RegAdminCmd("sm_updater_status", Command_Status, ADMFLAG_RCON, "View the status of Updater.");
|
|
|
|
// Initialize arrays.
|
|
g_hPluginPacks = CreateArray();
|
|
g_hDownloadQueue = CreateArray();
|
|
g_hRemoveQueue = CreateArray();
|
|
|
|
// Temp path for checking update files.
|
|
BuildPath(Path_SM, _sDataPath, sizeof(_sDataPath), "data/updater.txt");
|
|
|
|
#if !defined DEBUG
|
|
// Add this plugin to the autoupdater.
|
|
Updater_AddPlugin(GetMyHandle(), UPDATE_URL);
|
|
#endif
|
|
|
|
// Check for updates every 24 hours.
|
|
_hUpdateTimer = CreateTimer(86400.0, Timer_CheckUpdates, _, TIMER_REPEAT);
|
|
}
|
|
|
|
public void OnAllPluginsLoaded()
|
|
{
|
|
// Check for updates on startup.
|
|
TriggerTimer(_hUpdateTimer, true);
|
|
}
|
|
|
|
public Action Timer_CheckUpdates(Handle timer)
|
|
{
|
|
Updater_FreeMemory();
|
|
|
|
// Update everything!
|
|
int maxPlugins = GetMaxPlugins();
|
|
for (int i = 0; i < maxPlugins; i++)
|
|
{
|
|
if (Updater_GetStatus(i) == Status_Idle)
|
|
{
|
|
Updater_Check(i);
|
|
}
|
|
}
|
|
|
|
_fLastUpdate = GetTickedTime();
|
|
|
|
return Plugin_Continue;
|
|
}
|
|
|
|
public Action Command_Check(int client, int args)
|
|
{
|
|
float fNextUpdate = _fLastUpdate + 3600.0;
|
|
|
|
if (fNextUpdate > GetTickedTime())
|
|
{
|
|
ReplyToCommand(client, "[Updater] Updates can only be checked once per hour. %.1f minutes remaining.", (fNextUpdate - GetTickedTime()) / 60.0);
|
|
}
|
|
else
|
|
{
|
|
ReplyToCommand(client, "[Updater] Checking for updates.");
|
|
TriggerTimer(_hUpdateTimer, true);
|
|
}
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public Action Command_Status(int client, int args)
|
|
{
|
|
char sFilename[64];
|
|
Handle hPlugin = INVALID_HANDLE;
|
|
int maxPlugins = GetMaxPlugins();
|
|
|
|
ReplyToCommand(client, "[Updater] -- Status Begin --");
|
|
ReplyToCommand(client, "Plugins being monitored for updates:");
|
|
|
|
for (int i = 0; i < maxPlugins; i++)
|
|
{
|
|
hPlugin = IndexToPlugin(i);
|
|
|
|
if (IsValidPlugin(hPlugin))
|
|
{
|
|
GetPluginFilename(hPlugin, sFilename, sizeof(sFilename));
|
|
ReplyToCommand(client, " [%i] %s", i, sFilename);
|
|
}
|
|
}
|
|
|
|
ReplyToCommand(client, "Last update check was %.1f minutes ago.", (GetTickedTime() - _fLastUpdate) / 60.0);
|
|
ReplyToCommand(client, "[Updater] --- Status End ---");
|
|
|
|
return Plugin_Handled;
|
|
}
|
|
|
|
public void OnVersionChanged(Handle convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
if (!StrEqual(newValue, PLUGIN_VERSION))
|
|
{
|
|
SetConVarString(convar, PLUGIN_VERSION);
|
|
}
|
|
}
|
|
|
|
public void OnSettingsChanged(Handle convar, const char[] oldValue, const char[] newValue)
|
|
{
|
|
switch (GetConVarInt(convar))
|
|
{
|
|
case 1: // Notify only.
|
|
{
|
|
g_bGetDownload = false;
|
|
g_bGetSource = false;
|
|
}
|
|
|
|
case 2: // Download updates.
|
|
{
|
|
g_bGetDownload = true;
|
|
g_bGetSource = false;
|
|
}
|
|
|
|
case 3: // Download with source code.
|
|
{
|
|
g_bGetDownload = true;
|
|
g_bGetSource = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined DEBUG
|
|
public void Updater_OnPluginUpdated()
|
|
{
|
|
Updater_Log("Reloading Updater plugin... updates will resume automatically.");
|
|
|
|
// Reload this plugin.
|
|
char filename[64];
|
|
GetPluginFilename(INVALID_HANDLE, filename, sizeof(filename));
|
|
ServerCommand("sm plugins reload %s", filename);
|
|
}
|
|
#endif
|
|
|
|
void Updater_Check(int index)
|
|
{
|
|
if (Fwd_OnPluginChecking(IndexToPlugin(index)) == Plugin_Continue)
|
|
{
|
|
char url[MAX_URL_LENGTH];
|
|
Updater_GetURL(index, url, sizeof(url));
|
|
Updater_SetStatus(index, Status_Checking);
|
|
AddToDownloadQueue(index, url, _sDataPath);
|
|
}
|
|
}
|
|
|
|
void Updater_FreeMemory()
|
|
{
|
|
// Make sure that no threads are active.
|
|
if (g_bDownloading || GetArraySize(g_hDownloadQueue))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Remove all queued plugins.
|
|
int index;
|
|
int maxPlugins = GetArraySize(g_hRemoveQueue);
|
|
for (int i = 0; i < maxPlugins; i++)
|
|
{
|
|
index = PluginToIndex(GetArrayCell(g_hRemoveQueue, i));
|
|
|
|
if (index != -1)
|
|
{
|
|
Updater_RemovePlugin(index);
|
|
}
|
|
}
|
|
|
|
ClearArray(g_hRemoveQueue);
|
|
|
|
// Remove plugins that have been unloaded.
|
|
for (int i = 0; i < GetMaxPlugins(); i++)
|
|
{
|
|
if (!IsValidPlugin(IndexToPlugin(i)))
|
|
{
|
|
Updater_RemovePlugin(i);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Updater_Log(const char[] format, any:...)
|
|
{
|
|
char buffer[256];
|
|
char path[PLATFORM_MAX_PATH];
|
|
VFormat(buffer, sizeof(buffer), format, 2);
|
|
BuildPath(Path_SM, path, sizeof(path), "logs/Updater.log");
|
|
LogToFileEx(path, "%s", buffer);
|
|
}
|
|
|
|
#if defined DEBUG
|
|
int Updater_DebugLog(const char[] format, any:...)
|
|
{
|
|
char buffer[256];
|
|
char path[PLATFORM_MAX_PATH];
|
|
VFormat(buffer, sizeof(buffer), format, 2);
|
|
BuildPath(Path_SM, path, sizeof(path), "logs/Updater_Debug.log");
|
|
LogToFileEx(path, "%s", buffer);
|
|
}
|
|
#endif
|