diff --git a/PCL.Neo.Core/Models/Minecraft/Game/Data/GameEntity.cs b/PCL.Neo.Core/Models/Minecraft/Game/Data/GameEntity.cs
index fd4163ce..315602a0 100644
--- a/PCL.Neo.Core/Models/Minecraft/Game/Data/GameEntity.cs
+++ b/PCL.Neo.Core/Models/Minecraft/Game/Data/GameEntity.cs
@@ -4,16 +4,20 @@ namespace PCL.Neo.Core.Models.Minecraft.Game.Data;
public record GameEntityInfo
{
-#nullable disable
///
/// The Game Version information.
///
- public GameVersion GameVersion { get; set; }
+ public GameVersionNum? GameVersion { get; init; }
+
+ ///
+ /// String typed game version. eg: 25w19a、1.21.5-rc2、25w14craftmine.
+ ///
+ public required string GameVersionString { get; init; }
///
/// Game Name that is used to display in the UI.
///
- public string Name { get; set; }
+ public required string Name { get; set; }
///
/// Game Description that is used to display in the UI.
@@ -23,12 +27,12 @@ public record GameEntityInfo
///
/// Game Folder Path.
///
- public string GamePath { get; set; }
+ public required string GamePath { get; set; }
///
/// Game Root Path.
///
- public string RootPath { get; set; }
+ public required string RootPath { get; set; }
///
/// Game Icon that is used to display in the UI.
@@ -38,21 +42,21 @@ public record GameEntityInfo
///
/// The origin Game Json Content. Type is .
///
- public string JsonOrigContent { get; set; }
+ public required string JsonOrigContent { get; set; }
///
/// The Parsed Game Json Content. Type is .
///
- public MetadataFile JsonContent { get; set; }
+ public required MetadataFile JsonContent { get; set; }
///
/// Demonstrate the Game Version Type.
- /// Content is .
+ /// Content is .
///
- public VersionType Type { get; set; }
+ public VersionCardType Type { get; set; }
///
- /// If is .Modable, Loader will have value that is used to display in the UI.
+ /// If is .Moddable, Loader will have value that is used to display in the UI.
///
public ModLoader Loader { get; set; }
@@ -63,9 +67,9 @@ public record GameEntityInfo
///
/// Demonstrate is the version has been loader (runed).
///
- public bool IsLoadded { get; set; } = false;
+ public bool IsLoaded { get; set; } = false;
- private bool? _isIndie { get; set; }
+ private bool? _isIndie;
public bool IsIndie
{
@@ -86,8 +90,7 @@ public bool IsIndie
///
/// THe Game Jar File Path.
///
- public string JarPath { get; set; }
-#nullable enable
+ public required string JarPath { get; set; }
}
public class GameEntity
diff --git a/PCL.Neo.Core/Models/Minecraft/Game/Data/VersionInfo.cs b/PCL.Neo.Core/Models/Minecraft/Game/Data/VersionInfo.cs
index aac376ac..8ecbe01b 100644
--- a/PCL.Neo.Core/Models/Minecraft/Game/Data/VersionInfo.cs
+++ b/PCL.Neo.Core/Models/Minecraft/Game/Data/VersionInfo.cs
@@ -17,21 +17,34 @@ public enum Icons : byte
NeoForge = 12
}
- public enum VersionType : byte
+ public enum VersionCardType : byte
{
Auto = 0,
Hide = 1,
- Modable = 2,
+ Moddable = 2,
Normal = 3,
Unusual = 4,
- FoolDay = 5
+ FoolsDay = 5,
+ Error = 6,
}
- public record GameVersion
+ public record GameVersionNum(byte Sub, byte? Fix = null) : IComparable
{
- public byte Major { get; set; } = 1;
- public byte Sub { get; set; }
- public byte Fix { get; set; }
+ private readonly (byte Major, byte Sub, int Fix) _version = (1, Sub, Fix ?? 0);
+
+ public byte Major => _version.Major;
+ public byte Sub => _version.Sub;
+ public byte? Fix => _version.Fix > 0 ? (byte)_version.Fix : null;
+
+ public int CompareTo(GameVersionNum? other)
+ {
+ return other == null ? 1 : (Major, Sub, Fix ?? 0).CompareTo((other.Major, other.Sub, other.Fix ?? 0));
+ }
+
+ public override string ToString()
+ {
+ return Fix.HasValue ? $"{Major}.{Sub}.{Fix}" : $"{Major}.{Sub}";
+ }
}
public enum ModLoader : byte
@@ -44,4 +57,18 @@ public enum ModLoader : byte
Rift = 5,
Quilt = 6
}
+
+ public enum McVersionState
+ {
+ Error,
+ Vanilla,
+ Snapshot,
+ FoolsDay,
+ OptiFine,
+ Legacy,
+ Forge,
+ NeoForge,
+ LiteLoader,
+ Fabric,
+ }
}
diff --git a/PCL.Neo.Core/Models/Minecraft/Java/IJavaManager.cs b/PCL.Neo.Core/Models/Minecraft/Java/IJavaManager.cs
index 11eed3ae..58a23cd6 100644
--- a/PCL.Neo.Core/Models/Minecraft/Java/IJavaManager.cs
+++ b/PCL.Neo.Core/Models/Minecraft/Java/IJavaManager.cs
@@ -4,6 +4,6 @@ public interface IJavaManager
{
List JavaList { get; }
Task JavaListInit();
- Task ManualAdd(string javaDir);
+ Task<(JavaRuntime?, bool UpdateCurrent)> ManualAdd(string javaDir);
Task Refresh();
}
diff --git a/PCL.Neo.Core/Models/Minecraft/Java/JavaManager.cs b/PCL.Neo.Core/Models/Minecraft/Java/JavaManager.cs
index 317a1f31..d125bd19 100644
--- a/PCL.Neo.Core/Models/Minecraft/Java/JavaManager.cs
+++ b/PCL.Neo.Core/Models/Minecraft/Java/JavaManager.cs
@@ -56,14 +56,14 @@ public async Task JavaListInit()
}
}
- public async Task ManualAdd(string javaDir)
+ public async Task<(JavaRuntime?, bool UpdateCurrent)> ManualAdd(string javaDir)
{
- if (!IsInitialized) return;
+ if (!IsInitialized) return (null, false);
if (JavaList.FirstOrDefault(runtime => runtime.DirectoryPath == javaDir) is { } existingRuntime)
{
Console.WriteLine("选择的 Java 在列表中已存在,将其标记为手动导入。");
existingRuntime.IsUserImport = true;
- return;
+ return (existingRuntime, true);
}
var entity = await JavaRuntime.CreateJavaEntityAsync(javaDir, true);
@@ -71,8 +71,10 @@ public async Task ManualAdd(string javaDir)
{
JavaList.Add(entity);
Console.WriteLine("已成功添加!");
+ return (entity, false);
}
- else Console.WriteLine("添加的 Java 文件无法运行!");
+ Console.WriteLine("添加的 Java 文件无法运行!");
+ return (null, false);
}
public async Task Refresh()
@@ -93,7 +95,7 @@ public async Task Refresh()
else
Console.WriteLine($"[Java] 用户导入的 Java 已不可用,已自动剔除:{oldRuntime.DirectoryPath}");
JavaList = newEntities;
- Console.WriteLine("[Java] 刷新 Java 完成");
+ Console.WriteLine($"[Java] 刷新 Java 完成,现在共有 {JavaList.Count} 个Java");
if (JavaList.Count == 0)
{
// TODO)) 提示用户未找到已安装的 java,是否自动下载合适版本,然后再下载
diff --git a/PCL.Neo.Core/PCL.Neo.Core.csproj b/PCL.Neo.Core/PCL.Neo.Core.csproj
index a99d4376..2e2b8240 100644
--- a/PCL.Neo.Core/PCL.Neo.Core.csproj
+++ b/PCL.Neo.Core/PCL.Neo.Core.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/PCL.Neo/App.axaml.cs b/PCL.Neo/App.axaml.cs
index fc5add67..a164d431 100644
--- a/PCL.Neo/App.axaml.cs
+++ b/PCL.Neo/App.axaml.cs
@@ -1,21 +1,19 @@
using System.Linq;
using Avalonia;
-using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using PCL.Neo.Services;
-using Avalonia.Platform.Storage;
-using PCL.Neo.Helpers;
-using PCL.Neo.Utils;
+using PCL.Neo.Core.Models.Minecraft.Java;
using PCL.Neo.ViewModels;
using PCL.Neo.ViewModels.Download;
using PCL.Neo.ViewModels.Home;
using PCL.Neo.Views;
using System;
using System.Threading.Tasks;
+using PCL.Neo.ViewModels.Setup;
namespace PCL.Neo
{
@@ -39,8 +37,12 @@ public override void Initialize()
.AddTransient()
.AddTransient()
+ .AddTransient()
+ .AddTransient()
+
.AddSingleton(s => new NavigationService(s))
.AddSingleton()
+ .AddSingleton()
.BuildServiceProvider();
public override void OnFrameworkInitializationCompleted()
@@ -48,6 +50,7 @@ public override void OnFrameworkInitializationCompleted()
Ioc.Default.ConfigureServices(ConfigureServices());
var vm = Ioc.Default.GetRequiredService();
+ Task.Run(Ioc.Default.GetRequiredService().JavaListInit);
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
diff --git a/PCL.Neo/PCL.Neo.csproj b/PCL.Neo/PCL.Neo.csproj
index 962f4588..1c5bf909 100644
--- a/PCL.Neo/PCL.Neo.csproj
+++ b/PCL.Neo/PCL.Neo.csproj
@@ -38,7 +38,6 @@
-
@@ -68,10 +67,11 @@
-
-
-
+
+
+
+
diff --git a/PCL.Neo/ViewModels/MainWindowViewModel.cs b/PCL.Neo/ViewModels/MainWindowViewModel.cs
index 0bb8ad43..2b8a5fd4 100644
--- a/PCL.Neo/ViewModels/MainWindowViewModel.cs
+++ b/PCL.Neo/ViewModels/MainWindowViewModel.cs
@@ -70,6 +70,12 @@ public void NavigateToDownload()
this.NavigationService.Goto();
}
+ [RelayCommand]
+ public void NavigateToSetup()
+ {
+ this.NavigationService.Goto();
+ }
+
public void Close()
{
_window?.Close();
diff --git a/PCL.Neo/ViewModels/Setup/SetupLaunchViewModel.cs b/PCL.Neo/ViewModels/Setup/SetupLaunchViewModel.cs
new file mode 100644
index 00000000..cbb2eb07
--- /dev/null
+++ b/PCL.Neo/ViewModels/Setup/SetupLaunchViewModel.cs
@@ -0,0 +1,59 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using PCL.Neo.Core.Models.Minecraft.Java;
+using PCL.Neo.Services;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace PCL.Neo.ViewModels.Setup;
+
+public record JavaUiInfo(JavaRuntime Runtime)
+{
+ public string Identifier =>
+ $"{(Runtime.IsJre ? "JRE" : "JDK")} {Runtime.SlugVersion} ({Runtime.Version}) {Runtime.Architecture} {Runtime.Implementor}";
+
+ public string Path => Runtime.DirectoryPath;
+}
+
+[SubViewModelOf(typeof(SetupViewModel))]
+public partial class SetupLaunchViewModel : ViewModelBase
+{
+ private readonly IJavaManager _javaManager;
+ private readonly StorageService _storageService;
+ [ObservableProperty] private ObservableCollection _javaInfoList = [];
+
+ private void DoUiRefresh()
+ {
+ if (JavaInfoList.Count != 0) JavaInfoList.Clear();
+ foreach (JavaRuntime runtime in _javaManager.JavaList)
+ JavaInfoList.Add(new JavaUiInfo(runtime));
+ }
+
+ public SetupLaunchViewModel(IJavaManager javaManager, StorageService storageService)
+ {
+ _javaManager = javaManager;
+ _storageService = storageService;
+ DoUiRefresh();
+ }
+
+ [RelayCommand]
+ private async Task RefreshJava()
+ {
+ JavaInfoList.Clear();
+ await _javaManager.Refresh();
+ DoUiRefresh();
+ }
+
+ [RelayCommand]
+ private async Task ManualAdd()
+ {
+ string? javaPath = await _storageService.SelectFile("选择要添加的Java");
+ if (javaPath == null) return;
+ var dirPath = Path.GetDirectoryName(javaPath);
+ if (dirPath == null) return;
+ (JavaRuntime? resultRuntime, bool updateCurrent) = await _javaManager.ManualAdd(dirPath);
+ if (resultRuntime == null || updateCurrent) return;
+ JavaInfoList.Add(new JavaUiInfo(resultRuntime));
+ }
+}
\ No newline at end of file
diff --git a/PCL.Neo/ViewModels/SetupViewModel.cs b/PCL.Neo/ViewModels/SetupViewModel.cs
new file mode 100644
index 00000000..0f71d8ac
--- /dev/null
+++ b/PCL.Neo/ViewModels/SetupViewModel.cs
@@ -0,0 +1,9 @@
+using PCL.Neo.ViewModels.Setup;
+
+namespace PCL.Neo.ViewModels;
+
+[DefaultSubViewModel(typeof(SetupLaunchViewModel))]
+public class SetupViewModel : ViewModelBase
+{
+
+}
\ No newline at end of file
diff --git a/PCL.Neo/Views/MainWindow.axaml b/PCL.Neo/Views/MainWindow.axaml
index cc47233e..3cb747e9 100644
--- a/PCL.Neo/Views/MainWindow.axaml
+++ b/PCL.Neo/Views/MainWindow.axaml
@@ -182,7 +182,7 @@
Name="BtnTitleSelect2"
Padding="2,0"
Tag="2"
- Text="{DynamicResource LangTitleLink}" />
+ Text="{DynamicResource LangTitleLink}"/>
+ Text="{DynamicResource LangTitleSetup}"
+ Command="{Binding NavigateToSetup}"/>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PCL.Neo/Views/Setup/SetupLaunchView.axaml.cs b/PCL.Neo/Views/Setup/SetupLaunchView.axaml.cs
new file mode 100644
index 00000000..7ffd1b6a
--- /dev/null
+++ b/PCL.Neo/Views/Setup/SetupLaunchView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PCL.Neo.Views.Setup
+{
+ public partial class SetupLaunchView : UserControl
+ {
+ public SetupLaunchView()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PCL.Neo/Views/Setup/SetupOtherView.axaml b/PCL.Neo/Views/Setup/SetupOtherView.axaml
new file mode 100644
index 00000000..25336113
--- /dev/null
+++ b/PCL.Neo/Views/Setup/SetupOtherView.axaml
@@ -0,0 +1,9 @@
+
+ Welcome to Avalonia!
+
+
diff --git a/PCL.Neo/Views/Setup/SetupOtherView.axaml.cs b/PCL.Neo/Views/Setup/SetupOtherView.axaml.cs
new file mode 100644
index 00000000..4249e3d5
--- /dev/null
+++ b/PCL.Neo/Views/Setup/SetupOtherView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PCL.Neo.Views.Setup
+{
+ public partial class SetupOtherView : UserControl
+ {
+ public SetupOtherView()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PCL.Neo/Views/Setup/SetupStyleView.axaml b/PCL.Neo/Views/Setup/SetupStyleView.axaml
new file mode 100644
index 00000000..bd2ad1af
--- /dev/null
+++ b/PCL.Neo/Views/Setup/SetupStyleView.axaml
@@ -0,0 +1,9 @@
+
+ Welcome to Avalonia!
+
+
diff --git a/PCL.Neo/Views/Setup/SetupStyleView.axaml.cs b/PCL.Neo/Views/Setup/SetupStyleView.axaml.cs
new file mode 100644
index 00000000..8803f67e
--- /dev/null
+++ b/PCL.Neo/Views/Setup/SetupStyleView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PCL.Neo.Views.Setup
+{
+ public partial class SetupStyleView : UserControl
+ {
+ public SetupStyleView()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file
diff --git a/PCL.Neo/Views/SetupView.axaml b/PCL.Neo/Views/SetupView.axaml
new file mode 100644
index 00000000..fba3fb7a
--- /dev/null
+++ b/PCL.Neo/Views/SetupView.axaml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/PCL.Neo/Views/SetupView.axaml.cs b/PCL.Neo/Views/SetupView.axaml.cs
new file mode 100644
index 00000000..90351bc5
--- /dev/null
+++ b/PCL.Neo/Views/SetupView.axaml.cs
@@ -0,0 +1,14 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PCL.Neo.Views
+{
+ public partial class SetupView : UserControl
+ {
+ public SetupView()
+ {
+ InitializeComponent();
+ }
+ }
+}
\ No newline at end of file