diff --git a/IllusionInjector/BeatSaber/CompositeBSPlugin.cs b/IllusionInjector/BeatSaber/CompositeBSPlugin.cs index 8ba4fb7e..96d6ddf0 100644 --- a/IllusionInjector/BeatSaber/CompositeBSPlugin.cs +++ b/IllusionInjector/BeatSaber/CompositeBSPlugin.cs @@ -1,4 +1,5 @@ using IllusionPlugin; +using IllusionPlugin.BeatSaber; using System; using System.Collections.Generic; using System.Linq; @@ -78,16 +79,14 @@ namespace IllusionInjector { Invoke(plugin => plugin.OnFixedUpdate()); } - public string Name { - get { throw new NotImplementedException(); } - } + public string Name => throw new NotImplementedException(); - public Version Version { - get { throw new NotImplementedException(); } - } + public string Version => throw new NotImplementedException(); public Uri UpdateUri => throw new NotImplementedException(); + public ModsaberModInfo ModInfo => throw new NotImplementedException(); + public void OnLateUpdate() { Invoke(plugin => { if (plugin is IEnhancedBeatSaberPlugin) diff --git a/IllusionInjector/IllusionInjector.csproj b/IllusionInjector/IllusionInjector.csproj index 8e29ff76..c59cd9a7 100644 --- a/IllusionInjector/IllusionInjector.csproj +++ b/IllusionInjector/IllusionInjector.csproj @@ -69,6 +69,7 @@ + diff --git a/IllusionInjector/PluginComponent.cs b/IllusionInjector/PluginComponent.cs index 9db293ba..9f62e10c 100644 --- a/IllusionInjector/PluginComponent.cs +++ b/IllusionInjector/PluginComponent.cs @@ -1,5 +1,4 @@ -using IllusionInjector.Updating; -using System; +using System; using System.Collections.Generic; using System.Text; using UnityEngine; @@ -27,6 +26,7 @@ namespace IllusionInjector // this has no relevance since there is a new mod updater system //gameObject.AddComponent(); // AFTER plugins are loaded, but before most things + gameObject.AddComponent(); bsPlugins.OnApplicationStart(); ipaPlugins.OnApplicationStart(); diff --git a/IllusionInjector/PluginManager.cs b/IllusionInjector/PluginManager.cs index d1db08b7..1a03c771 100644 --- a/IllusionInjector/PluginManager.cs +++ b/IllusionInjector/PluginManager.cs @@ -1,5 +1,6 @@ using IllusionInjector.Logging; using IllusionPlugin; +using IllusionPlugin.BeatSaber; using System; using System.Collections.Generic; using System.Diagnostics; @@ -15,11 +16,12 @@ namespace IllusionInjector public static class PluginManager { #pragma warning disable CS0618 // Type or member is obsolete (IPlugin) - + public class BSPluginMeta { public IBeatSaberPlugin Plugin { get; internal set; } public string Filename { get; internal set; } + public ModsaberModInfo ModsaberInfo { get; internal set; } } public static IEnumerable BSPlugins @@ -100,15 +102,16 @@ namespace IllusionInjector foreach (string s in copiedPlugins) { var result = LoadPluginsFromFile(s, exeName); - _bsPlugins.AddRange(result.Item1.Select(p => new BSPluginMeta { Plugin = p, Filename = s })); + _bsPlugins.AddRange(result.Item1); _ipaPlugins.AddRange(result.Item2); } // DEBUG Logger.log.Info($"Running on Unity {UnityEngine.Application.unityVersion}"); + Logger.log.Info($"Game version {UnityEngine.Application.version}"); Logger.log.Info("-----------------------------"); - Logger.log.Info($"Loading plugins from {pluginDirectory} and found {_bsPlugins.Count}"); + Logger.log.Info($"Loading plugins from {GetRelativePath(pluginDirectory, Environment.CurrentDirectory)} and found {_bsPlugins.Count + _ipaPlugins.Count}"); Logger.log.Info("-----------------------------"); foreach (var plugin in _bsPlugins) { @@ -122,13 +125,25 @@ namespace IllusionInjector Logger.log.Info("-----------------------------"); } - private static Tuple, IEnumerable> LoadPluginsFromFile(string file, string exeName) + private static string GetRelativePath(string filespec, string folder) + { + Uri pathUri = new Uri(filespec); + // Folders must end in a slash + if (!folder.EndsWith(Path.DirectorySeparatorChar.ToString())) + { + folder += Path.DirectorySeparatorChar; + } + Uri folderUri = new Uri(folder); + return Uri.UnescapeDataString(folderUri.MakeRelativeUri(pathUri).ToString().Replace('/', Path.DirectorySeparatorChar)); + } + + private static Tuple, IEnumerable> LoadPluginsFromFile(string file, string exeName) { - List bsPlugins = new List(); + List bsPlugins = new List(); List ipaPlugins = new List(); if (!File.Exists(file) || !file.EndsWith(".dll", true, null)) - return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); + return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); T OptionalGetPlugin(Type t) where T : class { @@ -166,7 +181,12 @@ namespace IllusionInjector IBeatSaberPlugin bsPlugin = OptionalGetPlugin(t); if (bsPlugin != null) { - bsPlugins.Add(bsPlugin); + bsPlugins.Add(new BSPluginMeta + { + Plugin = bsPlugin, + Filename = file, + ModsaberInfo = bsPlugin.ModInfo + }); } else { @@ -184,7 +204,7 @@ namespace IllusionInjector Logger.log.Error($"Could not load {Path.GetFileName(file)}! {e}"); } - return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); + return new Tuple, IEnumerable>(bsPlugins, ipaPlugins); } public class AppInfo diff --git a/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs b/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs index d5b312ec..a4d3d047 100644 --- a/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs +++ b/IllusionInjector/Updating/ModsaberML/ApiEndpoint.cs @@ -9,8 +9,13 @@ namespace IllusionInjector.Updating.ModsaberML { class ApiEndpoint { - const string ApiBase = "https://www.modsaber.ml/api/"; - const string GetApprovedEndpoint = "public/temp/approved"; +#if DEBUG + public const string ApiBase = "https://www.modsaber.ml/api/"; + public const string GetApprovedEndpoint = "public/temp/approved"; +#else + public const string ApiBase = "https://www.modsaber.ml/api/"; + public const string GetApprovedEndpoint = "public/temp/approved"; +#endif public class Mod { @@ -35,17 +40,22 @@ namespace IllusionInjector.Updating.ModsaberML Author = obj["author"] }; - foreach (var item in obj["files"].Keys) + foreach (var item in obj["files"]) { - var key = item as JSONString; - if (key.Value == "steam") - outp.SteamFile = obj[key.Value]["url"]; - if (key.Value == "oculus") - outp.OculusFile = obj[key.Value]["url"]; + var key = item.Key; + if (key == "steam") + outp.SteamFile = item.Value["url"]; + if (key == "oculus") + outp.OculusFile = item.Value["url"]; } return outp; } + + public override string ToString() + { + return $"{{\"{Title} ({Name})\"v{Version} for {GameVersion} by {Author} with \"{SteamFile}\" and \"{OculusFile}\"}}"; + } } } diff --git a/IllusionInjector/Updating/ModsaberML/Updater.cs b/IllusionInjector/Updating/ModsaberML/Updater.cs new file mode 100644 index 00000000..64f998fc --- /dev/null +++ b/IllusionInjector/Updating/ModsaberML/Updater.cs @@ -0,0 +1,167 @@ +using SimpleJSON; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; +using Logger = IllusionInjector.Logging.Logger; + +namespace IllusionInjector.Updating.ModsaberML +{ + class Updater : MonoBehaviour + { + public Updater instance; + + public void Awake() + { + instance = this; + CheckForUpdates(); + } + + public void CheckForUpdates() + { + StartCoroutine(CheckForUpdatesCoroutine()); + } + + private Regex commentRegex = new Regex(@"(?: \/\/.+)?$", RegexOptions.Compiled | RegexOptions.Multiline); + private Dictionary cachedRequests = new Dictionary(); + IEnumerator CheckForUpdatesCoroutine() + { + Logger.log.Info("Checking for mod updates..."); + + var toUpdate = new List(); + + var modList = new List(); + using (var request = UnityWebRequest.Get(ApiEndpoint.ApiBase+ApiEndpoint.GetApprovedEndpoint)) + { + yield return request.SendWebRequest(); + + if (request.isNetworkError) + { + Logger.log.Error("Network error while trying to update mods"); + Logger.log.Error(request.error); + yield break; + } + if (request.isHttpError) + { + Logger.log.Error($"Server returned an error code while trying to update mods"); + Logger.log.Error(request.error); + } + + var json = request.downloadHandler.text; + + JSONObject obj = null; + try + { + obj = JSON.Parse(json).AsObject; + } + catch (InvalidCastException) + { + Logger.log.Error($"Parse error while trying to update mods"); + Logger.log.Error($"Response doesn't seem to be a JSON object"); + yield break; + } + catch (Exception e) + { + Logger.log.Error($"Parse error while trying to update mods"); + Logger.log.Error(e); + yield break; + } + + foreach (var modObj in obj["mods"].AsArray.Children) + { + try + { + modList.Add(ApiEndpoint.Mod.DecodeJSON(modObj.AsObject)); + } + catch (Exception e) + { + Logger.log.Error($"Parse error while trying to update mods"); + Logger.log.Error($"Response doesn't seem to be correctly formatted"); + Logger.log.Error(e); + break; + } + } + } + + var GameVersion = new Version(Application.version); + + foreach (var plugin in PluginManager.BSMetas) + { + var info = plugin.ModsaberInfo; + var modRegistry = modList.FirstOrDefault(o => o.Name == info.InternalName); + if (modRegistry != null) + { // a.k.a we found it + Logger.log.Debug($"Found Modsaber.ML registration for {plugin.Plugin.Name} ({info.InternalName})"); + Logger.log.Debug($"Installed version: {info.CurrentVersion}; Latest version: {modRegistry.Version}"); + if (modRegistry.Version > info.CurrentVersion) + { + Logger.log.Debug($"{plugin.Plugin.Name} needs an update!"); + if (modRegistry.GameVersion == GameVersion) + { + Logger.log.Debug($"Queueing update..."); + toUpdate.Add(plugin); + } + else + { + Logger.log.Warn($"Update avaliable for {plugin.Plugin.Name}, but for a different Beat Saber version!"); + } + } + } + } + + Logger.log.Info($"{toUpdate.Count} mods need updating"); + + if (toUpdate.Count == 0) yield break; + + string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + Path.GetRandomFileName()); + Directory.CreateDirectory(tempDirectory); + Logger.log.Debug($"Created temp download dirtectory {tempDirectory}"); + foreach (var item in toUpdate) + { + StartCoroutine(DownloadPluginCoroutine(tempDirectory, item)); + } + } + + IEnumerator DownloadPluginCoroutine(string tempdir, PluginManager.BSPluginMeta item) + { + + yield return null; + /*var file = Path.Combine(tempdir, item. + ".dll"); + + using (var req = UnityWebRequest.Get(item.DownloadUri)) + { + req.downloadHandler = new DownloadHandlerFile(file); + yield return req.SendWebRequest(); + + if (req.isNetworkError) + { + Logger.log.Error($"Network error while trying to download update for {item.Plugin.Plugin.Name}"); + Logger.log.Error(req.error); + yield break; + } + if (req.isHttpError) + { + Logger.log.Error($"Server returned an error code while trying to download update for {item.Plugin.Plugin.Name}"); + Logger.log.Error(req.error); + yield break; + } + } + + var pluginDir = Path.GetDirectoryName(item.Plugin.Filename); + var newFile = Path.Combine(pluginDir, item.Name + ".dll"); + + File.Delete(item.Plugin.Filename); + if (File.Exists(newFile)) + File.Delete(newFile); + File.Move(file, newFile); + + Logger.log.Info($"{item.Plugin.Plugin.Name} updated to {item.NewVersion}");*/ + } + } +} diff --git a/IllusionInjector/Updating/Old/ModUpdater.cs b/IllusionInjector/Updating/Old/ModUpdater.cs index 8c4000e9..99ab7409 100644 --- a/IllusionInjector/Updating/Old/ModUpdater.cs +++ b/IllusionInjector/Updating/Old/ModUpdater.cs @@ -17,6 +17,7 @@ using Logger = IllusionInjector.Logging.Logger; namespace IllusionInjector.Updating { +#if OLD_UPDATER class ModUpdater : MonoBehaviour { public ModUpdater instance; @@ -202,4 +203,5 @@ namespace IllusionInjector.Updating Logger.log.Info($"{item.Plugin.Plugin.Name} updated to {item.NewVersion}"); } } +#endif } diff --git a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache b/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache index 278fe638..fd4317e3 100644 --- a/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache +++ b/IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -1b7ab3d058c6a212976e4869e8586d3cc95bb707 +4b98b2d3f93866107d7a8621254570f2cc16296c diff --git a/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs b/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs index 89889cec..af5672aa 100644 --- a/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs +++ b/IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs @@ -1,4 +1,5 @@ -using System; +using IllusionPlugin.BeatSaber; +using System; using System.Collections.Generic; using System.Text; using UnityEngine.SceneManagement; @@ -20,13 +21,20 @@ namespace IllusionPlugin /// /// Gets the version of the plugin. /// - Version Version { get; } + string Version { get; } +#if OLD_UPDATER /// /// The URI to the update script for the plugin. May be . /// Actually tho this does nothing I just don't want to try to remove it completely /// Uri UpdateUri { get; } +#endif + + /// + /// Gets the info for the Modsaber release of this plugin. + /// + ModsaberModInfo ModInfo { get; } /// /// Gets invoked when the application is started. diff --git a/IllusionPlugin/BeatSaber/ModsaberModInfo.cs b/IllusionPlugin/BeatSaber/ModsaberModInfo.cs new file mode 100644 index 00000000..52942af3 --- /dev/null +++ b/IllusionPlugin/BeatSaber/ModsaberModInfo.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IllusionPlugin.BeatSaber +{ + public class ModsaberModInfo + { + public string InternalName { get; set; } + public Version CurrentVersion { get; set; } + } +} diff --git a/IllusionPlugin/IllusionPlugin.csproj b/IllusionPlugin/IllusionPlugin.csproj index 74f97711..9aa220d7 100644 --- a/IllusionPlugin/IllusionPlugin.csproj +++ b/IllusionPlugin/IllusionPlugin.csproj @@ -44,6 +44,7 @@ + diff --git a/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache b/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache index b45009b5..fcd958a6 100644 --- a/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache +++ b/IllusionPlugin/obj/Debug/IllusionPlugin.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -1bbd9ba53463d8e0c57caf8203d0145bfc70fa26 +9c8aaadb0a1830ce0d658b39574b7acf02507f4a