You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

205 lines
8.2 KiB

using IllusionInjector.Logging;
using SimpleJSON;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Networking;
using UnityEngine;
using IllusionPlugin;
using System.Text.RegularExpressions;
using Logger = IllusionInjector.Logging.Logger;
namespace IllusionInjector.Updating
{
class ModUpdater : MonoBehaviour
{
public ModUpdater instance;
public void Awake()
{
instance = this;
CheckForUpdates();
}
public void CheckForUpdates()
{
StartCoroutine(CheckForUpdatesCoroutine());
}
struct UpdateCheckQueueItem
{
public PluginManager.BSPluginMeta Plugin;
public Uri UpdateUri;
public string Name;
}
struct UpdateQueueItem
{
public PluginManager.BSPluginMeta Plugin;
public Uri DownloadUri;
public string Name;
public Version NewVersion;
}
private Regex commentRegex = new Regex(@"(?: \/\/.+)?$", RegexOptions.Compiled | RegexOptions.Multiline);
private Dictionary<Uri, UpdateScript> cachedRequests = new Dictionary<Uri, UpdateScript>();
IEnumerator CheckForUpdatesCoroutine()
{
Logger.log.Info("Checking for mod updates...");
var toUpdate = new List<UpdateQueueItem>();
var plugins = new Queue<UpdateCheckQueueItem>(PluginManager.BSMetas.Select(p => new UpdateCheckQueueItem { Plugin = p, UpdateUri = p.Plugin.UpdateUri, Name = p.Plugin.Name }));
for (; plugins.Count > 0;)
{
var plugin = plugins.Dequeue();
Logger.log.Debug($"Checking for updates for {plugin.Name}");
if (plugin.UpdateUri != null)
{
if (!cachedRequests.ContainsKey(plugin.UpdateUri))
using (var request = UnityWebRequest.Get(plugin.UpdateUri))
{
yield return request.SendWebRequest();
if (request.isNetworkError)
{
Logger.log.Error("Network error while trying to update mods");
Logger.log.Error(request.error);
break;
}
if (request.isHttpError)
{
Logger.log.Error($"Server returned an error code while trying to update mod {plugin.Name}");
Logger.log.Error(request.error);
}
var json = request.downloadHandler.text;
json = commentRegex.Replace(json, "");
JSONObject obj = null;
try
{
obj = JSON.Parse(json).AsObject;
}
catch (InvalidCastException)
{
Logger.log.Error($"Parse error while trying to update mod {plugin.Name}");
Logger.log.Error($"Response doesn't seem to be a JSON object");
continue;
}
catch (Exception e)
{
Logger.log.Error($"Parse error while trying to update mod {plugin.Name}");
Logger.log.Error(e);
continue;
}
UpdateScript ss;
try
{
ss = UpdateScript.Parse(obj);
}
catch (Exception e)
{
Logger.log.Error($"Parse error while trying to update mod {plugin.Name}");
Logger.log.Error($"Script at {plugin.UpdateUri} doesn't seem to be a valid update script");
Logger.log.Debug(e);
continue;
}
cachedRequests.Add(plugin.UpdateUri, ss);
}
var script = cachedRequests[plugin.UpdateUri];
if (script.Info.TryGetValue(plugin.Name, out UpdateScript.PluginVersionInfo info))
{
Logger.log.Debug($"Checking version info for {plugin.Name} ({plugin.Plugin.Plugin.Name})");
if (info.NewName != null || info.NewScript != null)
plugins.Enqueue(new UpdateCheckQueueItem
{
Plugin = plugin.Plugin,
Name = info.NewName ?? plugin.Name,
UpdateUri = info.NewScript ?? plugin.UpdateUri
});
else
{
Logger.log.Debug($"New version: {info.Version}, Current version: {plugin.Plugin.Plugin.Version}");
if (info.Version > plugin.Plugin.Plugin.Version)
{ // we should update plugin
Logger.log.Debug($"Queueing update for {plugin.Name} ({plugin.Plugin.Plugin.Name})");
toUpdate.Add(new UpdateQueueItem
{
Plugin = plugin.Plugin,
DownloadUri = info.Download,
Name = plugin.Name,
NewVersion = info.Version
});
}
}
}
else
{
Logger.log.Error($"Script defined for plugin {plugin.Name} doesn't define information for {plugin.Name}");
continue;
}
}
}
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, UpdateQueueItem item)
{
var file = Path.Combine(tempdir, item.Name + ".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}");
}
}
}