now it works
This commit is contained in:
66
Mlaumcherb.Client.Avalonia/Config.cs
Normal file
66
Mlaumcherb.Client.Avalonia/Config.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia;
|
||||||
|
|
||||||
|
public record Config
|
||||||
|
{
|
||||||
|
public bool debug { get; set; } =
|
||||||
|
#if DEBUG
|
||||||
|
true;
|
||||||
|
#else
|
||||||
|
false;
|
||||||
|
#endif
|
||||||
|
public string player_name { get; set; } = "";
|
||||||
|
public int max_memory { get; set; } = 4096;
|
||||||
|
public string minecraft_dir { get; set; } = ".";
|
||||||
|
public bool download_java { get; set; } = true;
|
||||||
|
public string? last_launched_version { get; set; }
|
||||||
|
public int max_parallel_downloads { get; set; } = 16;
|
||||||
|
|
||||||
|
[JsonIgnore] static IOPath _filePath = "config.json";
|
||||||
|
|
||||||
|
public static Config LoadFromFile()
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogInfo(nameof(Config), $"loading config from file '{_filePath}'");
|
||||||
|
if(!File.Exists(_filePath))
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogInfo(nameof(Config), "file doesn't exist");
|
||||||
|
return new Config();
|
||||||
|
}
|
||||||
|
|
||||||
|
string text = File.ReadAllText(_filePath);
|
||||||
|
string errorMessage = "config is empty";
|
||||||
|
Config? config = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config = JsonConvert.DeserializeObject<Config>(text);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
errorMessage = ex.Message;
|
||||||
|
}
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
IOPath _brokenPath = _filePath + ".broken";
|
||||||
|
File.Move(_filePath, _brokenPath, true);
|
||||||
|
ErrorHelper.ShowMessageBox(nameof(Config),
|
||||||
|
$"Can't reed config file '{_filePath}'.\n" +
|
||||||
|
$"New config file has been created. Old is renamed to '{_brokenPath}'.\n\n" +
|
||||||
|
$"Config parser error: {errorMessage}");
|
||||||
|
config = new Config();
|
||||||
|
config.SaveToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
LauncherApp.Logger.LogDebug(nameof(Config), $"config has been loaded: {config}");
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveToFile()
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogInfo(nameof(Config), $"saving config to file '{_filePath}'");
|
||||||
|
var text = JsonConvert.SerializeObject(this, Formatting.Indented);
|
||||||
|
File.WriteAllText(_filePath, text);
|
||||||
|
LauncherApp.Logger.LogDebug(nameof(Config), $"config has been saved: {text}");
|
||||||
|
}
|
||||||
|
}
|
||||||
229
Mlaumcherb.Client.Avalonia/GameVersion.cs
Normal file
229
Mlaumcherb.Client.Avalonia/GameVersion.cs
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
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 GameVersionProps _props;
|
||||||
|
public string Name => _props.Name;
|
||||||
|
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;
|
||||||
|
|
||||||
|
public static async Task<List<GameVersionProps>> GetAllVersionsAsync()
|
||||||
|
{
|
||||||
|
var propsSet = new HashSet<GameVersionProps>();
|
||||||
|
|
||||||
|
// local descriptors
|
||||||
|
Directory.Create(GetVersionsDir());
|
||||||
|
foreach (IOPath subdir in Directory.GetDirectories(GetVersionsDir()))
|
||||||
|
{
|
||||||
|
string name = subdir.LastName().ToString();
|
||||||
|
var d = new GameVersionProps(name, null);
|
||||||
|
if(!File.Exists(d.LocalDescriptorPath))
|
||||||
|
throw new Exception("Can't find version descriptor file in directory '{subdir}'. Rename it as directory name.");
|
||||||
|
propsSet.Add(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remote non-duplicating versions
|
||||||
|
foreach (var removeVersion in await Сеть.GetDownloadableVersions())
|
||||||
|
{
|
||||||
|
propsSet.Add(removeVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reverse sort
|
||||||
|
var propsList = propsSet.ToList();
|
||||||
|
propsList.Sort((a, b) => b.CompareTo(a));
|
||||||
|
return propsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<GameVersion> CreateFromPropsAsync(GameVersionProps props)
|
||||||
|
{
|
||||||
|
if (!File.Exists(props.LocalDescriptorPath))
|
||||||
|
{
|
||||||
|
if (props.RemoteDescriptorUrl is null)
|
||||||
|
throw new NullReferenceException("can't download game version descriptor '"
|
||||||
|
+ props.Name + "', because RemoteDescriptorUrl is null");
|
||||||
|
await Сеть.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GameVersion(props);
|
||||||
|
}
|
||||||
|
|
||||||
|
private GameVersion(GameVersionProps props)
|
||||||
|
{
|
||||||
|
_props = props;
|
||||||
|
string descriptorText = File.ReadAllText(props.LocalDescriptorPath);
|
||||||
|
_descriptor = JsonConvert.DeserializeObject<GameVersionDescriptor>(descriptorText)
|
||||||
|
?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'");
|
||||||
|
_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 UpdateFiles(bool checkHashes, Action<NetworkTask> networkTaskCreatedCallback)
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogInfo(Name, $"started updating version {Name}");
|
||||||
|
|
||||||
|
List<INetworkTaskFactory> taskFactories =
|
||||||
|
[
|
||||||
|
new AssetsDownloadTaskFactory(_descriptor),
|
||||||
|
new LibrariesDownloadTaskFactory(_descriptor, _libraries),
|
||||||
|
new VersionFileDownloadTaskFactory(_descriptor),
|
||||||
|
];
|
||||||
|
if(LauncherApp.Config.download_java)
|
||||||
|
{
|
||||||
|
taskFactories.Add(new JavaDownloadTaskFactory(_descriptor));
|
||||||
|
}
|
||||||
|
//TODO: modpack
|
||||||
|
/*if (modpack != null)
|
||||||
|
{
|
||||||
|
taskFactories.Add(new ModpackDownloadTaskFactory(modpack));
|
||||||
|
}*/
|
||||||
|
|
||||||
|
var networkTasks = new List<NetworkTask>();
|
||||||
|
for (int i = 0; i < taskFactories.Count; i++)
|
||||||
|
{
|
||||||
|
var nt = await taskFactories[i].CreateAsync(checkHashes);
|
||||||
|
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.IsDownloaded = true;
|
||||||
|
LauncherApp.Logger.LogInfo(Name, $"finished updating version {Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
//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", "2048" },
|
||||||
|
{ "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() },
|
||||||
|
{ "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)
|
||||||
|
.WithStandardOutputPipe(PipeTarget.ToDelegate(LogGameOut))
|
||||||
|
.WithStandardErrorPipe(PipeTarget.ToDelegate(LogGameError))
|
||||||
|
.WithValidation(CommandResultValidation.None);
|
||||||
|
LauncherApp.Logger.LogInfo(Name, "launching the game");
|
||||||
|
LauncherApp.Logger.LogDebug(Name, "java: " + command.TargetFilePath);
|
||||||
|
LauncherApp.Logger.LogDebug(Name, "working_dir: " + command.WorkingDirPath);
|
||||||
|
LauncherApp.Logger.LogDebug(Name, "arguments: \n\t" + argsList.MergeToString("\n\t"));
|
||||||
|
_gameCts = new();
|
||||||
|
_commandTask = command.ExecuteAsync(_gameCts.Token);
|
||||||
|
var result = await _commandTask;
|
||||||
|
LauncherApp.Logger.LogInfo(Name, $"game exited with code {result.ExitCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogGameOut(string line)
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogInfo(Name, line);
|
||||||
|
}
|
||||||
|
private void LogGameError(string line)
|
||||||
|
{
|
||||||
|
LauncherApp.Logger.LogWarn(Name, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
_gameCts?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override string ToString() => Name;
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Млаумчерб.Клиент;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia;
|
||||||
|
|
||||||
public class LauncherLogger : ILogger
|
public class LauncherLogger : ILogger
|
||||||
{
|
{
|
||||||
@@ -18,10 +20,6 @@ public class LauncherLogger : ILogger
|
|||||||
#endif
|
#endif
|
||||||
];
|
];
|
||||||
_compositeLogger = new CompositeLogger(loggers);
|
_compositeLogger = new CompositeLogger(loggers);
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
DebugLogEnabled = true;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record LogMessage(string context, LogSeverity severity, object message, ILogFormat format);
|
public record LogMessage(string context, LogSeverity severity, object message, ILogFormat format);
|
||||||
@@ -30,21 +30,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AvaloniaResource Include="капитал\**"/>
|
<AvaloniaResource Include="капитал\**"/>
|
||||||
|
<EmbeddedResource Include="встроенное\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Update="зримое\VersionItemView.axaml.cs">
|
|
||||||
<DependentUpon>VersionItemView.axaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="зримое\Окне.axaml.cs">
|
|
||||||
<DependentUpon>Окне.axaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="зримое\Приложение.axaml.cs">
|
|
||||||
<DependentUpon>Приложение.axaml</DependentUpon>
|
|
||||||
<SubType>Code</SubType>
|
|
||||||
</Compile>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -14,11 +14,11 @@ global using Directory = DTLib.Filesystem.Directory;
|
|||||||
global using Path = DTLib.Filesystem.Path;
|
global using Path = DTLib.Filesystem.Path;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент;
|
namespace Mlaumcherb.Client.Avalonia;
|
||||||
|
|
||||||
public class Главне
|
public class Program
|
||||||
{
|
{
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
@@ -31,13 +31,13 @@ public class Главне
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogError(nameof(Главне), ex);
|
LauncherApp.Logger.LogError(nameof(Program), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avalonia configuration, don't remove; also used by visual designer.
|
// Avalonia configuration, don't remove; also used by visual designer.
|
||||||
public static AppBuilder BuildAvaloniaApp()
|
public static AppBuilder BuildAvaloniaApp()
|
||||||
=> AppBuilder.Configure<Приложение>()
|
=> AppBuilder.Configure<LauncherApp>()
|
||||||
.UsePlatformDetect()
|
.UsePlatformDetect()
|
||||||
.LogToTrace();
|
.LogToTrace();
|
||||||
}
|
}
|
||||||
28
Mlaumcherb.Client.Avalonia/встроенное/log4j.xml
Normal file
28
Mlaumcherb.Client.Avalonia/встроенное/log4j.xml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<Configuration status="WARN" packages="com.mojang.util">
|
||||||
|
<Appenders>
|
||||||
|
<Console name="SysOut" target="SYSTEM_OUT">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n"/>
|
||||||
|
</Console>
|
||||||
|
<Queue name="ServerGuiConsole">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg%n"/>
|
||||||
|
</Queue>
|
||||||
|
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||||
|
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg%n"/>
|
||||||
|
<Policies>
|
||||||
|
<TimeBasedTriggeringPolicy/>
|
||||||
|
<OnStartupTriggeringPolicy/>
|
||||||
|
</Policies>
|
||||||
|
</RollingRandomAccessFile>
|
||||||
|
</Appenders>
|
||||||
|
<Loggers>
|
||||||
|
<Root level="info">
|
||||||
|
<filters>
|
||||||
|
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
<RegexFilter regex="(?s).*\$\{[^}]*\}.*" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||||
|
</filters>
|
||||||
|
<AppenderRef ref="SysOut"/>
|
||||||
|
<AppenderRef ref="File"/>
|
||||||
|
<AppenderRef ref="ServerGuiConsole"/>
|
||||||
|
</Root>
|
||||||
|
</Loggers>
|
||||||
|
</Configuration>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<UserControl xmlns="https://github.com/avaloniaui"
|
<UserControl xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="Млаумчерб.Клиент.зримое.NetworkTaskView"
|
x:Class="Mlaumcherb.Client.Avalonia.зримое.NetworkTaskView"
|
||||||
Padding="4" MinHeight="90" MinWidth="200"
|
Padding="4" MinHeight="90" MinWidth="200"
|
||||||
VerticalAlignment="Top"
|
VerticalAlignment="Top"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Млаумчерб.Клиент.сеть;
|
using Mlaumcherb.Client.Avalonia.сеть;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.зримое;
|
namespace Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
public partial class NetworkTaskView : UserControl
|
public partial class NetworkTaskView : UserControl
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<Application xmlns="https://github.com/avaloniaui"
|
<Application xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
x:Class="Млаумчерб.Клиент.зримое.Приложение"
|
x:Class="Mlaumcherb.Client.Avalonia.зримое.LauncherApp"
|
||||||
RequestedThemeVariant="Dark">
|
RequestedThemeVariant="Dark">
|
||||||
<Application.Styles>
|
<Application.Styles>
|
||||||
<SimpleTheme />
|
<SimpleTheme />
|
||||||
29
Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs
Normal file
29
Mlaumcherb.Client.Avalonia/зримое/LauncherApp.axaml.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls.ApplicationLifetimes;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
|
public class LauncherApp : Application
|
||||||
|
{
|
||||||
|
public static LauncherLogger Logger = new();
|
||||||
|
public static Config Config = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
Logger.LogInfo(nameof(LauncherApp), "приложение запущено");
|
||||||
|
Config = Config.LoadFromFile();
|
||||||
|
Logger.DebugLogEnabled = Config.debug;
|
||||||
|
AvaloniaXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFrameworkInitializationCompleted()
|
||||||
|
{
|
||||||
|
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
||||||
|
{
|
||||||
|
desktop.MainWindow = new MainWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFrameworkInitializationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Млаумчерб.Клиент.зримое.LogMessageView"
|
x:Class="Mlaumcherb.Client.Avalonia.зримое.LogMessageView"
|
||||||
FontSize="14">
|
FontSize="14">
|
||||||
<SelectableTextBlock Name="ContentTextBox"
|
<SelectableTextBlock Name="ContentTextBox"
|
||||||
FontSize="{Binding $parent.FontSize}"
|
FontSize="{Binding $parent.FontSize}"
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DTLib;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.зримое;
|
namespace Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
public partial class LogMessageView : UserControl
|
public partial class LogMessageView : UserControl
|
||||||
{
|
{
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
<Window xmlns="https://github.com/avaloniaui"
|
<Window xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:gif="clr-namespace:Avalonia.Labs.Gif;assembly=Avalonia.Labs.Gif"
|
xmlns:gif="clr-namespace:Avalonia.Labs.Gif;assembly=Avalonia.Labs.Gif"
|
||||||
xmlns:local="clr-namespace:Млаумчерб"
|
xmlns:local="clr-namespace:Mlaumcherb.Client.Avalonia"
|
||||||
x:Class="Млаумчерб.Клиент.зримое.Окне"
|
x:Class="Mlaumcherb.Client.Avalonia.зримое.MainWindow"
|
||||||
Name="window"
|
Name="window"
|
||||||
Title="млаумчерб"
|
Title="млаумчерб"
|
||||||
Icon="avares://млаумчерб/капитал/кубе.ico"
|
Icon="avares://млаумчерб/капитал/кубе.ico"
|
||||||
FontFamily="{StaticResource MonospaceFont}" FontSize="18"
|
FontFamily="{StaticResource MonospaceFont}" FontSize="18"
|
||||||
MinWidth="800" MinHeight="500"
|
MinWidth="1200" MinHeight="700"
|
||||||
Width="800" Height="500"
|
Width="800" Height="500"
|
||||||
WindowStartupLocation="CenterScreen">
|
WindowStartupLocation="CenterScreen">
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
<TextBlock>Ник:</TextBlock>
|
<TextBlock>Ник:</TextBlock>
|
||||||
<TextBox Background="Transparent"
|
<TextBox Background="Transparent"
|
||||||
Text="{Binding #window.Username}"/>
|
Text="{Binding #window.PlayerName}"/>
|
||||||
|
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run>Выделенная память:</Run>
|
<Run>Выделенная память:</Run>
|
||||||
@@ -45,9 +45,6 @@
|
|||||||
Value="{Binding #window.MemoryLimit}">
|
Value="{Binding #window.MemoryLimit}">
|
||||||
</Slider>
|
</Slider>
|
||||||
|
|
||||||
<CheckBox IsChecked="{Binding #window.Fullscreen}">
|
|
||||||
Запускать полноэкранное
|
|
||||||
</CheckBox>
|
|
||||||
<CheckBox IsChecked="{Binding #window.CheckGameFiles}">
|
<CheckBox IsChecked="{Binding #window.CheckGameFiles}">
|
||||||
Проверять файлы игры
|
Проверять файлы игры
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
@@ -4,41 +4,33 @@ using Avalonia.Data;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.зримое;
|
namespace Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
public partial class Окне : Window
|
public partial class MainWindow : Window
|
||||||
{
|
{
|
||||||
public static readonly StyledProperty<string> UsernameProperty =
|
public static readonly StyledProperty<string> PlayerNameProperty =
|
||||||
AvaloniaProperty.Register<Окне, string>(nameof(Username),
|
AvaloniaProperty.Register<MainWindow, string>(nameof(PlayerName),
|
||||||
defaultBindingMode: BindingMode.TwoWay);
|
defaultBindingMode: BindingMode.TwoWay);
|
||||||
public string Username
|
public string PlayerName
|
||||||
{
|
{
|
||||||
get => GetValue(UsernameProperty);
|
get => GetValue(PlayerNameProperty);
|
||||||
set => SetValue(UsernameProperty, value);
|
set => SetValue(PlayerNameProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<int> MemoryLimitProperty =
|
public static readonly StyledProperty<int> MemoryLimitProperty =
|
||||||
AvaloniaProperty.Register<Окне, int>(nameof(MemoryLimit),
|
AvaloniaProperty.Register<MainWindow, int>(nameof(MemoryLimit),
|
||||||
defaultBindingMode: BindingMode.TwoWay, defaultValue: 2048);
|
defaultBindingMode: BindingMode.TwoWay, defaultValue: 2048);
|
||||||
public int MemoryLimit
|
public int MemoryLimit
|
||||||
{
|
{
|
||||||
get => GetValue(MemoryLimitProperty);
|
get => GetValue(MemoryLimitProperty);
|
||||||
set => SetValue(MemoryLimitProperty, value);
|
set => SetValue(MemoryLimitProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> FullscreenProperty =
|
|
||||||
AvaloniaProperty.Register<Окне, bool>(nameof(Fullscreen),
|
|
||||||
defaultBindingMode: BindingMode.TwoWay, defaultValue: false);
|
|
||||||
public bool Fullscreen
|
|
||||||
{
|
|
||||||
get => GetValue(FullscreenProperty);
|
|
||||||
set => SetValue(FullscreenProperty, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> CheckGameFilesProperty =
|
public static readonly StyledProperty<bool> CheckGameFilesProperty =
|
||||||
AvaloniaProperty.Register<Окне, bool>(nameof(CheckGameFiles),
|
AvaloniaProperty.Register<MainWindow, bool>(nameof(CheckGameFiles),
|
||||||
defaultBindingMode: BindingMode.TwoWay, defaultValue: false);
|
defaultBindingMode: BindingMode.TwoWay, defaultValue: false);
|
||||||
public bool CheckGameFiles
|
public bool CheckGameFiles
|
||||||
{
|
{
|
||||||
@@ -48,7 +40,7 @@ public partial class Окне : Window
|
|||||||
|
|
||||||
|
|
||||||
public static readonly StyledProperty<bool> EnableJavaDownloadProperty =
|
public static readonly StyledProperty<bool> EnableJavaDownloadProperty =
|
||||||
AvaloniaProperty.Register<Окне, bool>(nameof(EnableJavaDownload),
|
AvaloniaProperty.Register<MainWindow, bool>(nameof(EnableJavaDownload),
|
||||||
defaultBindingMode: BindingMode.TwoWay, defaultValue: true);
|
defaultBindingMode: BindingMode.TwoWay, defaultValue: true);
|
||||||
public bool EnableJavaDownload
|
public bool EnableJavaDownload
|
||||||
{
|
{
|
||||||
@@ -56,7 +48,7 @@ public partial class Окне : Window
|
|||||||
set => SetValue(EnableJavaDownloadProperty, value);
|
set => SetValue(EnableJavaDownloadProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Окне()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
}
|
}
|
||||||
@@ -65,14 +57,12 @@ public partial class Окне : Window
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Приложение.Логгер.OnLogMessage += GuiLogMessage;
|
LauncherApp.Logger.OnLogMessage += GuiLogMessage;
|
||||||
|
|
||||||
Username = Приложение.Настройки.имя_пользователя;
|
PlayerName = LauncherApp.Config.player_name;
|
||||||
MemoryLimit = Приложение.Настройки.выделенная_память_мб;
|
MemoryLimit = LauncherApp.Config.max_memory;
|
||||||
Fullscreen = Приложение.Настройки.запускать_полноэкранное;
|
EnableJavaDownload = LauncherApp.Config.download_java;
|
||||||
EnableJavaDownload = Приложение.Настройки.скачивать_жабу;
|
|
||||||
|
|
||||||
Directory.Create(Пути.GetVersionDescriptorsDir());
|
|
||||||
VersionComboBox.SelectedIndex = 0;
|
VersionComboBox.SelectedIndex = 0;
|
||||||
VersionComboBox.IsEnabled = false;
|
VersionComboBox.IsEnabled = false;
|
||||||
var versions = await GameVersion.GetAllVersionsAsync();
|
var versions = await GameVersion.GetAllVersionsAsync();
|
||||||
@@ -81,8 +71,8 @@ public partial class Окне : Window
|
|||||||
foreach (var p in versions)
|
foreach (var p in versions)
|
||||||
{
|
{
|
||||||
VersionComboBox.Items.Add(new VersionItemView(p));
|
VersionComboBox.Items.Add(new VersionItemView(p));
|
||||||
if (Приложение.Настройки.последняя_запущенная_версия != null &&
|
if (LauncherApp.Config.last_launched_version != null &&
|
||||||
p.Name == Приложение.Настройки.последняя_запущенная_версия)
|
p.Name == LauncherApp.Config.last_launched_version)
|
||||||
VersionComboBox.SelectedIndex = VersionComboBox.Items.Count - 1;
|
VersionComboBox.SelectedIndex = VersionComboBox.Items.Count - 1;
|
||||||
}
|
}
|
||||||
VersionComboBox.IsEnabled = true;
|
VersionComboBox.IsEnabled = true;
|
||||||
@@ -90,7 +80,7 @@ public partial class Окне : Window
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
|
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,12 +105,11 @@ public partial class Окне : Window
|
|||||||
|
|
||||||
var selectedVersionView = (VersionItemView?)VersionComboBox.SelectedItem;
|
var selectedVersionView = (VersionItemView?)VersionComboBox.SelectedItem;
|
||||||
var selectedVersion = selectedVersionView?.Props;
|
var selectedVersion = selectedVersionView?.Props;
|
||||||
Приложение.Настройки.последняя_запущенная_версия = selectedVersion?.Name;
|
LauncherApp.Config.last_launched_version = selectedVersion?.Name;
|
||||||
Приложение.Настройки.имя_пользователя = Username;
|
LauncherApp.Config.player_name = PlayerName;
|
||||||
Приложение.Настройки.выделенная_память_мб = MemoryLimit;
|
LauncherApp.Config.max_memory = MemoryLimit;
|
||||||
Приложение.Настройки.запускать_полноэкранное = Fullscreen;
|
LauncherApp.Config.download_java = EnableJavaDownload;
|
||||||
Приложение.Настройки.скачивать_жабу = EnableJavaDownload;
|
LauncherApp.Config.SaveToFile();
|
||||||
Приложение.Настройки.СохранитьВФайл();
|
|
||||||
if (selectedVersion == null)
|
if (selectedVersion == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -138,10 +127,11 @@ public partial class Окне : Window
|
|||||||
{
|
{
|
||||||
CheckGameFiles = false;
|
CheckGameFiles = false;
|
||||||
});
|
});
|
||||||
|
await v.Launch();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
|
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -161,7 +151,7 @@ public partial class Окне : Window
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
|
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,12 +159,12 @@ public partial class Окне : Window
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Launcher.LaunchFileInfoAsync(new FileInfo(Приложение.Логгер.LogfileName.ToString()))
|
Launcher.LaunchFileInfoAsync(new FileInfo(LauncherApp.Logger.LogfileName.ToString()))
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
|
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +177,7 @@ public partial class Окне : Window
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Ошибки.ПоказатьСообщение(nameof(Окне), ex);
|
ErrorHelper.ShowMessageBox(nameof(MainWindow), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2,8 +2,8 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:local="clr-namespace:Млаумчерб.Клиент"
|
xmlns:local="clr-namespace:Mlaumcherb.Client.Avalonia"
|
||||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||||
x:Class="Млаумчерб.Клиент.зримое.VersionItemView">
|
x:Class="Mlaumcherb.Client.Avalonia.зримое.VersionItemView">
|
||||||
<TextBlock Name="text" Background="Transparent"/>
|
<TextBlock Name="text" Background="Transparent"/>
|
||||||
</UserControl>
|
</UserControl>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.зримое;
|
namespace Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
public partial class VersionItemView : ListBoxItem
|
public partial class VersionItemView : ListBoxItem
|
||||||
{
|
{
|
||||||
@@ -12,7 +12,7 @@ public partial class VersionItemView : ListBoxItem
|
|||||||
|
|
||||||
public VersionItemView()
|
public VersionItemView()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
public VersionItemView(GameVersionProps props)
|
public VersionItemView(GameVersionProps props)
|
||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -1,4 +1,4 @@
|
|||||||
namespace Млаумчерб.Клиент.классы;
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
public class GameVersionCatalog
|
public class GameVersionCatalog
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
// ReSharper disable CollectionNeverUpdated.Global
|
// ReSharper disable CollectionNeverUpdated.Global
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
public class GameVersionDescriptor
|
public class GameVersionDescriptor
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
public class JavaVersionCatalog
|
public class JavaVersionCatalog
|
||||||
{
|
{
|
||||||
@@ -54,7 +55,7 @@ public class JavaVersionCatalog
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw new PlatformNotSupportedException($"Can't download java {version.majorVersion} for your operating system. " +
|
throw new PlatformNotSupportedException($"Can't download java {version.majorVersion} for your operating system. " +
|
||||||
$"Download it manually to directory {Пути.GetJavaRuntimeDir(version.component)}");
|
$"Download it manually to directory {PathHelper.GetJavaRuntimeDir(version.component)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
40
Mlaumcherb.Client.Avalonia/классы/Буржуазия/Rules.cs
Normal file
40
Mlaumcherb.Client.Avalonia/классы/Буржуазия/Rules.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
|
public static class Rules
|
||||||
|
{
|
||||||
|
public static bool Check(ICollection<Rule>? rules, ICollection<string> features)
|
||||||
|
{
|
||||||
|
if(rules is null || rules.Count == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool allowed = false;
|
||||||
|
foreach (var r in rules)
|
||||||
|
{
|
||||||
|
if (r.os != null && !PlatformHelper.CheckOs(r.os))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (r.features == null)
|
||||||
|
allowed = r.action == "allow";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var feature in features)
|
||||||
|
{
|
||||||
|
if (r.features.TryGetValue(feature, out bool is_enabled))
|
||||||
|
{
|
||||||
|
if (is_enabled)
|
||||||
|
{
|
||||||
|
allowed = r.action == "allow";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(allowed)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
|
public class ArgumentsWithPlaceholders
|
||||||
|
{
|
||||||
|
protected List<string> _raw_args = new();
|
||||||
|
|
||||||
|
public IEnumerable<string> FillPlaceholders(Dictionary<string, string> values)
|
||||||
|
{
|
||||||
|
foreach (var _s in _raw_args)
|
||||||
|
{
|
||||||
|
string arg = _s;
|
||||||
|
int begin = arg.IndexOf("${", StringComparison.Ordinal);
|
||||||
|
while(begin != -1)
|
||||||
|
{
|
||||||
|
int keyBegin = begin + 2;
|
||||||
|
int end = arg.IndexOf('}', keyBegin);
|
||||||
|
if (end != -1)
|
||||||
|
{
|
||||||
|
var key = arg.Substring(keyBegin, end - keyBegin);
|
||||||
|
if (!values.TryGetValue(key, out var value))
|
||||||
|
throw new Exception($"can't find value for placeholder '{key}'");
|
||||||
|
arg = arg.Replace("${"+ key + "}", value);
|
||||||
|
}
|
||||||
|
if(end + 1 < arg.Length)
|
||||||
|
begin = arg.IndexOf("${", end + 1, StringComparison.Ordinal);
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
yield return arg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using DTLib.Extensions;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
|
public class GameArguments : ArgumentsWithPlaceholders
|
||||||
|
{
|
||||||
|
private static readonly string[] _initial_arguments =
|
||||||
|
[
|
||||||
|
"--width", "${resolution_width}",
|
||||||
|
"--height", "${resolution_height}",
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] _enabled_features =
|
||||||
|
[
|
||||||
|
];
|
||||||
|
|
||||||
|
public GameArguments(GameVersionDescriptor d)
|
||||||
|
{
|
||||||
|
_raw_args.AddRange(_initial_arguments);
|
||||||
|
|
||||||
|
if (d.minecraftArguments is not null)
|
||||||
|
{
|
||||||
|
_raw_args.AddRange(d.minecraftArguments.SplitToList(' ', quot: '"'));
|
||||||
|
}
|
||||||
|
else if (d.arguments is not null)
|
||||||
|
{
|
||||||
|
foreach (var av in d.arguments.game)
|
||||||
|
{
|
||||||
|
if(Rules.Check(av.rules, _enabled_features))
|
||||||
|
{
|
||||||
|
foreach (var arg in av.value)
|
||||||
|
{
|
||||||
|
if(!_raw_args.Contains(arg))
|
||||||
|
_raw_args.Add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else throw new Exception("no game arguments specified in descriptor");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
namespace Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
public class GameVersionProps : IComparable<GameVersionProps>, IEquatable<GameVersionProps>
|
public class GameVersionProps : IComparable<GameVersionProps>, IEquatable<GameVersionProps>
|
||||||
{
|
{
|
||||||
@@ -19,16 +21,13 @@ public class GameVersionProps : IComparable<GameVersionProps>, IEquatable<GameVe
|
|||||||
}
|
}
|
||||||
public event Action? OnDownloadCompleted;
|
public event Action? OnDownloadCompleted;
|
||||||
|
|
||||||
public GameVersionProps(string name, string? url, IOPath descriptorPath)
|
public GameVersionProps(string name, string? url)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
LocalDescriptorPath = descriptorPath;
|
LocalDescriptorPath = PathHelper.GetVersionDescriptorPath(name);
|
||||||
RemoteDescriptorUrl = url;
|
RemoteDescriptorUrl = url;
|
||||||
IsDownloaded = File.Exists(Пути.GetVersionJarFilePath(name));
|
IsDownloaded = File.Exists(PathHelper.GetVersionJarFilePath(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameVersionProps(string name, string? url) :
|
|
||||||
this(name, url, Пути.GetVersionDescriptorPath(name)) { }
|
|
||||||
|
|
||||||
public override string ToString() => Name;
|
public override string ToString() => Name;
|
||||||
|
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
|
public class JavaArguments : ArgumentsWithPlaceholders
|
||||||
|
{
|
||||||
|
private static readonly string[] _initial_arguments =
|
||||||
|
[
|
||||||
|
"-XX:+UnlockExperimentalVMOptions",
|
||||||
|
"-XX:+UseG1GC",
|
||||||
|
"-XX:G1NewSizePercent=20",
|
||||||
|
"-XX:G1ReservePercent=20",
|
||||||
|
"-XX:MaxGCPauseMillis=50",
|
||||||
|
"-XX:G1HeapRegionSize=32M",
|
||||||
|
"-XX:+DisableExplicitGC",
|
||||||
|
"-XX:+AlwaysPreTouch",
|
||||||
|
"-XX:+ParallelRefProcEnabled",
|
||||||
|
"-Xms${xms}M",
|
||||||
|
"-Xmx${xmx}M",
|
||||||
|
"-Dfile.encoding=UTF-8",
|
||||||
|
"-Dlog4j.configurationFile=${path}",
|
||||||
|
"-Djava.library.path=${natives_directory}",
|
||||||
|
"-Dminecraft.client.jar=${client_jar}",
|
||||||
|
"-Dminecraft.launcher.brand=${launcher_name}",
|
||||||
|
"-Dminecraft.launcher.version=${launcher_version}",
|
||||||
|
"-cp", "${classpath}"
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] _enabled_features =
|
||||||
|
[
|
||||||
|
];
|
||||||
|
|
||||||
|
public JavaArguments(GameVersionDescriptor d)
|
||||||
|
{
|
||||||
|
_raw_args.AddRange(_initial_arguments);
|
||||||
|
if (d.arguments is not null)
|
||||||
|
{
|
||||||
|
foreach (var av in d.arguments.jvm)
|
||||||
|
{
|
||||||
|
if (Rules.Check(av.rules, _enabled_features))
|
||||||
|
{
|
||||||
|
foreach (var arg in av.value)
|
||||||
|
{
|
||||||
|
if(!_raw_args.Contains(arg))
|
||||||
|
_raw_args.Add(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Runtime.InteropServices;
|
using DTLib.Extensions;
|
||||||
using DTLib.Extensions;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
namespace Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
public class Libraries
|
public class Libraries
|
||||||
{
|
{
|
||||||
@@ -20,7 +20,7 @@ public class Libraries
|
|||||||
|
|
||||||
foreach (var l in descriptor.libraries)
|
foreach (var l in descriptor.libraries)
|
||||||
{
|
{
|
||||||
if (l.rules != null && !Буржуазия.CheckRules(l.rules, enabled_features))
|
if (l.rules != null && !Rules.Check(l.rules, enabled_features))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (l.natives != null)
|
if (l.natives != null)
|
||||||
@@ -42,13 +42,7 @@ public class Libraries
|
|||||||
var span = nativesKey.AsSpan();
|
var span = nativesKey.AsSpan();
|
||||||
nativesKey = span.After("${").Before('}') switch
|
nativesKey = span.After("${").Before('}') switch
|
||||||
{
|
{
|
||||||
"arch" => RuntimeInformation.OSArchitecture switch
|
"arch" => span.Before("${").ToString() + PlatformHelper.GetArchOld(),
|
||||||
{
|
|
||||||
Architecture.X64 => span.Before("${").ToString() + "64",
|
|
||||||
Architecture.X86 => span.Before("${").ToString() + "32",
|
|
||||||
_ => throw new PlatformNotSupportedException(
|
|
||||||
$"Unsupported architecture: {RuntimeInformation.OSArchitecture}")
|
|
||||||
},
|
|
||||||
_ => throw new Exception($"unknown placeholder in {nativesKey}")
|
_ => throw new Exception($"unknown placeholder in {nativesKey}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -62,7 +56,7 @@ public class Libraries
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
string urlTail = artifact.url.AsSpan().After("://").After('/').ToString();
|
string urlTail = artifact.url.AsSpan().After("://").After('/').ToString();
|
||||||
IOPath jarFilePath = Path.Concat(Пути.GetLibrariesDir(), urlTail);
|
IOPath jarFilePath = Path.Concat(PathHelper.GetLibrariesDir(), urlTail);
|
||||||
libs.Add(new NativeLib(l.name, jarFilePath, artifact, l.extract));
|
libs.Add(new NativeLib(l.name, jarFilePath, artifact, l.extract));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -76,7 +70,7 @@ public class Libraries
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
string urlTail = artifact.url.AsSpan().After("://").After('/').ToString();
|
string urlTail = artifact.url.AsSpan().After("://").After('/').ToString();
|
||||||
IOPath jarFilePath = Path.Concat(Пути.GetLibrariesDir(), urlTail);
|
IOPath jarFilePath = Path.Concat(PathHelper.GetLibrariesDir(), urlTail);
|
||||||
libs.Add(new JarLib(l.name, jarFilePath, artifact));
|
libs.Add(new JarLib(l.name, jarFilePath, artifact));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Млаумчерб.Клиент.сеть;
|
namespace Mlaumcherb.Client.Avalonia.сеть;
|
||||||
|
|
||||||
public record struct DataSize(long Bytes)
|
public record struct DataSize(long Bytes)
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Timer = DTLib.Timer;
|
using Timer = DTLib.Timer;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть;
|
namespace Mlaumcherb.Client.Avalonia.сеть;
|
||||||
|
|
||||||
public record struct DownloadProgress(DataSize Downloaded, DataSize Total, DataSize PerSecond)
|
public record struct DownloadProgress(DataSize Downloaded, DataSize Total, DataSize PerSecond)
|
||||||
{
|
{
|
||||||
@@ -54,7 +54,7 @@ public class NetworkProgressReporter : IDisposable
|
|||||||
long bytesPerSec = (_curSize - _prevSize) / (_timerDelay / 1000);
|
long bytesPerSec = (_curSize - _prevSize) / (_timerDelay / 1000);
|
||||||
_prevSize = _curSize;
|
_prevSize = _curSize;
|
||||||
var p = new DownloadProgress(_curSize, _totalSize, bytesPerSec);
|
var p = new DownloadProgress(_curSize, _totalSize, bytesPerSec);
|
||||||
Приложение.Логгер.LogDebug(nameof(ReportProgress),
|
LauncherApp.Logger.LogDebug(nameof(ReportProgress),
|
||||||
$"download progress {p}");
|
$"download progress {p}");
|
||||||
_reportProgressDelegate(p);
|
_reportProgressDelegate(p);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Млаумчерб.Клиент.сеть;
|
namespace Mlaumcherb.Client.Avalonia.сеть;
|
||||||
|
|
||||||
public class NetworkTask : IDisposable
|
public class NetworkTask : IDisposable
|
||||||
{
|
{
|
||||||
@@ -2,11 +2,12 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DTLib.Extensions;
|
using DTLib.Extensions;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
using static Млаумчерб.Клиент.сеть.Сеть;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
using static Mlaumcherb.Client.Avalonia.сеть.Сеть;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
|
|
||||||
public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
||||||
{
|
{
|
||||||
@@ -20,7 +21,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
|||||||
{
|
{
|
||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_hasher = SHA1.Create();
|
_hasher = SHA1.Create();
|
||||||
_indexFilePath = Пути.GetAssetIndexFilePath(_descriptor.assetIndex.id);
|
_indexFilePath = PathHelper.GetAssetIndexFilePath(_descriptor.assetIndex.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<NetworkTask?> CreateAsync(bool checkHashes)
|
public async Task<NetworkTask?> CreateAsync(bool checkHashes)
|
||||||
@@ -38,9 +39,9 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
|||||||
{
|
{
|
||||||
if(!File.Exists(_indexFilePath))
|
if(!File.Exists(_indexFilePath))
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading asset index to '{_indexFilePath}'");
|
||||||
await DownloadFile(_descriptor.assetIndex.url, _indexFilePath);
|
await DownloadFile(_descriptor.assetIndex.url, _indexFilePath);
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading asset index");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), "finished downloading asset index");
|
||||||
}
|
}
|
||||||
|
|
||||||
string indexFileText = File.ReadAllText(_indexFilePath);
|
string indexFileText = File.ReadAllText(_indexFilePath);
|
||||||
@@ -101,10 +102,10 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
|||||||
|
|
||||||
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading assets '{_descriptor.assetIndex.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading assets '{_descriptor.assetIndex.id}'");
|
||||||
ParallelOptions opt = new()
|
ParallelOptions opt = new()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
|
MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
|
||||||
CancellationToken = ct
|
CancellationToken = ct
|
||||||
};
|
};
|
||||||
await Parallel.ForEachAsync(_assetsToDownload, opt,
|
await Parallel.ForEachAsync(_assetsToDownload, opt,
|
||||||
@@ -113,7 +114,7 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
|||||||
bool completed = false;
|
bool completed = false;
|
||||||
while(!completed)
|
while(!completed)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), $"downloading asset '{a.name}' {a.hash}");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await DownloadFile(a.url, a.filePath, _ct, pr.AddBytesCount);
|
await DownloadFile(a.url, a.filePath, _ct, pr.AddBytesCount);
|
||||||
@@ -124,13 +125,13 @@ public class AssetsDownloadTaskFactory : INetworkTaskFactory
|
|||||||
// wait on rate limit
|
// wait on rate limit
|
||||||
if(httpException.StatusCode == HttpStatusCode.TooManyRequests)
|
if(httpException.StatusCode == HttpStatusCode.TooManyRequests)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), "rate limit hit");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), "rate limit hit");
|
||||||
await Task.Delay(1000, _ct);
|
await Task.Delay(1000, _ct);
|
||||||
}
|
}
|
||||||
else throw;
|
else throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading assets '{_descriptor.assetIndex.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading assets '{_descriptor.assetIndex.id}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
|
|
||||||
public interface INetworkTaskFactory
|
public interface INetworkTaskFactory
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DTLib.Extensions;
|
using DTLib.Extensions;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
using static Млаумчерб.Клиент.сеть.Сеть;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
using static Mlaumcherb.Client.Avalonia.сеть.Сеть;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
|
|
||||||
public class JavaDownloadTaskFactory : INetworkTaskFactory
|
public class JavaDownloadTaskFactory : INetworkTaskFactory
|
||||||
{
|
{
|
||||||
@@ -19,7 +20,7 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory
|
|||||||
public JavaDownloadTaskFactory(GameVersionDescriptor descriptor)
|
public JavaDownloadTaskFactory(GameVersionDescriptor descriptor)
|
||||||
{
|
{
|
||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_javaVersionDir = Пути.GetJavaRuntimeDir(_descriptor.javaVersion.component);
|
_javaVersionDir = PathHelper.GetJavaRuntimeDir(_descriptor.javaVersion.component);
|
||||||
_hasher = SHA1.Create();
|
_hasher = SHA1.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,38 +84,38 @@ public class JavaDownloadTaskFactory : INetworkTaskFactory
|
|||||||
|
|
||||||
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), "started downloading java runtime " +
|
LauncherApp.Logger.LogInfo(nameof(Сеть), "started downloading java runtime " +
|
||||||
$"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
|
$"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
|
||||||
|
|
||||||
ParallelOptions opt = new()
|
ParallelOptions opt = new()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
|
MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
|
||||||
CancellationToken = ct
|
CancellationToken = ct
|
||||||
};
|
};
|
||||||
await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) =>
|
await Parallel.ForEachAsync(_filesToDownload, opt, async (f, _ct) =>
|
||||||
{
|
{
|
||||||
if (f.props.downloads!.lzma != null)
|
if (f.props.downloads!.lzma != null)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading lzma-compressed file '{f.path}'");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), $"downloading lzma-compressed file '{f.path}'");
|
||||||
await using var pipe = new TransformStream(await GetStream(f.props.downloads.lzma.url, _ct));
|
await using var pipe = new TransformStream(await GetStream(f.props.downloads.lzma.url, _ct));
|
||||||
pipe.AddTransform(pr.AddBytesCount);
|
pipe.AddTransform(pr.AddBytesCount);
|
||||||
await using var fs = File.OpenWrite(f.path);
|
await using var fs = File.OpenWrite(f.path);
|
||||||
LZMACompressor.Decompress(pipe, fs);
|
LZMAHelper.Decompress(pipe, fs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading raw file '{f.path}'");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), $"downloading raw file '{f.path}'");
|
||||||
await DownloadFile(f.props.downloads.raw.url, f.path, _ct, pr.AddBytesCount);
|
await DownloadFile(f.props.downloads.raw.url, f.path, _ct, pr.AddBytesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!OperatingSystem.IsWindows() && f.props.executable is true)
|
if(!OperatingSystem.IsWindows() && f.props.executable is true)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), $"adding execute rights to file '{f.path}'");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), $"adding execute rights to file '{f.path}'");
|
||||||
System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute);
|
System.IO.File.SetUnixFileMode(f.path.ToString(), UnixFileMode.UserExecute);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), "finished downloading java runtime " +
|
LauncherApp.Logger.LogInfo(nameof(Сеть), "finished downloading java runtime " +
|
||||||
$"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
|
$"{_descriptor.javaVersion.majorVersion} '{_descriptor.javaVersion.component}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DTLib.Extensions;
|
using DTLib.Extensions;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
using static Млаумчерб.Клиент.сеть.Сеть;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
using static Mlaumcherb.Client.Avalonia.сеть.Сеть;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
|
|
||||||
public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
||||||
{
|
{
|
||||||
@@ -20,7 +21,7 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_libraries = libraries;
|
_libraries = libraries;
|
||||||
_hasher = SHA1.Create();
|
_hasher = SHA1.Create();
|
||||||
_nativesDir = Пути.GetNativeLibrariesDir(descriptor.id);
|
_nativesDir = PathHelper.GetNativeLibrariesDir(descriptor.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<NetworkTask?> CreateAsync(bool checkHashes)
|
public Task<NetworkTask?> CreateAsync(bool checkHashes)
|
||||||
@@ -72,16 +73,16 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
|
|
||||||
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading libraries '{_descriptor.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading libraries '{_descriptor.id}'");
|
||||||
|
|
||||||
ParallelOptions opt = new()
|
ParallelOptions opt = new()
|
||||||
{
|
{
|
||||||
MaxDegreeOfParallelism = Приложение.Настройки.максимум_параллельных_загрузок,
|
MaxDegreeOfParallelism = LauncherApp.Config.max_parallel_downloads,
|
||||||
CancellationToken = ct
|
CancellationToken = ct
|
||||||
};
|
};
|
||||||
await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
|
await Parallel.ForEachAsync(_libsToDownload, opt, async (l, _ct) =>
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogDebug(nameof(Сеть), $"downloading library '{l.name}' to '{l.jarFilePath}'");
|
LauncherApp.Logger.LogDebug(nameof(Сеть), $"downloading library '{l.name}' to '{l.jarFilePath}'");
|
||||||
await DownloadFile(l.artifact.url, l.jarFilePath, _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)
|
||||||
{
|
{
|
||||||
@@ -101,6 +102,6 @@ public class LibrariesDownloadTaskFactory : INetworkTaskFactory
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading libraries '{_descriptor.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading libraries '{_descriptor.id}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using DTLib.Extensions;
|
using DTLib.Extensions;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Млаумчерб.Клиент.классы;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
using static Млаумчерб.Клиент.сеть.Сеть;
|
using Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
using static Mlaumcherb.Client.Avalonia.сеть.Сеть;
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
namespace Mlaumcherb.Client.Avalonia.сеть.TaskFactories;
|
||||||
|
|
||||||
public class VersionFileDownloadTaskFactory : INetworkTaskFactory
|
public class VersionFileDownloadTaskFactory : INetworkTaskFactory
|
||||||
{
|
{
|
||||||
@@ -15,7 +16,7 @@ public class VersionFileDownloadTaskFactory : INetworkTaskFactory
|
|||||||
public VersionFileDownloadTaskFactory(GameVersionDescriptor descriptor)
|
public VersionFileDownloadTaskFactory(GameVersionDescriptor descriptor)
|
||||||
{
|
{
|
||||||
_descriptor = descriptor;
|
_descriptor = descriptor;
|
||||||
_filePath = Пути.GetVersionJarFilePath(_descriptor.id);
|
_filePath = PathHelper.GetVersionJarFilePath(_descriptor.id);
|
||||||
_hasher = SHA1.Create();
|
_hasher = SHA1.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +50,8 @@ public class VersionFileDownloadTaskFactory : INetworkTaskFactory
|
|||||||
|
|
||||||
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
private async Task Download(NetworkProgressReporter pr, CancellationToken ct)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"started downloading version file '{_descriptor.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"started downloading version file '{_descriptor.id}'");
|
||||||
await DownloadFile(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount);
|
await DownloadFile(_descriptor.downloads.client.url, _filePath, ct, pr.AddBytesCount);
|
||||||
Приложение.Логгер.LogInfo(nameof(Сеть), $"finished downloading version file '{_descriptor.id}'");
|
LauncherApp.Logger.LogInfo(nameof(Сеть), $"finished downloading version file '{_descriptor.id}'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using Млаумчерб.Клиент.зримое;
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
using Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.сеть;
|
namespace Mlaumcherb.Client.Avalonia.сеть;
|
||||||
|
|
||||||
public static class Сеть
|
public static class Сеть
|
||||||
{
|
{
|
||||||
@@ -60,7 +59,7 @@ public static class Сеть
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogWarn(nameof(Сеть), ex);
|
LauncherApp.Logger.LogWarn(nameof(Сеть), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using DTLib.Demystifier;
|
using DTLib.Demystifier;
|
||||||
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
using MsBox.Avalonia;
|
using MsBox.Avalonia;
|
||||||
using MsBox.Avalonia.Dto;
|
using MsBox.Avalonia.Dto;
|
||||||
using MsBox.Avalonia.Enums;
|
using MsBox.Avalonia.Enums;
|
||||||
using MsBox.Avalonia.Models;
|
using MsBox.Avalonia.Models;
|
||||||
using Млаумчерб.Клиент.зримое;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент;
|
namespace Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
public static class Ошибки
|
public static class ErrorHelper
|
||||||
{
|
{
|
||||||
internal static void ПоказатьСообщение(string context, Exception err)
|
internal static void ShowMessageBox(string context, Exception err)
|
||||||
=> ПоказатьСообщение(context, err.ToStringDemystified());
|
=> ShowMessageBox(context, err.ToStringDemystified());
|
||||||
|
|
||||||
internal static async void ПоказатьСообщение(string context, string err)
|
internal static async void ShowMessageBox(string context, string err)
|
||||||
{
|
{
|
||||||
Приложение.Логгер.LogError(nameof(Ошибки), err);
|
LauncherApp.Logger.LogError(context, err);
|
||||||
var box = MessageBoxManager.GetMessageBoxCustom(new MessageBoxCustomParams
|
var box = MessageBoxManager.GetMessageBoxCustom(new MessageBoxCustomParams
|
||||||
{
|
{
|
||||||
ButtonDefinitions = new List<ButtonDefinition> { new() { Name = "пон" } },
|
ButtonDefinitions = new List<ButtonDefinition> { new() { Name = "пон" } },
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
namespace Млаумчерб.Клиент;
|
namespace Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// https://gist.github.com/ststeiger/cb9750664952f775a341
|
/// https://gist.github.com/ststeiger/cb9750664952f775a341
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class LZMACompressor
|
public static class LZMAHelper
|
||||||
{
|
{
|
||||||
public static void Decompress(Stream inputStream, Stream outputStream)
|
public static void Decompress(Stream inputStream, Stream outputStream)
|
||||||
{
|
{
|
||||||
57
Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs
Normal file
57
Mlaumcherb.Client.Avalonia/холопы/PathHelper.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using Mlaumcherb.Client.Avalonia.зримое;
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
|
public static class PathHelper
|
||||||
|
{
|
||||||
|
public static IOPath GetRootFullPath() =>
|
||||||
|
System.IO.Path.GetFullPath(new IOPath(LauncherApp.Config.minecraft_dir).ToString());
|
||||||
|
|
||||||
|
public static IOPath GetAssetsDir() =>
|
||||||
|
Path.Concat(GetRootFullPath(), "assets");
|
||||||
|
|
||||||
|
public static IOPath GetAssetIndexFilePath(string id) =>
|
||||||
|
Path.Concat(GetAssetsDir(), $"assets/indexes/{id}.json");
|
||||||
|
|
||||||
|
public static IOPath GetVersionDescriptorPath(string id) =>
|
||||||
|
Path.Concat(GetVersionDir(id), id + ".json");
|
||||||
|
|
||||||
|
public static IOPath GetVersionsDir() =>
|
||||||
|
Path.Concat(GetRootFullPath(), "versions");
|
||||||
|
|
||||||
|
public static IOPath GetVersionDir(string id) =>
|
||||||
|
Path.Concat(GetVersionsDir(), id);
|
||||||
|
|
||||||
|
public static IOPath GetVersionJarFilePath(string id) =>
|
||||||
|
Path.Concat(GetVersionDir(id), id + ".jar");
|
||||||
|
|
||||||
|
public static IOPath GetLibrariesDir() =>
|
||||||
|
Path.Concat(GetRootFullPath(), "libraries");
|
||||||
|
|
||||||
|
public static IOPath GetNativeLibrariesDir(string id) =>
|
||||||
|
Path.Concat(GetVersionDir(id), "natives", PlatformHelper.GetOsAndArch());
|
||||||
|
|
||||||
|
public static IOPath GetJavaRuntimesDir() =>
|
||||||
|
Path.Concat(GetRootFullPath(), "java");
|
||||||
|
|
||||||
|
|
||||||
|
public static IOPath GetJavaRuntimeDir(string id) =>
|
||||||
|
Path.Concat(GetJavaRuntimesDir(), PlatformHelper.GetOsAndArch(), id);
|
||||||
|
|
||||||
|
public static IOPath GetJavaBinDir(string id) =>
|
||||||
|
Path.Concat(GetJavaRuntimeDir(id), "bin");
|
||||||
|
public static IOPath GetJavaExecutablePath(string id, bool debug)
|
||||||
|
{
|
||||||
|
string executable_name = "java";
|
||||||
|
if (debug)
|
||||||
|
executable_name += "w";
|
||||||
|
if(OperatingSystem.IsWindows())
|
||||||
|
executable_name += ".exe";
|
||||||
|
return Path.Concat(GetJavaBinDir(id), executable_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetLog4jConfigFileName() => "log4j.xml";
|
||||||
|
|
||||||
|
public static IOPath GetLog4jConfigFilePath() =>
|
||||||
|
Path.Concat(GetAssetsDir(), GetLog4jConfigFileName());
|
||||||
|
}
|
||||||
55
Mlaumcherb.Client.Avalonia/холопы/PlatformHelper.cs
Normal file
55
Mlaumcherb.Client.Avalonia/холопы/PlatformHelper.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Mlaumcherb.Client.Avalonia.классы;
|
||||||
|
|
||||||
|
// ReSharper disable CollectionNeverUpdated.Global
|
||||||
|
|
||||||
|
namespace Mlaumcherb.Client.Avalonia.холопы;
|
||||||
|
|
||||||
|
public static class PlatformHelper
|
||||||
|
{
|
||||||
|
public static bool CheckOs(Os os) =>
|
||||||
|
os.name switch
|
||||||
|
{
|
||||||
|
null => true,
|
||||||
|
"osx" => OperatingSystem.IsMacOS(),
|
||||||
|
"linux" => OperatingSystem.IsLinux(),
|
||||||
|
"windows" => OperatingSystem.IsWindows(),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(os.name)
|
||||||
|
}
|
||||||
|
&& os.arch switch
|
||||||
|
{
|
||||||
|
null => true,
|
||||||
|
"i386" or "x86" => RuntimeInformation.OSArchitecture == Architecture.X86,
|
||||||
|
"x64" => RuntimeInformation.OSArchitecture == Architecture.X64,
|
||||||
|
"arm64" => RuntimeInformation.OSArchitecture == Architecture.Arm64,
|
||||||
|
_ => false
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetOs() =>
|
||||||
|
Environment.OSVersion.Platform switch
|
||||||
|
{
|
||||||
|
PlatformID.Win32NT => "windows",
|
||||||
|
PlatformID.Unix => "linux",
|
||||||
|
PlatformID.MacOSX => "osx",
|
||||||
|
_ => throw new PlatformNotSupportedException("OS not supported")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetArch() =>
|
||||||
|
RuntimeInformation.OSArchitecture switch
|
||||||
|
{
|
||||||
|
Architecture.X86 => "x86",
|
||||||
|
Architecture.X64 => "x64",
|
||||||
|
Architecture.Arm64 => "arm64",
|
||||||
|
_ => throw new PlatformNotSupportedException("OS not supported")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetArchOld() =>
|
||||||
|
RuntimeInformation.OSArchitecture switch
|
||||||
|
{
|
||||||
|
Architecture.X86 => "32",
|
||||||
|
Architecture.X64 => "64",
|
||||||
|
_ => throw new PlatformNotSupportedException("OS not supported")
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string GetOsAndArch() => GetOs() + '-' + GetArch();
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Млаумчерб.Клиент", "Млаумчерб.Клиент\Млаумчерб.Клиент.csproj", "{9B9D8B05-255F-49C3-89EC-3F43A66491D3}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mlaumcherb.Client.Avalonia", "Mlaumcherb.Client.Avalonia\Mlaumcherb.Client.Avalonia.csproj", "{9B9D8B05-255F-49C3-89EC-3F43A66491D3}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{A3217C18-CC0D-4CE8-9C48-1BDEC1E1B333}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionFolder", "SolutionFolder", "{A3217C18-CC0D-4CE8-9C48-1BDEC1E1B333}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using CliWrap;
|
|
||||||
using DTLib.Extensions;
|
|
||||||
using Млаумчерб.Клиент.зримое;
|
|
||||||
using Млаумчерб.Клиент.классы;
|
|
||||||
using Млаумчерб.Клиент.сеть;
|
|
||||||
using Млаумчерб.Клиент.сеть.NetworkTaskFactories;
|
|
||||||
using static Млаумчерб.Клиент.классы.Пути;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент;
|
|
||||||
|
|
||||||
public class GameVersion
|
|
||||||
{
|
|
||||||
private readonly GameVersionProps _props;
|
|
||||||
public string Name => _props.Name;
|
|
||||||
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;
|
|
||||||
|
|
||||||
public static async Task<List<GameVersionProps>> GetAllVersionsAsync()
|
|
||||||
{
|
|
||||||
var propsSet = new HashSet<GameVersionProps>();
|
|
||||||
|
|
||||||
// local descriptors
|
|
||||||
foreach (IOPath f in Directory.GetFiles(GetVersionDescriptorsDir()))
|
|
||||||
{
|
|
||||||
string name = GetVersionDescriptorName(f);
|
|
||||||
propsSet.Add(new GameVersionProps(name, null, f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// remote non-duplicating versions
|
|
||||||
foreach (var removeVersion in await Сеть.GetDownloadableVersions())
|
|
||||||
{
|
|
||||||
propsSet.Add(removeVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reverse sort
|
|
||||||
var propsList = propsSet.ToList();
|
|
||||||
propsList.Sort((a, b) => b.CompareTo(a));
|
|
||||||
return propsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task<GameVersion> CreateFromPropsAsync(GameVersionProps props)
|
|
||||||
{
|
|
||||||
if (!File.Exists(props.LocalDescriptorPath))
|
|
||||||
{
|
|
||||||
if (props.RemoteDescriptorUrl is null)
|
|
||||||
throw new NullReferenceException("can't download game version descriptor '"
|
|
||||||
+ props.Name + "', because RemoteDescriptorUrl is null");
|
|
||||||
await Сеть.DownloadFile(props.RemoteDescriptorUrl, props.LocalDescriptorPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new GameVersion(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
private GameVersion(GameVersionProps props)
|
|
||||||
{
|
|
||||||
_props = props;
|
|
||||||
string descriptorText = File.ReadAllText(props.LocalDescriptorPath);
|
|
||||||
descriptor = JsonConvert.DeserializeObject<GameVersionDescriptor>(descriptorText)
|
|
||||||
?? throw new Exception($"can't parse descriptor file '{props.LocalDescriptorPath}'");
|
|
||||||
javaArgs = new JavaArguments(descriptor);
|
|
||||||
gameArgs = new GameArguments(descriptor);
|
|
||||||
libraries = new Libraries(descriptor);
|
|
||||||
WorkingDirectory = GetVersionDir(descriptor.id);
|
|
||||||
JavaExecutableFilePath = GetJavaExecutablePath(descriptor.javaVersion.component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateFiles(bool checkHashes, Action<NetworkTask> networkTaskCreatedCallback)
|
|
||||||
{
|
|
||||||
Приложение.Логгер.LogInfo(nameof(GameVersion), $"started updating version {Name}");
|
|
||||||
|
|
||||||
List<INetworkTaskFactory> taskFactories =
|
|
||||||
[
|
|
||||||
new AssetsDownloadTaskFactory(descriptor),
|
|
||||||
new LibrariesDownloadTaskFactory(descriptor, libraries),
|
|
||||||
new VersionFileDownloadTaskFactory(descriptor),
|
|
||||||
];
|
|
||||||
if(Приложение.Настройки.скачивать_жабу)
|
|
||||||
{
|
|
||||||
taskFactories.Add(new JavaDownloadTaskFactory(descriptor));
|
|
||||||
}
|
|
||||||
//TODO: modpack
|
|
||||||
/*if (modpack != null)
|
|
||||||
{
|
|
||||||
taskFactories.Add(new ModpackDownloadTaskFactory(modpack));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
var networkTasks = new List<NetworkTask>();
|
|
||||||
for (int i = 0; i < taskFactories.Count; i++)
|
|
||||||
{
|
|
||||||
var nt = await taskFactories[i].CreateAsync(checkHashes);
|
|
||||||
if (nt != null)
|
|
||||||
{
|
|
||||||
networkTasks.Add(nt);
|
|
||||||
networkTaskCreatedCallback.Invoke(nt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var nt in networkTasks)
|
|
||||||
{
|
|
||||||
await nt.StartAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
_props.IsDownloaded = true;
|
|
||||||
Приложение.Логгер.LogInfo(nameof(GameVersion), $"finished updating version {Name}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Launch()
|
|
||||||
{
|
|
||||||
var javaArgsList = javaArgs.FillPlaceholders([]);
|
|
||||||
var gameArgsList = gameArgs.FillPlaceholders([]);
|
|
||||||
var command = Cli.Wrap(JavaExecutableFilePath.ToString())
|
|
||||||
.WithWorkingDirectory(WorkingDirectory.ToString())
|
|
||||||
.WithArguments(javaArgsList)
|
|
||||||
.WithArguments(gameArgsList);
|
|
||||||
Приложение.Логгер.LogInfo(nameof(GameVersion),
|
|
||||||
$"launching the game" +
|
|
||||||
"\njava: " + command.TargetFilePath +
|
|
||||||
"\nworking_dir: " + command.WorkingDirPath +
|
|
||||||
"\njava_arguments: \n\t" + javaArgsList.MergeToString("\n\t") +
|
|
||||||
"\ngame_arguments: \n\t" + gameArgsList.MergeToString("\n\t"));
|
|
||||||
gameCts = new();
|
|
||||||
commandTask = command.ExecuteAsync(gameCts.Token);
|
|
||||||
var result = await commandTask;
|
|
||||||
Приложение.Логгер.LogInfo(nameof(GameVersion), $"game exited with code {result.ExitCode}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
gameCts?.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString() => Name;
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
using Млаумчерб.Клиент.зримое;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент;
|
|
||||||
|
|
||||||
public record Настройки
|
|
||||||
{
|
|
||||||
public string имя_пользователя { get; set; } = "";
|
|
||||||
public int выделенная_память_мб { get; set; } = 4096;
|
|
||||||
public string путь_к_кубачу { get; set; } = ".";
|
|
||||||
public bool запускать_полноэкранное { get; set; } = false;
|
|
||||||
public bool скачивать_жабу { get; set; } = true;
|
|
||||||
public string? последняя_запущенная_версия { get; set; }
|
|
||||||
public int максимум_параллельных_загрузок { get; set; } = 16;
|
|
||||||
|
|
||||||
public static Настройки ЗагрузитьИзФайла(string имя_файла = "млаумчерб.настройки")
|
|
||||||
{
|
|
||||||
Приложение.Логгер.LogInfo(nameof(Настройки), $"загружаются настройки из файла '{имя_файла}'");
|
|
||||||
if(!File.Exists(имя_файла))
|
|
||||||
{
|
|
||||||
Приложение.Логгер.LogInfo(nameof(Настройки), "файл не существует");
|
|
||||||
return new Настройки();
|
|
||||||
}
|
|
||||||
|
|
||||||
string текст = File.ReadAllText(имя_файла);
|
|
||||||
Настройки? н = JsonConvert.DeserializeObject<Настройки>(текст);
|
|
||||||
if (н == null)
|
|
||||||
{
|
|
||||||
File.Move(имя_файла, имя_файла + ".старые", true);
|
|
||||||
Ошибки.ПоказатьСообщение("Настройки", $"Не удалось прочитать настройки.\n" +
|
|
||||||
$"Сломанный файл настроек переименован в '{имя_файла}.старые'.\n" +
|
|
||||||
$"Создаётся новый файл '{имя_файла}'.");
|
|
||||||
н = new Настройки();
|
|
||||||
н.СохранитьВФайл();
|
|
||||||
}
|
|
||||||
|
|
||||||
Приложение.Логгер.LogInfo(nameof(Настройки), $"настройки загружены: {н}");
|
|
||||||
return н;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void СохранитьВФайл(string имя_файла = "млаумчерб.настройки")
|
|
||||||
{
|
|
||||||
//TODO: file backup and restore
|
|
||||||
Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохраняются в файл '{имя_файла}'");
|
|
||||||
var текст = JsonConvert.SerializeObject(this, Formatting.Indented);
|
|
||||||
File.WriteAllText(имя_файла, текст);
|
|
||||||
Приложение.Логгер.LogDebug(nameof(Настройки), $"настройки сохранены: {текст}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
using Avalonia;
|
|
||||||
using Avalonia.Controls.ApplicationLifetimes;
|
|
||||||
using Avalonia.Markup.Xaml;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.зримое;
|
|
||||||
|
|
||||||
public class Приложение : Application
|
|
||||||
{
|
|
||||||
public static LauncherLogger Логгер = new();
|
|
||||||
public static Настройки Настройки = new();
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
Логгер.LogInfo(nameof(Приложение), "приложение запущено");
|
|
||||||
Настройки = Настройки.ЗагрузитьИзФайла();
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnFrameworkInitializationCompleted()
|
|
||||||
{
|
|
||||||
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
|
|
||||||
{
|
|
||||||
desktop.MainWindow = new Окне();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnFrameworkInitializationCompleted();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public class ArgumentsWithPlaceholders
|
|
||||||
{
|
|
||||||
protected List<string> raw_args = new();
|
|
||||||
|
|
||||||
public List<string> FillPlaceholders(Dictionary<string, string> values)
|
|
||||||
{
|
|
||||||
List<string> result = new();
|
|
||||||
foreach (var a in raw_args)
|
|
||||||
{
|
|
||||||
var f = a;
|
|
||||||
int begin = a.IndexOf('$');
|
|
||||||
if (begin != -1)
|
|
||||||
{
|
|
||||||
int keyBegin = begin + 2;
|
|
||||||
int end = a.IndexOf('}', keyBegin);
|
|
||||||
if (end != -1)
|
|
||||||
{
|
|
||||||
var key = a.Substring(keyBegin, end - keyBegin);
|
|
||||||
if (!values.TryGetValue(key, out var v))
|
|
||||||
throw new Exception($"can't find value for placeholder '{key}'");
|
|
||||||
f = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.Add(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using DTLib.Extensions;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public class GameArguments : ArgumentsWithPlaceholders
|
|
||||||
{
|
|
||||||
private static readonly string[] _enabled_features =
|
|
||||||
[
|
|
||||||
"has_custom_resolution"
|
|
||||||
];
|
|
||||||
|
|
||||||
public GameArguments(GameVersionDescriptor d)
|
|
||||||
{
|
|
||||||
if (d.minecraftArguments is not null)
|
|
||||||
{
|
|
||||||
raw_args.AddRange(d.minecraftArguments.SplitToList(' ', quot: '"'));
|
|
||||||
}
|
|
||||||
else if (d.arguments is not null)
|
|
||||||
{
|
|
||||||
foreach (var av in d.arguments.game)
|
|
||||||
{
|
|
||||||
if(Буржуазия.CheckRules(av.rules, _enabled_features))
|
|
||||||
raw_args.AddRange(av.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public class JavaArguments : ArgumentsWithPlaceholders
|
|
||||||
{
|
|
||||||
private static readonly string[] _initial_arguments =
|
|
||||||
[
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
private static readonly string[] _enabled_features =
|
|
||||||
[
|
|
||||||
|
|
||||||
];
|
|
||||||
|
|
||||||
public JavaArguments(GameVersionDescriptor d)
|
|
||||||
{
|
|
||||||
raw_args.AddRange(_initial_arguments);
|
|
||||||
if (d.arguments is not null)
|
|
||||||
{
|
|
||||||
foreach (var av in d.arguments.jvm)
|
|
||||||
{
|
|
||||||
if(Буржуазия.CheckRules(av.rules, _enabled_features))
|
|
||||||
raw_args.AddRange(av.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
// ReSharper disable CollectionNeverUpdated.Global
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public static class Буржуазия
|
|
||||||
{
|
|
||||||
public static bool CheckOs(Os os) =>
|
|
||||||
os.name switch
|
|
||||||
{
|
|
||||||
null => true,
|
|
||||||
"osx" => OperatingSystem.IsMacOS(),
|
|
||||||
"linux" => OperatingSystem.IsLinux(),
|
|
||||||
"windows" => OperatingSystem.IsWindows(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(os.name)
|
|
||||||
}
|
|
||||||
&& os.arch switch
|
|
||||||
{
|
|
||||||
null => true,
|
|
||||||
"i386" or "x86" => RuntimeInformation.OSArchitecture == Architecture.X86,
|
|
||||||
"x64" => RuntimeInformation.OSArchitecture == Architecture.X64,
|
|
||||||
"arm64" => RuntimeInformation.OSArchitecture == Architecture.Arm64,
|
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
|
|
||||||
public static bool CheckRules(ICollection<Rule> rules, ICollection<string> enabled_features)
|
|
||||||
{
|
|
||||||
bool allowed = false;
|
|
||||||
foreach (var r in rules)
|
|
||||||
{
|
|
||||||
if (r.os == null || CheckOs(r.os))
|
|
||||||
{
|
|
||||||
if (r.features != null)
|
|
||||||
{
|
|
||||||
foreach (var feature in enabled_features)
|
|
||||||
{
|
|
||||||
if(r.features.TryGetValue(feature, out bool is_enabled))
|
|
||||||
{
|
|
||||||
if (is_enabled)
|
|
||||||
{
|
|
||||||
if (r.action != "allow")
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r.action == "allow")
|
|
||||||
allowed = true;
|
|
||||||
else return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetOs() =>
|
|
||||||
Environment.OSVersion.Platform switch
|
|
||||||
{
|
|
||||||
PlatformID.Win32NT => "windows",
|
|
||||||
PlatformID.Unix => "linux",
|
|
||||||
PlatformID.MacOSX => "osx",
|
|
||||||
_ => throw new ArgumentOutOfRangeException(Environment.OSVersion.Platform.ToString())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public static class Пролетариат
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using Млаумчерб.Клиент.зримое;
|
|
||||||
|
|
||||||
namespace Млаумчерб.Клиент.классы;
|
|
||||||
|
|
||||||
public static class Пути
|
|
||||||
{
|
|
||||||
public static IOPath GetAssetIndexFilePath(string id) =>
|
|
||||||
Path.Concat(Приложение.Настройки.путь_к_кубачу, $"assets/indexes/{id}.json");
|
|
||||||
|
|
||||||
public static IOPath GetVersionDescriptorsDir() =>
|
|
||||||
Path.Concat(Приложение.Настройки.путь_к_кубачу, "versions");
|
|
||||||
|
|
||||||
public static string GetVersionDescriptorName(IOPath path) =>
|
|
||||||
path.LastName().RemoveExtension().ToString();
|
|
||||||
|
|
||||||
public static IOPath GetVersionDescriptorPath(string name) =>
|
|
||||||
Path.Concat(GetVersionDescriptorsDir(), Path.ReplaceRestrictedChars(name) + ".json");
|
|
||||||
|
|
||||||
public static IOPath GetVersionDir(string id) =>
|
|
||||||
Path.Concat(Приложение.Настройки.путь_к_кубачу, "versions", id);
|
|
||||||
|
|
||||||
public static IOPath GetVersionJarFilePath(string id) =>
|
|
||||||
Path.Concat(GetVersionDir(id), id + ".jar");
|
|
||||||
|
|
||||||
public static IOPath GetLibrariesDir() =>
|
|
||||||
Path.Concat(Приложение.Настройки.путь_к_кубачу, "libraries");
|
|
||||||
|
|
||||||
public static IOPath GetNativeLibrariesDir(string id) =>
|
|
||||||
Path.Concat(GetVersionDir(id), "natives", Буржуазия.GetOs());
|
|
||||||
|
|
||||||
public static IOPath GetJavaRuntimesDir() =>
|
|
||||||
Path.Concat(Приложение.Настройки.путь_к_кубачу, "java");
|
|
||||||
|
|
||||||
|
|
||||||
public static IOPath GetJavaRuntimeDir(string id) =>
|
|
||||||
Path.Concat(GetJavaRuntimesDir(), id);
|
|
||||||
|
|
||||||
public static IOPath GetJavaExecutablePath(string id) =>
|
|
||||||
Path.Concat(GetJavaRuntimeDir(id), "bin",
|
|
||||||
OperatingSystem.IsWindows() ? "javaw.exe" : "javaw");
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user