Added a lot

-  Self contained runtime
  -  Translated french to english
  -  Fixed java path and url
This commit is contained in:
Gilles Lazures 2026-02-01 20:15:54 +01:00
parent 8444698e2a
commit b33779e77d
13 changed files with 38 additions and 22 deletions

View File

@ -8,6 +8,13 @@
<AssemblyName>LentiaLauncher</AssemblyName> <AssemblyName>LentiaLauncher</AssemblyName>
<ApplicationIcon>./src/resources/icon.ico</ApplicationIcon> <ApplicationIcon>./src/resources/icon.ico</ApplicationIcon>
<PublishTrimmed>false</PublishTrimmed> <PublishTrimmed>false</PublishTrimmed>
<RollForward>Major</RollForward>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishTrimmed>false</PublishTrimmed>
<PublishReadyToRun>true</PublishReadyToRun>
<PublishSingleFile>true</PublishSingleFile>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
</PropertyGroup> </PropertyGroup>
<Target Name="PackRenderer" BeforeTargets="DispatchToInnerBuild;BeforeBuild"> <Target Name="PackRenderer" BeforeTargets="DispatchToInnerBuild;BeforeBuild">
<PropertyGroup> <PropertyGroup>

View File

@ -29,6 +29,7 @@ class Program {
BrikPackage bpkg = new BrikPackage(Path.Combine(__dirname, "root.dat"), 66); BrikPackage bpkg = new BrikPackage(Path.Combine(__dirname, "root.dat"), 66);
List<string> entries = bpkg.ListEntries(); List<string> entries = bpkg.ListEntries();
var window = new PhotinoWindow() var window = new PhotinoWindow()
.SetTitle("Lentia")
.SetUseOsDefaultLocation(false) .SetUseOsDefaultLocation(false)
.SetUseOsDefaultSize(false) .SetUseOsDefaultSize(false)
.SetContextMenuEnabled(false) .SetContextMenuEnabled(false)
@ -232,6 +233,15 @@ class Program {
case "launcher::game": case "launcher::game":
try { try {
await GenericFilesService.FetchAndSyncGenericFiles(gameRoot); await GenericFilesService.FetchAndSyncGenericFiles(gameRoot);
MinecraftVersion version = await GameHelper.PrepareGame(new LaunchOptions {
Version = new LaunchOptions.VersionOptions { Number = "1.12.2", Type = "release" },
ModLoader = LaunchOptions.ModLoaderType.Forge
}, gameRoot);
string javaPath = await JavaHelper.GetJavaExecutablePath(version, gameRoot);
SettingsManager.Set("JavaPath", javaPath);
var options = new LaunchOptions { var options = new LaunchOptions {
Version = new LaunchOptions.VersionOptions { Version = new LaunchOptions.VersionOptions {
Number = "1.12.2", Number = "1.12.2",
@ -241,13 +251,13 @@ class Program {
Max = $"{SettingsManager.ReadSettings().Ram.Max}M", Max = $"{SettingsManager.ReadSettings().Ram.Max}M",
Min = "512M" Min = "512M"
}, },
JavaPath = javaPath,
ModLoader = LaunchOptions.ModLoaderType.Forge, ModLoader = LaunchOptions.ModLoaderType.Forge,
CustomArgs = new List<string> { CustomArgs = new List<string> {
$"-javaagent:{Path.Combine(gameRoot, LauncherConstants.AgentsPath.AuthlibInjector)}={LauncherConstants.Urls.YggdrasilServer}" $"-javaagent:{Path.Combine(gameRoot, LauncherConstants.AgentsPath.AuthlibInjector)}={LauncherConstants.Urls.YggdrasilServer}"
} }
}; };
MinecraftVersion version = await GameHelper.PrepareGame(options, gameRoot);
await JavaHelper.GetJavaExecutablePath(version, gameRoot);
GameHelper.Launch(version, _authenticatedPlayer!, gameRoot, options, (logLine) => { GameHelper.Launch(version, _authenticatedPlayer!, gameRoot, options, (logLine) => {
var logMessage = new { var logMessage = new {
requestId = "game::log", requestId = "game::log",

View File

@ -7,8 +7,8 @@ namespace Lentia.Core.Constants {
public const string MojangManifest = "https://launchermeta.mojang.com/mc/game/version_manifest_v2.json"; public const string MojangManifest = "https://launchermeta.mojang.com/mc/game/version_manifest_v2.json";
public const string YggdrasilServer = "https://yggdrasil.azures.fr/"; public const string YggdrasilServer = "https://yggdrasil.azures.fr/";
public const string MojangAuthServer = YggdrasilServer + "authserver"; public const string MojangAuthServer = YggdrasilServer + "authserver";
public const string ApiUrl = "https://lentia-api.azures.fr"; public const string ApiUrl = "https://lentia-api.azures.fr";
public const string AllJavaRuntime = "https://launchermeta.mojang.com/v1/products/java-runtime/2ec0cc96c44e5a76b9c8b7c39df7210883d12871/all.json";
} }
public static class AgentsPath { public static class AgentsPath {

View File

@ -90,9 +90,9 @@ public class Authenticator {
var data = await response.Content.ReadFromJsonAsync<AuthenticateResponse>(); var data = await response.Content.ReadFromJsonAsync<AuthenticateResponse>();
return data != null ? AuthResult.Ok(data) : AuthResult.Fail("Erreur de lecture des données."); return data != null ? AuthResult.Ok(data) : AuthResult.Fail("Erreur de lecture des données.");
} catch (HttpRequestException ex) { } catch (HttpRequestException ex) {
return AuthResult.Fail($"Erreur réseau : {ex.Message}", "NETWORK_ERROR"); return AuthResult.Fail($"Network error : {ex.Message}", "NETWORK_ERROR");
} catch (Exception ex) { } catch (Exception ex) {
return AuthResult.Fail($"Erreur interne : {ex.Message}", "INTERNAL_ERROR"); return AuthResult.Fail($"Internal error : {ex.Message}", "INTERNAL_ERROR");
} }
} }
} }

View File

@ -40,12 +40,12 @@ public class Discord {
var response = await HttpHelper.FetchAsync(url, HttpMethod.Get); var response = await HttpHelper.FetchAsync(url, HttpMethod.Get);
if (response.IsSuccessStatusCode) { if (response.IsSuccessStatusCode) {
var data = await response.Content.ReadFromJsonAsync<AuthenticateResponse>(); var data = await response.Content.ReadFromJsonAsync<AuthenticateResponse>();
return data != null ? AuthResult.Ok(data) : AuthResult.Fail("Erreur de lecture des données."); return data != null ? AuthResult.Ok(data) : AuthResult.Fail("Data reading error.");
} else { } else {
var errorData = await response.Content.ReadFromJsonAsync<YggdrasilError>(); var errorData = await response.Content.ReadFromJsonAsync<YggdrasilError>();
Console.WriteLine(await response.Content.ReadAsStringAsync()); Console.WriteLine(await response.Content.ReadAsStringAsync());
return AuthResult.Fail( return AuthResult.Fail(
errorData?.errorMessage ?? "Identifiants invalides.", errorData?.errorMessage ?? "Invalid credentials.",
errorData?.error, errorData?.error,
errorData?.cause errorData?.cause
); );

View File

@ -31,10 +31,10 @@ public class AssetsHelper {
string json = await response.Content.ReadAsStringAsync(); string json = await response.Content.ReadAsStringAsync();
var index = JsonSerializer.Deserialize<AssetsIndex>(json, LauncherConstants._jsonOptions); var index = JsonSerializer.Deserialize<AssetsIndex>(json, LauncherConstants._jsonOptions);
return index ?? throw new Exception("L'index des assets est vide ou invalide."); return index ?? throw new Exception("The assets index is empty or invalid");
} }
throw new Exception($"Impossible de récupérer l'index des assets : {response.StatusCode}"); throw new Exception($"Assets index unavailable : {response.StatusCode}");
} }
public static async Task DownloadAssets(AssetsIndex index, string rootDir) { public static async Task DownloadAssets(AssetsIndex index, string rootDir) {
@ -87,7 +87,7 @@ public class AssetsHelper {
await FileHelper.DownloadFileAsync(url, localPath); await FileHelper.DownloadFileAsync(url, localPath);
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine(url); Console.WriteLine(url);
Console.WriteLine($"Échec : {hash} - {ex.Message}"); Console.WriteLine($"Fail : {hash} - {ex.Message}");
} }
}); });
} }

View File

@ -69,15 +69,13 @@ public static class JavaHelper {
} }
private static async Task DownloadJavaRuntime(string root, string platform, string component) { 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 allRuntimesResponse = await HttpHelper.FetchAsync(LauncherConstants.Urls.AllJavaRuntime);
var allRuntimesResponse = await HttpHelper.FetchAsync(allJsonUrl);
allRuntimesResponse.EnsureSuccessStatusCode(); allRuntimesResponse.EnsureSuccessStatusCode();
var allRuntimes = await allRuntimesResponse.Content.ReadFromJsonAsync<JsonElement>(LauncherConstants._jsonOptions); var allRuntimes = await allRuntimesResponse.Content.ReadFromJsonAsync<JsonElement>(LauncherConstants._jsonOptions);
if (!allRuntimes.TryGetProperty(platform, out var platformData) || if (!allRuntimes.TryGetProperty(platform, out var platformData) ||
!platformData.TryGetProperty(component, out var componentVersions)) { !platformData.TryGetProperty(component, out var componentVersions)) {
throw new Exception($"Le runtime {component} n'est pas disponible pour {platform}"); throw new Exception($"There is no {component} runtime for {platform}");
} }
string manifestUrl = componentVersions[0].GetProperty("manifest").GetProperty("url").GetString()!; string manifestUrl = componentVersions[0].GetProperty("manifest").GetProperty("url").GetString()!;

View File

@ -212,9 +212,9 @@ public static class VersionsHelper {
string jsonResponse = await response.Content.ReadAsStringAsync(); string jsonResponse = await response.Content.ReadAsStringAsync();
var version = JsonSerializer.Deserialize<MinecraftVersion>(jsonResponse, LauncherConstants._jsonOptions); var version = JsonSerializer.Deserialize<MinecraftVersion>(jsonResponse, LauncherConstants._jsonOptions);
return version ?? throw new Exception("Impossible de lire les détails de la version."); return version ?? throw new Exception("Version details data are unreadable.");
} else { } else {
throw new Exception($"Erreur lors de la récupération des détails : {response.StatusCode}"); throw new Exception($"Error when getting version details: {response.StatusCode}");
} }
} }

View File

@ -24,7 +24,7 @@ public static class ForgeHelper {
string promoKey = $"{mcVersion}-{(recommended ? "recommended" : "latest")}"; string promoKey = $"{mcVersion}-{(recommended ? "recommended" : "latest")}";
if (promoData?.Promos == null || !promoData.Promos.TryGetValue(promoKey, out string? buildVersion)) { if (promoData?.Promos == null || !promoData.Promos.TryGetValue(promoKey, out string? buildVersion)) {
throw new Exception($"Version Forge introuvable pour {promoKey}"); throw new Exception($"No forge version found for : {promoKey}");
} }
bool isModern = IsVersionModern(mcVersion); bool isModern = IsVersionModern(mcVersion);

View File

@ -2,6 +2,7 @@ namespace Lentia.Core.Utils;
public static class FileHelper { public static class FileHelper {
public static async Task DownloadFileAsync(string url, string destinationPath) { public static async Task DownloadFileAsync(string url, string destinationPath) {
Console.WriteLine($"Downloading file from {url} to {destinationPath}");
string? directory = Path.GetDirectoryName(destinationPath); string? directory = Path.GetDirectoryName(destinationPath);
if (!string.IsNullOrEmpty(directory)) { if (!string.IsNullOrEmpty(directory)) {
Directory.CreateDirectory(directory); Directory.CreateDirectory(directory);

View File

@ -8,6 +8,7 @@ public static class HttpHelper {
private static readonly HttpClient Http = new HttpClient(); private static readonly HttpClient Http = new HttpClient();
public static async Task<HttpResponseMessage> FetchAsync(string url, HttpMethod? method = null, object? body = null, Dictionary<string, string>? headers = null) { public static async Task<HttpResponseMessage> FetchAsync(string url, HttpMethod? method = null, object? body = null, Dictionary<string, string>? headers = null) {
Console.WriteLine($"Fetching {url} using {method} method");
var request = new HttpRequestMessage(method ?? HttpMethod.Get, url); var request = new HttpRequestMessage(method ?? HttpMethod.Get, url);
if (headers != null) { if (headers != null) {

View File

@ -64,7 +64,7 @@ public static class SettingsManager {
foreach (var k in keys) { foreach (var k in keys) {
var property = current.GetType().GetProperty(k); var property = current.GetType().GetProperty(k);
if (property == null) throw new Exception($"Clé {k} introuvable."); if (property == null) throw new Exception($"Key {k} not found.");
current = property.GetValue(current)!; current = property.GetValue(current)!;
} }
@ -85,7 +85,7 @@ public static class SettingsManager {
var prop = current.GetType().GetProperty(properties[i], var prop = current.GetType().GetProperty(properties[i],
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase); System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (prop == null) throw new Exception($"Niveau '{properties[i]}' introuvable."); if (prop == null) throw new Exception($"Level '{properties[i]}' not found.");
var next = prop.GetValue(current); var next = prop.GetValue(current);
if (next == null) { if (next == null) {
@ -99,14 +99,14 @@ public static class SettingsManager {
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase); System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (lastProp == null || !lastProp.CanWrite) if (lastProp == null || !lastProp.CanWrite)
throw new Exception($"Propriété '{properties[^1]}' introuvable ou verrouillée."); throw new Exception($"Property '{properties[^1]}' not found or locked.");
object? convertedValue = Convert.ChangeType(value.ToString(), lastProp.PropertyType); object? convertedValue = Convert.ChangeType(value.ToString(), lastProp.PropertyType);
lastProp.SetValue(current, convertedValue); lastProp.SetValue(current, convertedValue);
WriteSettings(settings); WriteSettings(settings);
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine($"[SettingsManager] Erreur Set sur '{path}': {ex.Message}"); Console.WriteLine($"[SettingsManager] \"set\" Error on '{path}': {ex.Message}");
} }
} }
} }

View File

@ -69,7 +69,6 @@ public static class WindowHelper {
Size loginSize = GetScaledSize(window, 310, 420); Size loginSize = GetScaledSize(window, 310, 420);
window window
.SetTitle("Lentia")
.SetIconFile(iconPath) .SetIconFile(iconPath)
.SetResizable(false) .SetResizable(false)
.SetSize(loginSize) .SetSize(loginSize)