Initial commit
This commit is contained in:
42
mod/PLibAVC/IModVersionChecker.cs
Normal file
42
mod/PLibAVC/IModVersionChecker.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Implemented by classes which can check the current mod version and detect if it is out
|
||||
/// of date.
|
||||
/// </summary>
|
||||
public interface IModVersionChecker {
|
||||
/// <summary>
|
||||
/// The event to subscribe for when the check completes.
|
||||
/// </summary>
|
||||
event PVersionCheck.OnVersionCheckComplete OnVersionCheckCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// Checks the mod and reports if it is out of date. The mod's current version as
|
||||
/// reported by its mod_info.yaml file is available on the packagedModInfo member.
|
||||
///
|
||||
/// This method might not be run on the foreground thread. Do not create new behaviors
|
||||
/// or components without a coroutine to an existing GameObject.
|
||||
/// </summary>
|
||||
/// <param name="mod">The mod whose version is being checked.</param>
|
||||
/// <returns>true if the version check has started, or false if it could not be
|
||||
/// started, which will trigger the next version checker in line.</returns>
|
||||
bool CheckVersion(KMod.Mod mod);
|
||||
}
|
||||
}
|
||||
146
mod/PLibAVC/JsonURLVersionChecker.cs
Normal file
146
mod/PLibAVC/JsonURLVersionChecker.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using KMod;
|
||||
using Newtonsoft.Json;
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Checks the mod version using a URL to a JSON file. The file at this URL must resolve
|
||||
/// to a JSON file which can deserialize to the JsonURLVersionChecker.ModVersions class.
|
||||
/// </summary>
|
||||
public sealed class JsonURLVersionChecker : IModVersionChecker {
|
||||
/// <summary>
|
||||
/// The timeout in seconds for the web request before declaring the check as failed.
|
||||
/// </summary>
|
||||
public const int REQUEST_TIMEOUT = 8;
|
||||
|
||||
/// <summary>
|
||||
/// The URL to query for checking the mod version.
|
||||
/// </summary>
|
||||
public string JsonVersionURL { get; }
|
||||
|
||||
public event PVersionCheck.OnVersionCheckComplete OnVersionCheckCompleted;
|
||||
|
||||
public JsonURLVersionChecker(string url) {
|
||||
if (string.IsNullOrEmpty(url))
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
JsonVersionURL = url;
|
||||
}
|
||||
|
||||
public bool CheckVersion(Mod mod) {
|
||||
if (mod == null)
|
||||
throw new ArgumentNullException(nameof(mod));
|
||||
var request = UnityWebRequest.Get(JsonVersionURL);
|
||||
request.SetRequestHeader("Content-Type", "application/json");
|
||||
request.SetRequestHeader("User-Agent", "PLib AVC");
|
||||
request.timeout = REQUEST_TIMEOUT;
|
||||
var operation = request.SendWebRequest();
|
||||
operation.completed += (_) => OnRequestFinished(request, mod);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a web request completes, triggers the handler for the next updater.
|
||||
/// </summary>
|
||||
/// <param name="request">The JSON web request data.</param>
|
||||
/// <param name="mod">The mod that needs to be checked.</param>
|
||||
private void OnRequestFinished(UnityWebRequest request, Mod mod) {
|
||||
ModVersionCheckResults result = null;
|
||||
if (request.result == UnityWebRequest.Result.Success) {
|
||||
// Parse the text
|
||||
ModVersions versions;
|
||||
using (var reader = new StreamReader(new MemoryStream(request.
|
||||
downloadHandler.data))) {
|
||||
versions = new JsonSerializer() {
|
||||
MaxDepth = 4, DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
}.Deserialize<ModVersions>(new JsonTextReader(reader));
|
||||
}
|
||||
if (versions != null)
|
||||
result = ParseModVersion(mod, versions);
|
||||
}
|
||||
request.Dispose();
|
||||
OnVersionCheckCompleted?.Invoke(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the JSON file and looks up the version for the specified mod.
|
||||
/// </summary>
|
||||
/// <param name="mod">The mod's static ID.</param>
|
||||
/// <param name="versions">The data from the web JSON file.</param>
|
||||
/// <returns>The results of the update, or null if the mod could not be found in the
|
||||
/// JSON.</returns>
|
||||
private ModVersionCheckResults ParseModVersion(Mod mod, ModVersions versions) {
|
||||
ModVersionCheckResults result = null;
|
||||
string id = mod.staticID;
|
||||
if (versions.mods != null)
|
||||
foreach (var modVersion in versions.mods)
|
||||
if (modVersion != null && modVersion.staticID == id) {
|
||||
string newVersion = modVersion.version?.Trim();
|
||||
if (string.IsNullOrEmpty(newVersion))
|
||||
result = new ModVersionCheckResults(id, true);
|
||||
else
|
||||
result = new ModVersionCheckResults(id, newVersion !=
|
||||
PVersionCheck.GetCurrentVersion(mod), newVersion);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The serialization type for JSONURLVersionChecker. Allows multiple mods to query
|
||||
/// the same URL.
|
||||
/// </summary>
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public sealed class ModVersions {
|
||||
[JsonProperty]
|
||||
public List<ModVersion> mods;
|
||||
|
||||
public ModVersions() {
|
||||
mods = new List<ModVersion>(16);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the current version of each mod.
|
||||
/// </summary>
|
||||
public sealed class ModVersion {
|
||||
/// <summary>
|
||||
/// The mod's static ID, as reported by its mod.yaml. If a mod does not specify its
|
||||
/// static ID, it gets the default ID mod.label.id + "_" + mod.label.
|
||||
/// distribution_platform.
|
||||
/// </summary>
|
||||
public string staticID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mod's current version.
|
||||
/// </summary>
|
||||
public string version { get; set; }
|
||||
|
||||
public override string ToString() {
|
||||
return "{0}: version={1}".F(staticID, version);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
70
mod/PLibAVC/ModVersionCheckResults.cs
Normal file
70
mod/PLibAVC/ModVersionCheckResults.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using PeterHan.PLib.Core;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// The results of checking the mod version.
|
||||
/// </summary>
|
||||
[JsonObject(MemberSerialization.OptIn)]
|
||||
public sealed class ModVersionCheckResults {
|
||||
/// <summary>
|
||||
/// true if the mod is up to date, or false if it is out of date.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public bool IsUpToDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The mod whose version was queried. The current mod version is available on this
|
||||
/// mod through its packagedModInfo.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string ModChecked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The new version of this mod. If it is not available, it can be null, even if
|
||||
/// IsUpdated is false. Not relevant if IsUpToDate reports true.
|
||||
/// </summary>
|
||||
[JsonProperty]
|
||||
public string NewVersion { get; set; }
|
||||
|
||||
public ModVersionCheckResults() : this("", false) { }
|
||||
|
||||
public ModVersionCheckResults(string id, bool updated, string newVersion = null) {
|
||||
IsUpToDate = updated;
|
||||
ModChecked = id;
|
||||
NewVersion = newVersion;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
return obj is ModVersionCheckResults other && other.ModChecked == ModChecked &&
|
||||
IsUpToDate == other.IsUpToDate && NewVersion == other.NewVersion;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return ModChecked.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "ModVersionCheckResults[{0},updated={1},newVersion={2}]".F(
|
||||
ModChecked, IsUpToDate, NewVersion ?? "");
|
||||
}
|
||||
}
|
||||
}
|
||||
22
mod/PLibAVC/PLibAVC.csproj
Normal file
22
mod/PLibAVC/PLibAVC.csproj
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Title>PLib Automatic Version Check</Title>
|
||||
<AssemblyTitle>PLib.AVC</AssemblyTitle>
|
||||
<Version>4.11.0.0</Version>
|
||||
<UsesPLib>false</UsesPLib>
|
||||
<RootNamespace>PeterHan.PLib.AVC</RootNamespace>
|
||||
<AssemblyVersion>4.11.0.0</AssemblyVersion>
|
||||
<DistributeMod>false</DistributeMod>
|
||||
<PLibCore>true</PLibCore>
|
||||
<Platforms>Vanilla;Mergedown</Platforms>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DocumentationFile>bin\$(Platform)\Release\PLibAVC.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../PLibCore/PLibCore.csproj" />
|
||||
<ProjectReference Include="../PLibUI/PLibUI.csproj" />
|
||||
<Reference Include="UnityEngine.UnityWebRequestModule" HintPath="$(GameFolderActive)/UnityEngine.UnityWebRequestModule.dll" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
340
mod/PLibAVC/PVersionCheck.cs
Normal file
340
mod/PLibAVC/PVersionCheck.cs
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using HarmonyLib;
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Implements a basic automatic version check, using either Steam or an external website.
|
||||
///
|
||||
/// The version of the current mod is taken from the mod version attribute of the provided
|
||||
/// mod.
|
||||
/// </summary>
|
||||
public sealed class PVersionCheck : PForwardedComponent {
|
||||
/// <summary>
|
||||
/// The delegate type used when a background version check completes.
|
||||
/// </summary>
|
||||
/// <param name="result">The results of the check. If null, the check has failed,
|
||||
/// and the next version should be tried.</param>
|
||||
public delegate void OnVersionCheckComplete(ModVersionCheckResults result);
|
||||
|
||||
/// <summary>
|
||||
/// The instantiated copy of this class.
|
||||
/// </summary>
|
||||
internal static PVersionCheck Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of this component. Uses the running PLib version.
|
||||
/// </summary>
|
||||
internal static readonly Version VERSION = new Version(PVersion.VERSION);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reported version of the specified assembly.
|
||||
/// </summary>
|
||||
/// <param name="assembly">The assembly to check.</param>
|
||||
/// <returns>The assembly's file version, or if that is unset, its assembly version.</returns>
|
||||
private static string GetCurrentVersion(Assembly assembly) {
|
||||
string version = null;
|
||||
if (assembly != null) {
|
||||
version = assembly.GetFileVersion();
|
||||
if (string.IsNullOrEmpty(version))
|
||||
version = assembly.GetName()?.Version?.ToString();
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current version of the mod. If the version is specified in mod_info.yaml,
|
||||
/// that version is reported. Otherwise, the assembly file version (and failing that,
|
||||
/// the assembly version) of the assembly defining the mod's first UserMod2 instance
|
||||
/// is reported.
|
||||
///
|
||||
/// This method will only work after mods have loaded.
|
||||
/// </summary>
|
||||
/// <param name="mod">The mod to check.</param>
|
||||
/// <returns>The current version of that mod.</returns>
|
||||
public static string GetCurrentVersion(KMod.Mod mod) {
|
||||
if (mod == null)
|
||||
throw new ArgumentNullException(nameof(mod));
|
||||
string version = mod.packagedModInfo?.version;
|
||||
if (string.IsNullOrEmpty(version)) {
|
||||
// Does it have UM2 instances?
|
||||
var instances = mod.loaded_mod_data?.userMod2Instances;
|
||||
var dlls = mod.loaded_mod_data?.dlls;
|
||||
if (instances != null)
|
||||
// Use first UserMod2
|
||||
foreach (var um2 in instances) {
|
||||
version = GetCurrentVersion(um2.Key);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
break;
|
||||
}
|
||||
else if (dlls != null && dlls.Count > 0)
|
||||
// Use first DLL
|
||||
foreach (var assembly in dlls) {
|
||||
version = GetCurrentVersion(assembly);
|
||||
if (!string.IsNullOrEmpty(version))
|
||||
break;
|
||||
}
|
||||
else
|
||||
// All methods of determining the version have failed
|
||||
version = "";
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
private static void MainMenu_OnSpawn_Postfix() {
|
||||
Instance?.RunVersionCheck();
|
||||
}
|
||||
|
||||
private static void ModsScreen_BuildDisplay_Postfix(System.Collections.IEnumerable
|
||||
___displayedMods) {
|
||||
// Must cast the type because ModsScreen.DisplayedMod is private
|
||||
if (Instance != null && ___displayedMods != null)
|
||||
foreach (var modEntry in ___displayedMods)
|
||||
Instance.AddWarningIfOutdated(modEntry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The mods whose version will be checked.
|
||||
/// </summary>
|
||||
private readonly IDictionary<string, VersionCheckMethods> checkVersions;
|
||||
|
||||
/// <summary>
|
||||
/// The location where the outcome of mod version checking will be stored.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, ModVersionCheckResults> results;
|
||||
|
||||
public override Version Version => VERSION;
|
||||
|
||||
public PVersionCheck() {
|
||||
checkVersions = new Dictionary<string, VersionCheckMethods>(8);
|
||||
results = new ConcurrentDictionary<string, ModVersionCheckResults>(2, 16);
|
||||
InstanceData = results.Values;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning to the mods screen if a mod is outdated.
|
||||
/// </summary>
|
||||
/// <param name="modEntry">The mod entry to modify.</param>
|
||||
private void AddWarningIfOutdated(object modEntry) {
|
||||
int index = -1;
|
||||
var type = modEntry.GetType();
|
||||
var indexVal = type.GetFieldSafe("mod_index", false)?.GetValue(modEntry);
|
||||
if (indexVal is int intVal)
|
||||
index = intVal;
|
||||
var rowInstance = type.GetFieldSafe("rect_transform", false)?.GetValue(
|
||||
modEntry) as RectTransform;
|
||||
var mods = Global.Instance.modManager?.mods;
|
||||
string id;
|
||||
if (rowInstance != null && mods != null && index >= 0 && index < mods.Count &&
|
||||
!string.IsNullOrEmpty(id = mods[index]?.staticID) && rowInstance.
|
||||
TryGetComponent(out HierarchyReferences hr) && results.TryGetValue(id,
|
||||
out ModVersionCheckResults data) && data != null)
|
||||
// Version text is thankfully known, even if other mods have added buttons
|
||||
AddWarningIfOutdated(data, hr.GetReference<LocText>("Version"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a warning to a mod version label if it is outdated.
|
||||
/// </summary>
|
||||
/// <param name="data">The updated mod version.</param>
|
||||
/// <param name="versionText">The current mod version label.</param>
|
||||
private void AddWarningIfOutdated(ModVersionCheckResults data, LocText versionText) {
|
||||
GameObject go;
|
||||
if (versionText != null && (go = versionText.gameObject) != null && !data.
|
||||
IsUpToDate) {
|
||||
string text = versionText.text;
|
||||
if (string.IsNullOrEmpty(text))
|
||||
text = PLibStrings.OUTDATED_WARNING;
|
||||
else
|
||||
text = text + " " + PLibStrings.OUTDATED_WARNING;
|
||||
versionText.text = text;
|
||||
go.AddOrGet<ToolTip>().toolTip = string.Format(PLibStrings.OUTDATED_TOOLTIP,
|
||||
data.NewVersion ?? "");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize(Harmony plibInstance) {
|
||||
Instance = this;
|
||||
plibInstance.Patch(typeof(MainMenu), "OnSpawn", postfix: PatchMethod(nameof(
|
||||
MainMenu_OnSpawn_Postfix)));
|
||||
plibInstance.Patch(typeof(ModsScreen), "BuildDisplay", postfix: PatchMethod(
|
||||
nameof(ModsScreen_BuildDisplay_Postfix)));
|
||||
}
|
||||
|
||||
public override void Process(uint operation, object args) {
|
||||
if (operation == 0 && args is System.Action runNext) {
|
||||
VersionCheckTask first = null, previous = null;
|
||||
results.Clear();
|
||||
foreach (var pair in checkVersions) {
|
||||
string staticID = pair.Key;
|
||||
var mod = pair.Value;
|
||||
#if DEBUG
|
||||
PUtil.LogDebug("Checking version for mod {0}".F(staticID));
|
||||
#endif
|
||||
foreach (var checker in mod.Methods) {
|
||||
var node = new VersionCheckTask(mod.ModToCheck, checker, results) {
|
||||
Next = runNext
|
||||
};
|
||||
if (previous != null)
|
||||
previous.Next = runNext;
|
||||
if (first == null)
|
||||
first = node;
|
||||
}
|
||||
}
|
||||
first?.Run();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the specified mod for automatic version checking. Mods will be registered
|
||||
/// using their static ID, so to avoid the default ID from being used instead, set this
|
||||
/// attribute in mod.yaml.
|
||||
///
|
||||
/// The same mod can be registered multiple times with different methods to check the
|
||||
/// mod versions. The methods will be attempted in order from first registered to last.
|
||||
/// However, the same mod must not be registered multiple times in different instances
|
||||
/// of PVersionCheck.
|
||||
/// </summary>
|
||||
/// <param name="mod">The mod instance to check.</param>
|
||||
/// <param name="checker">The method to use for checking the mod version.</param>
|
||||
public void Register(KMod.UserMod2 mod, IModVersionChecker checker) {
|
||||
var kmod = mod?.mod;
|
||||
if (kmod == null)
|
||||
throw new ArgumentNullException(nameof(mod));
|
||||
if (checker == null)
|
||||
throw new ArgumentNullException(nameof(checker));
|
||||
RegisterForForwarding();
|
||||
string staticID = kmod.staticID;
|
||||
if (!checkVersions.TryGetValue(staticID, out VersionCheckMethods checkers))
|
||||
checkVersions.Add(staticID, checkers = new VersionCheckMethods(kmod));
|
||||
checkers.Methods.Add(checker);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reports the results of the version check.
|
||||
/// </summary>
|
||||
private void ReportResults() {
|
||||
var allMods = PRegistry.Instance.GetAllComponents(ID);
|
||||
results.Clear();
|
||||
if (allMods != null)
|
||||
// Consolidate them through JSON roundtrip into results dictionary
|
||||
foreach (var mod in allMods) {
|
||||
var modResults = mod.GetInstanceDataSerialized<ICollection<
|
||||
ModVersionCheckResults>>();
|
||||
if (modResults != null)
|
||||
foreach (var result in modResults)
|
||||
results.TryAdd(result.ModChecked, result);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the automatic version check for all mods.
|
||||
/// </summary>
|
||||
internal void RunVersionCheck() {
|
||||
var allMods = PRegistry.Instance.GetAllComponents(ID);
|
||||
// See if Mod Updater triggered master disable
|
||||
if (!PRegistry.GetData<bool>("PLib.VersionCheck.ModUpdaterActive") &&
|
||||
allMods != null)
|
||||
new AllVersionCheckTask(allMods, this).Run();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks each mod's version one at a time to avoid saturating the network with
|
||||
/// generally nonessential traffic (in the case of yaml/json checkers).
|
||||
/// </summary>
|
||||
private sealed class AllVersionCheckTask {
|
||||
/// <summary>
|
||||
/// A list of actions that will check each version in turn.
|
||||
/// </summary>
|
||||
private readonly IList<PForwardedComponent> checkAllVersions;
|
||||
|
||||
/// <summary>
|
||||
/// The current location in the list.
|
||||
/// </summary>
|
||||
private int index;
|
||||
|
||||
/// <summary>
|
||||
/// Handles version check result reporting when complete.
|
||||
/// </summary>
|
||||
private readonly PVersionCheck parent;
|
||||
|
||||
internal AllVersionCheckTask(IEnumerable<PForwardedComponent> allMods,
|
||||
PVersionCheck parent) {
|
||||
if (allMods == null)
|
||||
throw new ArgumentNullException(nameof(allMods));
|
||||
checkAllVersions = new List<PForwardedComponent>(allMods);
|
||||
index = 0;
|
||||
this.parent = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs all checks and fires the callback when complete.
|
||||
/// </summary>
|
||||
internal void Run() {
|
||||
int n = checkAllVersions.Count;
|
||||
if (index >= n)
|
||||
parent.ReportResults();
|
||||
while (index < n) {
|
||||
var doCheck = checkAllVersions[index++];
|
||||
if (doCheck != null) {
|
||||
doCheck.Process(0, new System.Action(Run));
|
||||
break;
|
||||
} else if (index >= n)
|
||||
parent.ReportResults();
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "AllVersionCheckTask for {0:D} mods".F(checkAllVersions.Count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A placeholder class which stores all methods used to check a single mod.
|
||||
/// </summary>
|
||||
private sealed class VersionCheckMethods {
|
||||
/// <summary>
|
||||
/// The methods which will be used to check.
|
||||
/// </summary>
|
||||
internal IList<IModVersionChecker> Methods { get; }
|
||||
|
||||
// <summary>
|
||||
/// The mod whose version will be checked.
|
||||
/// </summary>
|
||||
internal KMod.Mod ModToCheck { get; }
|
||||
|
||||
internal VersionCheckMethods(KMod.Mod mod) {
|
||||
Methods = new List<IModVersionChecker>(8);
|
||||
ModToCheck = mod ?? throw new ArgumentNullException(nameof(mod));
|
||||
PUtil.LogDebug("Registered mod ID {0} for automatic version checking".F(
|
||||
ModToCheck.staticID));
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return ModToCheck.staticID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
168
mod/PLibAVC/SteamVersionChecker.cs
Normal file
168
mod/PLibAVC/SteamVersionChecker.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using KMod;
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Checks Steam to see if mods are out of date.
|
||||
/// </summary>
|
||||
public sealed class SteamVersionChecker : IModVersionChecker {
|
||||
/// <summary>
|
||||
/// A reference to the PublishedFileId_t type, or null if running on the EGS/WeGame
|
||||
/// version.
|
||||
/// </summary>
|
||||
private static readonly Type PUBLISHED_FILE_ID = PPatchTools.GetTypeSafe(
|
||||
"Steamworks.PublishedFileId_t");
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the SteamUGC type, or null if running on the EGS/WeGame version.
|
||||
/// </summary>
|
||||
private static readonly Type STEAM_UGC = PPatchTools.GetTypeSafe(
|
||||
"Steamworks.SteamUGC");
|
||||
|
||||
/// <summary>
|
||||
/// A reference to the game's version of SteamUGCService, or null if running on the
|
||||
/// EGS/WeGame version.
|
||||
/// </summary>
|
||||
private static readonly Type STEAM_UGC_SERVICE = PPatchTools.GetTypeSafe(
|
||||
nameof(SteamUGCService), "Assembly-CSharp");
|
||||
|
||||
/// <summary>
|
||||
/// Detours requires knowing the types at compile time, which might not be available,
|
||||
/// and these methods are only called once at startup.
|
||||
/// </summary>
|
||||
private static readonly MethodInfo FIND_MOD = STEAM_UGC_SERVICE?.GetMethodSafe(
|
||||
nameof(SteamUGCService.FindMod), false, PUBLISHED_FILE_ID);
|
||||
|
||||
private static readonly MethodInfo GET_ITEM_INSTALL_INFO = STEAM_UGC?.GetMethodSafe(
|
||||
"GetItemInstallInfo", true, PUBLISHED_FILE_ID, typeof(ulong).
|
||||
MakeByRefType(), typeof(string).MakeByRefType(), typeof(uint), typeof(uint).
|
||||
MakeByRefType());
|
||||
|
||||
private static readonly ConstructorInfo NEW_PUBLISHED_FILE_ID = PUBLISHED_FILE_ID?.
|
||||
GetConstructor(PPatchTools.BASE_FLAGS | BindingFlags.Instance, null,
|
||||
new Type[] { typeof(ulong) }, null);
|
||||
|
||||
/// <summary>
|
||||
/// The number of minutes allowed before a mod is considered out of date.
|
||||
/// </summary>
|
||||
public const double UPDATE_JITTER = 10.0;
|
||||
|
||||
/// <summary>
|
||||
/// The epoch time for Steam time stamps.
|
||||
/// </summary>
|
||||
private static readonly System.DateTime UNIX_EPOCH = new System.DateTime(1970, 1, 1,
|
||||
0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last modified date of a mod's local files. The time is returned in UTC.
|
||||
/// </summary>
|
||||
/// <param name="id">The mod ID to check.</param>
|
||||
/// <returns>The date and time of its last modification.</returns>
|
||||
private static System.DateTime GetLocalLastModified(ulong id) {
|
||||
var result = System.DateTime.UtcNow;
|
||||
// Create a published file object, leave it boxed
|
||||
if (GET_ITEM_INSTALL_INFO != null) {
|
||||
// 260 = MAX_PATH
|
||||
var methodArgs = new object[] {
|
||||
NEW_PUBLISHED_FILE_ID.Invoke(new object[] { id }), 0UL, "", 260U, 0U
|
||||
};
|
||||
if (GET_ITEM_INSTALL_INFO.Invoke(null, methodArgs) is bool success &&
|
||||
success && methodArgs.Length == 5 && methodArgs[4] is uint timestamp &&
|
||||
timestamp > 0U)
|
||||
result = UnixEpochToDateTime(timestamp);
|
||||
else
|
||||
PUtil.LogDebug("Unable to determine last modified date for: " + id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a time from Steam (seconds since Unix epoch) to a C# DateTime.
|
||||
/// </summary>
|
||||
/// <param name="timeSeconds">The timestamp since the epoch.</param>
|
||||
/// <returns>The UTC date and time that it represents.</returns>
|
||||
public static System.DateTime UnixEpochToDateTime(ulong timeSeconds) {
|
||||
return UNIX_EPOCH.AddSeconds(timeSeconds);
|
||||
}
|
||||
|
||||
public event PVersionCheck.OnVersionCheckComplete OnVersionCheckCompleted;
|
||||
|
||||
public bool CheckVersion(Mod mod) {
|
||||
// Epic editions of the game do not even have SteamUGCService
|
||||
return FIND_MOD != null && NEW_PUBLISHED_FILE_ID != null && DoCheckVersion(mod);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the mod on Steam and reports if it is out of date. This helper method
|
||||
/// avoids a type load error if a non-Steam version of the game is used to load this
|
||||
/// mod.
|
||||
/// </summary>
|
||||
/// <param name="mod">The mod whose version is being checked.</param>
|
||||
/// <returns>true if the version check has started, or false if it could not be
|
||||
/// started.</returns>
|
||||
private bool DoCheckVersion(Mod mod) {
|
||||
bool check = false;
|
||||
if (mod.label.distribution_platform == Label.DistributionPlatform.Steam && ulong.
|
||||
TryParse(mod.label.id, out ulong id)) {
|
||||
// Jump into a coroutine and wait for it to be initialized
|
||||
Global.Instance.StartCoroutine(WaitForSteamInit(id, mod));
|
||||
check = true;
|
||||
} else
|
||||
PUtil.LogWarning("SteamVersionChecker cannot check version for non-Steam mod {0}".
|
||||
F(mod.staticID));
|
||||
return check;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To avoid blowing the stack, waits for Steam to initialize in a coroutine.
|
||||
/// </summary>
|
||||
/// <param name="id">The Steam file ID of the mod.</param>
|
||||
/// <param name="mod">The mod to check for updates.</param>
|
||||
private System.Collections.IEnumerator WaitForSteamInit(ulong id, Mod mod) {
|
||||
var boxedID = NEW_PUBLISHED_FILE_ID.Invoke(new object[] { id });
|
||||
ModVersionCheckResults results = null;
|
||||
int timeout = 0;
|
||||
do {
|
||||
yield return null;
|
||||
var inst = SteamUGCService.Instance;
|
||||
// Mod takes time to be populated in the list
|
||||
if (inst != null && FIND_MOD.Invoke(inst, new object[] { boxedID }) is
|
||||
SteamUGCService.Mod steamMod) {
|
||||
ulong ticks = steamMod.lastUpdateTime;
|
||||
var steamUpdate = (ticks == 0U) ? System.DateTime.MinValue :
|
||||
UnixEpochToDateTime(ticks);
|
||||
bool updated = steamUpdate <= GetLocalLastModified(id).AddMinutes(
|
||||
UPDATE_JITTER);
|
||||
results = new ModVersionCheckResults(mod.staticID,
|
||||
updated, updated ? null : steamUpdate.ToString("f"));
|
||||
}
|
||||
// 2 seconds at 60 FPS
|
||||
} while (results == null && ++timeout < 120);
|
||||
if (results == null)
|
||||
PUtil.LogWarning("Unable to check version for mod {0} (SteamUGCService timeout)".
|
||||
F(mod.label.title));
|
||||
OnVersionCheckCompleted?.Invoke(results);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
109
mod/PLibAVC/VersionCheckTask.cs
Normal file
109
mod/PLibAVC/VersionCheckTask.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Represents a "task" to check a particular mod for updates.
|
||||
/// </summary>
|
||||
internal sealed class VersionCheckTask {
|
||||
/// <summary>
|
||||
/// The method which will be used to check.
|
||||
/// </summary>
|
||||
private readonly IModVersionChecker method;
|
||||
|
||||
/// <summary>
|
||||
/// The mod whose version will be checked.
|
||||
/// </summary>
|
||||
private readonly KMod.Mod mod;
|
||||
|
||||
/// <summary>
|
||||
/// The next task to run when the check completes, or null to not run any task.
|
||||
/// </summary>
|
||||
internal System.Action Next { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The location where the outcome of mod version checking will be stored.
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, ModVersionCheckResults> results;
|
||||
|
||||
internal VersionCheckTask(KMod.Mod mod, IModVersionChecker method,
|
||||
ConcurrentDictionary<string, ModVersionCheckResults> results) {
|
||||
this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
|
||||
this.method = method ?? throw new ArgumentNullException(nameof(method));
|
||||
this.results = results ?? throw new ArgumentNullException(nameof(results));
|
||||
Next = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Records the result of the mod version check, and runs the next checker in
|
||||
/// line, from this mod or a different one.
|
||||
/// </summary>
|
||||
/// <param name="result">The results from the version check.</param>
|
||||
private void OnComplete(ModVersionCheckResults result) {
|
||||
method.OnVersionCheckCompleted -= OnComplete;
|
||||
if (result != null) {
|
||||
results.TryAdd(result.ModChecked, result);
|
||||
if (!result.IsUpToDate)
|
||||
PUtil.LogWarning("Mod {0} is out of date! New version: {1}".F(result.
|
||||
ModChecked, result.NewVersion ?? "unknown"));
|
||||
else {
|
||||
#if DEBUG
|
||||
PUtil.LogDebug("Mod {0} is up to date".F(result.ModChecked));
|
||||
#endif
|
||||
}
|
||||
} else
|
||||
RunNext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the version check, and registers a callback to run the next one if
|
||||
/// it is not null.
|
||||
/// </summary>
|
||||
internal void Run() {
|
||||
if (results.ContainsKey(mod.staticID))
|
||||
RunNext();
|
||||
else {
|
||||
bool run = false;
|
||||
method.OnVersionCheckCompleted += OnComplete;
|
||||
// Version check errors should not crash the game
|
||||
try {
|
||||
run = method.CheckVersion(mod);
|
||||
} catch (Exception e) {
|
||||
PUtil.LogWarning("Unable to check version for mod " + mod.label.title + ":");
|
||||
PUtil.LogExcWarn(e);
|
||||
run = false;
|
||||
}
|
||||
if (!run) {
|
||||
method.OnVersionCheckCompleted -= OnComplete;
|
||||
RunNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs the next version check.
|
||||
/// </summary>
|
||||
private void RunNext() {
|
||||
Next?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
82
mod/PLibAVC/YamlURLVersionChecker.cs
Normal file
82
mod/PLibAVC/YamlURLVersionChecker.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2022 Peter Han
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
* and associated documentation files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all copies or
|
||||
* substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
* BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
using Klei;
|
||||
using KMod;
|
||||
using PeterHan.PLib.Core;
|
||||
using System;
|
||||
using UnityEngine.Networking;
|
||||
|
||||
namespace PeterHan.PLib.AVC {
|
||||
/// <summary>
|
||||
/// Checks the mod version using a URL to a YAML file. The file at this URL must resolve
|
||||
/// to a YAML file of the same format as the mod_info.yaml class.
|
||||
/// </summary>
|
||||
public sealed class YamlURLVersionChecker : IModVersionChecker {
|
||||
/// <summary>
|
||||
/// The URL to query for checking the mod version.
|
||||
/// </summary>
|
||||
public string YamlVersionURL { get; }
|
||||
|
||||
public event PVersionCheck.OnVersionCheckComplete OnVersionCheckCompleted;
|
||||
|
||||
public YamlURLVersionChecker(string url) {
|
||||
if (string.IsNullOrEmpty(url))
|
||||
throw new ArgumentNullException(nameof(url));
|
||||
YamlVersionURL = url;
|
||||
}
|
||||
|
||||
public bool CheckVersion(Mod mod) {
|
||||
if (mod == null)
|
||||
throw new ArgumentNullException(nameof(mod));
|
||||
var request = UnityWebRequest.Get(YamlVersionURL);
|
||||
request.SetRequestHeader("Content-Type", "application/x-yaml");
|
||||
request.SetRequestHeader("User-Agent", "PLib AVC");
|
||||
request.timeout = JsonURLVersionChecker.REQUEST_TIMEOUT;
|
||||
var operation = request.SendWebRequest();
|
||||
operation.completed += (_) => OnRequestFinished(request, mod);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a web request completes, triggers the handler for the next updater.
|
||||
/// </summary>
|
||||
/// <param name="request">The YAML web request data.</param>
|
||||
/// <param name="mod">The mod that needs to be checked.</param>
|
||||
private void OnRequestFinished(UnityWebRequest request, Mod mod) {
|
||||
ModVersionCheckResults result = null;
|
||||
if (request.result == UnityWebRequest.Result.Success) {
|
||||
// Parse the text
|
||||
var modInfo = YamlIO.Parse<Mod.PackagedModInfo>(request.downloadHandler.
|
||||
text, default);
|
||||
string newVersion = modInfo?.version;
|
||||
if (modInfo != null && !string.IsNullOrEmpty(newVersion)) {
|
||||
string curVersion = PVersionCheck.GetCurrentVersion(mod);
|
||||
#if DEBUG
|
||||
PUtil.LogDebug("Current version: {0} New YAML version: {1}".F(
|
||||
curVersion, newVersion));
|
||||
#endif
|
||||
result = new ModVersionCheckResults(mod.staticID, newVersion !=
|
||||
curVersion, newVersion);
|
||||
}
|
||||
}
|
||||
request.Dispose();
|
||||
OnVersionCheckCompleted?.Invoke(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user