Browse Source

Add filmscript auto-installer

master
NGnius (Graham) 3 years ago
parent
commit
fe96f71e6a
11 changed files with 301 additions and 6 deletions
  1. +33
    -0
      NScript/Filmscript/FilmscriptInstaller.cs
  2. +5
    -1
      NScript/Filmscript/GeneralBindings.cs
  3. +17
    -0
      NScript/Gitea/Asset.cs
  4. +19
    -0
      NScript/Gitea/Author.cs
  5. +22
    -0
      NScript/Gitea/Release.cs
  6. +65
    -0
      NScript/Gitea/ReleaseUtility.cs
  7. +11
    -0
      NScript/Install/IInstallable.cs
  8. +26
    -0
      NScript/Install/InstallTools.cs
  9. +76
    -0
      NScript/Install/Installer.cs
  10. +9
    -0
      NScript/NScript.csproj
  11. +18
    -5
      NScript/NScriptPlugin.cs

+ 33
- 0
NScript/Filmscript/FilmscriptInstaller.cs View File

@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;

namespace NScript.Filmscript
{
public class FilmscriptInstaller : Install.IInstallable
{
public string Name { get; } = "filmscript.dll";
public void Install()
{
List<Gitea.Release> releases = Gitea.ReleaseUtility.GetReleases("NGnius", "filmscript-rs");

GamecraftModdingAPI.Utility.Logging.Log($"Downloading {Name}");
#if DEBUG
var r = Gitea.ReleaseUtility.FindRelease(releases, prerelease: true)?? throw new Exception("Failed to find valid release candidate");
#else
var r = Gitea.ReleaseUtility.FindRelease(releases)?? throw new Exception("Failed to find valid release candidate");
#endif
var a = Gitea.ReleaseUtility.FindAsset(r, Name)?? throw new Exception($"Failed to find valid asset candidate for {Name}");
var download = Gitea.ReleaseUtility.DownloadAsset(a);
File.WriteAllBytes(NScript.Install.InstallTools.NativeDllFilepath(Name), download);
}

public bool IsInstalled()
{
// NOTE: Mono will only try to load a native DLL once, so trying to call a non-existent DLL
// will prevent it from being loaded later, even if it's downloaded between now and then.
return NScript.Install.InstallTools.NativeDllExists(Name);
}
}
}

+ 5
- 1
NScript/Filmscript/GeneralBindings.cs View File

@@ -5,16 +5,20 @@ namespace NScript.Filmscript
{
public static class GeneralBindings
{
// raw bindings
[DllImport("filmscript.dll")]
public static extern string filmscript_version();

private static bool? dllExists = null;

public static string Version()
{
return filmscript_version();
}

public static bool Exists()
public static bool DllExists()
{
if (dllExists != null) return dllExists.Value;
try
{
filmscript_version();


+ 17
- 0
NScript/Gitea/Asset.cs View File

@@ -0,0 +1,17 @@
using System;
using Newtonsoft.Json;

namespace NScript.Gitea
{
[JsonObject]
public struct Asset
{
[JsonProperty("id")] public int Id;
[JsonProperty("name")] public string Name;
[JsonProperty("size")] public ulong Size;
[JsonProperty("download_count")] public int DownloadCount;
[JsonProperty("created_at")] public DateTime CreatedAt;
[JsonProperty("uuid")] public string Uuid;
[JsonProperty("browser_download_url")] public string BrowserDownloadUrl;
}
}

+ 19
- 0
NScript/Gitea/Author.cs View File

@@ -0,0 +1,19 @@
using System;
using Newtonsoft.Json;

namespace NScript.Gitea
{
[JsonObject]
public struct Author
{
[JsonProperty("id")] public int Id;
[JsonProperty("login")] public string Login;
[JsonProperty("full_name")] public string FullName;
[JsonProperty("email")] public string Email;
[JsonProperty("avatar_url")] public string AvatarUrl;
[JsonProperty("language")] public string Language;
[JsonProperty("last_login")] public DateTime LastLogin;
[JsonProperty("created")] public DateTime CreatedAt;
[JsonProperty("username")] public string Username;
}
}

+ 22
- 0
NScript/Gitea/Release.cs View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace NScript.Gitea
{
[JsonObject]
public struct Release
{
[JsonProperty("id")] public int Id;
[JsonProperty("tag_name")] public string TagName;
[JsonProperty("name")] public string Name;
[JsonProperty("body")] public string Body;
[JsonProperty("url")] public string Url;
[JsonProperty("draft")] public bool Draft;
[JsonProperty("prerelease")] public bool PreRelease;
[JsonProperty("created_at")] public DateTime CreatedAt;
[JsonProperty("published_at")] public DateTime PublishedAt;
[JsonProperty("author")] public Author Author;
[JsonProperty("assets")] public List<Asset> Assets;
}
}

+ 65
- 0
NScript/Gitea/ReleaseUtility.cs View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using Newtonsoft.Json;

namespace NScript.Gitea
{
public class ReleaseUtility
{
public static List<Release> GetReleases(string owner, string repo,
string apiUrl = "https://git.exmods.org/api/v1")
{
string fullUrl = $"{apiUrl}/repos/{owner}/{repo}/releases";
WebRequest req = WebRequest.Create(fullUrl);
req.Method = "GET";
HttpWebResponse resp = (HttpWebResponse) req.GetResponse();
StreamReader body = new StreamReader(resp.GetResponseStream());
string respBody = body.ReadToEnd();
resp.Close();
return JsonConvert.DeserializeObject<List<Release>>(respBody);
}
public static byte[] DownloadAsset(Asset asset)
{
return DownloadAsset(asset.BrowserDownloadUrl);
}

public static byte[] DownloadAsset(string url)
{
WebRequest req = WebRequest.Create(url);
req.Method = "GET";
HttpWebResponse resp = (HttpWebResponse) req.GetResponse();
BinaryReader body = new BinaryReader(resp.GetResponseStream());
byte[] respBody = body.ReadBytes(int.MaxValue);
resp.Close();
return respBody;
}

public static Release? FindRelease(List<Release> releases, bool prerelease = false, bool draft = false)
{
foreach (Release release in releases)
{
if (release.PreRelease == prerelease && release.Draft == draft) return release;
}

return null;
}

public static Asset? FindAsset(Release release, string name)
{
return FindAsset(release.Assets, name);
}
public static Asset? FindAsset(List<Asset> assets, string name)
{
foreach (var asset in assets)
{
if (string.Equals(asset.Name, name, StringComparison.InvariantCulture)) return asset;
}

return null;
}
}
}

+ 11
- 0
NScript/Install/IInstallable.cs View File

@@ -0,0 +1,11 @@
namespace NScript.Install
{
public interface IInstallable
{
string Name { get; }

void Install();

bool IsInstalled();
}
}

+ 26
- 0
NScript/Install/InstallTools.cs View File

@@ -0,0 +1,26 @@
using System.IO;
using NScript.Filmscript;

namespace NScript.Install
{
public static class InstallTools
{
public static bool NativeDllExists(string name)
{
return File.Exists(NativeDllFilepath(name));
}

public static string NativeDllFilepath(string name = "")
{
return "TechbloxPreview_Data/Plugins/x86_64/" + name;
}

public static Installer BuildDefaultInstaller()
{
return new Installer(new IInstallable[]
{
new FilmscriptInstaller(),
});
}
}
}

+ 76
- 0
NScript/Install/Installer.cs View File

@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Threading;

namespace NScript.Install
{
public class Installer
{
private List<IInstallable> _toInstall;

private bool _isRunning = false;

private bool _isCompleted = false;
public bool Completed => _isCompleted;

private string _currentItemName = null;

public string CurrentlyInstalling => _currentItemName;

private Thread _worker = null;

public Installer()
{
_toInstall = new List<IInstallable>();
}
public Installer(IEnumerable<IInstallable> toInstall)
{
_toInstall = new List<IInstallable>(toInstall);
}
public void AddInstallable(IInstallable i)
{
if (_isRunning) throw new Exception("Cannot add IInstallable when already installing");
_toInstall.Add(i);
}

public void InstallEverything()
{
if (_worker != null) _worker.Join();
_worker = new Thread(InstallWorker);
_worker.Start();
}

public void WaitForCompletion()
{
if (_worker != null) _worker.Join();
_worker = null;
}

private void InstallWorker()
{
_isRunning = true;
foreach (var i in _toInstall)
{
_currentItemName = i.Name;
if (!i.IsInstalled())
{
try
{
i.Install();
}
catch (Exception e)
{
GamecraftModdingAPI.Utility.Logging.LogWarning(
$"Failed to install {_currentItemName}: {e.Message}");
}
}
}
_isRunning = false;
_isCompleted = true;
_currentItemName = null;
}
}
}

+ 9
- 0
NScript/NScript.csproj View File

@@ -1083,4 +1083,13 @@
</ItemGroup>
<!--End Dependencies-->

<!--<ItemGroup>
<PackageReference Include="ILRepack" Version="2.0.18" />
<PackageReference Include="Gitea.API" Version="1.0.20" />
</ItemGroup>

<Target Name="StaticLinkMergeAfterBuild" AfterTargets="Build">
<Exec Command="$(PkgILRepack)\tools\ILRepack.exe /ndebug /parallel /lib:..\ref\TechbloxPreview_Data\Managed\ /lib:..\ref\Plugins\ /lib:..\..\ref\TechbloxPreview_Data\Managed\ /lib:..\..\ref\Plugins\ /lib:bin\$(Configuration)\net472\ /out:bin\$(Configuration)\net472\FilmScript.dll bin\$(Configuration)\net472\NScript.dll bin\$(Configuration)\net472\Gitea.API.dll" />
</Target>-->

</Project>

+ 18
- 5
NScript/NScriptPlugin.cs View File

@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

using IllusionPlugin;
@@ -16,6 +18,8 @@ namespace NScript

private HarmonyLib.Harmony instance;

private Install.Installer _installer = null;

// called when Gamecraft shuts down
public override void OnApplicationQuit()
{
@@ -37,14 +41,18 @@ namespace NScript
instance.PatchAll(Assembly.GetExecutingAssembly());
// load external libraries
try
_installer = Install.InstallTools.BuildDefaultInstaller();
_installer.InstallEverything();
// TODO wait for this later, since these resources are usually only used when in a game
// (i.e. there's no need to prevent the main menu from loading just to download this stuff)
_installer.WaitForCompletion();
if (Filmscript.GeneralBindings.DllExists())
{
string filmscriptVersion = Filmscript.GeneralBindings.Version();
GamecraftModdingAPI.Utility.Logging.MetaLog($"filmscript.dll {filmscriptVersion}");
GamecraftModdingAPI.Utility.Logging.MetaLog($"filmscript.dll {Filmscript.GeneralBindings.Version()}");
}
catch (DllNotFoundException e)
else
{
GamecraftModdingAPI.Utility.Logging.MetaLog($"Failed to find filmscript DLL: {e.Message} ({e.TypeName} | {e.Source})");
GamecraftModdingAPI.Utility.Logging.MetaLog($"Failed to find filmscript DLL");
}

// Initialize this mod
@@ -71,6 +79,11 @@ namespace NScript
CameraPatch.AllowDefaultBehaviour = !CameraPatch.AllowDefaultBehaviour;
CameraEngine.useDefaultBehaviour = !CameraEngine.useDefaultBehaviour;
}

if (_installer != null && _installer.CurrentlyInstalling != null)
{
UnityEngine.GUILayout.Box($"Downloading {_installer.CurrentlyInstalling}...");
}
}
}
}

Loading…
Cancel
Save