Skip to content

Commit

Permalink
完成了游戏的安装逻辑
Browse files Browse the repository at this point in the history
可以说是写完了,但是说写完了不太可能,Bug有点小多
  • Loading branch information
YangSpring114 committed Jan 4, 2025
1 parent c353474 commit 9041428
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 12 deletions.
1 change: 1 addition & 0 deletions WonderLab/App.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ private static IHost ConfigureIoC(out IHost host) {
builder.Services.AddSingleton<ConfigService>();
builder.Services.AddSingleton<LaunchService>();
builder.Services.AddSingleton<AccountService>();
builder.Services.AddSingleton<DownloadService>();
builder.Services.AddSingleton<NotificationService>();

//Configure Window
Expand Down
1 change: 1 addition & 0 deletions WonderLab/Controls/Theme/NotificationCardTheme.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
<DataTemplate DataType="INotification">
<TextBlock Classes="Caption"
Text="{Binding Message}"
TextWrapping="WrapWithOverflow"
Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
</DataTemplate>
</ReversibleStackPanel.DataTemplates>
Expand Down
4 changes: 4 additions & 0 deletions WonderLab/Services/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public void Load() {
Entries = new Config();
}

if (Entries.MaxMemory == 0) {
Entries.MaxMemory = 1024;
}

if (string.IsNullOrEmpty(Entries.ActiveLanguage)) {
Entries.ActiveLanguage = "zh-Hans";
}
Expand Down
88 changes: 80 additions & 8 deletions WonderLab/Services/Download/DownloadService.cs
Original file line number Diff line number Diff line change
@@ -1,29 +1,101 @@
using MinecraftLaunch.Classes.Enums;
using Avalonia.Controls.Notifications;
using CommunityToolkit.Mvvm.Messaging;
using MinecraftLaunch.Classes.Models.Download;
using MinecraftLaunch.Classes.Models.Install;
using MinecraftLaunch.Components.Installer;
using System;
using System.Threading;
using System.Threading.Tasks;
using WonderLab.Infrastructure.Models;
using WonderLab.Infrastructure.Models.Messaging;
using WonderLab.Services.Launch;
using WonderLab.ViewModels.Tasks;

namespace WonderLab.Services.Download;

public sealed class DownloadService {
private readonly TaskService _taskService;
private readonly GameService _gameService;
private readonly ConfigService _configService;

public DownloadService(ConfigService configService, GameService gameService) {
public DownloadService(ConfigService configService, GameService gameService, TaskService taskService) {
_taskService = taskService;
_gameService = gameService;
_configService = configService;
}

public async Task InstallMinecraftAsync(LoaderType type, string gameId, string customGameId = default) {
public Task InstallMinecraftTaskAsync(string gameId, bool isInstallOptifine = false, object installEntry = default, string customGameId = default) {
var id = string.IsNullOrEmpty(customGameId) ? gameId : customGameId;
InstallMinecraftTaskViewModel task = new() {
JobName = $"游戏实例 {id} 的安装任务"
};

task.Completed += (_, _) => {
WeakReferenceMessenger.Default.Send(new NotificationMessage($"游戏实例 {id} 安装完成!", NotificationType.Information));

_gameService.RefreshGames();
};

_taskService.QueueJob(task);
return Task.Run(async () => await InstallMinecraftAsync(gameId, task, task.TaskCancellationToken, customGameId, installEntry, isInstallOptifine));
}

public async Task InstallMinecraftAsync(
string gameId,
IProgress<TaskProgress> progress,
CancellationToken cancellationToken,
string customGameId = default,
object installEntry = default,
bool isInstallOptifine = false) {
var dc = new DownloaderConfiguration {
IsEnableFragmentedDownload = true,
MaxThread = _configService.Entries.ThreadCount
};
//CompositionInstaller
//var installer = type switch {
// LoaderType.Any => new VanlliaInstaller(_gameService.GameResolver, gameId, dc),
// LoaderType.Forge => new ForgeInstaller(_gameService.GameResolver.GetGameEntity())
//};

try {
var mainInstaller = new VanlliaInstaller(_gameService.GameResolver, gameId, dc);
InstallerBase installer = installEntry is null
? mainInstaller
: installEntry switch {
ForgeInstallEntry => new CompositionInstaller(mainInstaller, new ForgeInstaller((ForgeInstallEntry)installEntry, _configService.Entries.ActiveJava.JavaPath, customGameId, dc), customGameId),
FabricBuildEntry => new CompositionInstaller(mainInstaller, new FabricInstaller((FabricBuildEntry)installEntry, customGameId, dc), customGameId),
QuiltBuildEntry => new CompositionInstaller(mainInstaller, new QuiltInstaller((QuiltBuildEntry)installEntry, customGameId, dc), customGameId),
_ => throw new NotSupportedException()
};

progress.Report(new(1, 1d));
cancellationToken.ThrowIfCancellationRequested();

if (installer is CompositionInstaller composition) {
bool isSub1Installed = false;
composition.SubInstallerCompleted += (_, _) => {
progress.Report(new(2, 1d));
isSub1Installed = true;
};

composition.ProgressChanged += (_, arg) => {
if (isSub1Installed) {
progress.Report(new(3, arg.Progress, Speed: arg.Speed));
} else {
progress.Report(new(2, arg.Progress, Speed: arg.Speed));
}
};

composition.Completed += (_, arg) => progress.Report(new(4, 1d));
} else {
installer.ProgressChanged += (_, arg) => {
progress.Report(new(2, arg.Progress, Speed: arg.Speed));
};

installer.Completed += (_, arg) => {
progress.Report(new(3, 1d));
progress.Report(new(4, 1d));
};
}

await installer.InstallAsync(cancellationToken);
} catch (Exception ex) {
progress.Report(new(-1, 1d, ex));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
using Avalonia.Threading;
using Avalonia.Controls.Notifications;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using DialogHostAvalonia;
using MinecraftLaunch.Classes.Enums;
using MinecraftLaunch.Classes.Models.Install;
using MinecraftLaunch.Components.Installer;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using WonderLab.Extensions;
using WonderLab.Infrastructure.Models.Messaging;
using WonderLab.Services.Download;

namespace WonderLab.ViewModels.Dialog.Download;

public sealed partial class InstallMinecraftDialogViewModel : ObservableObject {
private readonly DownloadService _downloadService;

private LoaderType _loaderType;

public string GameCoreId { get; set; }
Expand All @@ -27,7 +35,7 @@ public sealed partial class InstallMinecraftDialogViewModel : ObservableObject {
[ObservableProperty] private object _currentModLoader;

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(InstallCommand))]
[NotifyCanExecuteChangedFor(nameof(InstallCommand))]
private string _customGameCoreId;

[ObservableProperty]
Expand All @@ -50,6 +58,10 @@ public sealed partial class InstallMinecraftDialogViewModel : ObservableObject {
[NotifyPropertyChangedFor(nameof(IsNeoforgeLoaded))]
private ObservableCollection<object> _neoforges;

public InstallMinecraftDialogViewModel(DownloadService downloadService) {
_downloadService = downloadService;
}

private bool CanInstall() => !string.IsNullOrEmpty(CustomGameCoreId);

[RelayCommand]
Expand All @@ -59,11 +71,21 @@ private async Task OnLoaded() {
}

[RelayCommand]
private void Close() => DialogHost.Close("PART_DialogHost");
private Task Close() => Dispatcher.UIThread.InvokeAsync(async () => {
DialogHost.Close("PART_DialogHost");
});

[RelayCommand(CanExecute = nameof(CanInstall))]
private Task Install() => Task.Run(() => {
//var text = I18NExtension.Translate(LanguageKeys.Launch_Notification);

WeakReferenceMessenger.Default.Send(new NotificationMessage($"正在安装游戏实例 {CustomGameCoreId},点击此通知以查看详情",
NotificationType.Information, () => {
WeakReferenceMessenger.Default.Send<PageNotificationMessage>(new("TaskList"));
}));

_downloadService.InstallMinecraftTaskAsync(GameCoreId, IsInstallOptifine, CurrentModLoader, CustomGameCoreId);
Close();
});

[RelayCommand]
Expand Down Expand Up @@ -122,5 +144,18 @@ async void LoadModLoader(LoaderType type) {

protected override void OnPropertyChanged(PropertyChangedEventArgs e) {
base.OnPropertyChanged(e);

if (e.PropertyName is nameof(CurrentModLoader) && CurrentModLoader != null) {
var loader = CurrentModLoader;

string loaderInfo = _loaderType switch {
LoaderType.Forge => $"{((ForgeInstallEntry)loader).ForgeVersion}{(string.IsNullOrEmpty(((ForgeInstallEntry)loader).Branch) ? string.Empty : $"-{((ForgeInstallEntry)loader).Branch}")}",
LoaderType.Quilt => ((QuiltBuildEntry)loader).Loader.Version,
LoaderType.Fabric => ((FabricBuildEntry)loader).Loader.Version,
_ => throw new NotSupportedException()
};

CustomGameCoreId = $"{GameCoreId}-{_loaderType}_{loaderInfo}";
}
}
}
74 changes: 74 additions & 0 deletions WonderLab/ViewModels/Tasks/InstallMinecraftTaskViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using Avalonia.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MinecraftLaunch.Components.Installer;
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using WonderLab.Infrastructure.Interfaces;
using WonderLab.Infrastructure.Models;

namespace WonderLab.ViewModels.Tasks;

public sealed partial class InstallMinecraftTaskViewModel : ObservableObject, ITaskJob<TaskProgress> {
public readonly CancellationTokenSource InstallCancellationTokenSource = new();

public double MaxProgress => 1d;
public string ProgressText => Progress.ToString("P2");

public ImmutableArray<TaskStep> TaskSteps { get; } = [
new TaskStep { StepName = "Check if the specified id exists", TaskStatus = TaskStatus.Running },
new TaskStep { StepName = "Download minecraft" },
new TaskStep { StepName = "Install mod loader" },
];

public event EventHandler Completed;

public Exception Exception { get; set; }
public required string JobName { get; set; }

[ObservableProperty] private TaskStatus _taskStatus;
[ObservableProperty] private bool _isIndeterminate = true;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(ProgressText))]
private double _progress;

public CancellationToken TaskCancellationToken => InstallCancellationTokenSource.Token;

public void Report(TaskProgress value) => Dispatcher.UIThread.InvokeAsync(() => {
switch (value.Step) {
case 1:
TaskStatus = TaskStatus.Running;
IsIndeterminate = false;
TaskSteps[0].Progress = value.Progress;
break;
case 2:
TaskSteps[0].TaskStatus = TaskStatus.RanToCompletion;
TaskSteps[1].TaskStatus = TaskStatus.Running;

TaskSteps[1].Speed = value.Speed;
TaskSteps[1].Progress = value.Progress;
break;
case 3:
TaskSteps[1].TaskStatus = TaskStatus.RanToCompletion;
TaskSteps[2].TaskStatus = TaskStatus.Running;

TaskSteps[2].Speed = value.Speed;
TaskSteps[2].Progress = value.Progress;
break;
case 4:
Completed?.Invoke(this, EventArgs.Empty);
break;
}

Progress = TaskSteps.Select(x => x.Progress).Sum() / (double)TaskSteps.Length;
}, DispatcherPriority.ApplicationIdle);

[RelayCommand]
private void CancelTask() {
InstallCancellationTokenSource.Cancel();
}
}

0 comments on commit 9041428

Please sign in to comment.