LentiaLauncher/src/main/Program.cs
2026-01-27 03:22:24 +01:00

273 lines
13 KiB
C#

using AzuresPackager;
using Lentia.Core.Auth.OAuth2;
using Lentia.Core.Constants;
using Lentia.Core.Game;
using Lentia.Utils;
using Photino.NET;
using System.Reflection;
using System.Text;
using System.Text.Json;
using LentRules = Lentia.Core.Auth.Yggdrasil;
namespace Lentia;
class Program {
private static readonly string __dirname = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
private static LentRules.AuthenticateResponse? _authenticatedPlayer;
private static readonly string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
private static readonly string gameRoot = Path.Combine(appData, ".lentia2");
private static readonly LentRules.Authenticator _ygg = new();
[STAThread]
static void Main(string[] args) {
SettingsManager.InitSettings(gameRoot);
CreateLoginWindow().WaitForClose();
}
public static PhotinoWindow CreateLoginWindow() {
AzuresPackage azp = new AzuresPackage(Path.Combine(__dirname, "root.dat"), 66);
List<string> entries = azp.ListEntries();
var window = new PhotinoWindow()
.SetUseOsDefaultLocation(false)
.SetUseOsDefaultSize(false)
.SetContextMenuEnabled(false)
.SetChromeless(true)
.RegisterCustomSchemeHandler("http", (object sender, string scheme, string url, out string contentType) =>{
Uri uri = new Uri(url);
if (uri.Host == "internal") {
string internalPath = uri.AbsolutePath;
contentType = MimeHelper.GetMimeType(uri.AbsolutePath);
byte[] fileData = azp.GetFileBytes(internalPath.TrimStart('/'));
return fileData != null ? new MemoryStream(fileData) : null;
}
contentType = null!;
return null;
});
window.WindowCreated += (sender, e) => {
SetupBridge(window);
WindowHelper.MakeLoginWindow(window);
};
window.Load("http://internal/login.html");
return window;
}
public static void SetupBridge(PhotinoWindow window) {
window.RegisterWebMessageReceivedHandler(async (object? sender, string message) => {
try {
var json = JsonDocument.Parse(message);
string requestId = json.RootElement.GetProperty("requestId").GetString()!;
string method = json.RootElement.GetProperty("functionName").GetString()!;
var jsonPayload = json.RootElement.GetProperty("payload");
var pw = (PhotinoWindow)sender!;
object? payload = null;
switch (method) {
case "launcher::version":
payload = "v1.0.0-beta";
break;
case "window::close":
pw.Close();
break;
case "lentia::dynmap":
payload = "http://azures.fr:8123/";
break;
case "hardware::ram":
payload = Hardware.GetRamUsage();
break;
case "auth::lentia":
string user = jsonPayload.GetProperty("username").GetString()!;
string pass = jsonPayload.GetProperty("password").GetString()!;
var lentiaAuthResult = await _ygg.Login(user, pass);
if (lentiaAuthResult.Success) {
_authenticatedPlayer = lentiaAuthResult.Player;
WindowHelper.MakeStandardWindow(pw, 1356, 720, "http://internal/logged.html");
payload = new { success = true };
} else {
payload = new { success = false, error = lentiaAuthResult.Error };
}
break;
case "auth::logout":
_authenticatedPlayer = null;
pw.Load("http://internal/login.html");
WindowHelper.MakeLoginWindow(pw);
payload = true;
break;
case "dialog::error":
string title = jsonPayload.TryGetProperty("title", out var t) ? t.GetString()! : "Lentia";
string msg = jsonPayload.GetProperty("message").GetString()!;
pw.ShowMessage(title, msg, PhotinoDialogButtons.Ok, PhotinoDialogIcon.Error);
payload = new { success = true };
break;
case "settings::read":
payload = SettingsManager.ReadSettings();
break;
case "settings::set":
if (jsonPayload.TryGetProperty("key", out var keyElement) && jsonPayload.TryGetProperty("value", out var valueElement)) {
string key = keyElement.GetString()!;
object? valueToSet = valueElement.ValueKind switch {
JsonValueKind.String => valueElement.GetString(),
JsonValueKind.Number => valueElement.TryGetInt32(out int i) ? i : valueElement.GetDouble(),
JsonValueKind.True => true,
JsonValueKind.False => false,
JsonValueKind.Null => null,
_ => valueElement
};
if (valueToSet != null) {
SettingsManager.Set(key, valueToSet);
payload = new { success = true };
} else {
payload = new { success = false, error = "Incorrect or null value" };
}
}
break;
case "dialog::selectSkin":
var imageFilter = new (string Name, string[] Extensions)[] {
("Portable Network Graphic Image", new[] { "*.png" })
};
string[] files = window.ShowOpenFile("Choisis ton plus beau skin mon reuf", null, false, imageFilter);
payload = new {
success = files != null && files.Length > 0,
path = (files != null && files.Length > 0) ? files[0] : null
};
break;
case "network::uploadSkin":
try {
string path = jsonPayload.GetProperty("path").GetString()!;
string variant = jsonPayload.GetProperty("variant").GetString()!;
string token = _authenticatedPlayer?.AccessToken!;
string skinResult = await MojangAPI.UploadSkinAsync(path, variant, token);
payload = new { success = true };
} catch (Exception ex) {
payload = new { success = false, error = ex.Message };
}
break;
case "network::getPlayer":
try {
var profileData = await MojangAPI.GetPlayerProfileAsync(_authenticatedPlayer!.AccessToken);
payload = profileData;
} catch (Exception ex) {
payload = new { success = false, error = ex.Message };
}
break;
case "network::hideCape":
try {
var hideCapeResult = await MojangAPI.HideCapeAsync(_authenticatedPlayer!.AccessToken);
payload = new { success = true, data = hideCapeResult };
} catch (Exception ex) {
payload = new { success = false, error = ex.Message };
}
break;
case "network::showCape":
try {
string token = _authenticatedPlayer?.AccessToken!;
string capeId = jsonPayload.GetProperty("capeId").GetString()!;
var showCapeResult = await MojangAPI.ShowCapeAsync(token, capeId);
payload = new { success = true, data = showCapeResult };
} catch (Exception ex) {
payload = new { success = false, error = ex.Message };
}
break;
case "network::changeUsername":
try {
string newName = jsonPayload.GetProperty("username").GetString()!;
string token = _authenticatedPlayer?.AccessToken!;
if (string.IsNullOrEmpty(token)) throw new Exception("Session expirée ou invalide.");
var updatedProfile = await MojangAPI.ChangeUsernameAsync(newName, token);
if (_authenticatedPlayer != null) {
_authenticatedPlayer.User.Username = newName;
}
payload = new { success = true, profile = updatedProfile };
} catch (Exception ex) {
payload = new { success = false, error = ex.Message };
}
break;
case "oauth2::discord":
BashUtils.OpenUrl("https://yggdrasil.azures.fr/auth/provider/discord/login");
string? code = await Discord.ListenForCode();
var discordLoginResult = await Discord.LoginWithDiscordOAuth2(code!);
if (discordLoginResult.Success) {
_authenticatedPlayer = discordLoginResult.Player;
WindowHelper.MakeStandardWindow(pw, 1356, 720, "http://internal/logged.html");
payload = new { success = true };
} else {
Console.WriteLine(discordLoginResult);
payload = new { success = false, error = discordLoginResult.Error };
}
break;
case "launcher::game":
try {
await GenericFilesService.FetchAndSyncGenericFiles(gameRoot);
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,
CustomArgs = new List<string> {
$"-javaagent:{Path.Combine(gameRoot, LauncherConstants.AgentsPath.AuthlibInjector)}={LauncherConstants.Urls.YggdrasilServer}"
}
};
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() }
};
window.SendWebMessage(JsonSerializer.Serialize(logMessage, LauncherConstants._jsonOptions));
});
payload = new { success = true };
} catch (Exception) {
payload = new { success = false };
throw;
}
break;
}
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}");
}
});
}
}