Compare commits

...

6 Commits

Author SHA1 Message Date
Frog Business
74133b271c Merge 6d78e71fc7 into 2ffaeb2803 2025-02-17 02:37:49 +02:00
Evan Husted
2ffaeb2803 HLE: LDN: Reduce NAT timeout from 5 seconds to 2.5 2025-02-16 17:48:19 -06:00
Willians
b16b844760 Update PT-BR Big Translation & Add DKC Returns HD to compatibility list (#666)
Today I had a free day to look at and work on the entire Brazilian
Portuguese language.

Translations that had not been done before have been added
Fixed several translations that didn't make sense
Translated description of functions that were not translated correctly
Words were written as close to the original English
Add Donkey Kong Country Returns HD to compatibility list
2025-02-16 15:39:42 -06:00
Vudjun
bc07bc482d Gracefully handle errors in socket creation (#662)
In all other calls, exceptions are handled within the ManagedSocket
class, this can't easily be done here as the exception is thrown from
the constructor, so the exception is handled in ISocket and the correct
error is passed to the application.

This should fix #660
2025-02-16 02:26:37 -06:00
Vudjun
61975ca44d Add logging in socket implementation (#661)
Adds logging to most Socket calls to help with debugging network
issues.

Shouldn't affect any functionality. There's a small chance it could spam
the log in some games.
2025-02-16 01:03:44 -06:00
Barış Hamil
6d78e71fc7 Handheld Gyro 2025-02-01 22:26:05 +03:00
23 changed files with 701 additions and 391 deletions

View File

@@ -3,6 +3,7 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="Alimer.Bindings.SDL" Version="3.7.1" />
<PackageVersion Include="Avalonia" Version="11.0.13" /> <PackageVersion Include="Avalonia" Version="11.0.13" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" /> <PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.0.13" />
<PackageVersion Include="Avalonia.Desktop" Version="11.0.13" /> <PackageVersion Include="Avalonia.Desktop" Version="11.0.13" />

View File

@@ -75,6 +75,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Input.SDL3", "src\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj", "{3BF24278-547D-42C2-9D43-182B978F54DD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Metal", "src\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj", "{C08931FA-1191-417A-864F-3882D93E683B}"
@@ -259,6 +261,10 @@ Global
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Debug|Any CPU.Build.0 = Debug|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.ActiveCfg = Release|Any CPU
{81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU {81EA598C-DBA1-40B0-8DA4-4796B78F2037}.Release|Any CPU.Build.0 = Release|Any CPU
{3BF24278-547D-42C2-9D43-182B978F54DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BF24278-547D-42C2-9D43-182B978F54DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BF24278-547D-42C2-9D43-182B978F54DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BF24278-547D-42C2-9D43-182B978F54DD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -969,6 +969,7 @@
0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34 0100751007ADA000,"Don't Starve: Nintendo Switch Edition",nvdec,playable,2022-02-05 20:43:34
010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26 010088B010DD2000,"Dongo Adventure",,playable,2022-10-04 16:22:26
0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10 0100C1F0051B6000,"Donkey Kong Country™: Tropical Freeze",,playable,2024-08-05 16:46:10
01009D901BC56000,"Donkey Kong Country™: Returns HD",gpu,ingame,2025-02-16 13:44:12
0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48 0100F2C00F060000,"Doodle Derby",,boots,2020-12-04 22:51:48
0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07 0100416004C00000,"DOOM",gpu;slow;nvdec;online-broken,ingame,2024-09-23 15:40:07
010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19 010018900DD00000,"DOOM (1993)",nvdec;online-broken,menus,2022-09-06 13:32:19
1 title_id game_name labels status last_updated
969 0100751007ADA000 Don't Starve: Nintendo Switch Edition nvdec playable 2022-02-05 20:43:34
970 010088B010DD2000 Dongo Adventure playable 2022-10-04 16:22:26
971 0100C1F0051B6000 Donkey Kong Country™: Tropical Freeze playable 2024-08-05 16:46:10
972 01009D901BC56000 Donkey Kong Country™: Returns HD gpu ingame 2025-02-16 13:44:12
973 0100F2C00F060000 Doodle Derby boots 2020-12-04 22:51:48
974 0100416004C00000 DOOM gpu;slow;nvdec;online-broken ingame 2024-09-23 15:40:07
975 010018900DD00000 DOOM (1993) nvdec;online-broken menus 2022-09-06 13:32:19

View File

@@ -56,6 +56,7 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
return motionBackendType switch return motionBackendType switch
{ {
MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController), MotionInputBackendType.GamepadDriver => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController),
MotionInputBackendType.Handheld => JsonSerializer.Deserialize(ref reader, _serializerContext.StandardMotionConfigController),
MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, _serializerContext.CemuHookMotionConfigController), MotionInputBackendType.CemuHook => JsonSerializer.Deserialize(ref reader, _serializerContext.CemuHookMotionConfigController),
_ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"), _ => throw new InvalidOperationException($"Unknown backend type {motionBackendType}"),
}; };
@@ -66,6 +67,7 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
switch (value.MotionBackend) switch (value.MotionBackend)
{ {
case MotionInputBackendType.GamepadDriver: case MotionInputBackendType.GamepadDriver:
case MotionInputBackendType.Handheld:
JsonSerializer.Serialize(writer, value as StandardMotionConfigController, _serializerContext.StandardMotionConfigController); JsonSerializer.Serialize(writer, value as StandardMotionConfigController, _serializerContext.StandardMotionConfigController);
break; break;
case MotionInputBackendType.CemuHook: case MotionInputBackendType.CemuHook:

View File

@@ -9,5 +9,6 @@ namespace Ryujinx.Common.Configuration.Hid.Controller.Motion
Invalid, Invalid,
GamepadDriver, GamepadDriver,
CemuHook, CemuHook,
Handheld,
} }
} }

View File

@@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy
public async Task<ushort> NatPunch() public async Task<ushort> NatPunch()
{ {
NatDiscoverer discoverer = new(); NatDiscoverer discoverer = new();
CancellationTokenSource cts = new(5000); CancellationTokenSource cts = new(2500);
NatDevice device; NatDevice device;

View File

@@ -34,6 +34,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
if (errorCode != LinuxError.SUCCESS) if (errorCode != LinuxError.SUCCESS)
{ {
if (errorCode != LinuxError.EWOULDBLOCK)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Operation failed with error {errorCode}.");
}
result = -1; result = -1;
} }
@@ -66,6 +70,8 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32(); BsdSocketType type = (BsdSocketType)context.RequestData.ReadInt32();
ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32(); ProtocolType protocol = (ProtocolType)context.RequestData.ReadInt32();
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Creating socket with domain={domain}, type={type}, protocol={protocol}");
BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift); BsdSocketCreationFlags creationFlags = (BsdSocketCreationFlags)((int)type >> (int)BsdSocketCreationFlags.FlagsShift);
type &= BsdSocketType.TypeMask; type &= BsdSocketType.TypeMask;
@@ -95,12 +101,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
} }
} }
ISocket newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
{
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
};
LinuxError errno = LinuxError.SUCCESS; LinuxError errno = LinuxError.SUCCESS;
ISocket newBsdSocket;
try
{
newBsdSocket = new ManagedSocket(netDomain, (SocketType)type, protocol, context.Device.Configuration.MultiplayerLanInterfaceId)
{
Blocking = !creationFlags.HasFlag(BsdSocketCreationFlags.NonBlocking),
};
}
catch (SocketException exception)
{
LinuxError errNo = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
return WriteBsdResult(context, 0, errNo);
}
int newSockFd = _context.RegisterFileDescriptor(newBsdSocket); int newSockFd = _context.RegisterFileDescriptor(newBsdSocket);
@@ -111,6 +126,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if (exempt) if (exempt)
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Disconnecting exempt socket.");
newBsdSocket.Disconnect(); newBsdSocket.Disconnect();
} }
@@ -797,7 +813,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
errno = socket.Listen(backlog); errno = socket.Listen(backlog);
} }
else
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid socket fd '{socketFd}'.");
}
return WriteBsdResult(context, 0, errno); return WriteBsdResult(context, 0, errno);
} }

View File

@@ -92,18 +92,30 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
newSocket = new ManagedSocket(Socket.Accept()); newSocket = new ManagedSocket(Socket.Accept());
IPEndPoint remoteEndPoint = newSocket.RemoteEndPoint;
bool isPrivateIp = remoteEndPoint.Address.ToString().StartsWith("192.168.");
Logger.Info?.PrintMsg(LogClass.ServiceBsd,
isPrivateIp
? $"Accepted connection from {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}"
: $"Accepted connection from {ProtocolType}/***:{remoteEndPoint.Port}");
return LinuxError.SUCCESS; return LinuxError.SUCCESS;
} }
catch (SocketException exception) catch (SocketException exception)
{ {
newSocket = null; newSocket = null;
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
public LinuxError Bind(IPEndPoint localEndPoint) public LinuxError Bind(IPEndPoint localEndPoint)
{ {
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket binding to: {ProtocolType}/{localEndPoint.Port}");
try try
{ {
Socket.Bind(localEndPoint); Socket.Bind(localEndPoint);
@@ -112,6 +124,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -123,6 +139,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public LinuxError Connect(IPEndPoint remoteEndPoint) public LinuxError Connect(IPEndPoint remoteEndPoint)
{ {
bool isLDNPrivateIP = remoteEndPoint.Address.ToString().StartsWith("192.168.");
if (isLDNPrivateIP)
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/{remoteEndPoint.Address}:{remoteEndPoint.Port}");
}
else
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Connecting to: {ProtocolType}/***:{remoteEndPoint.Port}");
}
try try
{ {
Socket.Connect(remoteEndPoint); Socket.Connect(remoteEndPoint);
@@ -137,6 +162,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
else else
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -144,11 +173,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
public void Disconnect() public void Disconnect()
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Socket disconnecting");
Socket.Disconnect(true); Socket.Disconnect(true);
} }
public void Dispose() public void Dispose()
{ {
Logger.Info?.Print(LogClass.ServiceBsd, "Socket closed");
Socket.Close(); Socket.Close();
Socket.Dispose(); Socket.Dispose();
} }
@@ -159,10 +190,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
Socket.Listen(backlog); Socket.Listen(backlog);
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket listening: {ProtocolType}/{(Socket.LocalEndPoint as IPEndPoint).Port}");
return LinuxError.SUCCESS; return LinuxError.SUCCESS;
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -182,11 +219,15 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
bool hasEmittedBlockingWarning = false; private bool _hasEmittedBlockingWarning;
public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags) public LinuxError Receive(out int receiveSize, Span<byte> buffer, BsdSocketFlags flags)
{ {
@@ -202,10 +243,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true; shouldBlockAfterOperation = true;
} }
if (Blocking && !hasEmittedBlockingWarning) if (Blocking && !_hasEmittedBlockingWarning)
{ {
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
hasEmittedBlockingWarning = true; _hasEmittedBlockingWarning = true;
} }
receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags)); receiveSize = Socket.Receive(buffer, ConvertBsdSocketFlags(flags));
@@ -214,6 +255,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1; receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -245,10 +290,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
shouldBlockAfterOperation = true; shouldBlockAfterOperation = true;
} }
if (Blocking && !hasEmittedBlockingWarning) if (Blocking && !_hasEmittedBlockingWarning)
{ {
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors."); Logger.Warning?.PrintMsg(LogClass.ServiceBsd, "Blocking socket operations are not yet working properly. Expect network errors.");
hasEmittedBlockingWarning = true; _hasEmittedBlockingWarning = true;
} }
if (!Socket.IsBound) if (!Socket.IsBound)
@@ -265,6 +310,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
receiveSize = -1; receiveSize = -1;
result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode); result = WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -288,6 +337,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1; sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -304,6 +357,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
sendSize = -1; sendSize = -1;
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
@@ -341,6 +398,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -387,6 +448,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -519,6 +584,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }
@@ -557,6 +626,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
} }
catch (SocketException exception) catch (SocketException exception)
{ {
if (exception.SocketErrorCode != SocketError.WouldBlock)
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Socket Exception: {exception}");
}
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode); return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
} }
} }

View File

@@ -1,4 +1,6 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy; using Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnRyu.Proxy;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -64,10 +66,18 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Proxy
{ {
if (_proxy.Supported(domain, type, protocol)) if (_proxy.Supported(domain, type, protocol))
{ {
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Socket is using LDN proxy");
return new LdnProxySocket(domain, type, protocol, _proxy); return new LdnProxySocket(domain, type, protocol, _proxy);
} }
else
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"LDN proxy does not support socket {domain}, {type}, {protocol}");
}
}
else
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Opening socket using host networking stack");
} }
return new DefaultSocket(domain, type, protocol, lanInterfaceId); return new DefaultSocket(domain, type, protocol, lanInterfaceId);
} }
} }

View File

@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
<PackageReference Include="Alimer.Bindings.SDL" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,85 @@
using SDL3;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using static SDL3.SDL3;
namespace Ryujinx.Input.SDL3
{
public unsafe class SDL3MotionDriver : IHandheld, IDisposable
{
private readonly Dictionary<SDL_SensorType, SDL_Sensor> sensors;
private bool _disposed;
public SDL3MotionDriver()
{
int result = SDL_Init(SDL_InitFlags.Sensor);
if (result < 0)
{
throw new InvalidOperationException($"SDL sensor initialization failed: {SDL_GetError()}");
}
sensors = SDL_GetSensors().ToArray().ToDictionary(SDL_GetSensorTypeForID, SDL_OpenSensor);
}
~SDL3MotionDriver()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing && sensors != null)
{
foreach (var sensor in sensors.Values)
{
if (sensor != IntPtr.Zero)
{
SDL_CloseSensor(sensor);
}
}
}
_disposed = true;
}
public Vector3 GetMotionData(MotionInputId inputType)
{
ObjectDisposedException.ThrowIf(_disposed, this);
return inputType switch
{
MotionInputId.Gyroscope => GetSensorVector(SDL_SensorType.Gyro) * 180 / MathF.PI,
MotionInputId.Accelerometer => GetSensorVector(SDL_SensorType.Accel) / SDL_STANDARD_GRAVITY,
_ => Vector3.Zero
};
}
private Vector3 GetSensorVector(SDL_SensorType sensorType)
{
if (!sensors.TryGetValue(sensorType, out SDL_Sensor sensor))
{
return Vector3.Zero;
}
var data = stackalloc float[3];
if (SDL_GetSensorData(sensor, data, 3) < 0)
{
return Vector3.Zero;
}
return new Vector3(data[0], data[1], data[2]);
}
}
}

View File

@@ -2,12 +2,13 @@ using System;
namespace Ryujinx.Input.HLE namespace Ryujinx.Input.HLE
{ {
public class InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver) public class InputManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IHandheld handheld)
: IDisposable : IDisposable
{ {
public IGamepadDriver KeyboardDriver { get; } = keyboardDriver; public IGamepadDriver KeyboardDriver { get; } = keyboardDriver;
public IGamepadDriver GamepadDriver { get; } = gamepadDriver; public IGamepadDriver GamepadDriver { get; } = gamepadDriver;
public IGamepadDriver MouseDriver { get; private set; } public IGamepadDriver MouseDriver { get; private set; }
public IHandheld Handheld { get; } = handheld;
public void SetMouseDriver(IGamepadDriver mouseDriver) public void SetMouseDriver(IGamepadDriver mouseDriver)
{ {
@@ -18,7 +19,7 @@ namespace Ryujinx.Input.HLE
public NpadManager CreateNpadManager() public NpadManager CreateNpadManager()
{ {
return new NpadManager(KeyboardDriver, GamepadDriver, MouseDriver); return new NpadManager(KeyboardDriver, GamepadDriver, MouseDriver, Handheld);
} }
public TouchScreenManager CreateTouchScreenManager() public TouchScreenManager CreateTouchScreenManager()
@@ -38,6 +39,7 @@ namespace Ryujinx.Input.HLE
KeyboardDriver?.Dispose(); KeyboardDriver?.Dispose();
GamepadDriver?.Dispose(); GamepadDriver?.Dispose();
MouseDriver?.Dispose(); MouseDriver?.Dispose();
Handheld?.Dispose();
} }
} }

View File

@@ -218,12 +218,14 @@ namespace Ryujinx.Input.HLE
public string Id { get; private set; } public string Id { get; private set; }
private readonly CemuHookClient _cemuHookClient; private readonly CemuHookClient _cemuHookClient;
private readonly IHandheld _handheld;
public NpadController(CemuHookClient cemuHookClient) public NpadController(CemuHookClient cemuHookClient, IHandheld handheld)
{ {
State = default; State = default;
Id = null; Id = null;
_cemuHookClient = cemuHookClient; _cemuHookClient = cemuHookClient;
_handheld = handheld;
} }
public bool UpdateDriverConfiguration(IGamepadDriver gamepadDriver, InputConfig config) public bool UpdateDriverConfiguration(IGamepadDriver gamepadDriver, InputConfig config)
@@ -287,6 +289,18 @@ namespace Ryujinx.Input.HLE
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion) if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
{ {
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.Handheld)
{
Vector3 accelerometer = _handheld.GetMotionData(MotionInputId.Accelerometer);
Vector3 gyroscope = _handheld.GetMotionData(MotionInputId.Gyroscope);
accelerometer = new Vector3(accelerometer.X, -accelerometer.Z, accelerometer.Y);
gyroscope = new Vector3(gyroscope.X, -gyroscope.Z, gyroscope.Y);
_leftMotionInput.Update(accelerometer, gyroscope, (ulong)PerformanceCounter.ElapsedNanoseconds / 1000, controllerConfig.Motion.Sensitivity, (float)controllerConfig.Motion.GyroDeadzone);
_rightMotionInput = _leftMotionInput;
}
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver) if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
{ {
if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion)) if (gamepad.Features.HasFlag(GamepadFeaturesFlag.Motion))

View File

@@ -31,6 +31,7 @@ namespace Ryujinx.Input.HLE
private readonly IGamepadDriver _keyboardDriver; private readonly IGamepadDriver _keyboardDriver;
private readonly IGamepadDriver _gamepadDriver; private readonly IGamepadDriver _gamepadDriver;
private readonly IGamepadDriver _mouseDriver; private readonly IGamepadDriver _mouseDriver;
private readonly IHandheld _handheld;
private bool _isDisposed; private bool _isDisposed;
private List<InputConfig> _inputConfig; private List<InputConfig> _inputConfig;
@@ -38,7 +39,7 @@ namespace Ryujinx.Input.HLE
private bool _enableMouse; private bool _enableMouse;
private Switch _device; private Switch _device;
public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver) public NpadManager(IGamepadDriver keyboardDriver, IGamepadDriver gamepadDriver, IGamepadDriver mouseDriver, IHandheld handheld)
{ {
_controllers = new NpadController[MaxControllers]; _controllers = new NpadController[MaxControllers];
_cemuHookClient = new CemuHookClient(this); _cemuHookClient = new CemuHookClient(this);
@@ -47,6 +48,7 @@ namespace Ryujinx.Input.HLE
_gamepadDriver = gamepadDriver; _gamepadDriver = gamepadDriver;
_mouseDriver = mouseDriver; _mouseDriver = mouseDriver;
_inputConfig = []; _inputConfig = [];
_handheld = handheld;
_gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _gamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _gamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
@@ -139,7 +141,7 @@ namespace Ryujinx.Input.HLE
} }
else else
{ {
controller = new(_cemuHookClient); controller = new(_cemuHookClient, _handheld);
} }
bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry); bool isValid = DriverConfigurationUpdate(ref controller, inputConfigEntry);

View File

@@ -0,0 +1,10 @@
using System;
using System.Numerics;
namespace Ryujinx.Input
{
public interface IHandheld : IDisposable
{
Vector3 GetMotionData(MotionInputId gyroscope);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2; using Ryujinx.Input.SDL2;
using Ryujinx.Input.SDL3;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -182,7 +183,7 @@ namespace Ryujinx.Headless
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile); _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
_userChannelPersistence = new UserChannelPersistence(); _userChannelPersistence = new UserChannelPersistence();
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver()); _inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver(), new SDL3MotionDriver());
GraphicsConfig.EnableShaderCache = !option.DisableShaderCache; GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;

View File

@@ -76,6 +76,7 @@
<ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Metal\Ryujinx.Graphics.Metal.csproj" />
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" /> <ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
<ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" /> <ProjectReference Include="..\Ryujinx.Input.SDL2\Ryujinx.Input.SDL2.csproj" />
<ProjectReference Include="..\Ryujinx.Input.SDL3\Ryujinx.Input.SDL3.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" /> <ProjectReference Include="..\Ryujinx.Audio.Backends.OpenAL\Ryujinx.Audio.Backends.OpenAL.csproj" />
<ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" /> <ProjectReference Include="..\Ryujinx.Audio.Backends.SoundIo\Ryujinx.Audio.Backends.SoundIo.csproj" />
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" /> <ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />

View File

@@ -10,6 +10,7 @@ namespace Ryujinx.Ava.UI.Models.Input
public partial class GamepadInputConfig : BaseModel public partial class GamepadInputConfig : BaseModel
{ {
public bool EnableCemuHookMotion { get; set; } public bool EnableCemuHookMotion { get; set; }
public bool EnableHandheldMotion { get; set; }
public string DsuServerHost { get; set; } public string DsuServerHost { get; set; }
public int DsuServerPort { get; set; } public int DsuServerPort { get; set; }
public int Slot { get; set; } public int Slot { get; set; }
@@ -162,7 +163,7 @@ namespace Ryujinx.Ava.UI.Models.Input
EnableMotion = controllerInput.Motion.EnableMotion; EnableMotion = controllerInput.Motion.EnableMotion;
GyroDeadzone = controllerInput.Motion.GyroDeadzone; GyroDeadzone = controllerInput.Motion.GyroDeadzone;
Sensitivity = controllerInput.Motion.Sensitivity; Sensitivity = controllerInput.Motion.Sensitivity;
EnableHandheldMotion = controllerInput.Motion.MotionBackend == MotionInputBackendType.Handheld;
if (controllerInput.Motion is CemuHookMotionConfigController cemuHook) if (controllerInput.Motion is CemuHookMotionConfigController cemuHook)
{ {
EnableCemuHookMotion = true; EnableCemuHookMotion = true;
@@ -285,7 +286,7 @@ namespace Ryujinx.Ava.UI.Models.Input
config.Motion = new StandardMotionConfigController config.Motion = new StandardMotionConfigController
{ {
EnableMotion = EnableMotion, EnableMotion = EnableMotion,
MotionBackend = MotionInputBackendType.GamepadDriver, MotionBackend = EnableHandheldMotion ? MotionInputBackendType.Handheld : MotionInputBackendType.GamepadDriver,
GyroDeadzone = GyroDeadzone, GyroDeadzone = GyroDeadzone,
Sensitivity = Sensitivity, Sensitivity = Sensitivity,
}; };

View File

@@ -18,6 +18,34 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
[ObservableProperty] private double _gyroDeadzone; [ObservableProperty] private double _gyroDeadzone;
[ObservableProperty] private bool _enableCemuHookMotion; private bool _enableCemuHookMotion;
public bool EnableCemuHookMotion
{
get => _enableCemuHookMotion;
set
{
if (value)
{
EnableHandheldMotion = false;
}
_enableCemuHookMotion = value;
OnPropertyChanged();
}
}
private bool _enableHandheldMotion;
public bool EnableHandheldMotion
{
get => _enableHandheldMotion;
set
{
if (value)
{
EnableCemuHookMotion = false;
}
_enableHandheldMotion = value;
OnPropertyChanged();
}
}
} }
} }

View File

@@ -61,6 +61,17 @@
Margin="5, 0" Margin="5, 0"
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" /> Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
</StackPanel> </StackPanel>
<Separator
Height="1"
Margin="0,5" />
<CheckBox
Margin="5"
IsChecked="{Binding EnableHandheldMotion}">
<TextBlock
Margin="0,3,0,0"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsMotionUseHandheldCompatibleMotion}" />
</CheckBox>
<Separator <Separator
Height="1" Height="1"
Margin="0,5" /> Margin="0,5" />

View File

@@ -30,6 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Input
Sensitivity = config.Sensitivity, Sensitivity = config.Sensitivity,
GyroDeadzone = config.GyroDeadzone, GyroDeadzone = config.GyroDeadzone,
EnableCemuHookMotion = config.EnableCemuHookMotion, EnableCemuHookMotion = config.EnableCemuHookMotion,
EnableHandheldMotion = config.EnableHandheldMotion,
}; };
InitializeComponent(); InitializeComponent();
@@ -58,6 +59,7 @@ namespace Ryujinx.Ava.UI.Views.Input
config.DsuServerHost = content._viewModel.DsuServerHost; config.DsuServerHost = content._viewModel.DsuServerHost;
config.DsuServerPort = content._viewModel.DsuServerPort; config.DsuServerPort = content._viewModel.DsuServerPort;
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion; config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
config.EnableHandheldMotion = content._viewModel.EnableHandheldMotion;
config.MirrorInput = content._viewModel.MirrorInput; config.MirrorInput = content._viewModel.MirrorInput;
}; };

View File

@@ -31,6 +31,7 @@ using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2; using Ryujinx.Input.SDL2;
using Ryujinx.Input.SDL3;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -109,7 +110,7 @@ namespace Ryujinx.Ava.UI.Windows
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver(), new SDL3MotionDriver());
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it); _ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
this.ScalingChanged += OnScalingChanged; this.ScalingChanged += OnScalingChanged;