diff --git a/Automation/gen_csproj.py b/Automation/gen_csproj.py
index fc854b8..0c88372 100755
--- a/Automation/gen_csproj.py
+++ b/Automation/gen_csproj.py
@@ -41,7 +41,7 @@ if __name__ == "__main__":
args = parser.parse_args()
print("Building Assembly references")
- asmXml = buildReferencesXml("../ref/Techblox_Data/Managed")
+ asmXml = buildReferencesXml("../ref_TB/Techblox_Data/Managed")
# print(asmXml)
with open("../TechbloxModdingAPI/TechbloxModdingAPI.csproj", "r") as xmlFile:
diff --git a/CodeGenerator/CodeGenerator.csproj b/CodeGenerator/CodeGenerator.csproj
index 766eefc..6952a6f 100644
--- a/CodeGenerator/CodeGenerator.csproj
+++ b/CodeGenerator/CodeGenerator.csproj
@@ -19,7 +19,7 @@
DEBUG;TEST;TRACE
-
+
diff --git a/TechbloxModdingAPI/Main.cs b/TechbloxModdingAPI/Main.cs
index 554debe..de38cc6 100644
--- a/TechbloxModdingAPI/Main.cs
+++ b/TechbloxModdingAPI/Main.cs
@@ -104,7 +104,7 @@ namespace TechbloxModdingAPI
}
Scheduler.Dispose();
var currentAssembly = Assembly.GetExecutingAssembly();
- harmony.UnpatchAll(currentAssembly.GetName().Name);
+ harmony.UnpatchSelf();
harmony = null;
Logging.MetaLog($"{currentAssembly.GetName().Name} v{currentAssembly.GetName().Version} shutdown");
}
diff --git a/TechbloxModdingAPI/TechbloxModdingAPI.csproj b/TechbloxModdingAPI/TechbloxModdingAPI.csproj
index 2138283..5f6e410 100644
--- a/TechbloxModdingAPI/TechbloxModdingAPI.csproj
+++ b/TechbloxModdingAPI/TechbloxModdingAPI.csproj
@@ -1,40 +1,33 @@
-
+
+
netstandard2.1
- true
- 2.3.0
+ TechbloxModdingAPI
+ An object-oriented API for Techblox mods.
+ 3.0.0
Exmods
GNU General Public Licence 3+
https://git.exmods.org/modtainers/GamecraftModdingAPI
en-CA
true
- 9
-
-
-
-
-
-
- DEBUG;TEST;TRACE
+ latest
+
-
+
+
+
+
+
+
+
+
+
-
-
-
-
- ..\ref_TB\Techblox_Data\Managed\IllusionInjector.dll
- ..\..\ref_TB\Techblox_Data\Managed\IllusionInjector.dll
-
-
- ..\ref_TB\Techblox_Data\Managed\IllusionPlugin.dll
- ..\..\ref_TB\Techblox_Data\Managed\IllusionPlugin.dll
-
..\ref_TB\Techblox_Data\Managed\Accessibility.dll
..\..\ref_TB\Techblox_Data\Managed\Accessibility.dll
@@ -51,6 +44,10 @@
..\ref_TB\Techblox_Data\Managed\Assembly-CSharp.dll
..\..\ref_TB\Techblox_Data\Managed\Assembly-CSharp.dll
+
+ ..\ref_TB\Techblox_Data\Managed\Assembly-Csharp.dll
+ ..\..\ref_TB\Techblox_Data\Managed\Assembly-Csharp.dll
+
..\ref_TB\Techblox_Data\Managed\AWSSDK.Core.dll
..\..\ref_TB\Techblox_Data\Managed\AWSSDK.Core.dll
@@ -1584,9 +1581,12 @@
..\..\ref_TB\Techblox_Data\Managed\ZFBrowser.dll
+
+
+
-
+
-
\ No newline at end of file
+
diff --git a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
index 32ed3e5..1f9c5f9 100644
--- a/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
+++ b/TechbloxModdingAPI/Tests/TechbloxModdingAPIPluginTest.cs
@@ -1,406 +1,84 @@
using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
using System.Reflection;
using System.Text;
-using System.Text.RegularExpressions;
-using DataLoader;
-using TechbloxModdingAPI.App;
+using BepInEx;
+using BepInEx.Bootstrap;
using HarmonyLib;
-using IllusionInjector;
-// test
using RobocraftX.FrontEnd;
-using ServiceLayer;
-using Unity.Mathematics;
-using UnityEngine;
-using Svelto.Tasks;
-using Svelto.Tasks.Lean;
-using TechbloxModdingAPI.Blocks;
+using TechbloxModdingAPI.App;
using TechbloxModdingAPI.Commands;
-using TechbloxModdingAPI.Players;
-using TechbloxModdingAPI.Tasks;
-using TechbloxModdingAPI.Utility;
namespace TechbloxModdingAPI.Tests
{
-#if DEBUG
- // unused by design
- ///
- /// Modding API implemented as a standalone IPA Plugin.
- /// Ideally, TechbloxModdingAPI should be loaded by another mod; not itself
- ///
- class TechbloxModdingAPIPluginTest : IllusionPlugin.IEnhancedPlugin
+ #if DEBUG // The API should be loaded by other plugins, but it can be used by itself for testing
+ [BepInPlugin("org.exmods.TechbloxModdingAPIPluginTest", PluginInfo.PLUGIN_NAME, PluginInfo.PLUGIN_VERSION)]
+ [BepInProcess("Techblox.exe")]
+ public class TechbloxModdingAPIPluginTest : BaseUnityPlugin
{
+ private void Awake()
+ {
+ Main.Init();
+ Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
+ Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
+ Game.Enter += (s, a) =>
+ {
+ Player.LocalPlayer.SeatEntered += (sender, args) =>
+ Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}");
+ Player.LocalPlayer.SeatExited += (sender, args) =>
+ Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}");
+ };
+
+ CommandBuilder.Builder()
+ .Name("Exit")
+ .Description("Close Techblox immediately, without any prompts")
+ .Action(() => { UnityEngine.Application.Quit(); })
+ .Build();
- private static Harmony harmony { get; set; }
+ CommandBuilder.Builder()
+ .Name("SetFOV")
+ .Description("Set the player camera's field of view")
+ .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
+ .Build();
+
+ Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
+
+ // Plugin startup logic
+ Logger.LogInfo($"Plugin {PluginInfo.PLUGIN_GUID} is loaded!");
+
+#if TEST
+ TestRoot.RunTests();
+#endif
+ }
- public override string Name { get; } = Assembly.GetExecutingAssembly().GetName().Name;
-
- public override string Version { get; } = Assembly.GetExecutingAssembly().GetName().Version.ToString();
-
- public string HarmonyID { get; } = "org.git.exmods.modtainers.techbloxmoddingapi";
-
- public override void OnApplicationQuit()
+ private void OnDestroy()
{
Main.Shutdown();
}
- public override void OnApplicationStart()
- {
- FileLog.Reset();
- Harmony.DEBUG = true;
- Main.Init();
- Logging.MetaDebugLog($"Version group id {ApiExclusiveGroups.versionGroup}");
- // disable background music
- Logging.MetaDebugLog("Audio Mixers: " + string.Join(",", AudioTools.GetMixers()));
- //AudioTools.SetVolume(0.0f, "Music"); // The game now sets this from settings again after this is called :(
-
- //Utility.VersionTracking.Enable();//(very) unstable
-
- // debug/test handlers
- Client.EnterMenu += (sender, args) => throw new Exception("Test handler always throws an exception!");
- Client.EnterMenu += (sender, args) => Console.WriteLine("EnterMenu handler after erroring handler");
- Game.Enter += (s, a) =>
- {
- Player.LocalPlayer.SeatEntered += (sender, args) =>
- Console.WriteLine($"Player {Player.LocalPlayer} entered seat {args.Seat}");
- Player.LocalPlayer.SeatExited += (sender, args) =>
- Console.WriteLine($"Player {Player.LocalPlayer} exited seat {args.Seat}");
- };
-
- // debug/test commands
- if (Dependency.Hell("ExtraCommands"))
- {
- CommandBuilder.Builder()
- .Name("Exit")
- .Description("Close Techblox immediately, without any prompts")
- .Action(() => { UnityEngine.Application.Quit(); })
- .Build();
-
- CommandBuilder.Builder()
- .Name("SetFOV")
- .Description("Set the player camera's field of view")
- .Action((float d) => { UnityEngine.Camera.main.fieldOfView = d; })
- .Build();
-
- CommandBuilder.Builder()
- .Name("MoveLastBlock")
- .Description("Move the most-recently-placed block, and any connected blocks by the given offset")
- .Action((float x, float y, float z) =>
- {
- if (GameState.IsBuildMode())
- foreach (var block in Block.GetLastPlacedBlock().GetConnectedCubes())
- block.Position += new Unity.Mathematics.float3(x, y, z);
- else
- Logging.CommandLogError("Blocks can only be moved in Build mode!");
- }).Build();
-
- CommandBuilder.Builder()
- .Name("PlaceAluminium")
- .Description("Place a block of aluminium at the given coordinates")
- .Action((float x, float y, float z) =>
- {
- var block = Block.PlaceNew(BlockIDs.Cube, new float3(x, y, z));
- Logging.CommandLog("Block placed with type: " + block.Type);
- })
- .Build();
-
- CommandBuilder.Builder()
- .Name("PlaceAluminiumLots")
- .Description("Place a lot of blocks of aluminium at the given coordinates")
- .Action((float x, float y, float z) =>
- {
- Logging.CommandLog("Starting...");
- var sw = Stopwatch.StartNew();
- for (int i = 0; i < 100; i++)
- for (int j = 0; j < 100; j++)
- Block.PlaceNew(BlockIDs.Cube, new float3(x + i, y, z + j));
- sw.Stop();
- Logging.CommandLog("Finished in " + sw.ElapsedMilliseconds + "ms");
- })
- .Build();
-
- Block b = null;
- CommandBuilder.Builder("moveBlockInSim", "Run in build mode first while looking at a block, then in sim to move it up")
- .Action(() =>
- {
- if (b == null)
- {
- b = new Player(PlayerType.Local).GetBlockLookedAt();
- Logging.CommandLog("Block saved: " + b);
- }
- else
- Logging.CommandLog("Block moved to: " + (b.GetSimBody().Position += new float3(0, 2, 0)));
- }).Build();
-
- CommandBuilder.Builder("Error", "Throw an error to make sure SimpleCustomCommandEngine's wrapper catches it.")
- .Action(() => { throw new Exception("Error Command always throws an error"); })
- .Build();
-
- CommandBuilder.Builder("ColorBlock",
- "Change color of the block looked at if there's any.")
- .Action(str =>
- {
- if (!Enum.TryParse(str, out BlockColors color))
- {
- Logging.CommandLog("Color " + str + " not found! Interpreting as 4 color values.");
- var s = str.Split(' ');
- new Player(PlayerType.Local).GetBlockLookedAt().CustomColor = new float4(float.Parse(s[0]),
- float.Parse(s[1]), float.Parse(s[2]), float.Parse(s[3]));
- return;
- }
-
- new Player(PlayerType.Local).GetBlockLookedAt().Color = color;
- Logging.CommandLog("Colored block to " + color);
-
- }).Build();
-
- CommandBuilder.Builder("MoveBlockByID", "Gets a block based on its object identifier and teleports it up.")
- .Action(ch =>
- {
- foreach (var body in SimBody.GetFromObjectID(ch))
- {
- Logging.CommandLog("SimBody: " + body);
- body.Position += new float3(0, 10, 0);
- foreach (var bodyConnectedBody in body.GetConnectedBodies())
- {
- Logging.CommandLog("Moving " + bodyConnectedBody);
- bodyConnectedBody.Position += new float3(0, 10, 0);
- }
- }
- }).Build();
-
- CommandBuilder.Builder("TestChunkHealth", "Sets the chunk looked at to the given health.")
- .Action((float val, float max) =>
- {
- var body = new Player(PlayerType.Local).GetSimBodyLookedAt();
- if (body == null) return;
- body.CurrentHealth = val;
- body.InitialHealth = max;
- Logging.CommandLog("Health set to: " + val);
- }).Build();
-
- CommandBuilder.Builder("placeBlockGroup", "Places some blocks in a group")
- .Action((float x, float y, float z) =>
- {
- var pos = new float3(x, y, z);
- var group = BlockGroup.Create(new Block(BlockIDs.Cube, pos) {Color = BlockColors.Aqua});
- new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
- {Color = BlockColors.Blue, BlockGroup = group};
- new Block(BlockIDs.Cube, pos += new float3(1, 0, 0))
- {Color = BlockColors.Green, BlockGroup = group};
- new Block(BlockIDs.Cube, pos + new float3(1, 0, 0))
- {Color = BlockColors.Lime, BlockGroup = group};
- }).Build();
-
- CommandBuilder.Builder("placeCustomBlock", "Places a custom block, needs a custom catalog and assets.")
- .Action((float x, float y, float z) =>
- {
- Logging.CommandLog("Block placed: " +
- Block.PlaceNew((BlockIDs) 500, new float3(0, 0, 0)));
- }).Build();
-
- CommandBuilder.Builder("toggleTimeMode", "Enters or exits simulation.")
- .Action((float x, float y, float z) =>
- {
- Game.CurrentGame().ToggleTimeMode();
- }).Build();
-
- CommandBuilder.Builder("testColorBlock", "Tests coloring a block to default color")
- .Action(() => Player.LocalPlayer.GetBlockLookedAt().Color = BlockColors.Default).Build();
- CommandBuilder.Builder("testMaterialBlock", "Tests materialing a block to default material")
- .Action(() => Player.LocalPlayer.GetBlockLookedAt().Material = BlockMaterial.Default).Build();
- CommandBuilder.Builder("testGameName", "Tests changing the game name")
- .Action(() => Game.CurrentGame().Name = "Test").Build();
- CommandBuilder.Builder("makeBlockStatic", "Makes a block you look at static")
- .Action(() => Player.LocalPlayer.GetBlockLookedAt().Static = true).Build();
-
- Game.AddPersistentDebugInfo("InstalledMods", InstalledMods);
- /*Block.Placed += (sender, args) =>
- Logging.MetaDebugLog("Placed block " + args.Block);
- Block.Removed += (sender, args) =>
- Logging.MetaDebugLog("Removed block " + args.Block);*/
- }
-
- // dependency test
- if (Dependency.Hell("TechbloxScripting", new Version("0.0.1.0")))
- {
- Logging.LogWarning("You're in TechbloxScripting dependency hell");
- }
- else
- {
- Logging.Log("Compatible TechbloxScripting detected");
- }
- // Interface test
- /*Group uiGroup = new Group(new Rect(20, 20, 200, 500), "TechbloxModdingAPI_UITestGroup", true);
- var button = new Button("TEST");
- button.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
- var button2 = new Button("TEST2");
- button2.OnClick += (b, __) => { Logging.MetaDebugLog($"Click on {((Interface.IMGUI.Button)b).Name}");};
- Text uiText = new Text("", multiline: true);
- uiText.OnEdit += (t, txt) => { Logging.MetaDebugLog($"Text in {((Text)t).Name} is now '{txt}'"); };
- Label uiLabel = new Label("Label!");
- Image uiImg = new Image(name:"Behold this texture!");
- uiImg.Enabled = false;
- uiGroup.AddElement(button);
- uiGroup.AddElement(button2);
- uiGroup.AddElement(uiText);
- uiGroup.AddElement(uiLabel);
- uiGroup.AddElement(uiImg);*/
-
- /*Addressables.LoadAssetAsync("Assets/Art/Textures/UI/FrontEndMap/RCX_Blue_Background_5k.jpg")
- .Completed +=
- handle =>
- {
- uiImg.Texture = handle.Result;
- uiImg.Enabled = true;
- Logging.MetaDebugLog($"Got blue bg asset {handle.Result}");
- };*/
-
- /*((FasterList)AccessTools.Property(typeof(GuiInputMap), "GuiInputsButtonDown").GetValue(null))
- .Add(new GuiInputMap.GuiInputMapElement(RewiredConsts.Action.ToggleCommandLine, GuiIn))*/
-
- /*Game.Enter += (sender, e) =>
- {
- ushort lastKey = ushort.MaxValue;
- foreach (var kv in FullGameFields._dataDb.GetValues()
- .OrderBy(kv=>ushort.Parse(kv.Key)))
- {
- var data = (CubeListData) kv.Value;
- ushort currentKey = ushort.Parse(kv.Key);
- var toReplace = new Dictionary
- {
- {"Scalable", ""}, {"Qtr", "Quarter"}, {"RNeg", "Rounded Negative"},
- {"Neg", "Negative"}, {"Tetra", "Tetrahedron"},
- {"RWedge", "Rounded Wedge"}, {"RTetra", "Rounded Tetrahedron"}
- };
- string name = LocalizationService.Localize(data.CubeNameKey).Split(' ').Select(str =>
- str.Length > 0 ? char.ToUpper(str[0]) + str.Substring(1) : str).Aggregate((a, b) => a + b)
- .Replace("-", "");
- foreach (var rkv in toReplace)
- {
- name = Regex.Replace(name, rkv.Key + "([A-Z]|$)", rkv.Value + "$1");
- }
- Console.WriteLine($"{name}{(currentKey != lastKey + 1 ? $" = {currentKey}" : "")},");
- lastKey = currentKey;
- }
- };
- Game.Enter += (sender, e) =>
- {
- ushort lastKey = ushort.MaxValue;
- Console.WriteLine("Materials:\n" + FullGameFields._dataDb.GetValues()
- .OrderBy(kv => ushort.Parse(kv.Key))
- .Select(kv =>
- {
- ushort currentKey = ushort.Parse(kv.Key);
- string result = $"{((MaterialPropertiesData)kv.Value).Name}{(currentKey != lastKey + 1 ? $" = {kv.Key}" : "")},";
- lastKey = currentKey;
- return result;
- })
- .Aggregate((a, b) => a + "\n" + b));
- };*/
-
- CommandBuilder.Builder("takeScreenshot", "Enables the screenshot taker")
- .Action(() =>
- {
- Game.CurrentGame().EnableScreenshotTaker();
- }).Build();
-
- CommandBuilder.Builder("testPositionDefault", "Tests the Block.Position property's default value.")
- .Action(() =>
- {
- IEnumerator Loop()
- {
- for (int i = 0; i < 2; i++)
- {
- Console.WriteLine("A");
- var block = Block.PlaceNew(BlockIDs.Cube, 1);
- Console.WriteLine("B");
- while (!block.Exists)
- yield return Yield.It;
- Console.WriteLine("C");
- block.Remove();
- Console.WriteLine("D");
- while (block.Exists)
- yield return Yield.It;
- Console.WriteLine("E - Pos: " + block.Position);
- block.Position = 4;
- Console.WriteLine("F - Pos: " + block.Position);
- }
- }
-
- Loop().RunOn(Scheduler.leanRunner);
- }).Build();
-
- CommandBuilder.Builder("importAssetBundle")
- .Action(() =>
- {
- Logging.CommandLog("Importing asset bundle...");
- var ab = AssetBundle.LoadFromFile(
- @"filepath");
- Logging.CommandLog("Imported asset bundle: " + ab);
- var assets = ab.LoadAllAssets();
- Logging.CommandLog("Loaded " + assets.Length + " assets");
- foreach (var asset in assets)
- {
- Logging.CommandLog(asset);
- }
- }).Build();
-
- bool shouldTestGhostBlock = false;
- CommandBuilder.Builder("testGhostBlock")
- .Action(() =>
- {
- if (shouldTestGhostBlock)
- {
- shouldTestGhostBlock = false;
- Logging.CommandLog("Test disabled");
- }
-
- shouldTestGhostBlock = true;
- Scheduler.Schedule(new Repeatable(() =>
- {
- var ghostBlock = Player.LocalPlayer.GetGhostBlock();
- if (ghostBlock == null) return;
- ghostBlock.Position = Player.LocalPlayer.Position + 2;
- ghostBlock.Color = new BlockColor(BlockColors.Lime);
- }, () => shouldTestGhostBlock));
- Logging.CommandLog("Test enabled");
- }).Build();
-
- Game.Enter += (sender, args) =>
- Console.WriteLine(
- $"Current game selection data: {FullGameFields._gameSelectionData.gameMode} - {FullGameFields._gameSelectionData.saveType}");
-#if TEST
- TestRoot.RunTests();
-#endif
- }
-
- private string modsString;
+ private string modsString;
private string InstalledMods()
{
- if (modsString != null) return modsString;
- StringBuilder sb = new StringBuilder("Installed mods:");
- foreach (var plugin in PluginManager.Plugins)
- sb.Append("\n" + plugin.Name + " - " + plugin.Version);
- return modsString = sb.ToString();
- }
-
- [HarmonyPatch]
- public class MinimumSpecsPatch
- {
- public static bool Prefix(ref bool __result)
- {
- __result = true;
- return false;
- }
-
- public static MethodInfo TargetMethod()
- {
- return ((Func) MinimumSpecsCheck.CheckRequirementsMet).Method;
- }
+ if (modsString != null) return modsString;
+ StringBuilder sb = new StringBuilder("Installed mods:");
+ foreach (var (_, plugin) in Chainloader.PluginInfos)
+ sb.Append("\n" + plugin.Metadata.Name + " - " + plugin.Metadata.Version);
+ return modsString = sb.ToString();
}
}
-#endif
+
+ [HarmonyPatch]
+ public class MinimumSpecsPatch
+ {
+ public static bool Prefix(ref bool __result)
+ {
+ __result = true;
+ return false;
+ }
+
+ public static MethodInfo TargetMethod()
+ {
+ return ((Func) MinimumSpecsCheck.CheckRequirementsMet).Method;
+ }
+ }
+ #endif
}
diff --git a/TechbloxModdingAPI/Utility/Dependency.cs b/TechbloxModdingAPI/Utility/Dependency.cs
index 3f33277..b160ac9 100644
--- a/TechbloxModdingAPI/Utility/Dependency.cs
+++ b/TechbloxModdingAPI/Utility/Dependency.cs
@@ -1,7 +1,5 @@
using System;
-
-using IllusionInjector;
-using IllusionPlugin;
+using BepInEx.Bootstrap;
namespace TechbloxModdingAPI.Utility
{
@@ -15,11 +13,11 @@ namespace TechbloxModdingAPI.Utility
///
/// The plugin.
/// The plugin's name.
- public static IPlugin GetPlugin(string name)
+ public static BepInEx.PluginInfo GetPlugin(string name)
{
- foreach(IPlugin plugin in PluginManager.Plugins)
+ foreach(var plugin in Chainloader.PluginInfos.Values)
{
- if (plugin.Name == name)
+ if (plugin.Metadata.Name == name)
{
return plugin;
}
@@ -35,45 +33,15 @@ namespace TechbloxModdingAPI.Utility
/// The plugin's name.
public static Version GetPluginVersion(string name)
{
- IPlugin plugin = GetPlugin(name);
+ var plugin = GetPlugin(name);
if (plugin != null) {
try
{
- return new Version(plugin.Version);
- } catch (Exception e) when (
- e is ArgumentException
- || e is ArgumentNullException
- || e is ArgumentOutOfRangeException
- || e is FormatException
- || e is OverflowException) {}
+ return plugin.Metadata.Version;
+ } catch (Exception e) when (e is ArgumentException or FormatException or OverflowException) {}
return plugin.GetType().Assembly.GetName().Version;
}
return null;
}
-
- // (I'm leaving the auto-generated version)
- //
- // Hell the specified name and version.
- //
- // The hell.
- // Name.
- // Version.
- ///
- /// Detect if you're in dependency hell with respect to the plugin.
- /// ie Check if the plugin doesn't exist or is out of date.
- /// When version is null, this only checks if the plugin exists.
- /// The version is retrieved using GetPluginVersion(string name).
- ///
- /// Are you in dependency hell?
- /// The plugin's name'
- /// The target version.
- public static bool Hell(string name, Version version = null)
- {
- Version pluginVersion = GetPluginVersion(name);
- if (version == null) {
- return pluginVersion == null;
- }
- return (pluginVersion == null || pluginVersion < version);
- }
}
}