Skip to content
Open
3 changes: 2 additions & 1 deletion source/Reloaded.Mod.Launcher.Lib/Lib.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static void Init(IDictionaryResourceProvider provider, SynchronizationCon
Actions.ShowModLoaderUpdateDialogDelegate showModLoaderUpdate, Actions.ShowModUpdateDialogDelegate showModUpdate, Actions.ConfigureNuGetFeedsDialogDelegate configureNuGetFeeds,
Actions.ConfigureModDialogDelegate configureModDialog, Actions.ShowMissingCoreDependencyDialogDelegate showMissingCoreDependency,
Actions.EditModDialogDelegate editModDialog, Actions.PublishModDialogDelegate publishModDialog,
Actions.ShowEditModUserConfigDialogDelegate showEditModUserConfig, Actions.ShowFetchPackageDialogDelegate showFetchPackageDialog,
Actions.ShowEditModUserConfigDialogDelegate showEditModUserConfig, Actions.ShowFetchPackageDialogDelegate showFetchPackageDialog, Actions.ShowInstallPackageDialogDelegate showInstallPackageDialog,
Actions.ShowSelectAddedGameDialogDelegate showSelectAddedGameDialog, Actions.ShowAddAppHashMismatchDialogDelegate showAddAppMismatchDialog,
Actions.ShowApplicationWarningDialogDelegate showApplicationWarningDialog, Actions.ShowRunAppViaWineDialogDelegate showRunAppViaWineDialog,
Actions.ShowEditPackDialogDelegate showEditPackDialog, Actions.ShowInstallModPackDialogDelegate showInstallModPackDialog, Action initControllerSupport)
Expand All @@ -85,6 +85,7 @@ public static void Init(IDictionaryResourceProvider provider, SynchronizationCon
Actions.PublishModDialog = publishModDialog;
Actions.ShowEditModUserConfig = showEditModUserConfig;
Actions.ShowFetchPackageDialog = showFetchPackageDialog;
Actions.ShowInstallPackageDialog = showInstallPackageDialog;
Actions.ShowSelectAddedGameDialog = showSelectAddedGameDialog;
Actions.ShowAddAppHashMismatchDialog = showAddAppMismatchDialog;
Actions.ShowApplicationWarningDialog = showApplicationWarningDialog;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public CreateModViewModel(ModConfigService modConfigService)
ReleaseMetadataFileName = $"{ModId}.ReleaseMetadata.json"
};

config.IgnoreRegexes.Add($"{Regex.Escape($@"{config.ModId}.nuspec")}");
config.IncludeRegexes.Add(Regex.Escape(ModConfig.ConfigFileName));
var modDirectory = Path.Combine(IoC.Get<LoaderConfig>().GetModConfigDirectory(), IOEx.ForceValidFilePath(ModId));
var filePath = Path.Combine(modDirectory, ModConfig.ConfigFileName);
await IConfig<ModConfig>.ToPathAsync(config, filePath);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
namespace Reloaded.Mod.Launcher.Lib.Models.ViewModel.Dialog;

/// <summary>
/// ViewModel for downloading an individual package.
/// </summary>
public class InstallPackageViewModel : INotifyPropertyChanged
Copy link
Member

Choose a reason for hiding this comment

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

You should use autogeneration for INotifyPropertyChanged here.
It's been a while since I've worked on R2 but I believe this uses Fody.PropertyChanged.

Copy link
Member

Choose a reason for hiding this comment

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

This is not the way the rest of the project does this IIRC. Have a quick peek around.

{
private string _text;
public string Text
{
get => _text;
set
{
if (_text != value)
{
_text = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text)));
}
}
}

private double _progress;
public double Progress
{
get => _progress;
set
{
if (_progress != value)
{
_progress = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Progress)));
}
}
}

private bool _isComplete;
public bool IsComplete
{
get => _isComplete;
set
{
if (_isComplete != value)
{
_isComplete = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsComplete)));
}
}
}

public event PropertyChangedEventHandler PropertyChanged;

Check warning on line 50 in source/Reloaded.Mod.Launcher.Lib/Models/ViewModel/Dialog/InstallPackageViewModel.cs

View workflow job for this annotation

GitHub Actions / build

Nullability of reference types in type of 'event PropertyChangedEventHandler InstallPackageViewModel.PropertyChanged' doesn't match implicitly implemented member 'event PropertyChangedEventHandler? INotifyPropertyChanged.PropertyChanged'.
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,23 +103,49 @@ public PublishModDialogViewModel(PathTuple<ModConfig> modTuple)
_modTuple = modTuple;
PackageName = IOEx.ForceValidFilePath(_modTuple.Config.ModName.Replace(' ', '_'));
OutputFolder = Path.Combine(Path.GetTempPath(), $"{IOEx.ForceValidFilePath(_modTuple.Config.ModId)}.Publish");

// Set default Regexes.
IgnoreRegexes = new ObservableCollection<StringWrapper>()
IgnoreRegexes = new ObservableCollection<StringWrapper>(
Copy link
Member

Choose a reason for hiding this comment

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

Is this not overkill?
You can just save it when the menu is closed.

Copy link
Member

Choose a reason for hiding this comment

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

It might also save you from unnecessarily implementing PropertyChanged in the other place.

Copy link
Member

Choose a reason for hiding this comment

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

I think this still needs moving.

_modTuple.Config.IgnoreRegexes.Select(x => new StringWrapper { Value = x })
);
foreach (StringWrapper item in IgnoreRegexes)
item.PropertyChanged += (_, __) => UpdateConfig(_modTuple.Config.IgnoreRegexes, IgnoreRegexes);
IgnoreRegexes.CollectionChanged += (s, e) =>
{
@".*\.json", // Config files
$"{Regex.Escape($@"{_modTuple.Config.ModId}.nuspec")}"
if (e.NewItems != null)
foreach (StringWrapper item in e.NewItems)
item.PropertyChanged += (_, __) => UpdateConfig(_modTuple.Config.IgnoreRegexes, IgnoreRegexes);

if (e.OldItems != null)
foreach (StringWrapper item in e.OldItems)
item.PropertyChanged -= (_, __) => UpdateConfig(_modTuple.Config.IgnoreRegexes, IgnoreRegexes);
_modTuple.Config.IgnoreRegexes.Clear();
_modTuple.Config.IgnoreRegexes.AddRange(IgnoreRegexes.Select(x => x.Value));
_modTuple.SaveAsync();
};

IncludeRegexes = new ObservableCollection<StringWrapper>()
IncludeRegexes = new ObservableCollection<StringWrapper>(
_modTuple.Config.IncludeRegexes.Select(x => new StringWrapper { Value = x })
);
foreach (StringWrapper item in IncludeRegexes)
item.PropertyChanged += (_, __) => UpdateConfig(_modTuple.Config.IncludeRegexes, IncludeRegexes);
IncludeRegexes.CollectionChanged += (s, e) =>
{
Regex.Escape(ModConfig.ConfigFileName),
@"\.deps\.json",
@"\.runtimeconfig\.json",
if (e.NewItems != null)
foreach (StringWrapper item in e.NewItems)
item.PropertyChanged += (_, __) => UpdateConfig(_modTuple.Config.IncludeRegexes, IncludeRegexes);

if (e.OldItems != null)
foreach (StringWrapper item in e.OldItems)
item.PropertyChanged -= (_, __) => UpdateConfig(_modTuple.Config.IncludeRegexes, IncludeRegexes);
_modTuple.Config.IncludeRegexes.Clear();
_modTuple.Config.IncludeRegexes.AddRange(IncludeRegexes.Select(x => x.Value));
_modTuple.SaveAsync();
};
}

// Set notifications
PropertyChanged += ChangeUiVisbilityOnPropertyChanged;
void UpdateConfig(List<string> list, ObservableCollection<StringWrapper> collection)
{
list.Clear();
list.AddRange(collection.Select(x => x.Value));
_modTuple.SaveAsync();
}

/// <summary>
Expand Down Expand Up @@ -148,8 +174,8 @@ await PublishAsync(new PublishArgs()
PublishTarget = PublishTarget,
OutputFolder = OutputFolder,
ModTuple = _modTuple,
IgnoreRegexes = IgnoreRegexes.Select(x => x.Value).ToList(),
IncludeRegexes = IncludeRegexes.Select(x => x.Value).ToList(),
IgnoreRegexes = _modTuple.Config.IgnoreRegexes,
IncludeRegexes = _modTuple.Config.IncludeRegexes,
Progress = new Progress<double>(d => BuildProgress = d * 100),
AutomaticDelta = AutomaticDelta,
CompressionLevel = CompressionLevel,
Expand Down
9 changes: 9 additions & 0 deletions source/Reloaded.Mod.Launcher.Lib/Static/Actions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ public static class Actions
/// </summary>
public static ShowFetchPackageDialogDelegate ShowFetchPackageDialog { get; set; } = null!;

public static ShowInstallPackageDialogDelegate ShowInstallPackageDialog { get; set; } = null!;


/// <summary>
/// Shows a dialog that can be used to select the added game.
/// </summary>
Expand Down Expand Up @@ -254,6 +257,12 @@ public enum MessageBoxType
/// <param name="viewModel">The ViewModel used for downloading the individual package.</param>
public delegate bool ShowFetchPackageDialogDelegate(DownloadPackageViewModel viewModel);

/// <summary>
/// Shows a dialog that can be used to download an individual package.
/// </summary>
/// <param name="viewModel">The ViewModel used for downloading the individual package.</param>
public delegate bool ShowInstallPackageDialogDelegate(InstallPackageViewModel viewModel);

/// <summary>
/// Shows a dialog that can be used to select the added game.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions source/Reloaded.Mod.Launcher/Assets/Languages/en-GB.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,12 @@
<!-- Download Mod Archive -->
<sys:String x:Key="DownloadModArchiveTitle">Download Mod Archive</sys:String>
<sys:String x:Key="DownloadModArchiveName">File Name</sys:String>

<!-- Install Mod Archive -->
<sys:String x:Key="InstallModArchiveTitle">Installing Mod Archive</sys:String>
<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>

<!-- 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
2 changes: 2 additions & 0 deletions source/Reloaded.Mod.Launcher/LibraryBindings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static void Init(IResourceFileSelector? languageSelector, IResourceFileSe
publishModDialog: PublishModDialog,
showEditModUserConfig: ShowEditModUserConfig,
showFetchPackageDialog: ShowFetchPackageDialog,
showInstallPackageDialog: ShowInstallPackageDialog,
showSelectAddedGameDialog: ShowSelectAddedGameDialog,
showAddAppMismatchDialog: ShowAddAppMismatchDialog,
showApplicationWarningDialog: ShowApplicationWarningDialog,
Expand Down Expand Up @@ -106,6 +107,7 @@ private static bool EditModDialog(EditModDialogViewModel viewmodel, object? owne
private static bool ShowEditModUserConfig(EditModUserConfigDialogViewModel viewmodel) => ShowDialogAndGetResult(new EditModUserConfigDialog(viewmodel));
private static bool PublishModDialog(PublishModDialogViewModel viewmodel) => ShowDialogAndGetResult(new PublishModDialog(viewmodel));
private static bool ShowFetchPackageDialog(DownloadPackageViewModel viewmodel) => ShowDialogAndGetResult(new DownloadPackageDialog(viewmodel));
private static bool ShowInstallPackageDialog(InstallPackageViewModel viewmodel) => ShowDialogAndGetResult(new InstallPackageDialog(viewmodel));
private static bool ShowAddAppMismatchDialog(AddAppHashMismatchDialogViewModel viewmodel) => ShowDialogAndGetResult(new AddAppHashMismatchDialog(viewmodel));
private static bool ShowApplicationWarningDialog(AddApplicationWarningDialogViewModel viewmodel) => ShowDialogAndGetResult(new ShowApplicationWarningDialog(viewmodel));
private static bool ShowInstallModPackDialog(InstallModPackDialogViewModel viewmodel) => ShowDialogAndGetResult(new InstallModPackDialog(viewmodel));
Expand Down
38 changes: 34 additions & 4 deletions source/Reloaded.Mod.Launcher/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Text;
using NuGet.Common;
using Reloaded.Mod.Loader.Update.Providers.Web;
using Sewer56.DeltaPatchGenerator.Lib.Utility;
using Sewer56.Update.Extractors.SevenZipSharp;
using System.Text;
using System.Windows.Threading;
using static Reloaded.Mod.Launcher.Lib.Static.Resources;

namespace Reloaded.Mod.Launcher;
Expand Down Expand Up @@ -88,10 +90,38 @@ private async void InstallMod_Drop(object sender, DragEventArgs e)
/* Extract to Temp Directory */
using var tempFolder = new TemporaryFolderAllocation();
var archiveExtractor = new SevenZipSharpExtractor();
await archiveExtractor.ExtractPackageAsync(file, tempFolder.FolderPath, new Progress<double>(), default);

/* Get name of package. */
WebDownloadablePackage.CopyPackagesFromExtractFolderToTargetDir(modsFolder!, tempFolder.FolderPath, default);
var installVM = new InstallPackageViewModel
{
Text = (string)Application.Current.Resources["ExtractingLocalModArchive"],
Progress = 0
};

var progress = new Progress<double>(value =>
{
installVM.Progress = value * 100;
});

var timer = new DispatcherTimer
{
Interval = TimeSpan.FromSeconds(3)
};

timer.Tick += (s, e) =>
{
timer.Stop();
if (installVM.Progress != 100)
{
Actions.ShowInstallPackageDialog.Invoke(installVM);
}
};
timer.Start();

await archiveExtractor.ExtractPackageAsync(file, tempFolder.FolderPath, progress, default);

installVM.Text = (string)Application.Current.Resources["InstallingModWait"];
await WebDownloadablePackage.CopyPackagesFromExtractFolderToTargetDir(modsFolder!, tempFolder.FolderPath, default);
installVM.IsComplete = true;
}

// Find the new mods
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<default:ReloadedWindow x:Class="Reloaded.Mod.Launcher.Pages.Dialogs.InstallPackageDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Reloaded.Mod.Launcher.Pages.Dialogs"
xmlns:converters="clr-namespace:Reloaded.Mod.Launcher.Converters"
xmlns:default="clr-namespace:Reloaded.WPF.Theme.Default;assembly=Reloaded.WPF.Theme.Default"
mc:Ignorable="d"
SizeToContent="Height"
Width="500"
Title="{DynamicResource InstallModArchiveTitle}"
Style="{DynamicResource ReloadedWindow}">
<Grid Margin="{DynamicResource PageMarginSmall}" DataContext="{Binding Path=ViewModel, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Window}}}">

<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<TextBlock HorizontalAlignment="Center"
Grid.Row="0"
Text="{Binding Text}"
Visibility="{Binding Text, Converter={x:Static converters:StringToVisibilityConverter.Instance}}"
Style="{DynamicResource DefaultTextBlock}" />

<Grid Margin="{DynamicResource PanelMargin}"
Grid.Row="1"/>

<Grid Margin="{DynamicResource PanelMargin}" Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="{DynamicResource GridInterPanelMargin}" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>

<ProgressBar Grid.Column="0"
Height="25"
Value="{Binding Progress, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Maximum="100" />
</Grid>
</Grid>
</default:ReloadedWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Button = System.Windows.Controls.Button;

namespace Reloaded.Mod.Launcher.Pages.Dialogs;

/// <summary>
/// Interaction logic for DownloadPackageDialog.xaml
/// </summary>
public partial class InstallPackageDialog : ReloadedWindow
{
public new InstallPackageViewModel ViewModel { get; set; }

/// <inheritdoc />
public InstallPackageDialog(InstallPackageViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
viewModel.PropertyChanged += (s, e) =>
{
if (e.PropertyName == nameof(InstallPackageViewModel.IsComplete) && viewModel.IsComplete)
{
ActionWrappers.ExecuteWithApplicationDispatcher(this.Close);
}
};
}
}
5 changes: 5 additions & 0 deletions source/Reloaded.Mod.Loader.IO/Config/ModConfig.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Reloaded.Memory.Extensions;
using System.Text.RegularExpressions;

namespace Reloaded.Mod.Loader.IO.Config;

Expand Down Expand Up @@ -36,6 +37,10 @@ public class ModConfig : ObservableObject, IConfig<ModConfig>, IModConfig
public bool IsLibrary { get; set; } = false;
public string ReleaseMetadataFileName { get; set; } = "Sewer56.Update.ReleaseMetadata.json";

/// Publishing
public List<string> IgnoreRegexes { get; set; } = [@".*\.json"];
public List<string> IncludeRegexes { get; set; } = [@"\.deps\.json", @"\.runtimeconfig\.json"];

[JsonIgnore]
public string ModSubDirs { get; set; } = string.Empty;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ await retryPolicy.ExecuteAsync(async () =>
await archiveExtractor.ExtractPackageAsync(tempDownloadPath, tempExtractDir.FolderPath, extractSlice, token);

// Copy all packages from download.
return WebDownloadablePackage.CopyPackagesFromExtractFolderToTargetDir(packageFolder, tempExtractDir.FolderPath, token);
return await WebDownloadablePackage.CopyPackagesFromExtractFolderToTargetDir(packageFolder, tempExtractDir.FolderPath, token);
}

#pragma warning disable CS0067 // Event never used
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@
await archiveExtractor.ExtractPackageAsync(tempFilePath, tempExtractDirectory.FolderPath, extractProgress, token);

/* Get name of package. */
return CopyPackagesFromExtractFolderToTargetDir(packageFolder, tempExtractDirectory.FolderPath, token);
return await CopyPackagesFromExtractFolderToTargetDir(packageFolder, tempExtractDirectory.FolderPath, token);
}

/// <inheritdoc />
Expand All @@ -139,7 +139,7 @@
/// Finds all mods in <paramref name="tempExtractDir"/> and copies them to appropriate subfolders in <paramref name="packageFolder"/>.
/// </summary>
/// <returns>Path to last folder copied.</returns>
public static string CopyPackagesFromExtractFolderToTargetDir(string packageFolder, string tempExtractDir, CancellationToken token)
public async static Task<string> CopyPackagesFromExtractFolderToTargetDir(string packageFolder, string tempExtractDir, CancellationToken token)

Check warning on line 142 in source/Reloaded.Mod.Loader.Update/Providers/Web/WebDownloadablePackage.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Check warning on line 142 in source/Reloaded.Mod.Loader.Update/Providers/Web/WebDownloadablePackage.cs

View workflow job for this annotation

GitHub Actions / build

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
var configs = ConfigReader<ModConfig>.ReadConfigurations(tempExtractDir, ModConfig.ConfigFileName, token, int.MaxValue, 0);
var returnResult = "";
Expand All @@ -162,7 +162,7 @@
// Obtain the name of the file.
try
{
var fileReq = WebRequest.CreateHttp(url);

Check warning on line 165 in source/Reloaded.Mod.Loader.Update/Providers/Web/WebDownloadablePackage.cs

View workflow job for this annotation

GitHub Actions / build

'WebRequest.CreateHttp(Uri)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)

Check warning on line 165 in source/Reloaded.Mod.Loader.Update/Providers/Web/WebDownloadablePackage.cs

View workflow job for this annotation

GitHub Actions / build

'WebRequest.CreateHttp(Uri)' is obsolete: 'WebRequest, HttpWebRequest, ServicePoint, and WebClient are obsolete. Use HttpClient instead.' (https://aka.ms/dotnet-warnings/SYSLIB0014)
using var fileResp = await fileReq.GetResponseAsync();

try
Expand Down
14 changes: 13 additions & 1 deletion source/Reloaded.Mod.Loader.Update/Utilities/StringWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ namespace Reloaded.Mod.Loader.Update.Utilities;
[JsonConverter(typeof(StringWrapperConverter))]
public class StringWrapper : ObservableObject
{
private string _value = "";
/// <summary>
/// Value of the string wrapper.
/// </summary>
public string Value { get; set; } = "";
public string Value
{
get => _value;
set
{
if (_value != value)
{
_value = value;
RaisePropertyChangedEvent(nameof(Value));
}
}
}

/// <summary/>
public static implicit operator string(StringWrapper wrapper) => wrapper.Value;
Expand Down
Loading