189 lines
7.6 KiB
C#
189 lines
7.6 KiB
C#
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;
|
||
} |