diff --git a/TShockAPI/Commands.cs b/TShockAPI/Commands.cs index b1b3db61..7e4770c9 100644 --- a/TShockAPI/Commands.cs +++ b/TShockAPI/Commands.cs @@ -24,6 +24,7 @@ using System.Linq; using System.Net; using System.Text; using System.Threading; +using TShockAPI.PluginUpdater; using Terraria; using TShockAPI.DB; using System.Reflection; @@ -160,6 +161,7 @@ namespace TShockAPI add(Permissions.maintenance, Restart, "restart"); //Added restart command add(Permissions.maintenance, OffNoSave, "off-nosave", "exit-nosave"); add(Permissions.maintenance, CheckUpdates, "checkupdates"); + add(Permissions.updateplugins, UpdatePlugins, "updateplugins"); add(Permissions.causeevents, DropMeteor, "dropmeteor"); add(Permissions.causeevents, Star, "star"); add(Permissions.causeevents, Fullmoon, "fullmoon"); @@ -1312,6 +1314,13 @@ namespace TShockAPI ThreadPool.QueueUserWorkItem(UpdateManager.CheckUpdate); } + private static void UpdatePlugins(CommandArgs args) + { + args.Player.SendInfoMessage("Starting plugin update process:"); + args.Player.SendInfoMessage("This may take a while, do not turn off the server!"); + new PluginUpdaterThread(args.Player); + } + #endregion Server Maintenence Commands #region Cause Events and Spawn Monsters Commands diff --git a/TShockAPI/Permissions.cs b/TShockAPI/Permissions.cs index 83315dca..b853b32f 100644 --- a/TShockAPI/Permissions.cs +++ b/TShockAPI/Permissions.cs @@ -176,6 +176,8 @@ namespace TShockAPI [Description("User can use /spawn.")] public static readonly string spawn; [Description("User can elevate other users' groups temporarily.")] public static readonly string settempgroup; + + [Description("User can download updates to plugins that are currently running.")] public static readonly string updateplugins; static Permissions() { foreach (var field in typeof (Permissions).GetFields()) diff --git a/TShockAPI/PluginUpdater/PluginUpdaterThread.cs b/TShockAPI/PluginUpdater/PluginUpdaterThread.cs new file mode 100644 index 00000000..127d208e --- /dev/null +++ b/TShockAPI/PluginUpdater/PluginUpdaterThread.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Terraria; + +namespace TShockAPI.PluginUpdater +{ + class PluginUpdaterThread + { + private TSPlayer invoker; + public PluginUpdaterThread(TSPlayer player) + { + invoker = player; + PluginVersionCheck.PluginUpdate += PluginUpdate; + HandleUpdate(); + } + + private void HandleUpdate() + { + foreach(PluginContainer cont in ProgramServer.Plugins) + new Thread(PluginVersionCheck.CheckPlugin).Start(cont.Plugin); + } + + private int Updates = 0; + private void PluginUpdate(UpdateArgs args) + { + Updates++; + if(args.Success && String.IsNullOrEmpty(args.Error)) + { + invoker.SendSuccessMessage(String.Format("{0} was downloaded successfully.", args.Plugin.Name)); + } + else if(args.Success) + { + invoker.SendSuccessMessage(String.Format("{0} was skipped. Reason: {1}", args.Plugin.Name, args.Error)); + } + else + { + invoker.SendSuccessMessage(String.Format("{0} failed to downloaded. Error: {1}", args.Plugin.Name, args.Error)); + } + + if(Updates >= Terraria.ProgramServer.Plugins.Count) + { + PluginVersionCheck.PluginUpdate -= PluginUpdate; + + invoker.SendSuccessMessage("All plugins have been downloaded. Now copying them to the plugin folder..."); + + string folder = Path.Combine(TShock.SavePath, "UpdatedPlugins"); + string dest = Path.Combine(TShock.SavePath, "..", "ServerPlugins"); + + foreach (string dir in Directory.GetDirectories(folder, "*", System.IO.SearchOption.AllDirectories)) + { + string new_folder = dest + dir.Substring(folder.Length); + if (!Directory.Exists(new_folder)) + Directory.CreateDirectory(new_folder); + } + + foreach (string file_name in Directory.GetFiles(folder, "*.*", System.IO.SearchOption.AllDirectories)) + { + TSPlayer.Server.SendSuccessMessage(String.Format("Copied {0}", file_name)); + File.Copy(file_name, dest + file_name.Substring(folder.Length), true); + } + + + Directory.Delete(folder, true); + + invoker.SendSuccessMessage("All plugins have been processed. Restart the server to have access to the new plugins."); + } + } + } +} diff --git a/TShockAPI/PluginUpdater/PluginVersionCheck.cs b/TShockAPI/PluginUpdater/PluginVersionCheck.cs new file mode 100644 index 00000000..4218740c --- /dev/null +++ b/TShockAPI/PluginUpdater/PluginVersionCheck.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using JsonLoader; +using Newtonsoft.Json; + +namespace TShockAPI.PluginUpdater +{ + public class PluginVersionCheck + { + public delegate void PluginUpdateD(UpdateArgs e); + public static event PluginUpdateD PluginUpdate; + public static void OnPluginUpdate(UpdateArgs args) + { + if (PluginUpdate == null) + { + return; + } + + PluginUpdate(args); + } + + public static void CheckPlugin(object p) + { + TerrariaPlugin plugin = (TerrariaPlugin)p; + UpdateArgs args = new UpdateArgs {Plugin = plugin, Success = true, Error = ""}; + List files = new List(); + + try + { + if (!String.IsNullOrEmpty(plugin.UpdateURL)) + { + var request = HttpWebRequest.Create(plugin.UpdateURL); + VersionInfo vi; + + request.Timeout = 5000; + using (var response = request.GetResponse()) + { + using (var reader = new StreamReader(response.GetResponseStream())) + { + vi = JsonConvert.DeserializeObject(reader.ReadToEnd()); + } + } + + System.Version v = System.Version.Parse((vi.version.ToString())); + + if (!v.Equals(plugin.Version)) + { + DownloadPackage pkg; + request = HttpWebRequest.Create(vi.url); + + request.Timeout = 5000; + using (var response = request.GetResponse()) + { + using (var reader = new StreamReader(response.GetResponseStream())) + { + pkg = JsonConvert.DeserializeObject(reader.ReadToEnd()); + } + } + + foreach (PluginFile f in pkg.files) + { + using (WebClient Client = new WebClient()) + { + string dir = Path.Combine(TShock.SavePath, "UpdatedPlugins"); + if (!Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + Client.DownloadFile(f.url, + Path.Combine(dir, f.destination)); + + files.Add(Path.Combine(dir, f.destination)); + } + } + } + else + { + args.Error = "Plugin is up to date."; + } + } + else + { + args.Error = "Plugin has no updater recorded."; + } + } + catch(Exception e) + { + args.Success = false; + args.Error = e.Message; + if(files.Count > 0) + { + foreach(string s in files) + { + File.Delete(s); + } + } + } + + OnPluginUpdate(args); + } + } + + public class UpdateArgs + { + public TerrariaPlugin Plugin { get; set; } + public bool Success { get; set; } + public string Error { get; set; } + } +} diff --git a/TShockAPI/PluginUpdater/VersionInfo.cs b/TShockAPI/PluginUpdater/VersionInfo.cs new file mode 100644 index 00000000..799426ef --- /dev/null +++ b/TShockAPI/PluginUpdater/VersionInfo.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace JsonLoader +{ + class VersionInfo + { + public Version version; + public string url; + } + + public class Version + { + public int Major; + public int Minor; + public int Build; + public int Revision; + public int MajorRevision; + public int MinorRevision; + + public Version() + { + SetVersion(0,0,0,0); + } + + public Version(int m) + { + SetVersion(m, 0, 0, 0); + } + + public Version(int ma, int mi) + { + SetVersion(ma, mi, 0, 0); + } + + public Version(int ma, int mi, int b) + { + SetVersion(ma, mi, b, 0); + } + + public Version(int ma, int mi, int b, int r) + { + SetVersion(ma, mi, b, r); + } + + private void SetVersion(int ma, int mi, int b, int r) + { + Major = ma; + Minor = mi; + Build = b; + Revision = r; + } + + public string ToString() + { + return String.Format("{0}.{1}.{2}.{3}", Major, Minor, Build, Revision); + } + } + + class DownloadPackage + { + public List files; + } + + class PluginFile + { + public string url; + public string destination = ""; + } +} diff --git a/TShockAPI/TShock.cs b/TShockAPI/TShock.cs index 1f7cf1e8..3a8908fb 100644 --- a/TShockAPI/TShock.cs +++ b/TShockAPI/TShock.cs @@ -97,6 +97,10 @@ namespace TShockAPI get { return "The administration modification of the future."; } } + public override string UpdateURL + { + get { return ""; } + } public TShock(Main game) : base(game) { diff --git a/TShockAPI/TShockAPI.csproj b/TShockAPI/TShockAPI.csproj index 697474e2..01ff3b32 100644 --- a/TShockAPI/TShockAPI.csproj +++ b/TShockAPI/TShockAPI.csproj @@ -85,6 +85,9 @@ + + + @@ -187,7 +190,7 @@ - +