Browse Source

Re-activated and updated the updater, needs more testing

Bump version
pull/46/head
Anairkoen Schno 5 years ago
parent
commit
990b08fbbb
10 changed files with 126 additions and 102 deletions
  1. +1
    -1
      IPA.Injector/Updates.cs
  2. +1
    -1
      IPA.Loader/Config/SelfConfig.cs
  3. +2
    -3
      IPA.Loader/IPA.Loader.csproj
  4. +0
    -25
      IPA.Loader/JsonConverters/ModSaberDependencyConverter.cs
  5. +1
    -1
      IPA.Loader/Loader/PluginComponent.cs
  6. +3
    -3
      IPA.Loader/Loader/manifest.json
  7. +73
    -36
      IPA.Loader/Updating/BeatMods/ApiEndpoint.cs
  8. +42
    -29
      IPA.Loader/Updating/BeatMods/Updater.cs
  9. +2
    -2
      IPA/Properties/AssemblyInfo.cs
  10. +1
    -1
      appveyor.yml

+ 1
- 1
IPA.Injector/Updates.cs View File

@ -9,7 +9,7 @@ namespace IPA.Injector
{ {
internal static class Updates internal static class Updates
{ {
private const string DeleteFileName = Updating.ModSaber.Updater.SpecialDeletionsFile;
private const string DeleteFileName = Updating.BeatMods.Updater.SpecialDeletionsFile;
public static void InstallPendingUpdates() public static void InstallPendingUpdates()
{ {
var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending"); var pendingDir = Path.Combine(BeatSaber.InstallPath, "IPA", "Pending");


+ 1
- 1
IPA.Loader/Config/SelfConfig.cs View File

@ -33,7 +33,7 @@ namespace IPA.Config
} }
internal const string IPAName = "Beat Saber IPA"; internal const string IPAName = "Beat Saber IPA";
internal const string IPAVersion = "3.12.9";
internal const string IPAVersion = "3.12.10";
public bool Regenerate = true; public bool Regenerate = true;


+ 2
- 3
IPA.Loader/IPA.Loader.csproj View File

@ -89,7 +89,6 @@
<Compile Include="Logging\Logger.cs" /> <Compile Include="Logging\Logger.cs" />
<Compile Include="Logging\LogPrinter.cs" /> <Compile Include="Logging\LogPrinter.cs" />
<Compile Include="Config\ModPrefs.cs" /> <Compile Include="Config\ModPrefs.cs" />
<Compile Include="JsonConverters\ModSaberDependencyConverter.cs" />
<Compile Include="JsonConverters\SemverRangeConverter.cs" /> <Compile Include="JsonConverters\SemverRangeConverter.cs" />
<Compile Include="JsonConverters\SemverVersionConverter.cs" /> <Compile Include="JsonConverters\SemverVersionConverter.cs" />
<Compile Include="Utilities\BeatSaber.cs" /> <Compile Include="Utilities\BeatSaber.cs" />
@ -105,8 +104,8 @@
<Compile Include="Loader\PluginComponent.cs" /> <Compile Include="Loader\PluginComponent.cs" />
<Compile Include="Loader\PluginManager.cs" /> <Compile Include="Loader\PluginManager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Updating\ModSaber\ApiEndpoint.cs" />
<Compile Include="Updating\ModSaber\Updater.cs" />
<Compile Include="Updating\BeatMods\ApiEndpoint.cs" />
<Compile Include="Updating\BeatMods\Updater.cs" />
<Compile Include="Updating\SelfPlugin.cs" /> <Compile Include="Updating\SelfPlugin.cs" />
<Compile Include="Utilities\Extensions.cs" /> <Compile Include="Utilities\Extensions.cs" />
<Compile Include="Utilities\Utils.cs" /> <Compile Include="Utilities\Utils.cs" />


+ 0
- 25
IPA.Loader/JsonConverters/ModSaberDependencyConverter.cs View File

@ -1,25 +0,0 @@
using System;
using IPA.Updating.ModSaber;
using Newtonsoft.Json;
using SemVer;
namespace IPA.JsonConverters
{
internal class ModSaberDependencyConverter : JsonConverter<ApiEndpoint.Mod.Dependency>
{
public override ApiEndpoint.Mod.Dependency ReadJson(JsonReader reader, Type objectType, ApiEndpoint.Mod.Dependency existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var parts = (reader.Value as string)?.Split('@');
return new ApiEndpoint.Mod.Dependency
{
Name = parts?[0],
VersionRange = new Range(parts?[1])
};
}
public override void WriteJson(JsonWriter writer, ApiEndpoint.Mod.Dependency value, JsonSerializer serializer)
{
writer.WriteValue($"{value.Name}@{value.VersionRange}");
}
}
}

+ 1
- 1
IPA.Loader/Loader/PluginComponent.cs View File

@ -28,7 +28,7 @@ namespace IPA.Loader
#pragma warning restore 618 #pragma warning restore 618
/* kill this for now, until theres a new system */ /* kill this for now, until theres a new system */
//gameObject.AddComponent<Updating.ModSaber.Updater>();
gameObject.AddComponent<Updating.BeatMods.Updater>();
bsPlugins.OnApplicationStart(); bsPlugins.OnApplicationStart();
ipaPlugins.OnApplicationStart(); ipaPlugins.OnApplicationStart();


+ 3
- 3
IPA.Loader/Loader/manifest.json View File

@ -3,9 +3,9 @@
"author": "DaNike", "author": "DaNike",
"description": "A mod loader specifically for Beat Saber.", "description": "A mod loader specifically for Beat Saber.",
"gameVersion": "0.13.2", "gameVersion": "0.13.2",
"id": "beatsaber-ipa-reloaded",
"name": "BSIPA",
"version": "3.12.9",
"id": "BSIPA",
"name": "Beat Saber IPA",
"version": "3.12.10",
"features": [ "features": [
"define-feature(print, IPA.Loader.Features.PrintFeature)", "define-feature(print, IPA.Loader.Features.PrintFeature)",
"define-feature(debug, IPA.Loader.Features.DebugFeature)", "define-feature(debug, IPA.Loader.Features.DebugFeature)",


IPA.Loader/Updating/ModSaber/ApiEndpoint.cs → IPA.Loader/Updating/BeatMods/ApiEndpoint.cs View File

@ -7,13 +7,14 @@ using Newtonsoft.Json;
using SemVer; using SemVer;
using Version = SemVer.Version; using Version = SemVer.Version;
namespace IPA.Updating.ModSaber
namespace IPA.Updating.BeatMods
{ {
class ApiEndpoint class ApiEndpoint
{ {
public const string ApiBase = "https://www.modsaber.org/";
public const string GetModInfoEndpoint = "registry/{0}/{1}";
public const string GetModsWithSemver = "api/v1.1/mods/semver/{0}/{1}";
public const string BeatModBase = "https://beatmods.com";
public const string ApiBase = BeatModBase + "/api/v1/mod";
public const string GetModInfoEndpoint = "?name={0}&version={1}";
public const string GetModsByName = "?name={0}";
class HexArrayConverter : JsonConverter class HexArrayConverter : JsonConverter
{ {
@ -64,6 +65,15 @@ namespace IPA.Updating.ModSaber
public class Mod public class Mod
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
/// <summary>
/// Will be a useless string of characters. Do not use.
/// </summary>
[JsonProperty("_id")]
public string Id;
[JsonProperty("required")]
public bool Required;
[JsonProperty("name")] [JsonProperty("name")]
public string Name; public string Name;
@ -74,15 +84,18 @@ namespace IPA.Updating.ModSaber
[Serializable] [Serializable]
public class AuthorType public class AuthorType
{ {
[JsonProperty("name")]
[JsonProperty("username")]
public string Name; public string Name;
[JsonProperty("id")]
[JsonProperty("_id")]
public string Id; public string Id;
public override string ToString() => Name; public override string ToString() => Name;
} }
[Serializable]
[JsonProperty("author")]
public AuthorType Author;
/*[Serializable]
public class DetailsData public class DetailsData
{ {
[JsonProperty("author")] [JsonProperty("author")]
@ -96,35 +109,23 @@ namespace IPA.Updating.ModSaber
} }
[JsonProperty("details")] [JsonProperty("details")]
public DetailsData Details;
public DetailsData Details;*/
[Serializable]
public class ApprovalStatus
{
[JsonProperty("status")]
public bool Status;
[JsonProperty("modified")]
public string LastModified;
}
[JsonProperty("status")]
public string Status;
public const string ApprovedStatus = "approved";
[JsonProperty("approval")]
public ApprovalStatus Approval;
[Serializable]
public class GameVersionType
{
[JsonProperty("value"),
JsonConverter(typeof(SemverVersionConverter))]
public Version Version;
[JsonProperty("manifest")]
public string Manifest;
}
[JsonProperty("description")]
public string Description;
[JsonProperty("gameVersion")]
public GameVersionType GameVersion;
[JsonProperty("category")]
public string Category;
[JsonProperty("link")]
public Uri Link;
#pragma warning restore CS0649 #pragma warning restore CS0649
[Serializable]
/*[Serializable]
public class PlatformFile public class PlatformFile
{ {
[JsonProperty("hash"), [JsonProperty("hash"),
@ -152,9 +153,42 @@ namespace IPA.Updating.ModSaber
} }
[JsonProperty("files")] [JsonProperty("files")]
public FilesObject Files;
public class Dependency
public FilesObject Files;*/
[Serializable]
public class DownloadsObject
{
public const string TypeUniversal = "universal";
public const string TypeSteam = "steam";
public const string TypeOculus = "oculus";
[JsonProperty("type")]
public string Type;
[JsonProperty("url")]
public string Path;
[Serializable]
public class HashObject
{
[JsonProperty("hash"), JsonConverter(typeof(HexArrayConverter))]
public byte[] Hash;
[JsonProperty("file")]
public string File;
}
/// <summary>
/// Hashes stored are MD5
/// </summary>
[JsonProperty("hashMd5")]
public HashObject[] Hashes;
}
[JsonProperty("downloads")]
public DownloadsObject[] Downloads;
/*public class Dependency
{ {
public string Name = null; public string Name = null;
public Range VersionRange = null; public Range VersionRange = null;
@ -174,11 +208,14 @@ namespace IPA.Updating.ModSaber
public LinksType Links; public LinksType Links;
[JsonProperty("oldVersions", ItemConverterType = typeof(SemverVersionConverter))] [JsonProperty("oldVersions", ItemConverterType = typeof(SemverVersionConverter))]
public Version[] OldVersions = new Version[0];
public Version[] OldVersions = new Version[0];*/
[JsonProperty("dependencies")]
public Mod[] Dependencies;
public override string ToString() public override string ToString()
{ {
return $"{{\"{Details.Title} ({Name})\"v{Version} for {GameVersion.Version} by {Details.Author} with \"{Files.Steam}\" and \"{Files.Oculus}\"}}";
return $"{{\"{Name}\"v{Version} by {Author} files for {string.Join(", ", Downloads.Select(d => d.Type))}}}";
} }
} }

IPA.Loader/Updating/ModSaber/Updater.cs → IPA.Loader/Updating/BeatMods/Updater.cs View File

@ -20,7 +20,7 @@ using static IPA.Loader.PluginManager;
using Logger = IPA.Logging.Logger; using Logger = IPA.Logging.Logger;
using Version = SemVer.Version; using Version = SemVer.Version;
namespace IPA.Updating.ModSaber
namespace IPA.Updating.BeatMods
{ {
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] [SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
internal class Updater : MonoBehaviour internal class Updater : MonoBehaviour
@ -72,7 +72,7 @@ namespace IPA.Updating.ModSaber
} }
private readonly Dictionary<string, string> requestCache = new Dictionary<string, string>(); private readonly Dictionary<string, string> requestCache = new Dictionary<string, string>();
private IEnumerator GetModsaberEndpoint(string url, Ref<string> result)
private IEnumerator GetBeatModsEndpoint(string url, Ref<string> result)
{ {
if (requestCache.TryGetValue(url, out string value)) if (requestCache.TryGetValue(url, out string value))
{ {
@ -121,11 +121,11 @@ namespace IPA.Updating.ModSaber
{ {
Ref<string> reqResult = new Ref<string>(""); Ref<string> reqResult = new Ref<string>("");
yield return GetModsaberEndpoint(uri, reqResult);
yield return GetBeatModsEndpoint(uri, reqResult);
try try
{ {
result.Value = JsonConvert.DeserializeObject<ApiEndpoint.Mod>(reqResult.Value);
result.Value = JsonConvert.DeserializeObject<List<ApiEndpoint.Mod>>(reqResult.Value).First();
modCache[uri] = result.Value; modCache[uri] = result.Value;
} }
@ -137,9 +137,9 @@ namespace IPA.Updating.ModSaber
} }
private readonly Dictionary<string, List<ApiEndpoint.Mod>> modVersionsCache = new Dictionary<string, List<ApiEndpoint.Mod>>(); private readonly Dictionary<string, List<ApiEndpoint.Mod>> modVersionsCache = new Dictionary<string, List<ApiEndpoint.Mod>>();
private IEnumerator GetModVersionsMatching(string modName, string range, Ref<List<ApiEndpoint.Mod>> result)
private IEnumerator GetModVersionsMatching(string modName, Range range, Ref<List<ApiEndpoint.Mod>> result)
{ {
var uri = string.Format(ApiEndpoint.GetModsWithSemver, Uri.EscapeUriString(modName), Uri.EscapeUriString(range));
var uri = string.Format(ApiEndpoint.GetModsByName, Uri.EscapeUriString(modName));
if (modVersionsCache.TryGetValue(uri, out List<ApiEndpoint.Mod> value)) if (modVersionsCache.TryGetValue(uri, out List<ApiEndpoint.Mod> value))
{ {
@ -149,11 +149,12 @@ namespace IPA.Updating.ModSaber
{ {
Ref<string> reqResult = new Ref<string>(""); Ref<string> reqResult = new Ref<string>("");
yield return GetModsaberEndpoint(uri, reqResult);
yield return GetBeatModsEndpoint(uri, reqResult);
try try
{ {
result.Value = JsonConvert.DeserializeObject<List<ApiEndpoint.Mod>>(reqResult.Value);
result.Value = JsonConvert.DeserializeObject<List<ApiEndpoint.Mod>>(reqResult.Value)
.Where(m => range.IsSatisfied(m.Version)).ToList();
modVersionsCache[uri] = result.Value; modVersionsCache[uri] = result.Value;
} }
@ -217,8 +218,15 @@ namespace IPA.Updating.ModSaber
continue; continue;
} }
list.Value.AddRange(mod.Value.Links.Dependencies.Select(d => new DependencyObject { Name = d.Name, Requirement = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
list.Value.AddRange(mod.Value.Links.Conflicts.Select(d => new DependencyObject { Name = d.Name, Conflicts = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
list.Value.AddRange(mod.Value.Dependencies.Select(m => new DependencyObject
{
Name = m.Name,
Requirement = new Range($">={m.Version}"),
Consumers = new HashSet<string> { dep.Name }
}));
// currently no conflicts exist in BeatMods
//list.Value.AddRange(mod.Value.Links.Dependencies.Select(d => new DependencyObject { Name = d.Name, Requirement = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
//list.Value.AddRange(mod.Value.Links.Conflicts.Select(d => new DependencyObject { Name = d.Name, Conflicts = d.VersionRange, Consumers = new HashSet<string> { dep.Name } }));
} }
var depNames = new HashSet<string>(); var depNames = new HashSet<string>();
@ -266,7 +274,7 @@ namespace IPA.Updating.ModSaber
} }
var modsMatching = new Ref<List<ApiEndpoint.Mod>>(null); var modsMatching = new Ref<List<ApiEndpoint.Mod>>(null);
yield return GetModVersionsMatching(dep.Name, dep.Requirement.ToString(), modsMatching);
yield return GetModVersionsMatching(dep.Name, dep.Requirement, modsMatching);
try { modsMatching.Verify(); } try { modsMatching.Verify(); }
catch (Exception e) catch (Exception e)
{ {
@ -278,8 +286,8 @@ namespace IPA.Updating.ModSaber
var ver = modsMatching.Value var ver = modsMatching.Value
.Where(nullCheck => nullCheck != null) // entry is not null .Where(nullCheck => nullCheck != null) // entry is not null
.Where(versionCheck => versionCheck.GameVersion.Version == BeatSaber.GameVersion) // game version matches
.Where(approvalCheck => approvalCheck.Approval.Status) // version approved
//.Where(versionCheck => versionCheck.GameVersion.Version == BeatSaber.GameVersion) // game version matches
.Where(approvalCheck => approvalCheck.Status == ApiEndpoint.Mod.ApprovedStatus) // version approved
.Where(conflictsCheck => dep.Conflicts == null || !dep.Conflicts.IsSatisfied(conflictsCheck.Version)) // not a conflicting version .Where(conflictsCheck => dep.Conflicts == null || !dep.Conflicts.IsSatisfied(conflictsCheck.Version)) // not a conflicting version
.Select(mod => mod.Version).Max(); // (2.1) get the max version .Select(mod => mod.Version).Max(); // (2.1) get the max version
// ReSharper disable once AssignmentInConditionalExpression // ReSharper disable once AssignmentInConditionalExpression
@ -329,13 +337,18 @@ namespace IPA.Updating.ModSaber
yield break; yield break;
} }
ApiEndpoint.Mod.PlatformFile platformFile;
/*
ApiEndpoint.Mod.DownloadsObject platformFile;
if (BeatSaber.ReleaseType == BeatSaber.Release.Steam || mod.Value.Files.Oculus == null) if (BeatSaber.ReleaseType == BeatSaber.Release.Steam || mod.Value.Files.Oculus == null)
platformFile = mod.Value.Files.Steam; platformFile = mod.Value.Files.Steam;
else else
platformFile = mod.Value.Files.Oculus;
platformFile = mod.Value.Files.Oculus;*/
var releaseName = BeatSaber.ReleaseType == BeatSaber.Release.Steam
? ApiEndpoint.Mod.DownloadsObject.TypeSteam : ApiEndpoint.Mod.DownloadsObject.TypeOculus;
var platformFile = mod.Value.Downloads.First(f => f.Type == ApiEndpoint.Mod.DownloadsObject.TypeUniversal || f.Type == releaseName);
string url = platformFile.DownloadPath;
string url = ApiEndpoint.BeatModBase + platformFile.Path;
Logger.updater.Debug($"URL = {url}"); Logger.updater.Debug($"URL = {url}");
@ -386,7 +399,7 @@ namespace IPA.Updating.ModSaber
if (downloadTask.IsFaulted) if (downloadTask.IsFaulted)
{ {
if (downloadTask.Exception != null && downloadTask.Exception.InnerExceptions.Any(e => e is ModsaberInterceptException))
if (downloadTask.Exception != null && downloadTask.Exception.InnerExceptions.Any(e => e is BeatmodsInterceptException))
{ // any exception is an intercept exception { // any exception is an intercept exception
Logger.updater.Error($"Modsaber did not return expected data for {item.Name}"); Logger.updater.Error($"Modsaber did not return expected data for {item.Name}");
} }
@ -451,15 +464,15 @@ namespace IPA.Updating.ModSaber
} }
} }
private void ExtractPluginAsync(MemoryStream stream, DependencyObject item, ApiEndpoint.Mod.PlatformFile fileInfo)
private void ExtractPluginAsync(MemoryStream stream, DependencyObject item, ApiEndpoint.Mod.DownloadsObject fileInfo)
{ // (3.3) { // (3.3)
Logger.updater.Debug($"Extracting ZIP file for {item.Name}"); Logger.updater.Debug($"Extracting ZIP file for {item.Name}");
var data = stream.GetBuffer();
/*var data = stream.GetBuffer();
SHA1 sha = new SHA1CryptoServiceProvider(); SHA1 sha = new SHA1CryptoServiceProvider();
var hash = sha.ComputeHash(data); var hash = sha.ComputeHash(data);
if (!Utils.UnsafeCompare(hash, fileInfo.Hash)) if (!Utils.UnsafeCompare(hash, fileInfo.Hash))
throw new Exception("The hash for the file doesn't match what is defined");
throw new Exception("The hash for the file doesn't match what is defined");*/
var targetDir = Path.Combine(BeatSaber.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending"); var targetDir = Path.Combine(BeatSaber.InstallPath, "IPA", Path.GetRandomFileName() + "_Pending");
Directory.CreateDirectory(targetDir); Directory.CreateDirectory(targetDir);
@ -489,17 +502,17 @@ namespace IPA.Updating.ModSaber
entry.Extract(ostream); entry.Extract(ostream);
ostream.Seek(0, SeekOrigin.Begin); ostream.Seek(0, SeekOrigin.Begin);
sha = new SHA1CryptoServiceProvider();
var fileHash = sha.ComputeHash(ostream);
var md5 = new MD5CryptoServiceProvider();
var fileHash = md5.ComputeHash(ostream);
try try
{ {
if (!Utils.UnsafeCompare(fileHash, fileInfo.FileHashes[entry.FileName]))
if (!Utils.UnsafeCompare(fileHash, fileInfo.Hashes.Where(h => h.File == entry.FileName).Select(h => h.Hash).First()))
throw new Exception("The hash for the file doesn't match what is defined"); throw new Exception("The hash for the file doesn't match what is defined");
} }
catch (KeyNotFoundException) catch (KeyNotFoundException)
{ {
throw new ModsaberInterceptException("ModSaber did not send the hashes for the zip's content!");
throw new BeatmodsInterceptException("BeatMods did not send the hashes for the zip's content!");
} }
ostream.Seek(0, SeekOrigin.Begin); ostream.Seek(0, SeekOrigin.Begin);
@ -582,21 +595,21 @@ namespace IPA.Updating.ModSaber
} }
[Serializable] [Serializable]
internal class ModsaberInterceptException : Exception
internal class BeatmodsInterceptException : Exception
{ {
public ModsaberInterceptException()
public BeatmodsInterceptException()
{ {
} }
public ModsaberInterceptException(string message) : base(message)
public BeatmodsInterceptException(string message) : base(message)
{ {
} }
public ModsaberInterceptException(string message, Exception innerException) : base(message, innerException)
public BeatmodsInterceptException(string message, Exception innerException) : base(message, innerException)
{ {
} }
protected ModsaberInterceptException(SerializationInfo info, StreamingContext context) : base(info, context)
protected BeatmodsInterceptException(SerializationInfo info, StreamingContext context) : base(info, context)
{ {
} }
} }

+ 2
- 2
IPA/Properties/AssemblyInfo.cs View File

@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.12.9")]
[assembly: AssemblyFileVersion("3.12.9")]
[assembly: AssemblyVersion("3.12.10")]
[assembly: AssemblyFileVersion("3.12.10")]

+ 1
- 1
appveyor.yml View File

@ -1,6 +1,6 @@
version: 'BSIPA-{branch}-{build}' version: 'BSIPA-{branch}-{build}'
environment: environment:
bsipa_version: '3.12.9'
bsipa_version: '3.12.10'
pull_requests: pull_requests:
do_not_increment_build_number: true do_not_increment_build_number: true
install: install:


Loading…
Cancel
Save