Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4c87269
Prompt user to select supported applications for a mod in the case wh…
TheBestAstroNOT Dec 10, 2025
7f88eee
Web downloads most likely work now
TheBestAstroNOT Dec 10, 2025
76864cc
Update Resources.cs
TheBestAstroNOT Dec 11, 2025
6930137
Mod manager downloads probably work now, also changed OK/CANCEL to YE…
TheBestAstroNOT Dec 12, 2025
93dea82
Merge branch 'master' of https://github.com/TheBestAstroNOT/Reloaded-II
TheBestAstroNOT Dec 12, 2025
5e177ac
Merge branch 'Reloaded-Project:master' into master
TheBestAstroNOT Dec 12, 2025
7fab29d
Merge branch 'master' into master
TheBestAstroNOT Dec 17, 2025
5249e8f
Update Resources.cs
TheBestAstroNOT Dec 17, 2025
64947d6
Refactor EditModDialogViewModel constructor usage
TheBestAstroNOT Jan 18, 2026
de781ef
Fix incorrect universal config check
TheBestAstroNOT Jan 18, 2026
c41c61b
Merge branch 'master' into master
TheBestAstroNOT Jan 18, 2026
115715c
Merge branch 'add-double-slider'
Sewer56 Jan 25, 2026
2a5d8da
Update version placeholders to 1.30.0 for new resource properties
Sewer56 Jan 25, 2026
58c5558
Merge remote-tracking branch 'origin/master'
Sewer56 Jan 25, 2026
1fe7401
Remove unused variable assignments for EditModDialog return value
Sewer56 Jan 25, 2026
4ed58eb
Add comment explaining O(N²) complexity is acceptable for small app c…
Sewer56 Jan 25, 2026
88f6bfe
Refactor: extract mod app compatibility validation into shared helper
Sewer56 Jan 25, 2026
b0ee37f
Added: Changelog entry for mod app compatibility warning (PR #769)
Sewer56 Jan 25, 2026
bdce2e8
Removed: Unused 'items' field
Sewer56 Jan 25, 2026
b4126f5
Cleanup: Lift the logic for verifying at least 1 compatible app
Sewer56 Jan 25, 2026
3d2699c
Added: Missing author contribution to changelog text
Sewer56 Jan 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ public EditModCommand(PathTuple<ModConfig>? modTuple, object? parent)
public bool CanExecute(object? parameter) => _modTuple != null;

/// <inheritdoc />
public void Execute(object? parameter) => Actions.EditModDialog(new EditModDialogViewModel(_modTuple!, IoC.Get<ApplicationConfigService>(), IoC.Get<ModConfigService>()), _parent);
public void Execute(object? parameter) => Actions.EditModDialog(new EditModDialogViewModel(_modTuple!, IoC.Get<ApplicationConfigService>().Items, IoC.Get<ModConfigService>().Items), _parent);
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,30 +67,28 @@ public class EditModDialogViewModel : ObservableObject, IDisposable
/// </summary>
public ObservableCollection<ResolverFactoryConfiguration> Updates { get; set; } = new ObservableCollection<ResolverFactoryConfiguration>();

private readonly ApplicationConfigService _applicationConfigService;
private SetModImageCommand _setModImageCommand;
private Action? _close;

/// <inheritdoc />
public EditModDialogViewModel(PathTuple<ModConfig> modTuple, ApplicationConfigService applicationConfigService, ModConfigService modConfigService)
public EditModDialogViewModel(PathTuple<ModConfig> modTuple, ObservableCollection<PathTuple<ApplicationConfig>> appItems, ObservableCollection<PathTuple<ModConfig>> modsItems)
{
_applicationConfigService = applicationConfigService;
ConfigTuple = modTuple;
Config = modTuple.Config;

// Add Tags
Tags.AddRange(Config.Tags);

// Add Known Apps
var apps = applicationConfigService.Items;
var apps = appItems;
foreach (var app in apps)
{
bool isAppEnabled = modTuple.Config.SupportedAppId.Contains(app.Config.AppId, StringComparer.OrdinalIgnoreCase);
Applications.Add(new BooleanGenericTuple<IApplicationConfig>(isAppEnabled, app.Config));
}

// Build Dependencies
var mods = modConfigService.Items; // In case collection changes during window open.
var mods = modsItems; // In case collection changes during window open.
foreach (var mod in mods)
{
bool isModEnabled = modTuple.Config.ModDependencies.Contains(mod.Config.ModId, StringComparer.OrdinalIgnoreCase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ public void Execute(object? parameter)
_viewModel.DownloadPackageStatus = DownloadPackageStatus.Downloading;
try
{
var modConfigService = IoC.GetConstant<ModConfigService>();
var modsBefore = new Dictionary<string, PathTuple<ModConfig>>(modConfigService.ItemsById);
_canExecute = false;
RaiseCanExecute(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));

Expand All @@ -307,6 +309,40 @@ public void Execute(object? parameter)
await Update.ResolveMissingPackagesAsync();
});

modConfigService.ForceRefresh();
var newConfigs = new List<ModConfig>();
foreach (var item in modConfigService.ItemsById.ToArray())
{
if (!modsBefore.ContainsKey(item.Key))
{
newConfigs.Add(item.Value.Config);
Comment thread
Sewer56 marked this conversation as resolved.
if (item.Value.Config.IsUniversalMod || item.Value.Config.SupportedAppId.Length > 0)
{
var match = IoC.Get<ApplicationConfigService>().Items.FirstOrDefault(app => item.Value.Config.SupportedAppId.Contains(app.Config.AppId));
if (match == null)
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel!.Invoke(Resources.NoCompatibleAppsInConfigTitle.Get(), $"{Resources.NoCompatibleAppsInConfigDescription.Get()}\n{Resources.AppSelectionQuestion.Get()}", Resources.Yes.Get(), Resources.No.Get());
if (loadAppPage)
{
var viewmodel = new EditModDialogViewModel(item.Value, IoC.Get<ApplicationConfigService>().Items, modConfigService.Items);
viewmodel.Page = EditModPage.Special;
var createModDialog = Actions.EditModDialog(viewmodel, null);
Comment thread
TheBestAstroNOT marked this conversation as resolved.
Outdated
}
}
}
else
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel!.Invoke(Resources.NoAppsInConfigTitle.Get(), $"{Resources.NoAppsInConfigDescription.Get()}\n{Resources.AppSelectionQuestion.Get()}", Resources.Yes.Get(), Resources.No.Get());
if (loadAppPage)
{
var viewmodel = new EditModDialogViewModel(item.Value, IoC.Get<ApplicationConfigService>().Items, modConfigService.Items);
viewmodel.Page = EditModPage.Special;
var createModDialog = Actions.EditModDialog(viewmodel, null);
Comment thread
TheBestAstroNOT marked this conversation as resolved.
Outdated
}
}
}
}
Comment thread
Sewer56 marked this conversation as resolved.

_canExecute = true;
RaiseCanExecute(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
Expand Down
47 changes: 47 additions & 0 deletions source/Reloaded.Mod.Launcher.Lib/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ private static void LaunchApplicationAndExit(string applicationToLaunch, bool fo
[MethodImpl(MethodImplOptions.NoInlining)]
private static void DownloadModAndExit(string downloadUrl)
{
var loaderConfig = IoC.Get<LoaderConfig>();
var modConfigDir = loaderConfig.GetModConfigDirectory();
var modConfig = ModConfig.GetAllMods(modConfigDir);
var allAppsList = ApplicationConfig.GetAllApplications(loaderConfig.GetApplicationConfigDirectory());
var allApps = new ObservableCollection<PathTuple<ApplicationConfig>>(allAppsList);
var oldItemsById = modConfig.ToDictionary(
x => x.Config.ModId,
x => x,
StringComparer.OrdinalIgnoreCase
);

if (downloadUrl.StartsWith($"{Constants.ReloadedProtocol}:", StringComparison.InvariantCultureIgnoreCase))
downloadUrl = downloadUrl.Substring(Constants.ReloadedProtocol.Length + 1);

Expand All @@ -119,6 +130,42 @@ private static void DownloadModAndExit(string downloadUrl)

Actions.ShowFetchPackageDialog(viewModel);
Update.ResolveMissingPackages();

modConfig = ModConfig.GetAllMods(modConfigDir);
var itemsById = modConfig.ToDictionary(x => x.Config.ModId, x => x, StringComparer.OrdinalIgnoreCase);
var items = new ObservableCollection<PathTuple<ModConfig>>(modConfig);
foreach (var item in itemsById.ToArray())
{
if (!oldItemsById.ContainsKey(item.Key))
{
if (item.Value.Config.IsUniversalMod || item.Value.Config.SupportedAppId.Length > 0)
{
var match = IoC.Get<ApplicationConfigService>().Items.FirstOrDefault(app => item.Value.Config.SupportedAppId.Contains(app.Config.AppId));
if (match == null)
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel(Resources.NoCompatibleAppsInConfigTitle.Get(), $"{Resources.NoCompatibleAppsInConfigDescription.Get()}\n{Resources.AppSelectionQuestion.Get()}", Resources.Yes.Get(), Resources.No.Get());
if (loadAppPage)
{
var viewmodel = new EditModDialogViewModel(item.Value, allApps, items);
viewmodel.Page = EditModPage.Special;
var createModDialog = Actions.EditModDialog(viewmodel, null);
Comment thread
Sewer56 marked this conversation as resolved.
Outdated
}
}
}
Comment thread
Sewer56 marked this conversation as resolved.
Outdated
else
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel(Resources.NoAppsInConfigTitle.Get(), $"{Resources.NoAppsInConfigDescription.Get()}\n{Resources.AppSelectionQuestion.Get()}", Resources.Yes.Get(), Resources.No.Get());
if (loadAppPage)
{
var viewmodel = new EditModDialogViewModel(item.Value, allApps, items);
viewmodel.Page = EditModPage.Special;
var createModDialog = Actions.EditModDialog(viewmodel, null);
Comment thread
Sewer56 marked this conversation as resolved.
Outdated
}
}
}

}

Actions.DisplayMessagebox(Resources.PackageDownloaderDownloadCompleteTitle.Get(), Resources.PackageDownloaderDownloadCompleteDescription.Get(), new Actions.DisplayMessageBoxParams()
{
Type = Actions.MessageBoxType.Ok,
Expand Down
13 changes: 11 additions & 2 deletions source/Reloaded.Mod.Launcher.Lib/Static/Resources.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,18 @@ public static void Init(IDictionaryResourceProvider provider)
public static IDictionaryResource<string> ErrorStacktraceTitle { get; set; }
public static IDictionaryResource<string> ErrorStacktraceSubtitle { get; set; }

// Update 1.X.X: New Progress Window for Local Mod Installation (UPDATE LAUNCHER VER BEFORE RELEASE)
// Update 1.29.3: New Progress Window for Local Mod Installation
public static IDictionaryResource<string> InstallModArchiveTitle { get; set; }
public static IDictionaryResource<string> InstalledModName { get; set; }
public static IDictionaryResource<string> InstallingModWait { get; set; }
public static IDictionaryResource<string> ExtractingLocalModArchive { get; set; }
}

// Update 1.XX.XX: Add warning during installation of mods with no apps in their supported apps list or when no compatible app is installed (UPDATE LAUNCHER VER BEFORE RELEASE).
public static IDictionaryResource<string> NoAppsInConfigTitle { get; set; }
public static IDictionaryResource<string> NoAppsInConfigDescription { get; set; }
public static IDictionaryResource<string> NoCompatibleAppsInConfigTitle { get; set; }
public static IDictionaryResource<string> NoCompatibleAppsInConfigDescription { get; set; }
public static IDictionaryResource<string> AppSelectionQuestion { get; set; }
public static IDictionaryResource<string> Yes { get; set; }
public static IDictionaryResource<string> No { get; set; }
}
12 changes: 12 additions & 0 deletions source/Reloaded.Mod.Launcher/Assets/Languages/en-GB.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,13 @@
<sys:String x:Key="InstalledModName">Mod Name</sys:String>
<sys:String x:Key="InstallingModWait">Please wait while we install the mod!</sys:String>
<sys:String x:Key="ExtractingLocalModArchive">Extracting a local mod, please wait!</sys:String>

<!-- App Resolution Failure Message-->
<sys:String x:Key="NoAppsInConfigTitle">Unable to find any supported app</sys:String>
<sys:String x:Key="NoAppsInConfigDescription">Reloaded-II was unable to find any supported apps in the mod's config.</sys:String>
<sys:String x:Key="NoCompatibleAppsInConfigTitle">Unable to find a compatible app</sys:String>
<sys:String x:Key="NoCompatibleAppsInConfigDescription">Reloaded-II was unable to find an installed app which is compatible with this mod.</sys:String>
<sys:String x:Key="AppSelectionQuestion">Do you want to select a supported app manually?</sys:String>

<!-- WMI Administrator Message -->
<sys:String x:Key="RunAsAdminMessage">You need to run this application as administrator.&#x0a;Administrative privileges are needed to receive application launch/exit events from Windows Management Instrumentation (WMI).&#x0a;Developers: Run your favourite IDE e.g. Visual Studio as Admin.</sys:String>
Expand Down Expand Up @@ -739,4 +746,9 @@ For more info, refer to the tutorial. Don't forget to set correct Publish target

<!-- Update 1.30.1: Missing Dependencies using same code as Installer -->
<sys:String x:Key="MissingDependenciesText3">Please download the requirements above by using the button below. You may need administrator permissions.</sys:String>

<!-- Update 1.XX.X: Uhh no strings for Yes and No? -->
<sys:String x:Key="Yes">Yes</sys:String>
<sys:String x:Key="No">No</sys:String>
Comment thread
coderabbitai[bot] marked this conversation as resolved.

</ResourceDictionary>
30 changes: 30 additions & 0 deletions source/Reloaded.Mod.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using NuGet.Common;
using Reloaded.Mod.Launcher.Pages.BaseSubpages.Dialogs;
using Reloaded.Mod.Loader.Update.Providers.Web;
using Sewer56.DeltaPatchGenerator.Lib.Utility;
using Sewer56.Update.Extractors.SevenZipSharp;
Expand Down Expand Up @@ -132,7 +133,36 @@ private async void InstallMod_Drop(object sender, DragEventArgs e)
foreach (var item in modConfigService.ItemsById.ToArray())
{
if (!modsBefore.ContainsKey(item.Key))
{
newConfigs.Add(item.Value.Config);
if(item.Value.Config.IsUniversalMod || item.Value.Config.SupportedAppId.Length > 0)
{
var match = Lib.IoC.Get<ApplicationConfigService>().Items.FirstOrDefault(app => item.Value.Config.SupportedAppId.Contains(app.Config.AppId));
if(match == null)
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel!.Invoke(NoCompatibleAppsInConfigTitle.Get(), $"{NoCompatibleAppsInConfigDescription.Get()}\n{AppSelectionQuestion.Get()}", Yes.Get(), No.Get());
if (loadAppPage)
{
var createModDialog = new EditModDialog(new EditModDialogViewModel(item.Value, Lib.IoC.Get<ApplicationConfigService>().Items, modConfigService.Items));
createModDialog.Owner = System.Windows.Window.GetWindow(this);
createModDialog.RealViewModel.Page = EditModPage.Special;
createModDialog.ShowDialog();
}
}
}
else
{
bool loadAppPage = Actions.DisplayResourceMessageBoxOkCancel!.Invoke(NoAppsInConfigTitle.Get(), $"{NoAppsInConfigDescription.Get()}\n{AppSelectionQuestion.Get()}" , Yes.Get(), No.Get());
if (loadAppPage)
{
var createModDialog = new EditModDialog(new EditModDialogViewModel(item.Value, Lib.IoC.Get<ApplicationConfigService>().Items, modConfigService.Items));
createModDialog.Owner = System.Windows.Window.GetWindow(this);
createModDialog.RealViewModel.Page = EditModPage.Special;
createModDialog.ShowDialog();
}
}
}

}

if (newConfigs.Count <= 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task Save()
var mod = await ActionWrappers.TryGetValueAsync(() => modConfigService.ItemsById[createdMod.Config.ModId], 5000, 32);
if (mod != null)
{
var createModDialog = new EditModDialog(new EditModDialogViewModel(mod, Lib.IoC.Get<ApplicationConfigService>(), modConfigService));
var createModDialog = new EditModDialog(new EditModDialogViewModel(mod, Lib.IoC.Get<ApplicationConfigService>().Items, modConfigService.Items));
createModDialog.Owner = Window.GetWindow(this);
createModDialog.ShowDialog();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ public class XamlResourceMessageBoxOkCancel : MessageBoxOkCancel

}

public XamlResourceMessageBoxOkCancel(string titleResourceName, string descriptionResourceName, string okButtonTextResourceName, string cancelButtonTextResourceName) : base(new XamlResource<string>(titleResourceName).Get(), new XamlResource<string>(descriptionResourceName).Get())
public XamlResourceMessageBoxOkCancel(string title, string message, string okText, string cancelText) : base(title, message)
{
this.CancelBtn.Content = Lib.Static.Resources.ResourceProvider.Get<string>(cancelButtonTextResourceName);
this.OKBtn.Content = Lib.Static.Resources.ResourceProvider.Get<string>(okButtonTextResourceName);
Comment on lines -13 to -16
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I'm missing something, wouldn't this replace every instance of Ok/Cancel to Yes/No?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uhh no? You are passing the text every time you call it. Plus other message boxes use another delegate to display their text. Only I'm using this one.

this.OKBtn.Content = okText;
this.CancelBtn.Content = cancelText;
}
}
Loading