diff --git a/src/main/Program.cs b/src/main/Program.cs index 4c0c235..52496f8 100644 --- a/src/main/Program.cs +++ b/src/main/Program.cs @@ -212,8 +212,19 @@ class Program { case "launcher::game": try { - MinecraftVersion version = await GameHelper.PrepareGame("1.12.2", gameRoot, true); - GameHelper.Launch(version, _authenticatedPlayer!, gameRoot, (logLine) => { + var options = new LaunchOptions { + Version = new LaunchOptions.VersionOptions { + Number = "1.12.2", + Type = "release" + }, + Memory = new LaunchOptions.MemoryOptions { + Max = $"{SettingsManager.ReadSettings().Ram.Max}M", + Min = "512M" + }, + ModLoader = LaunchOptions.ModLoaderType.Forge, + }; + MinecraftVersion version = await GameHelper.PrepareGame(options, gameRoot); + GameHelper.Launch(version, _authenticatedPlayer!, gameRoot, options, (logLine) => { var logMessage = new { requestId = "game::log", payload = new { message = logLine.ToString() } @@ -231,6 +242,7 @@ class Program { var response = new { requestId, payload }; window.SendWebMessage(JsonSerializer.Serialize(response, LauncherConstants._jsonOptions)); } catch (Exception ex) { + Console.WriteLine($"Error stacktrace: {ex.StackTrace}"); Console.WriteLine($"Bridge error: {ex.Message}"); } }); diff --git a/src/main/core/Constants.cs b/src/main/core/Constants.cs index 6a5bc3f..7203259 100644 --- a/src/main/core/Constants.cs +++ b/src/main/core/Constants.cs @@ -1,11 +1,8 @@ using System.Net.Http; using System.Text.Json; -namespace Lentia.Core.Constants -{ - public static class LauncherConstants - { - public static readonly HttpClient Http = new HttpClient(); +namespace Lentia.Core.Constants { + public static class LauncherConstants { public static readonly JsonSerializerOptions _jsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public static class Urls { diff --git a/src/main/core/auth/Yggdrasil.cs b/src/main/core/auth/Yggdrasil.cs index f5e0c09..1ad5b57 100644 --- a/src/main/core/auth/Yggdrasil.cs +++ b/src/main/core/auth/Yggdrasil.cs @@ -1,6 +1,7 @@ using Lentia.Core.Constants; using System.Text.Json.Serialization; using System.Net.Http.Json; +using Lentia.Core.Utils; namespace Lentia.Core.Auth.Yggdrasil; @@ -76,7 +77,7 @@ public class Authenticator { }; try { - var response = await LauncherConstants.Http.PostAsJsonAsync(LauncherConstants.Urls.MojangAuthServer + "/authenticate", payload); + var response = await HttpHelper.FetchAsync(LauncherConstants.Urls.MojangAuthServer + "/authenticate", HttpMethod.Post, payload); if (!response.IsSuccessStatusCode) { var errorData = await response.Content.ReadFromJsonAsync(); return AuthResult.Fail( diff --git a/src/main/core/auth/oauth2/Discord.cs b/src/main/core/auth/oauth2/Discord.cs index 9b6f572..d2724e3 100644 --- a/src/main/core/auth/oauth2/Discord.cs +++ b/src/main/core/auth/oauth2/Discord.cs @@ -3,6 +3,7 @@ using System.Net.Http.Json; using System.Text.Json; using Lentia.Core.Auth.Yggdrasil; using Lentia.Core.Constants; +using Lentia.Core.Utils; namespace Lentia.Core.Auth.OAuth2; @@ -36,7 +37,7 @@ public class Discord { public static async Task LoginWithDiscordOAuth2(string authCode) { var url = $"https://yggdrasil.azures.fr/auth/provider/discord/login/callback?code={authCode}&requestUser=true"; try { - var response = await LauncherConstants.Http.GetAsync(url); + var response = await HttpHelper.FetchAsync(url, HttpMethod.Get); if (response.IsSuccessStatusCode) { var data = await response.Content.ReadFromJsonAsync(); return data != null ? AuthResult.Ok(data) : AuthResult.Fail("Erreur de lecture des données."); diff --git a/src/main/core/game/Assets.cs b/src/main/core/game/Assets.cs index e67d565..e67cfd3 100644 --- a/src/main/core/game/Assets.cs +++ b/src/main/core/game/Assets.cs @@ -25,7 +25,7 @@ public class AssetsHelper { } public static async Task GetAssetsIndexAsync(string url) { - var response = await LauncherConstants.Http.GetAsync(url); + var response = await HttpHelper.FetchAsync(url, HttpMethod.Get); if (response.IsSuccessStatusCode) { string json = await response.Content.ReadAsStringAsync(); @@ -86,6 +86,7 @@ public class AssetsHelper { try { await FileHelper.DownloadFileAsync(url, localPath); } catch (Exception ex) { + Console.WriteLine(url); Console.WriteLine($"Échec : {hash} - {ex.Message}"); } }); diff --git a/src/main/core/game/Game.cs b/src/main/core/game/Game.cs index fc012d2..5c22b66 100644 --- a/src/main/core/game/Game.cs +++ b/src/main/core/game/Game.cs @@ -1,55 +1,164 @@ using System.Diagnostics; +using System.Text.Json; using Lentia.Core.Auth.Yggdrasil; +using Lentia.Core.Constants; using Lentia.Core.Game.Extra; using Lentia.Core.Utils; using Lentia.Utils; namespace Lentia.Core.Game; + +public class LaunchOptions { + + public enum ModLoaderType { + Vanilla, + Forge, + Fabric, + NeoForge + } + + public VersionOptions Version { get; set; } = new(); + public ModLoaderType ModLoader { get; set; } = ModLoaderType.Vanilla; + public string? Forge { get; set; } + + public MemoryOptions Memory { get; set; } = new(); + public string JavaPath { get; set; } = "java"; + public int Timeout { get; set; } = 10000; + + public List CustomArgs { get; set; } = new(); + public List CustomLaunchArgs { get; set; } = new(); + public List Features { get; set; } = new(); + + public WindowOptions Window { get; set; } = new(); + + public QuickPlayOptions? QuickPlay { get; set; } + + public ProxyOptions? Proxy { get; set; } + + public OverridesOptions Overrides { get; set; } = new(); + + public class VersionOptions { + public string Number { get; set; } = "1.12.2"; + public string Type { get; set; } = "release"; + public string? Custom { get; set; } + } + + public class MemoryOptions { + public string Max { get; set; } = "2048M"; + public string Min { get; set; } = "512M"; + } + + public class WindowOptions { + public int? Width { get; set; } + public int? Height { get; set; } + public bool Fullscreen { get; set; } = false; + } + + public class QuickPlayOptions { + public string Type { get; set; } = "singleplayer"; + public string? Identifier { get; set; } + public string? Path { get; set; } + } + + public class ProxyOptions { + public string? Host { get; set; } + public string Port { get; set; } = "8080"; + public string? Username { get; set; } + public string? Password { get; set; } + } + + public class OverridesOptions { + public string? GameDirectory { get; set; } + public string? MinecraftJar { get; set; } + public string? VersionName { get; set; } + public string? VersionJson { get; set; } + public string? Directory { get; set; } + public string? Natives { get; set; } + public string? AssetRoot { get; set; } + public string? AssetIndex { get; set; } + public string? LibraryRoot { get; set; } + public string? Cwd { get; set; } + public bool Detached { get; set; } = false; + public List? Classes { get; set; } + public string? Log4jConfigurationFile { get; set; } + + public UrlOverrides Url { get; set; } = new(); + } + + public class UrlOverrides { + public string Meta { get; set; } = "https://launchermeta.mojang.com"; + public string Resource { get; set; } = "https://resources.download.minecraft.net"; + public string MavenForge { get; set; } = "https://maven.minecraftforge.net/"; + public string DefaultRepoForge { get; set; } = "https://libraries.minecraft.net/"; + public string FallbackMaven { get; set; } = "https://search.maven.org/remotecontent?filepath="; + } +} + public static class GameHelper { - public static async Task PrepareGame(string targetVersion, string gameRoot, bool installForge) { - try { - string versionUrl = await VersionsHelper.GetVersionUrlAsync(targetVersion); - MinecraftVersion version = await VersionsHelper.GetVersionDetailsAsync(versionUrl); - MinecraftVersion workingVersion = version; + public static async Task PrepareGame(LaunchOptions options, string gameRoot) { + string root = options.Overrides.GameDirectory ?? gameRoot; - if (installForge) { - MinecraftVersion? forgeMeta = await ForgeHelper.ProcessForge(version, gameRoot); - if (forgeMeta != null) { - workingVersion = version; - } - } - - AssetsIndex assetsIndex = await AssetsHelper.GetAssetsIndexAsync(version.AssetIndex!.Url!); - await AssetsHelper.DownloadAssetsParallel(assetsIndex, gameRoot); - await JavaHelper.GetJavaExecutablePath(version, gameRoot); - await VersionsHelper.DownloadClientJar(version, gameRoot); - await LibrariesHelper.DownloadLibraries(workingVersion.Libraries!, gameRoot); - LibrariesHelper.ExtractNatives(workingVersion, gameRoot); - await VersionsHelper.DownloadLoggingConfig(version, gameRoot); - - return workingVersion; - } catch (Exception) { - throw; + MinecraftVersion baseVersion; + if (!string.IsNullOrEmpty(options.Overrides.VersionJson)) { + string jsonContent = await File.ReadAllTextAsync(options.Overrides.VersionJson); + baseVersion = JsonSerializer.Deserialize(jsonContent, LauncherConstants._jsonOptions)!; + } else { + string versionUrl = await VersionsHelper.GetVersionUrlAsync(options.Version.Number); + baseVersion = await VersionsHelper.GetVersionDetailsAsync(versionUrl); } + + MinecraftVersion workingVersion = baseVersion; + + switch (options.ModLoader) { + case LaunchOptions.ModLoaderType.Forge: + var forgeMeta = await ForgeHelper.ProcessForge(baseVersion, root); + if (forgeMeta != null) { + workingVersion = baseVersion; + } + break; + } + + if (baseVersion.AssetIndex == null) + throw new Exception("Erreur : L'index des assets est manquant dans le manifest de version."); + + string assetRoot = options.Overrides.AssetRoot ?? Path.Combine(root, "assets"); + AssetsIndex assetsIndex = await AssetsHelper.GetAssetsIndexAsync(baseVersion.AssetIndex.Url!); + await AssetsHelper.DownloadAssetsParallel(assetsIndex, assetRoot); + + string libRoot = options.Overrides.LibraryRoot ?? Path.Combine(root, "libraries"); + await LibrariesHelper.DownloadLibraries(workingVersion.Libraries!, libRoot); + + await VersionsHelper.DownloadClientJar(baseVersion, root); + LibrariesHelper.ExtractNatives(workingVersion, root); + + return workingVersion; } public static string BuildClasspath(MinecraftVersion version, string gameRoot) { var paths = new List(); string libRoot = Path.Combine(gameRoot, "libraries"); - paths.Add(Path.Combine(gameRoot, "versions", version.Id!, $"{version.Id}.jar")); + string clientJar = Path.Combine(gameRoot, "versions", version.Id!, $"{version.Id}.jar"); + if (File.Exists(clientJar)) paths.Add(clientJar); - foreach (var lib in version.Libraries!) { - if (LibrariesHelper.IsAllowed(lib.Rules, OsHelper.GetOSName())) { - string relPath = ForgeHelper.GeneratePathFromArtifactName(lib.Name!); - paths.Add(Path.Combine(libRoot, relPath)); + if (version.Libraries != null) { + foreach (var lib in version.Libraries) { + if (!LibrariesHelper.IsAllowed(lib.Rules, OsHelper.GetOSName())) continue; + + string? relPath = lib.Downloads?.Artifact?.Path ?? ForgeHelper.GeneratePathFromArtifactName(lib.Name!); + + string fullPath = Path.Combine(libRoot, relPath); + + if (File.Exists(fullPath)) { + paths.Add(fullPath); + } else { + Console.WriteLine($"[Classpath] Manquant: {fullPath}"); + } } } - - string separator = Path.PathSeparator.ToString(); - return string.Join(separator, paths); + return string.Join(Path.PathSeparator.ToString(), paths); } public static List GenerateGameArguments(MinecraftVersion version, AuthenticateResponse auth, string gameRoot) { @@ -73,52 +182,89 @@ public static class GameHelper { return rawArgs.Split(" ", StringSplitOptions.RemoveEmptyEntries).ToList(); } - public static void Launch(MinecraftVersion version, AuthenticateResponse auth, string gameRoot, Action logHandler) { - string javaPath = SettingsManager.ReadSettings().JavaPath; + public static void Launch(MinecraftVersion version, AuthenticateResponse auth, string gameRoot, LaunchOptions options, Action logHandler) { + string root = options.Overrides.GameDirectory ?? gameRoot; + string nativesDir = options.Overrides.Natives ?? Path.Combine(root, "versions", version.Id!, "natives"); + string javaPath = options.JavaPath ?? SettingsManager.ReadSettings().JavaPath; - string nativesDir = Path.Combine(gameRoot, "versions", version.Id!, "natives"); - var jvmArgs = new List { - $"-Xmx{SettingsManager.ReadSettings().Ram.Max}M", + $"-Xms{options.Memory.Min}", + $"-Xmx{options.Memory.Max}", $"-Djava.library.path={nativesDir}", "-Dminecraft.launcher.brand=Lentia", "-Dminecraft.launcher.version=1.0.0" }; - // if (version.Logging?.Client?.Argument != null) { - // string logConfig = Path.Combine(gameRoot, "assets", "log_configs", version.Logging.Client.File?.Id ?? "client-log4j2.xml"); - // jvmArgs.Add(version.Logging.Client.Argument.Replace("${path}", logConfig)); + // string? logConfigFile = options.Overrides.Log4jConfigurationFile; + // if (string.IsNullOrEmpty(logConfigFile) && version.Logging?.Client?.Argument != null) { + // logConfigFile = Path.Combine(root, "assets", "log_configs", version.Logging.Client.File?.Id ?? "client-log4j2.xml"); + // jvmArgs.Add(version.Logging.Client.Argument.Replace("${path}", logConfigFile)); + // } else if (!string.IsNullOrEmpty(logConfigFile)) { + // jvmArgs.Add($"-Dlog4j.configurationFile={logConfigFile}"); // } - string classpath = BuildClasspath(version, gameRoot); + if (options.CustomArgs != null && options.CustomArgs.Any()) { + jvmArgs.AddRange(options.CustomArgs); + } + + string classpath = (options.Overrides.Classes != null && options.Overrides.Classes.Any()) + ? string.Join(Path.PathSeparator.ToString(), options.Overrides.Classes) + : BuildClasspath(version, root); + jvmArgs.Add("-cp"); jvmArgs.Add(classpath); jvmArgs.Add(version.MainClass ?? "net.minecraft.client.main.Main"); - var gameArgs = GenerateGameArguments(version, auth, gameRoot); - jvmArgs.AddRange(gameArgs); + var gameArgs = GenerateGameArguments(version, auth, root); + + if (options.QuickPlay != null) { + gameArgs.Add($"--quickPlayPath {options.QuickPlay.Path}"); + gameArgs.Add($"--quickPlay{options.QuickPlay.Type} {options.QuickPlay.Identifier}"); + } + + if (options.Window.Fullscreen) { + gameArgs.Add("--fullscreen"); + } + if (options.Window.Width.HasValue) { + gameArgs.Add("--width"); gameArgs.Add(options.Window.Width.Value.ToString()); + } + if (options.Window.Height.HasValue) { + gameArgs.Add("--height"); gameArgs.Add(options.Window.Height.Value.ToString()); + } + + if (options.CustomLaunchArgs != null && options.CustomLaunchArgs.Any()) { + gameArgs.AddRange(options.CustomLaunchArgs); + } var startInfo = new ProcessStartInfo(javaPath) { - WorkingDirectory = gameRoot, - Arguments = string.Join(" ", jvmArgs.Select(a => a.Contains(" ") ? $"\"{a}\"" : a)), - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; + WorkingDirectory = options.Overrides.Cwd ?? root, + Arguments = string.Join(" ", jvmArgs.Concat(gameArgs).Select(a => a.Contains(" ") ? $"\"{a}\"" : a)), + UseShellExecute = options.Overrides.Detached, + RedirectStandardOutput = !options.Overrides.Detached, + RedirectStandardError = !options.Overrides.Detached, + CreateNoWindow = true + }; + + Console.WriteLine($"Start command: {javaPath} {startInfo.Arguments}"); var process = new Process { StartInfo = startInfo, EnableRaisingEvents = true }; - process.OutputDataReceived += (s, e) => { if (e.Data != null) logHandler(e.Data); }; - process.ErrorDataReceived += (s, e) => { if (e.Data != null) logHandler($"[ERROR] {e.Data}"); }; + if (!options.Overrides.Detached) { + process.OutputDataReceived += (s, e) => { if (e.Data != null) logHandler(e.Data); }; + process.ErrorDataReceived += (s, e) => { if (e.Data != null) logHandler($"[ERROR] {e.Data}"); }; + } + process.Exited += (sender, e) => { logHandler("GAME_CLOSED"); }; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); + if (process.Start()) { + if (!options.Overrides.Detached) { + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + } + } } } \ No newline at end of file diff --git a/src/main/core/game/Java.cs b/src/main/core/game/Java.cs index d80e2e1..116e593 100644 --- a/src/main/core/game/Java.cs +++ b/src/main/core/game/Java.cs @@ -70,7 +70,10 @@ public static class JavaHelper { private static async Task DownloadJavaRuntime(string root, string platform, string component) { string allJsonUrl = "https://piston-meta.mojang.com/v1/products/java-runtime/2ec0cc6c0dba9f635e09f3900331038590632291/all.json"; - var allRuntimes = await LauncherConstants.Http.GetFromJsonAsync(allJsonUrl); + + var allRuntimesResponse = await HttpHelper.FetchAsync(allJsonUrl); + allRuntimesResponse.EnsureSuccessStatusCode(); + var allRuntimes = await allRuntimesResponse.Content.ReadFromJsonAsync(LauncherConstants._jsonOptions); if (!allRuntimes.TryGetProperty(platform, out var platformData) || !platformData.TryGetProperty(component, out var componentVersions)) { @@ -78,15 +81,19 @@ public static class JavaHelper { } string manifestUrl = componentVersions[0].GetProperty("manifest").GetProperty("url").GetString()!; - var manifest = await LauncherConstants.Http.GetFromJsonAsync(manifestUrl); + var manifestResponse = await HttpHelper.FetchAsync(manifestUrl); + manifestResponse.EnsureSuccessStatusCode(); + var manifest = await manifestResponse.Content.ReadFromJsonAsync(LauncherConstants._jsonOptions); string javaDir = Path.Combine(root, "runtime", component, platform); await Parallel.ForEachAsync(manifest!.Files!, new ParallelOptions { MaxDegreeOfParallelism = 8 }, async (entry, token) => { string localPath = Path.Combine(javaDir, entry.Key); + if (entry.Value.Type == "directory") { Directory.CreateDirectory(localPath); } else if (entry.Value.Downloads?.Raw != null) { var download = entry.Value.Downloads.Raw; + if (!File.Exists(localPath) || new FileInfo(localPath).Length != download.Size) { await FileHelper.DownloadFileAsync(download.Url!, localPath); } diff --git a/src/main/core/game/Libraries.cs b/src/main/core/game/Libraries.cs index 816a196..21d5b83 100644 --- a/src/main/core/game/Libraries.cs +++ b/src/main/core/game/Libraries.cs @@ -7,7 +7,7 @@ public static class LibrariesHelper { public static async Task DownloadLibraries(List libraries, string root) { string osName = OsHelper.GetOSName(); - string librariesDir = Path.Combine(root, "libraries"); + string librariesDir = Path.Combine(root); var downloadsToProcess = new List(); diff --git a/src/main/core/game/Versions.cs b/src/main/core/game/Versions.cs index dd49711..3241734 100644 --- a/src/main/core/game/Versions.cs +++ b/src/main/core/game/Versions.cs @@ -196,7 +196,7 @@ public class JavaVersionInfo { public static class VersionsHelper { public static async Task GetVersionUrlAsync(string targetVersion) { - var response = await LauncherConstants.Http.GetAsync("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"); + var response = await HttpHelper.FetchAsync("https://piston-meta.mojang.com/mc/game/version_manifest_v2.json", HttpMethod.Get); var manifest = await response.Content.ReadFromJsonAsync(); var versionEntry = manifest?.Versions?.FirstOrDefault(v => v.Id == targetVersion); @@ -206,7 +206,7 @@ public static class VersionsHelper { } public static async Task GetVersionDetailsAsync(string versionUrl) { - var response = await LauncherConstants.Http.GetAsync(versionUrl); + var response = await HttpHelper.FetchAsync(versionUrl, HttpMethod.Get); if (response.IsSuccessStatusCode) { string jsonResponse = await response.Content.ReadAsStringAsync(); diff --git a/src/main/core/game/extra/Forge.cs b/src/main/core/game/extra/Forge.cs index fee7b22..60b5a62 100644 --- a/src/main/core/game/extra/Forge.cs +++ b/src/main/core/game/extra/Forge.cs @@ -16,9 +16,11 @@ public static class ForgeHelper { public static async Task FetchAndDownloadForge(string mcVersion, string root, bool recommended = true) { string promoUrl = "https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"; - LauncherConstants.Http.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + var response = await HttpHelper.FetchAsync(promoUrl, HttpMethod.Get); - var promoData = await LauncherConstants.Http.GetFromJsonAsync(promoUrl); + response.EnsureSuccessStatusCode(); + + var promoData = await response.Content.ReadFromJsonAsync(LauncherConstants._jsonOptions); string promoKey = $"{mcVersion}-{(recommended ? "recommended" : "latest")}"; if (promoData?.Promos == null || !promoData.Promos.TryGetValue(promoKey, out string? buildVersion)) { diff --git a/src/main/core/utils/File.cs b/src/main/core/utils/File.cs index 1ac73dc..a70053f 100644 --- a/src/main/core/utils/File.cs +++ b/src/main/core/utils/File.cs @@ -1,5 +1,3 @@ -using Lentia.Core.Constants; - namespace Lentia.Core.Utils; public static class FileHelper { @@ -13,7 +11,7 @@ public static class FileHelper { File.Delete(destinationPath); } - using var response = await LauncherConstants.Http.GetAsync(url, HttpCompletionOption.ResponseHeadersRead); + using var response = await HttpHelper.FetchAsync(url); response.EnsureSuccessStatusCode(); using var streamToReadFrom = await response.Content.ReadAsStreamAsync(); diff --git a/src/main/core/utils/Http.cs b/src/main/core/utils/Http.cs new file mode 100644 index 0000000..6556fae --- /dev/null +++ b/src/main/core/utils/Http.cs @@ -0,0 +1,32 @@ +using System.Net.Http.Json; +using Lentia.Core.Constants; + +namespace Lentia.Core.Utils; + +public static class HttpHelper { + + private static readonly HttpClient Http = new HttpClient(); + + public static async Task FetchAsync(string url, HttpMethod? method = null, object? body = null, Dictionary? headers = null) { + var request = new HttpRequestMessage(method ?? HttpMethod.Get, url); + + request.Headers.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + + if (headers != null) { + foreach (var header in headers) { + request.Headers.TryAddWithoutValidation(header.Key, header.Value); + } + } + + if (body != null) { + if (body is string s) { + request.Content = new StringContent(s, System.Text.Encoding.UTF8, "application/json"); + } else { + request.Content = JsonContent.Create(body, options: LauncherConstants._jsonOptions); + } + } + + return await Http.SendAsync(request); + } + +} \ No newline at end of file diff --git a/src/main/utils/MojangAPI.cs b/src/main/utils/MojangAPI.cs index 19b1e6f..25cf69f 100644 --- a/src/main/utils/MojangAPI.cs +++ b/src/main/utils/MojangAPI.cs @@ -1,27 +1,23 @@ using System.Net.Http.Headers; -using System.Text; using System.Text.Json; using Lentia.Core.Constants; +using Lentia.Core.Utils; namespace Lentia.Utils; public static class MojangAPI { public static async Task UploadSkinAsync(string filePath, string variant, string token) { - LauncherConstants.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); - using var content = new MultipartFormDataContent(); - content.Add(new StringContent(variant), "variant"); byte[] fileBytes = await File.ReadAllBytesAsync(filePath); var fileContent = new ByteArrayContent(fileBytes); fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("image/png"); - content.Add(fileContent, "file", Path.GetFileName(filePath)); - var response = await LauncherConstants.Http.PostAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/skins", content); - + var response = await HttpHelper.FetchAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/skins", method: HttpMethod.Post, body: content, headers: new() { { "Authorization", $"Bearer {token}" } } ); + if (response.IsSuccessStatusCode) { return await response.Content.ReadAsStringAsync(); } else { @@ -29,12 +25,8 @@ public static class MojangAPI { } } - public static async Task GetPlayerProfileAsync(string accessToken) { - LauncherConstants.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - LauncherConstants.Http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - - var response = await LauncherConstants.Http.GetAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile"); + var response = await HttpHelper.FetchAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile", headers: new() { { "Authorization", $"Bearer {accessToken}" }, { "Accept", "application/json" } } ); if (response.IsSuccessStatusCode) { string jsonResponse = await response.Content.ReadAsStringAsync(); @@ -45,9 +37,7 @@ public static class MojangAPI { } public static async Task HideCapeAsync(string accessToken) { - LauncherConstants.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - - var response = await LauncherConstants.Http.DeleteAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/capes/active"); + var response = await HttpHelper.FetchAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/capes/active", method: HttpMethod.Delete, headers: new() { { "Authorization", $"Bearer {accessToken}" } } ); if (response.IsSuccessStatusCode) { string jsonResponse = await response.Content.ReadAsStringAsync(); @@ -58,13 +48,7 @@ public static class MojangAPI { } public static async Task ShowCapeAsync(string accessToken, string capeId) { - LauncherConstants.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - - var body = new { capeId = capeId }; - string jsonBody = JsonSerializer.Serialize(body); - var content = new StringContent(jsonBody, Encoding.UTF8, "application/json"); - - var response = await LauncherConstants.Http.PutAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/capes/active", content); + var response = await HttpHelper.FetchAsync("https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/capes/active", method: HttpMethod.Put, body: new { capeId = capeId }, headers: new() { { "Authorization", $"Bearer {accessToken}" } } ); if (response.IsSuccessStatusCode) { string jsonResponse = await response.Content.ReadAsStringAsync(); @@ -75,18 +59,14 @@ public static class MojangAPI { } public static async Task ChangeUsernameAsync(string newName, string accessToken) { - LauncherConstants.Http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); - var url = $"https://yggdrasil.azures.fr/minecraftservices/minecraft/profile/name/{newName}"; - var request = new HttpRequestMessage(HttpMethod.Put, url); - var response = await LauncherConstants.Http.SendAsync(request); + var response = await HttpHelper.FetchAsync(url, method: HttpMethod.Put, headers: new() { { "Authorization", $"Bearer {accessToken}" } } ); if (response.IsSuccessStatusCode) { string jsonResponse = await response.Content.ReadAsStringAsync(); return JsonSerializer.Deserialize(jsonResponse, LauncherConstants._jsonOptions)!; - } - else { + } else { throw new Exception($"Erreur lors du changement de pseudo (Code: {response.StatusCode})"); } }