Browse Source

Added then discovered that my updating system is obsolete

refactor
Anairkoen Schno 6 years ago
parent
commit
5363cb450e
10 changed files with 381 additions and 43 deletions
  1. +2
    -0
      IllusionInjector/BeatSaber/CompositeBSPlugin.cs
  2. +32
    -31
      IllusionInjector/ConsoleWindow.cs
  3. +10
    -2
      IllusionInjector/IllusionInjector.csproj
  4. +6
    -1
      IllusionInjector/PluginComponent.cs
  5. +21
    -7
      IllusionInjector/PluginManager.cs
  6. +205
    -0
      IllusionInjector/Updating/ModUpdater.cs
  7. +98
    -0
      IllusionInjector/Updating/UpdateScript.cs
  8. +1
    -1
      IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache
  9. +1
    -1
      IllusionInjector/packages.config
  10. +5
    -0
      IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs

+ 2
- 0
IllusionInjector/BeatSaber/CompositeBSPlugin.cs View File

@ -86,6 +86,8 @@ namespace IllusionInjector {
get { throw new NotImplementedException(); }
}
// public Uri UpdateUri => throw new NotImplementedException();
public void OnLateUpdate() {
Invoke(plugin => {
if (plugin is IEnhancedBeatSaberPlugin)


+ 32
- 31
IllusionInjector/ConsoleWindow.cs View File

@ -3,6 +3,7 @@ using System.Collections;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Windows
{
@ -11,39 +12,39 @@ namespace Windows
{
public static void CreateConsole()
{
if (hasConsole)
return;
if (oldOut == IntPtr.Zero)
oldOut = GetStdHandle( -11 );
if (! AllocConsole())
throw new Exception("AllocConsole() failed");
conOut = CreateFile( "CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero );
if (! SetStdHandle(-11, conOut))
throw new Exception("SetStdHandle() failed");
StreamToConsole();
hasConsole = true;
if (hasConsole)
return;
if (oldOut == IntPtr.Zero)
oldOut = GetStdHandle( -11 );
if (! AllocConsole())
throw new Exception("AllocConsole() failed");
conOut = CreateFile( "CONOUT$", 0x40000000, 2, IntPtr.Zero, 3, 0, IntPtr.Zero );
if (! SetStdHandle(-11, conOut))
throw new Exception("SetStdHandle() failed");
StreamToConsole();
hasConsole = true;
}
public static void ReleaseConsole()
{
if (! hasConsole)
return;
if (! CloseHandle(conOut))
throw new Exception("CloseHandle() failed");
conOut = IntPtr.Zero;
if (! FreeConsole())
throw new Exception("FreeConsole() failed");
if (! SetStdHandle(-11, oldOut))
throw new Exception("SetStdHandle() failed");
StreamToConsole();
hasConsole = false;
if (! hasConsole)
return;
if (! CloseHandle(conOut))
throw new Exception("CloseHandle() failed");
conOut = IntPtr.Zero;
if (! FreeConsole())
throw new Exception("FreeConsole() failed");
if (! SetStdHandle(-11, oldOut))
throw new Exception("SetStdHandle() failed");
StreamToConsole();
hasConsole = false;
}
private static void StreamToConsole()
{
Stream cstm = Console.OpenStandardOutput();
StreamWriter cstw = new StreamWriter( cstm, Encoding.Default );
cstw.AutoFlush = true;
Console.SetOut( cstw );
Console.SetError( cstw );
Stream cstm = Console.OpenStandardOutput();
StreamWriter cstw = new StreamWriter( cstm, Encoding.Default );
cstw.AutoFlush = true;
Console.SetOut( cstw );
Console.SetError( cstw );
}
private static bool hasConsole = false;
private static IntPtr conOut;
@ -59,11 +60,11 @@ namespace Windows
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern IntPtr CreateFile(
string fileName,
int desiredAccess,
int shareMode,
int desiredAccess,
int shareMode,
IntPtr securityAttributes,
int creationDisposition,
int flagsAndAttributes,
int creationDisposition,
int flagsAndAttributes,
IntPtr templateFile );
[DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)]
private static extern bool CloseHandle(IntPtr handle);


+ 10
- 2
IllusionInjector/IllusionInjector.csproj View File

@ -34,8 +34,8 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Ionic.Zlib, Version=1.9.1.5, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL">
<HintPath>..\packages\Ionic.Zlib.1.9.1.5\lib\Ionic.Zlib.dll</HintPath>
<Reference Include="Ionic.Zip, Version=1.9.1.8, Culture=neutral, PublicKeyToken=edbe51ad942a3f5c, processorArchitecture=MSIL">
<HintPath>..\packages\Ionic.Zip.1.9.1.8\lib\Ionic.Zip.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
@ -48,6 +48,10 @@
<HintPath>..\Libs\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UnityWebRequestModule">
<HintPath>..\..\..\..\..\..\Game Library\Steam\steamapps\common\Beat Saber\Beat Saber_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Bootstrapper.cs" />
@ -64,6 +68,9 @@
<Compile Include="PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="PluginComponent.cs" />
<Compile Include="Updating\ModUpdater.cs" />
<Compile Include="Updating\UpdateScript.cs" />
<Compile Include="Utilities\SimpleJson.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\IllusionPlugin\IllusionPlugin.csproj">
@ -74,6 +81,7 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.


+ 6
- 1
IllusionInjector/PluginComponent.cs View File

@ -1,4 +1,5 @@
using System;
using IllusionInjector.Updating;
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
@ -23,6 +24,10 @@ namespace IllusionInjector
bsPlugins = new CompositeBSPlugin(PluginManager.BSPlugins);
ipaPlugins = new CompositeIPAPlugin(PluginManager.IPAPlugins);
// this has no relevance since there is a new mod updater system
//gameObject.AddComponent<ModUpdater>(); // AFTER plugins are loaded, but before most things
bsPlugins.OnApplicationStart();
ipaPlugins.OnApplicationStart();


+ 21
- 7
IllusionInjector/PluginManager.cs View File

@ -16,9 +16,12 @@ namespace IllusionInjector
{
#pragma warning disable CS0618 // Type or member is obsolete (IPlugin)
/// <summary>
/// Gets the list of loaded plugins and loads them if necessary.
/// </summary>
public class BSPluginMeta
{
public IBeatSaberPlugin Plugin { get; internal set; }
public string Filename { get; internal set; }
}
public static IEnumerable<IBeatSaberPlugin> BSPlugins
{
get
@ -27,10 +30,21 @@ namespace IllusionInjector
{
LoadPlugins();
}
return _bsPlugins.Select(p => p.Plugin);
}
}
private static List<BSPluginMeta> _bsPlugins = null;
internal static IEnumerable<BSPluginMeta> BSMetas
{
get
{
if (_bsPlugins == null)
{
LoadPlugins();
}
return _bsPlugins;
}
}
private static List<IBeatSaberPlugin> _bsPlugins = null;
public static IEnumerable<IPlugin> IPAPlugins
{
@ -54,7 +68,7 @@ namespace IllusionInjector
// so we need to resort to P/Invoke
string exeName = Path.GetFileNameWithoutExtension(AppInfo.StartupPath);
Logger.log.Info(exeName);
_bsPlugins = new List<IBeatSaberPlugin>();
_bsPlugins = new List<BSPluginMeta>();
_ipaPlugins = new List<IPlugin>();
if (!Directory.Exists(pluginDirectory)) return;
@ -86,7 +100,7 @@ namespace IllusionInjector
foreach (string s in copiedPlugins)
{
var result = LoadPluginsFromFile(s, exeName);
_bsPlugins.AddRange(result.Item1);
_bsPlugins.AddRange(result.Item1.Select(p => new BSPluginMeta { Plugin = p, Filename = s }));
_ipaPlugins.AddRange(result.Item2);
}
@ -98,7 +112,7 @@ namespace IllusionInjector
Logger.log.Info("-----------------------------");
foreach (var plugin in _bsPlugins)
{
Logger.log.Info($"{plugin.Name}: {plugin.Version}");
Logger.log.Info($"{plugin.Plugin.Name}: {plugin.Plugin.Version}");
}
Logger.log.Info("-----------------------------");
foreach (var plugin in _ipaPlugins)


+ 205
- 0
IllusionInjector/Updating/ModUpdater.cs View File

@ -0,0 +1,205 @@
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}");
}
}
}

+ 98
- 0
IllusionInjector/Updating/UpdateScript.cs View File

@ -0,0 +1,98 @@
using SimpleJSON;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace IllusionInjector.Updating
{
/** // JSON format
* {
* "_updateScript": "0.1", // version
* "<pluginName>": { // an entry for your plugin, using its annotated name
* "version": "<version>", // required, should be in .NET Version class format
* // note: only required if neither newName nor newScript is specified
* "newName": "<newName>", // optional, defines a new name for the plugin (gets saved under this name)
* // (updater will also check this file for this name to get latest)
* "newScript": "<newScript>", // optional, defines a new location for the update script
* // updater will look here for latest version too
* // note: if both newName and newScript are defined, the updater will only look in newScript
* // for newName, and not any other combination
* "download": "<url>", // required, defines URL to use for downloading new version
* // note: only required if neither newName nor newScript is specified
* },
* ...
* }
*/
class UpdateScript
{
static readonly Version ScriptVersion = new Version(0, 1);
public Version Version { get; private set; }
private Dictionary<string, PluginVersionInfo> info = new Dictionary<string, PluginVersionInfo>();
public IReadOnlyDictionary<string, PluginVersionInfo> Info { get => info; }
public class PluginVersionInfo
{
public Version Version { get; protected internal set; }
public string NewName { get; protected internal set; }
public Uri NewScript { get; protected internal set; }
public Uri Download { get; protected internal set; }
}
public static UpdateScript Parse(JSONObject jscript)
{
var script = new UpdateScript
{
Version = Version.Parse(jscript["_updateScript"].Value)
};
if (script.Version != ScriptVersion)
throw new UpdateScriptParseException("Script version mismatch");
jscript.Remove("_updateScript");
foreach (var kvp in jscript)
{
var obj = kvp.Value.AsObject;
var pvi = new PluginVersionInfo
{
Version = obj.Linq.Any(p => p.Key == "version") ? Version.Parse(obj["version"].Value) : null,
Download = obj.Linq.Any(p => p.Key == "download") ? new Uri(obj["download"].Value) : null,
NewName = obj.Linq.Any(p => p.Key == "newName") ? obj["newName"] : null,
NewScript = obj.Linq.Any(p => p.Key == "newScript") ? new Uri(obj["newScript"]) : null
};
if (pvi.NewName == null && pvi.NewScript == null && (pvi.Version == null || pvi.Download == null))
throw new UpdateScriptParseException($"Required fields missing from object {kvp.Key}");
script.info.Add(kvp.Key, pvi);
}
return script;
}
[Serializable]
private class UpdateScriptParseException : Exception
{
public UpdateScriptParseException()
{
}
public UpdateScriptParseException(string message) : base(message)
{
}
public UpdateScriptParseException(string message, Exception innerException) : base(message, innerException)
{
}
protected UpdateScriptParseException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
}

+ 1
- 1
IllusionInjector/obj/Debug/IllusionInjector.csproj.CoreCompileInputs.cache View File

@ -1 +1 @@
2fe547896965157e6254a6138df5fa8e17aceac2
c45de925bb8a7971d5428fabdc9f32b04b599913

+ 1
- 1
IllusionInjector/packages.config View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Ionic.Zlib" version="1.9.1.5" targetFramework="net46" />
<package id="Ionic.Zip" version="1.9.1.8" targetFramework="net46" />
</packages>

+ 5
- 0
IllusionPlugin/BeatSaber/IBeatSaberPlugin.cs View File

@ -22,6 +22,11 @@ namespace IllusionPlugin
/// </summary>
Version Version { get; }
/// <summary>
/// The URI to the update script for the plugin. May be <see langword="null"/>.
/// </summary>
//Uri UpdateUri { get; }
/// <summary>
/// Gets invoked when the application is started.
/// </summary>


Loading…
Cancel
Save