mlaumcherb/Mlaumcherb.Client.Avalonia/GameVersion.cs

189 lines
7.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Linq;
using System.Security.Cryptography;
using CliWrap;
using DTLib.Extensions;
using Mlaumcherb.Client.Avalonia.зримое;
using Mlaumcherb.Client.Avalonia.классы;
using Mlaumcherb.Client.Avalonia.сеть;
using Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
using Mlaumcherb.Client.Avalonia.холопы;
using static Mlaumcherb.Client.Avalonia.холопы.PathHelper;
namespace Mlaumcherb.Client.Avalonia;
public class GameVersion
{
private readonly InstalledGameVersionProps _props;
public string Id => _props.Id;
public IOPath WorkingDirectory { get; }
private IOPath JavaExecutableFilePath;
private GameVersionDescriptor _descriptor;
private JavaArguments _javaArgs;
private GameArguments _gameArgs;
private Libraries _libraries;
private CancellationTokenSource? _gameCts;
private CommandTask<CommandResult>? _commandTask;
internal GameVersion(InstalledGameVersionProps props, GameVersionDescriptor descriptor)
{
_props = props;
_descriptor = descriptor;
_javaArgs = new JavaArguments(_descriptor);
_gameArgs = new GameArguments(_descriptor);
_libraries = new Libraries(_descriptor);
WorkingDirectory = GetVersionDir(_descriptor.id);
JavaExecutableFilePath = GetJavaExecutablePath(_descriptor.javaVersion.component, LauncherApp.Config.debug);
}
public async Task Download(bool update, Action<NetworkTask> networkTaskCreatedCallback)
{
LauncherApp.Logger.LogInfo(Id, $"started updating version {Id}");
List<INetworkTaskFactory> taskFactories =
[
new AssetsDownloadTaskFactory(_descriptor),
new LibrariesDownloadTaskFactory(_descriptor, _libraries),
];
if (LauncherApp.Config.download_java)
taskFactories.Add(new JavaDownloadTaskFactory(_descriptor));
if (_descriptor.modpack != null)
taskFactories.Add(new ModpackDownloadTaskFactory(_descriptor));
// has to be downloaded last because it is used to check if version is installed
taskFactories.Add(new VersionJarDownloadTaskFactory(_descriptor));
var networkTasks = new List<NetworkTask>();
for (int i = 0; i < taskFactories.Count; i++)
{
var nt = await taskFactories[i].CreateAsync(update);
if (nt != null)
{
networkTasks.Add(nt);
networkTaskCreatedCallback.Invoke(nt);
}
}
foreach (var nt in networkTasks)
{
await nt.StartAsync();
}
// create log4j config file
if (!File.Exists(GetLog4jConfigFilePath()))
{
await using var res = EmbeddedResources.GetResourceStream(
$"Mlaumcherb.Client.Avalonia.встроенное.{GetLog4jConfigFileName()}");
await using var file = File.OpenWrite(GetLog4jConfigFilePath());
await res.CopyToAsync(file);
}
_props.IsInstalled = true;
LauncherApp.Logger.LogInfo(Id, $"finished updating version {Id}");
}
//minecraft player uuid explanation
//https://gist.github.com/CatDany/0e71ca7cd9b42a254e49/
//java uuid generation in c#
//https://stackoverflow.com/questions/18021808/uuid-interop-with-c-sharp-code
public static string GetPlayerUUID(string name)
{
byte[] name_bytes = Encoding.UTF8.GetBytes("OfflinePlayer:" + name);
var md5 = MD5.Create();
byte[] hash = md5.ComputeHash(name_bytes);
hash[6] &= 0x0f;
hash[6] |= 0x30;
hash[8] &= 0x3f;
hash[8] |= 0x80;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
sb.Append(hash[i].ToString("x2"));
sb.Insert(8, '-').Insert(13, '-').Insert(18, '-').Insert(23, '-');
return sb.ToString();
}
public async Task Launch()
{
if (string.IsNullOrWhiteSpace(LauncherApp.Config.player_name))
throw new Exception("invalid player name");
Directory.Create(WorkingDirectory);
string uuid = GetPlayerUUID(LauncherApp.Config.player_name);
Dictionary<string, string> placeholder_values = new() {
{ "resolution_width", "1600" },
{ "resolution_height", "900" },
{ "xms", LauncherApp.Config.max_memory.ToString() },
{ "xmx", LauncherApp.Config.max_memory.ToString() },
{ "auth_player_name", LauncherApp.Config.player_name },
{ "auth_access_token", uuid },
{ "auth_session", $"token:{uuid}:{uuid}" },
{ "auth_xuid", "" },
{ "auth_uuid", uuid },
{ "clientid", "" },
{ "user_properties", "{}" },
{ "user_type", "userType" },
{ "launcher_name", "java-minecraft-launcher" },
{ "launcher_version", "1.6.84-j" },
{ "classpath", _libraries.Libs.Select(l => l.jarFilePath)
.Append(GetVersionJarFilePath(_descriptor.id))
.MergeToString(';') },
{ "assets_index_name", _descriptor.assets },
{ "assets_root", GetAssetsDir().ToString() },
{ "game_assets", GetAssetsDir().ToString() },
{ "game_directory", WorkingDirectory.ToString() },
{ "natives_directory", GetNativeLibrariesDir(_descriptor.id).ToString() },
{ "library_directory", GetLibrariesDir().ToString() },
{ "classpath_separator", ";"},
{ "path", GetLog4jConfigFilePath().ToString() },
{ "version_name", _descriptor.id },
{ "version_type", _descriptor.type },
{ "arch", PlatformHelper.GetArchOld() },
// { "quickPlayMultiplayer", "" },
// { "quickPlayPath", "" },
// { "quickPlayRealms", "" },
// { "quickPlaySingleplayer", "" },
{ "client_jar", GetVersionJarFilePath(_descriptor.id).ToString() },
};
List<string> argsList = new();
argsList.AddRange(_javaArgs.FillPlaceholders(placeholder_values));
argsList.Add(_descriptor.mainClass);
argsList.AddRange(_gameArgs.FillPlaceholders(placeholder_values));
var command = Cli.Wrap(JavaExecutableFilePath.ToString())
.WithWorkingDirectory(WorkingDirectory.ToString())
.WithArguments(argsList)
.WithValidation(CommandResultValidation.None);
if (LauncherApp.Config.redirect_game_output)
{
command = command
.WithStandardOutputPipe(PipeTarget.ToDelegate(LogGameOut))
.WithStandardErrorPipe(PipeTarget.ToDelegate(LogGameError));
}
LauncherApp.Logger.LogInfo(Id, "launching the game");
LauncherApp.Logger.LogDebug(Id, "java: " + command.TargetFilePath);
LauncherApp.Logger.LogDebug(Id, "working_dir: " + command.WorkingDirPath);
LauncherApp.Logger.LogDebug(Id, "arguments: \n\t" + argsList.MergeToString("\n\t"));
_gameCts = new();
_commandTask = command.ExecuteAsync(_gameCts.Token);
var result = await _commandTask;
LauncherApp.Logger.LogInfo(Id, $"game exited with code {result.ExitCode}");
}
private void LogGameOut(string line)
{
LauncherApp.Logger.LogInfo(Id, line);
}
private void LogGameError(string line)
{
LauncherApp.Logger.LogWarn(Id, line);
}
public void Close()
{
_gameCts?.Cancel();
}
public override string ToString() => Id;
}