These changes allow players to matchmake for local wireless using a LDN server. The network implementation originates from Berry's public TCP RyuLDN fork. Logo and unrelated changes have been removed. Additionally displays LDN game status in the game selection window when RyuLDN is enabled. Functionality is only enabled while network mode is set to "RyuLDN" in the settings.
265 lines
11 KiB
C#
265 lines
11 KiB
C#
using Avalonia;
|
|
using Avalonia.Threading;
|
|
using DiscordRPC;
|
|
using Gommon;
|
|
using Projektanker.Icons.Avalonia;
|
|
using Projektanker.Icons.Avalonia.FontAwesome;
|
|
using Projektanker.Icons.Avalonia.MaterialDesign;
|
|
using Ryujinx.Ava.Common.Locale;
|
|
using Ryujinx.Ava.UI.Helpers;
|
|
using Ryujinx.Ava.UI.Windows;
|
|
using Ryujinx.Common;
|
|
using Ryujinx.Common.Configuration;
|
|
using Ryujinx.Common.GraphicsDriver;
|
|
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Common.SystemInterop;
|
|
using Ryujinx.Graphics.Vulkan.MoltenVK;
|
|
using Ryujinx.SDL2.Common;
|
|
using Ryujinx.UI.App.Common;
|
|
using Ryujinx.UI.Common;
|
|
using Ryujinx.UI.Common.Configuration;
|
|
using Ryujinx.UI.Common.Helper;
|
|
using Ryujinx.UI.Common.SystemInfo;
|
|
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Ryujinx.Ava
|
|
{
|
|
internal partial class Program
|
|
{
|
|
public static double WindowScaleFactor { get; set; }
|
|
public static double DesktopScaleFactor { get; set; } = 1.0;
|
|
public static string Version { get; private set; }
|
|
public static string ConfigurationPath { get; private set; }
|
|
public static bool PreviewerDetached { get; private set; }
|
|
public static bool UseHardwareAcceleration { get; private set; }
|
|
|
|
[LibraryImport("user32.dll", SetLastError = true)]
|
|
public static partial int MessageBoxA(nint hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
|
|
|
|
private const uint MbIconwarning = 0x30;
|
|
|
|
public static int Main(string[] args)
|
|
{
|
|
Version = ReleaseInformation.Version;
|
|
|
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
|
{
|
|
_ = MessageBoxA(nint.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
|
|
}
|
|
|
|
PreviewerDetached = true;
|
|
|
|
Initialize(args);
|
|
|
|
LoggerAdapter.Register();
|
|
|
|
IconProvider.Current
|
|
.Register<FontAwesomeIconProvider>()
|
|
.Register<MaterialDesignIconProvider>();
|
|
|
|
return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
|
|
}
|
|
|
|
public static AppBuilder BuildAvaloniaApp() =>
|
|
AppBuilder.Configure<App>()
|
|
.UsePlatformDetect()
|
|
.With(new X11PlatformOptions
|
|
{
|
|
EnableMultiTouch = true,
|
|
EnableIme = true,
|
|
EnableInputFocusProxy = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP") == "gamescope",
|
|
RenderingMode = UseHardwareAcceleration
|
|
? [X11RenderingMode.Glx, X11RenderingMode.Software]
|
|
: [X11RenderingMode.Software]
|
|
})
|
|
.With(new Win32PlatformOptions
|
|
{
|
|
WinUICompositionBackdropCornerRadius = 8.0f,
|
|
RenderingMode = UseHardwareAcceleration
|
|
? [Win32RenderingMode.AngleEgl, Win32RenderingMode.Software]
|
|
: [Win32RenderingMode.Software]
|
|
});
|
|
|
|
private static void Initialize(string[] args)
|
|
{
|
|
// Ensure Discord presence timestamp begins at the absolute start of when Ryujinx is launched
|
|
DiscordIntegrationModule.StartedAt = Timestamps.Now;
|
|
|
|
// Parse arguments
|
|
CommandLineState.ParseArguments(args);
|
|
|
|
if (OperatingSystem.IsMacOS())
|
|
{
|
|
MVKInitialization.InitializeResolver();
|
|
}
|
|
|
|
// Delete backup files after updating.
|
|
Task.Run(Updater.CleanupUpdate);
|
|
|
|
Console.Title = $"{App.FullAppName} Console {Version}";
|
|
|
|
// Hook unhandled exception and process exit events.
|
|
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
|
=> ProcessUnhandledException(sender, e.ExceptionObject as Exception, e.IsTerminating);
|
|
AppDomain.CurrentDomain.ProcessExit += (_, _) => Exit();
|
|
|
|
// Setup base data directory.
|
|
AppDataManager.Initialize(CommandLineState.BaseDirPathArg);
|
|
|
|
// Set the delegate for localizing the word "never" in the UI
|
|
ApplicationData.LocalizedNever = () => LocaleManager.Instance[LocaleKeys.Never];
|
|
|
|
// Initialize the configuration.
|
|
ConfigurationState.Initialize();
|
|
|
|
// Initialize the logger system.
|
|
LoggerModule.Initialize();
|
|
|
|
// Initialize Discord integration.
|
|
DiscordIntegrationModule.Initialize();
|
|
|
|
// Initialize SDL2 driver
|
|
SDL2Driver.MainThreadDispatcher = action => Dispatcher.UIThread.InvokeAsync(action, DispatcherPriority.Input);
|
|
|
|
ReloadConfig();
|
|
|
|
WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor();
|
|
|
|
// Logging system information.
|
|
PrintSystemInfo();
|
|
|
|
// Enable OGL multithreading on the driver, and some other flags.
|
|
DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
|
|
|
// Check if keys exists.
|
|
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
|
{
|
|
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
|
{
|
|
MainWindow.ShowKeyErrorOnLoad = true;
|
|
}
|
|
}
|
|
|
|
if (CommandLineState.LaunchPathArg != null)
|
|
{
|
|
MainWindow.DeferLoadApplication(CommandLineState.LaunchPathArg, CommandLineState.LaunchApplicationId, CommandLineState.StartFullscreenArg);
|
|
}
|
|
}
|
|
|
|
public static void ReloadConfig()
|
|
{
|
|
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
|
|
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
|
|
|
|
// Now load the configuration as the other subsystems are now registered
|
|
if (File.Exists(localConfigurationPath))
|
|
{
|
|
ConfigurationPath = localConfigurationPath;
|
|
}
|
|
else if (File.Exists(appDataConfigurationPath))
|
|
{
|
|
ConfigurationPath = appDataConfigurationPath;
|
|
}
|
|
|
|
if (ConfigurationPath == null)
|
|
{
|
|
// No configuration, we load the default values and save it to disk
|
|
ConfigurationPath = appDataConfigurationPath;
|
|
Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}");
|
|
|
|
ConfigurationState.Instance.LoadDefault();
|
|
ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath);
|
|
}
|
|
else
|
|
{
|
|
Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}");
|
|
|
|
if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat))
|
|
{
|
|
ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath);
|
|
}
|
|
else
|
|
{
|
|
Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}");
|
|
|
|
ConfigurationState.Instance.LoadDefault();
|
|
}
|
|
}
|
|
|
|
UseHardwareAcceleration = ConfigurationState.Instance.EnableHardwareAcceleration;
|
|
|
|
// Check if graphics backend was overridden
|
|
if (CommandLineState.OverrideGraphicsBackend is not null)
|
|
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = CommandLineState.OverrideGraphicsBackend.ToLower() switch
|
|
{
|
|
"opengl" => GraphicsBackend.OpenGl,
|
|
"vulkan" => GraphicsBackend.Vulkan,
|
|
_ => ConfigurationState.Instance.Graphics.GraphicsBackend
|
|
};
|
|
|
|
// Check if docked mode was overriden.
|
|
if (CommandLineState.OverrideDockedMode.HasValue)
|
|
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
|
|
|
|
|
|
// Check if HideCursor was overridden.
|
|
if (CommandLineState.OverrideHideCursor is not null)
|
|
ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor.ToLower() switch
|
|
{
|
|
"never" => HideCursorMode.Never,
|
|
"onidle" => HideCursorMode.OnIdle,
|
|
"always" => HideCursorMode.Always,
|
|
_ => ConfigurationState.Instance.HideCursor,
|
|
};
|
|
|
|
|
|
// Check if hardware-acceleration was overridden.
|
|
if (CommandLineState.OverrideHardwareAcceleration != null)
|
|
UseHardwareAcceleration = CommandLineState.OverrideHardwareAcceleration.Value;
|
|
}
|
|
|
|
private static void PrintSystemInfo()
|
|
{
|
|
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
|
SystemInfo.Gather().Print();
|
|
|
|
var enabledLogLevels = Logger.GetEnabledLevels().ToArray();
|
|
|
|
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogLevels.Length is 0
|
|
? "<None>"
|
|
: enabledLogLevels.JoinToString(", "))}");
|
|
|
|
Logger.Notice.Print(LogClass.Application,
|
|
AppDataManager.Mode == AppDataManager.LaunchMode.Custom
|
|
? $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}"
|
|
: $"Launch Mode: {AppDataManager.Mode}");
|
|
}
|
|
|
|
private static void ProcessUnhandledException(object sender, Exception ex, bool isTerminating)
|
|
{
|
|
Logger.Log log = Logger.Error ?? Logger.Notice;
|
|
string message = $"Unhandled exception caught: {ex}";
|
|
|
|
// ReSharper disable once ConstantConditionalAccessQualifier
|
|
if (sender?.GetType()?.AsPrettyString() is { } senderName)
|
|
log.Print(LogClass.Application, message, senderName);
|
|
else
|
|
log.PrintMsg(LogClass.Application, message);
|
|
|
|
if (isTerminating)
|
|
Exit();
|
|
}
|
|
|
|
public static void Exit()
|
|
{
|
|
DiscordIntegrationModule.Exit();
|
|
|
|
Logger.Shutdown();
|
|
}
|
|
}
|
|
}
|