forge support
This commit is contained in:
parent
cb85b132c3
commit
631f5c9126
@ -17,16 +17,16 @@ public record Config
|
|||||||
public int max_memory { get; set; } = 4096;
|
public int max_memory { get; set; } = 4096;
|
||||||
public string minecraft_dir { get; set; } = ".";
|
public string minecraft_dir { get; set; } = ".";
|
||||||
public bool download_java { get; set; } = true;
|
public bool download_java { get; set; } = true;
|
||||||
|
public bool redirect_game_output { get; set; } = false;
|
||||||
public string? last_launched_version { get; set; }
|
public string? last_launched_version { get; set; }
|
||||||
public int max_parallel_downloads { get; set; } = 16;
|
public int max_parallel_downloads { get; set; } = 16;
|
||||||
|
|
||||||
|
|
||||||
public VersionCatalogProps[] version_catalogs { get; set; } =
|
public VersionCatalogProps[] version_catalogs { get; set; } =
|
||||||
[
|
[
|
||||||
new() { Name = "Mojang", Url = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" }
|
new() { Name = "Mojang", Url = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" }
|
||||||
];
|
];
|
||||||
|
|
||||||
[JsonIgnore] static IOPath _filePath = "config.json";
|
|
||||||
|
[JsonIgnore] private static IOPath _filePath = "config.json";
|
||||||
|
|
||||||
public static Config LoadFromFile()
|
public static Config LoadFromFile()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using Mlaumcherb.Client.Avalonia.классы;
|
|||||||
using Mlaumcherb.Client.Avalonia.сеть;
|
using Mlaumcherb.Client.Avalonia.сеть;
|
||||||
using Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
using Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
using Mlaumcherb.Client.Avalonia.холопы;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using static Mlaumcherb.Client.Avalonia.холопы.PathHelper;
|
using static Mlaumcherb.Client.Avalonia.холопы.PathHelper;
|
||||||
|
|
||||||
namespace Mlaumcherb.Client.Avalonia;
|
namespace Mlaumcherb.Client.Avalonia;
|
||||||
@ -62,8 +63,31 @@ public class GameVersion
|
|||||||
private GameVersion(GameVersionProps props)
|
private GameVersion(GameVersionProps props)
|
||||||
{
|
{
|
||||||
_props = props;
|
_props = props;
|
||||||
|
|
||||||
string descriptorText = File.ReadAllText(props.LocalDescriptorPath);
|
string descriptorText = File.ReadAllText(props.LocalDescriptorPath);
|
||||||
_descriptor = JsonConvert.DeserializeObject<GameVersionDescriptor>(descriptorText)
|
JObject descriptorRaw = JObject.Parse(descriptorText);
|
||||||
|
|
||||||
|
// Descriptors can inherit from other descriptors.
|
||||||
|
// For example, 1.12.2-forge-14.23.5.2860 inherits from 1.12.2
|
||||||
|
if (descriptorRaw.TryGetValue("inheritsFrom", out var v))
|
||||||
|
{
|
||||||
|
string parentDescriptorId = v.Value<string>()
|
||||||
|
?? throw new Exception("inheritsFrom is null");
|
||||||
|
LauncherApp.Logger.LogInfo(Name, $"merging descriptor '{parentDescriptorId}' with '{Name}'");
|
||||||
|
IOPath parentDescriptorPath = GetVersionDescriptorPath(parentDescriptorId);
|
||||||
|
if (!File.Exists(parentDescriptorPath))
|
||||||
|
throw new Exception($"Версия '{Name} требует установить версию '{parentDescriptorId}'");
|
||||||
|
string parentDescriptorText = File.ReadAllText(parentDescriptorPath);
|
||||||
|
JObject parentDescriptorRaw = JObject.Parse(parentDescriptorText);
|
||||||
|
parentDescriptorRaw.Merge(descriptorRaw,
|
||||||
|
new JsonMergeSettings { MergeArrayHandling = MergeArrayHandling.Concat });
|
||||||
|
descriptorRaw = parentDescriptorRaw;
|
||||||
|
// removing dependency
|
||||||
|
descriptorRaw.Remove("inheritsFrom");
|
||||||
|
File.WriteAllText(props.LocalDescriptorPath, descriptorRaw.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
_descriptor = descriptorRaw.ToObject<GameVersionDescriptor>()
|
||||||
?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'");
|
?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'");
|
||||||
_javaArgs = new JavaArguments(_descriptor);
|
_javaArgs = new JavaArguments(_descriptor);
|
||||||
_gameArgs = new GameArguments(_descriptor);
|
_gameArgs = new GameArguments(_descriptor);
|
||||||
@ -190,9 +214,13 @@ public class GameVersion
|
|||||||
var command = Cli.Wrap(JavaExecutableFilePath.ToString())
|
var command = Cli.Wrap(JavaExecutableFilePath.ToString())
|
||||||
.WithWorkingDirectory(WorkingDirectory.ToString())
|
.WithWorkingDirectory(WorkingDirectory.ToString())
|
||||||
.WithArguments(argsList)
|
.WithArguments(argsList)
|
||||||
.WithStandardOutputPipe(PipeTarget.ToDelegate(LogGameOut))
|
|
||||||
.WithStandardErrorPipe(PipeTarget.ToDelegate(LogGameError))
|
|
||||||
.WithValidation(CommandResultValidation.None);
|
.WithValidation(CommandResultValidation.None);
|
||||||
|
if (LauncherApp.Config.redirect_game_output)
|
||||||
|
{
|
||||||
|
command = command
|
||||||
|
.WithStandardOutputPipe(PipeTarget.ToDelegate(LogGameOut))
|
||||||
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(LogGameError));
|
||||||
|
}
|
||||||
LauncherApp.Logger.LogInfo(Name, "launching the game");
|
LauncherApp.Logger.LogInfo(Name, "launching the game");
|
||||||
LauncherApp.Logger.LogDebug(Name, "java: " + command.TargetFilePath);
|
LauncherApp.Logger.LogDebug(Name, "java: " + command.TargetFilePath);
|
||||||
LauncherApp.Logger.LogDebug(Name, "working_dir: " + command.WorkingDirPath);
|
LauncherApp.Logger.LogDebug(Name, "working_dir: " + command.WorkingDirPath);
|
||||||
|
|||||||
@ -15,6 +15,12 @@ public class LauncherApp : Application
|
|||||||
Config = Config.LoadFromFile();
|
Config = Config.LoadFromFile();
|
||||||
Logger.DebugLogEnabled = Config.debug;
|
Logger.DebugLogEnabled = Config.debug;
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
|
||||||
|
// some file required by forge installer
|
||||||
|
if (!File.Exists("launcher_profiles.json"))
|
||||||
|
{
|
||||||
|
File.WriteAllText("launcher_profiles.json", "{}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
|||||||
@ -90,6 +90,9 @@
|
|||||||
<CheckBox IsChecked="{Binding #window.EnableJavaDownload}">
|
<CheckBox IsChecked="{Binding #window.EnableJavaDownload}">
|
||||||
Скачивать джаву
|
Скачивать джаву
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<CheckBox IsChecked="{Binding #window.RedirectGameOutput}">
|
||||||
|
Выводить лог игры в лаунчер
|
||||||
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
<Button Name="LaunchButton" Grid.Row="1"
|
<Button Name="LaunchButton" Grid.Row="1"
|
||||||
|
|||||||
@ -51,6 +51,15 @@ public partial class MainWindow : Window
|
|||||||
set => SetValue(EnableJavaDownloadProperty, value);
|
set => SetValue(EnableJavaDownloadProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static readonly StyledProperty<bool> RedirectGameOutputProperty =
|
||||||
|
AvaloniaProperty.Register<MainWindow, bool>(nameof(RedirectGameOutput),
|
||||||
|
defaultBindingMode: BindingMode.TwoWay, defaultValue: false);
|
||||||
|
public bool RedirectGameOutput
|
||||||
|
{
|
||||||
|
get => GetValue(RedirectGameOutputProperty);
|
||||||
|
set => SetValue(RedirectGameOutputProperty, value);
|
||||||
|
}
|
||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
@ -65,6 +74,7 @@ public partial class MainWindow : Window
|
|||||||
PlayerName = LauncherApp.Config.player_name;
|
PlayerName = LauncherApp.Config.player_name;
|
||||||
MemoryLimit = LauncherApp.Config.max_memory;
|
MemoryLimit = LauncherApp.Config.max_memory;
|
||||||
EnableJavaDownload = LauncherApp.Config.download_java;
|
EnableJavaDownload = LauncherApp.Config.download_java;
|
||||||
|
RedirectGameOutput = LauncherApp.Config.redirect_game_output;
|
||||||
|
|
||||||
InstalledVersionComboBox.SelectedIndex = 0;
|
InstalledVersionComboBox.SelectedIndex = 0;
|
||||||
InstalledVersionComboBox.IsEnabled = false;
|
InstalledVersionComboBox.IsEnabled = false;
|
||||||
@ -119,6 +129,7 @@ public partial class MainWindow : Window
|
|||||||
LauncherApp.Config.player_name = PlayerName;
|
LauncherApp.Config.player_name = PlayerName;
|
||||||
LauncherApp.Config.max_memory = MemoryLimit;
|
LauncherApp.Config.max_memory = MemoryLimit;
|
||||||
LauncherApp.Config.download_java = EnableJavaDownload;
|
LauncherApp.Config.download_java = EnableJavaDownload;
|
||||||
|
LauncherApp.Config.redirect_game_output = RedirectGameOutput;
|
||||||
LauncherApp.Config.SaveToFile();
|
LauncherApp.Config.SaveToFile();
|
||||||
if (selectedVersion == null)
|
if (selectedVersion == null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -21,6 +21,7 @@ public class GameVersionDescriptor
|
|||||||
|
|
||||||
public class Artifact
|
public class Artifact
|
||||||
{
|
{
|
||||||
|
public string? path = null;
|
||||||
[JsonRequired] public string url { get; set; } = "";
|
[JsonRequired] public string url { get; set; } = "";
|
||||||
[JsonRequired] public string sha1 { get; set; } = "";
|
[JsonRequired] public string sha1 { get; set; } = "";
|
||||||
[JsonRequired] public int size { get; set; }
|
[JsonRequired] public int size { get; set; }
|
||||||
|
|||||||
@ -7,18 +7,22 @@ public class Libraries
|
|||||||
{
|
{
|
||||||
private static readonly string[] enabled_features = [];
|
private static readonly string[] enabled_features = [];
|
||||||
|
|
||||||
public record JarLib(string name, IOPath? jarFilePath, Artifact artifact);
|
public record JarLib(string name, IOPath jarFilePath, Artifact artifact);
|
||||||
public record NativeLib(string name, IOPath? jarFilePath, Artifact artifact, Extract? extractionOptions)
|
public record NativeLib(string name, IOPath jarFilePath, Artifact artifact, Extract? extractionOptions)
|
||||||
: JarLib(name, jarFilePath, artifact);
|
: JarLib(name, jarFilePath, artifact);
|
||||||
|
|
||||||
public IReadOnlyCollection<JarLib> Libs { get; }
|
public IReadOnlyCollection<JarLib> Libs { get; }
|
||||||
|
|
||||||
private IOPath? TryGetJarFilePath(Artifact artifact)
|
private IOPath GetJarFilePath(Artifact artifact)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrEmpty(artifact.url))
|
string relativePath;
|
||||||
return null;
|
if (!string.IsNullOrEmpty(artifact.path))
|
||||||
string urlTail = artifact.url.AsSpan().After("://").After('/').ToString();
|
relativePath = artifact.path;
|
||||||
return Path.Concat(PathHelper.GetLibrariesDir(), urlTail);
|
else if (!string.IsNullOrEmpty(artifact.url))
|
||||||
|
relativePath = artifact.url.AsSpan().After("://").After('/').ToString();
|
||||||
|
else throw new ArgumentException("Artifact must have a path or url");
|
||||||
|
|
||||||
|
return Path.Concat(PathHelper.GetLibrariesDir(), relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Libraries(GameVersionDescriptor descriptor)
|
public Libraries(GameVersionDescriptor descriptor)
|
||||||
@ -63,7 +67,7 @@ public class Libraries
|
|||||||
if(!libHashes.Add(artifact.sha1))
|
if(!libHashes.Add(artifact.sha1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
libs.Add(new NativeLib(l.name, TryGetJarFilePath(artifact), artifact, l.extract));
|
libs.Add(new NativeLib(l.name, GetJarFilePath(artifact), artifact, l.extract));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -75,7 +79,7 @@ public class Libraries
|
|||||||
if(!libHashes.Add(artifact.sha1))
|
if(!libHashes.Add(artifact.sha1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
libs.Add(new JarLib(l.name, TryGetJarFilePath(artifact), artifact));
|
libs.Add(new JarLib(l.name, GetJarFilePath(artifact), artifact));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,15 +43,7 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
|
|
||||||
foreach (var l in _libraries.Libs)
|
foreach (var l in _libraries.Libs)
|
||||||
{
|
{
|
||||||
// Forge installer downloads some libraries manually.
|
if (!File.Exists(l.jarFilePath))
|
||||||
// Such libraries have empty url in descriptor.
|
|
||||||
if(l.jarFilePath is null)
|
|
||||||
{
|
|
||||||
LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"library artifact has empty url: '{l.name}'");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(l.jarFilePath.Value))
|
|
||||||
{
|
{
|
||||||
_libsToDownload.Add(l);
|
_libsToDownload.Add(l);
|
||||||
}
|
}
|
||||||
@ -61,7 +53,7 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
}
|
}
|
||||||
else if (checkHashes)
|
else if (checkHashes)
|
||||||
{
|
{
|
||||||
using var fs = File.OpenRead(l.jarFilePath.Value);
|
using var fs = File.OpenRead(l.jarFilePath);
|
||||||
string hash = _hasher.ComputeHash(fs).HashToString();
|
string hash = _hasher.ComputeHash(fs).HashToString();
|
||||||
if(hash != l.artifact.sha1)
|
if(hash != l.artifact.sha1)
|
||||||
_libsToDownload.Add(l);
|
_libsToDownload.Add(l);
|
||||||
@ -91,12 +83,12 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
|
await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
|
||||||
{
|
{
|
||||||
LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading library '{l.name}' to '{l.jarFilePath}'");
|
LauncherApp.Logger.LogDebug(nameof(NetworkHelper), $"downloading library '{l.name}' to '{l.jarFilePath}'");
|
||||||
if(l.jarFilePath is null)
|
if(string.IsNullOrEmpty(l.artifact.url))
|
||||||
throw new Exception("jarFilePath is null");
|
throw new Exception($"library '{l.name}' doesn't have a url to download");
|
||||||
await DownloadFile(l.artifact.url, l.jarFilePath.Value, _ct, pr.AddBytesCount);
|
await DownloadFile(l.artifact.url, l.jarFilePath, _ct, pr.AddBytesCount);
|
||||||
if (l is Libraries.NativeLib n)
|
if (l is Libraries.NativeLib n)
|
||||||
{
|
{
|
||||||
var zipf = File.OpenRead(n.jarFilePath!.Value);
|
var zipf = File.OpenRead(n.jarFilePath);
|
||||||
ZipFile.ExtractToDirectory(zipf, _nativesDir.ToString(), true);
|
ZipFile.ExtractToDirectory(zipf, _nativesDir.ToString(), true);
|
||||||
if (n.extractionOptions?.exclude != null)
|
if (n.extractionOptions?.exclude != null)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user