diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..a3ce42f3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+# Auto detect text files and perform LF normalization
+* text=auto
+
+*.cs text diff=csharp
+*.csx text diff=csharp
+*.sln text eol=crlf
+*.csproj text eol=crlf
+
+*.csv text eol=lf
diff --git a/.github/README.md b/.github/README.md
index b92b4a1f..5b07a8c8 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -56,7 +56,7 @@ KSP2_Root_Folder/
│ │ │ │ │ ├── *.bundle
│ │ │ │ ├── images/
│ │ │ │ │ ├── *
-│ │ │ ├── localization/
+│ │ │ ├── localizations/
│ │ │ │ ├── *.csv
│ │ │ ├── addressables/
│ │ │ │ ├── catalog.json
diff --git a/Directory.Build.props b/Directory.Build.props
index 81ae7375..cdb60af6 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,13 +1,15 @@
- 1.1.3
- net472
+ 1.2.0
+ netstandard2.0
11
true
false
false
-
- CS0436
+
+ $(NoWarn),CS0436,CS1591
+ true
+
+ true
+ Space Warp contributors
+ A C# modding API for KSP2 built on top of BepInEx
+ icon.png
+ SpaceWarp
+ https://raw.githubusercontent.com/SpaceWarpDev/SpaceWarp/main/LICENSE
+ ..\build
+ https://spacewarp.org
+ README.md
+ SpaceWarp;KSP2;modding
+ https://github.com/SpaceWarpDev/SpaceWarp
+ Space Warp
+ $(NoWarn),NU5118,NU5125
+
+
+
+
+
+
+
+ true
+ lib\$(TargetFramework)
+
+
+
+
\ No newline at end of file
diff --git a/SpaceWarp/SpaceWarpManager.cs b/SpaceWarp/SpaceWarpManager.cs
index 983c23b2..0848f1c6 100644
--- a/SpaceWarp/SpaceWarpManager.cs
+++ b/SpaceWarp/SpaceWarpManager.cs
@@ -5,7 +5,6 @@
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
-using I2.Loc;
using SpaceWarpPatcher;
using Newtonsoft.Json;
using SpaceWarp.API.Assets;
@@ -14,7 +13,7 @@
using SpaceWarp.API.Mods.JSON;
using SpaceWarp.API.UI.Appbar;
using SpaceWarp.Backend.UI.Appbar;
-using SpaceWarp.UI;
+using SpaceWarp.UI.ModList;
using UnityEngine;
namespace SpaceWarp;
@@ -41,7 +40,7 @@ internal static class SpaceWarpManager
private static GUISkin _skin;
- public static ModListUI ModListUI { get; internal set; }
+ public static ModListController ModListController { get; internal set; }
public static GUISkin Skin
{
@@ -144,7 +143,7 @@ internal static void GetSpaceWarpPlugins()
public static void Initialize(SpaceWarpPlugin spaceWarpPlugin)
{
- Logger = spaceWarpPlugin.Logger;
+ Logger = SpaceWarpPlugin.Logger;
SpaceWarpFolder = Path.GetDirectoryName(spaceWarpPlugin.Info.Location);
@@ -159,21 +158,35 @@ internal static void CheckKspVersions()
?.GetValue(null) as string;
foreach (var plugin in SpaceWarpPlugins)
{
- ModsUnsupported[plugin.SpaceWarpMetadata.ModID] =
- !plugin.SpaceWarpMetadata.SupportedKsp2Versions.IsSupported(kspVersion);
+ CheckModKspVersion(plugin.Info.Metadata.GUID, plugin.SpaceWarpMetadata, kspVersion);
}
foreach (var info in NonSpaceWarpInfos)
{
- ModsUnsupported[info.Item2.ModID] = !info.Item2.SupportedKsp2Versions.IsSupported(kspVersion);
+ CheckModKspVersion(info.Item1.Info.Metadata.GUID, info.Item2, kspVersion);
}
foreach (var info in DisabledInfoPlugins)
{
- ModsUnsupported[info.Item2.ModID] = !info.Item2.SupportedKsp2Versions.IsSupported(kspVersion);
+ CheckModKspVersion(info.Item1.Metadata.GUID, info.Item2, kspVersion);
}
}
+ private static void CheckModKspVersion(string guid, ModInfo modInfo, string kspVersion)
+ {
+ var unsupported = true;
+ try
+ {
+ unsupported = !modInfo.SupportedKsp2Versions.IsSupported(kspVersion);
+ }
+ catch (Exception e)
+ {
+ Logger.LogError($"Unable to check KSP version for {guid} due to error {e}");
+ }
+
+ ModsUnsupported[guid] = unsupported;
+ }
+
private static List<(string name, UnityObject asset)> AssetBundleLoadingAction(string internalPath, string filename)
{
var assetBundle = AssetBundle.LoadFromFile(filename);
@@ -213,18 +226,17 @@ internal static void CheckKspVersions()
{
var tex = new Texture2D(2, 2, TextureFormat.ARGB32, false)
{
- filterMode = FilterMode.Point
+ filterMode = FilterMode.Point
};
var fileData = File.ReadAllBytes(filename);
tex.LoadImage(fileData); // Will automatically resize
- List<(string name, UnityObject asset)> assets = new();
- assets.Add(($"images/{internalPath}",tex));
+ List<(string name, UnityObject asset)> assets = new() { ($"images/{internalPath}", tex) };
return assets;
}
internal static void InitializeSpaceWarpsLoadingActions()
{
- Loading.AddAssetLoadingAction("bundles","loading asset bundles",AssetBundleLoadingAction,"bundle");
- Loading.AddAssetLoadingAction("images","loading images",ImageLoadingAction);
+ Loading.AddAssetLoadingAction("bundles", "loading asset bundles", AssetBundleLoadingAction, "bundle");
+ Loading.AddAssetLoadingAction("images", "loading images", ImageLoadingAction);
}
}
\ No newline at end of file
diff --git a/SpaceWarp/SpaceWarpPlugin.cs b/SpaceWarp/SpaceWarpPlugin.cs
index 61c8e8a7..a3baaced 100644
--- a/SpaceWarp/SpaceWarpPlugin.cs
+++ b/SpaceWarp/SpaceWarpPlugin.cs
@@ -3,26 +3,32 @@
using System;
using System.Collections;
using System.Reflection;
+using System.Xml;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using KSP.Messages;
+using UitkForKsp2.API;
using Newtonsoft.Json;
+using SpaceWarp.API.Assets;
using SpaceWarp.API.Game.Messages;
using SpaceWarp.API.Mods;
using SpaceWarp.API.Mods.JSON;
-using SpaceWarp.API.UI;
using SpaceWarp.API.Versions;
using SpaceWarp.UI;
using SpaceWarp.UI.Debug;
+using SpaceWarp.UI.ModList;
+using UitkForKsp2;
using UnityEngine;
using UnityEngine.Networking;
+using UnityEngine.UIElements;
namespace SpaceWarp;
[BepInDependency(ConfigurationManager.ConfigurationManager.GUID, ConfigurationManager.ConfigurationManager.Version)]
+[BepInDependency(UitkForKsp2Plugin.ModGuid, UitkForKsp2Plugin.ModVer)]
[BepInPlugin(ModGuid, ModName, ModVer)]
public sealed class SpaceWarpPlugin : BaseSpaceWarpPlugin
{
@@ -44,7 +50,12 @@ public sealed class SpaceWarpPlugin : BaseSpaceWarpPlugin
internal ConfigEntry ConfigWarningColor;
private string _kspVersion;
- internal new ManualLogSource Logger => base.Logger;
+ internal new static ManualLogSource Logger;
+
+ public SpaceWarpPlugin()
+ {
+ Logger = base.Logger;
+ }
public void Awake()
{
@@ -78,9 +89,9 @@ public void Awake()
BepInEx.Logging.Logger.Listeners.Add(new SpaceWarpConsoleLogListener(this));
Harmony.CreateAndPatchAll(typeof(SpaceWarpPlugin).Assembly, ModGuid);
-
+
SpaceWarpManager.InitializeSpaceWarpsLoadingActions();
-
+
SpaceWarpManager.Initialize(this);
}
@@ -93,7 +104,6 @@ public override void OnInitialized()
Game.Messages.Subscribe(typeof(GameStateLeftMessage), StateChanges.OnGameStateLeft, false, true);
Game.Messages.Subscribe(typeof(GameStateChangedMessage), StateChanges.OnGameStateChanged, false, true);
- InitializeUI();
if (_configFirstLaunch.Value)
{
_configFirstLaunch.Value = false;
@@ -103,6 +113,10 @@ public override void OnInitialized()
prompt.spaceWarpPlugin = this;
}
+ SpaceWarpManager.CheckKspVersions();
+
+ InitializeUI();
+
if (ConfigCheckVersions.Value)
{
CheckVersions();
@@ -111,25 +125,23 @@ public override void OnInitialized()
{
ClearVersions();
}
-
- SpaceWarpManager.CheckKspVersions();
}
public void ClearVersions()
{
foreach (var plugin in SpaceWarpManager.SpaceWarpPlugins)
{
- SpaceWarpManager.ModsOutdated[plugin.SpaceWarpMetadata.ModID] = false;
+ SpaceWarpManager.ModsOutdated[plugin.Info.Metadata.GUID] = false;
}
foreach (var info in SpaceWarpManager.NonSpaceWarpInfos)
{
- SpaceWarpManager.ModsOutdated[info.Item2.ModID] = false;
+ SpaceWarpManager.ModsOutdated[info.Item1.Info.Metadata.GUID] = false;
}
foreach (var info in SpaceWarpManager.DisabledInfoPlugins)
{
- SpaceWarpManager.ModsOutdated[info.Item2.ModID] = false;
+ SpaceWarpManager.ModsOutdated[info.Item1.Metadata.GUID] = false;
}
}
@@ -140,7 +152,7 @@ public void CheckVersions()
{
if (plugin.SpaceWarpMetadata.VersionCheck != null)
{
- StartCoroutine(CheckVersion(plugin.SpaceWarpMetadata));
+ StartCoroutine(CheckVersion(plugin.Info.Metadata.GUID, plugin.SpaceWarpMetadata));
}
}
@@ -148,7 +160,7 @@ public void CheckVersions()
{
if (info.Item2.VersionCheck != null)
{
- StartCoroutine(CheckVersion(info.Item2));
+ StartCoroutine(CheckVersion(info.Item1.Info.Metadata.GUID, info.Item2));
}
}
@@ -156,43 +168,83 @@ public void CheckVersions()
{
if (info.Item2.VersionCheck != null)
{
- StartCoroutine(CheckVersion(info.Item2));
+ StartCoroutine(CheckVersion(info.Item1.Metadata.GUID, info.Item2));
}
}
}
- private static bool OlderThan(string currentVersion, string onlineVersion)
+ private IEnumerator CheckVersion(string guid, ModInfo modInfo)
{
- return VersionUtility.CompareSemanticVersionStrings(currentVersion, onlineVersion) < 0;
- }
-
- private IEnumerator CheckVersion(ModInfo pluginInfo)
- {
- var www = UnityWebRequest.Get(pluginInfo.VersionCheck);
+ var www = UnityWebRequest.Get(modInfo.VersionCheck);
yield return www.SendWebRequest();
if (www.result != UnityWebRequest.Result.Success)
{
- Logger.LogInfo($"Unable to check version for {pluginInfo.ModID} due to error {www.error}");
+ Logger.LogInfo($"Unable to check version for {guid} due to error {www.error}");
}
else
{
+ var isOutdated = false;
var results = www.downloadHandler.text;
try
{
- var checkInfo = JsonConvert.DeserializeObject(results);
- if (!checkInfo.SupportedKsp2Versions.IsSupported(_kspVersion))
+ isOutdated = modInfo.VersionCheckType switch
{
- yield break;
- }
-
- SpaceWarpManager.ModsOutdated[pluginInfo.ModID] = OlderThan(pluginInfo.Version, checkInfo.Version);
+ VersionCheckType.SwInfo => CheckJsonVersion(guid, modInfo.Version, results),
+ VersionCheckType.Csproj => CheckCsprojVersion(guid, modInfo.Version, results),
+ _ => throw new ArgumentOutOfRangeException(nameof(modInfo), "Invalid version_check_type")
+ };
}
catch (Exception e)
{
- Logger.LogError($"Unable to check version for {pluginInfo.ModID} due to error {e}");
+ Logger.LogError($"Unable to check version for {guid} due to error {e}");
}
+
+ SpaceWarpManager.ModListController.UpdateOutdated(guid, isOutdated);
+ }
+ }
+
+ private bool CheckJsonVersion(string guid, string version, string json)
+ {
+ var checkInfo = JsonConvert.DeserializeObject(json);
+ if (!checkInfo.SupportedKsp2Versions.IsSupported(_kspVersion))
+ {
+ return false;
+ }
+
+ var isOutdated = VersionUtility.IsOlderThan(version, checkInfo.Version);
+ SpaceWarpManager.ModsOutdated[guid] = isOutdated;
+ return isOutdated;
+ }
+
+ private bool CheckCsprojVersion(string guid, string version, string csproj)
+ {
+ var document = new XmlDocument();
+ document.LoadXml(csproj);
+
+ var ksp2VersionMin = document.GetElementsByTagName("Ksp2VersionMin")[0]?.InnerText
+ ?? SupportedVersionsInfo.DefaultMin;
+ var ksp2VersionMax = document.GetElementsByTagName("Ksp2VersionMax")[0]?.InnerText
+ ?? SupportedVersionsInfo.DefaultMax;
+
+ if (!VersionUtility.IsSupported(_kspVersion, ksp2VersionMin, ksp2VersionMax))
+ {
+ return false;
}
+
+ var checkVersionTags = document.GetElementsByTagName("Version");
+ var checkVersion = checkVersionTags[0]?.InnerText;
+ if (checkVersion == null || checkVersionTags.Count != 1)
+ {
+ throw new ArgumentOutOfRangeException(
+ nameof(csproj),
+ "There must be exactly 1 Version tag in the checked .csproj"
+ );
+ }
+
+ var isOutdated = VersionUtility.IsOlderThan(version, checkVersion);
+ SpaceWarpManager.ModsOutdated[guid] = isOutdated;
+ return isOutdated;
}
private void InitializeUI()
@@ -200,20 +252,16 @@ private void InitializeUI()
SpaceWarpManager.ConfigurationManager =
(ConfigurationManager.ConfigurationManager)Chainloader
.PluginInfos[ConfigurationManager.ConfigurationManager.GUID].Instance;
- GameObject modUIObject = new("Space Warp Mod UI");
- modUIObject.Persist();
- modUIObject.transform.SetParent(transform);
- SpaceWarpManager.ModListUI = modUIObject.AddComponent();
-
- modUIObject.SetActive(true);
+ var modListUxml = AssetManager.GetAsset($"spacewarp/modlist/modlist.uxml");
+ var modList = Window.CreateFromUxml(modListUxml, "Space Warp Mod List", transform, true);
+ SpaceWarpManager.ModListController = modList.gameObject.AddComponent();
+ modList.gameObject.Persist();
GameObject consoleUIObject = new("Space Warp Console");
consoleUIObject.Persist();
consoleUIObject.transform.SetParent(Chainloader.ManagerObject.transform);
consoleUIObject.AddComponent();
consoleUIObject.SetActive(true);
-
- MainMenu.RegisterLocalizedMenuButton("SpaceWarp/Mods", SpaceWarpManager.ModListUI.ToggleVisible);
}
}
\ No newline at end of file
diff --git a/SpaceWarp/UI/ModList/ModListController.cs b/SpaceWarp/UI/ModList/ModListController.cs
new file mode 100644
index 00000000..18d6c4d8
--- /dev/null
+++ b/SpaceWarp/UI/ModList/ModListController.cs
@@ -0,0 +1,544 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using BepInEx;
+using I2.Loc;
+using SpaceWarp.API.Assets;
+using SpaceWarp.API.Mods.JSON;
+using SpaceWarp.API.UI;
+using SpaceWarpPatcher;
+using UitkForKsp2.API;
+using UnityEngine;
+using UnityEngine.UIElements;
+
+namespace SpaceWarp.UI.ModList;
+
+public class ModListController : MonoBehaviour
+{
+ private VisualTreeAsset _listEntryTemplate;
+ private VisualTreeAsset _dependencyTemplate;
+
+ // Mod list UI element references
+ private VisualElement _container;
+
+ private Button _enableAllButton;
+ private Button _disableAllButton;
+ private Button _revertChangesButton;
+ private Label _changesLabel;
+ private readonly LocalizedString _changesLabelText = "SpaceWarp/ModList/ChangesDetected";
+
+ private Foldout _spaceWarpModFoldout;
+ private VisualElement _spaceWarpModList;
+ private Foldout _otherModFoldout;
+ private VisualElement _otherInfoModList;
+ private VisualElement _otherModList;
+ private Foldout _disabledModFoldout;
+ private VisualElement _disabledInfoModList;
+ private VisualElement _disabledModList;
+
+ private Button _openModsFolderButton;
+ private Button _openConfigManagerButton;
+
+ // Details UI element references
+ private VisualElement _detailsContainer;
+ private Label _detailsNameLabel;
+ private Label _detailsIdLabel;
+ private Label _detailsAuthorLabel;
+ private Label _detailsVersionLabel;
+ private Button _detailsSourceLink;
+ private StyleFloat _detailsSourceLinkInitialBorderWidth;
+ private Label _detailsDescriptionLabel;
+ private Label _detailsKspVersionLabel;
+ private VisualElement _detailsOutdatedWarning;
+ private VisualElement _detailsUnsupportedWarning;
+ private VisualElement _detailsDisabledWarning;
+ private Foldout _detailsDependenciesFoldout;
+ private VisualElement _detailsDependenciesList;
+
+ // State
+ private bool _isLoaded;
+ private bool _isWindowVisible;
+
+ private readonly Dictionary _modItemElements = new();
+
+ private Dictionary _toggles;
+ private Dictionary _initialToggles;
+
+ private static readonly IReadOnlyList NoToggleGuids = new List
+ {
+ SpaceWarpPlugin.ModGuid,
+ ConfigurationManager.ConfigurationManager.GUID
+ };
+
+ private void Awake()
+ {
+ _listEntryTemplate = AssetManager.GetAsset($"spacewarp/modlist/modlistitem.uxml");
+ _dependencyTemplate = AssetManager.GetAsset($"spacewarp/modlist/modlistdependency.uxml");
+
+ MainMenu.RegisterLocalizedMenuButton("SpaceWarp/Mods", ToggleWindow);
+ }
+
+ private void OnEnable()
+ {
+ if (_isLoaded)
+ {
+ return;
+ }
+
+ SetupDocument();
+ InitializeElements();
+ FillModLists();
+ SetupToggles();
+ SetupButtons();
+ _isLoaded = true;
+ }
+
+ private void Update()
+ {
+ if (Input.GetKey(KeyCode.LeftAlt) && Input.GetKeyDown(KeyCode.M))
+ {
+ ToggleWindow();
+ }
+
+ if (_isWindowVisible && Input.GetKey(KeyCode.Escape))
+ {
+ HideWindow();
+ }
+ }
+
+ private void SetupDocument()
+ {
+ var document = GetComponent();
+ if (document.TryGetComponent(out var localization))
+ {
+ localization.Localize();
+ }
+ else
+ {
+ document.EnableLocalization();
+ }
+
+ _container = document.rootVisualElement;
+
+ StartCoroutine(SetupWindow());
+ }
+
+ private IEnumerator SetupWindow()
+ {
+ yield return new WaitForFixedUpdate();
+
+ var root = _container.hierarchy[0];
+ root.transform.position = new Vector3(
+ (Screen.width - root.boundingBox.width) / 2,
+ (Screen.height - root.boundingBox.height) / 2
+ );
+
+ yield return new WaitForFixedUpdate();
+
+ _container.style.display = DisplayStyle.None;
+ }
+
+ private void InitializeElements()
+ {
+ // Register a callback for the back button
+ _container.Q