Motion Fixes (#1589)
* fix stalling when server is offline * add retry timer to fail server connections, fix alt slot number * fix alt slot key issue * fix crash when saving controller config with empty fields * code fixes * add index check in motion hid update, made HandleResponse async Co-authored-by: Emmanuel <nhv3@localhost.localdomain>
This commit is contained in:
parent
c9841dab38
commit
9f13f957af
6 changed files with 218 additions and 139 deletions
|
@ -337,6 +337,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
||||||
{
|
{
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
|
if (i >= states.Count)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SetSixAxisState(states[i], true);
|
SetSixAxisState(states[i], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ namespace Ryujinx.Motion
|
||||||
private readonly Dictionary<int, UdpClient> _clients;
|
private readonly Dictionary<int, UdpClient> _clients;
|
||||||
|
|
||||||
private bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length];
|
private bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length];
|
||||||
|
private long[] _clientRetryTimer = new long[Enum.GetValues(typeof(PlayerIndex)).Length];
|
||||||
|
|
||||||
public Client()
|
public Client()
|
||||||
{
|
{
|
||||||
|
@ -63,18 +64,25 @@ namespace Ryujinx.Motion
|
||||||
|
|
||||||
public void RegisterClient(int player, string host, int port)
|
public void RegisterClient(int player, string host, int port)
|
||||||
{
|
{
|
||||||
if (_clients.ContainsKey(player))
|
if (_clients.ContainsKey(player) || !CanConnect(player))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
lock (_clients)
|
lock (_clients)
|
||||||
|
{
|
||||||
|
if (_clients.ContainsKey(player) || !CanConnect(player))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UdpClient client = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(host), port);
|
IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(host), port);
|
||||||
|
|
||||||
UdpClient client = new UdpClient(host, port);
|
client = new UdpClient(host, port);
|
||||||
|
|
||||||
_clients.Add(player, client);
|
_clients.Add(player, client);
|
||||||
_hosts.Add(player, endPoint);
|
_hosts.Add(player, endPoint);
|
||||||
|
@ -86,7 +94,6 @@ namespace Ryujinx.Motion
|
||||||
ReceiveLoop(player);
|
ReceiveLoop(player);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (FormatException fex)
|
catch (FormatException fex)
|
||||||
{
|
{
|
||||||
if (!_clientErrorStatus[player])
|
if (!_clientErrorStatus[player])
|
||||||
|
@ -96,14 +103,31 @@ namespace Ryujinx.Motion
|
||||||
_clientErrorStatus[player] = true;
|
_clientErrorStatus[player] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (SocketException ex)
|
catch (SocketException sex)
|
||||||
{
|
{
|
||||||
if (!_clientErrorStatus[player])
|
if (!_clientErrorStatus[player])
|
||||||
{
|
{
|
||||||
Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error code {ex.ErrorCode}");
|
Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error code {sex.ErrorCode}");
|
||||||
|
|
||||||
_clientErrorStatus[player] = true;
|
_clientErrorStatus[player] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveClient(player);
|
||||||
|
|
||||||
|
client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(player);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_clientErrorStatus[player] = true;
|
||||||
|
|
||||||
|
RemoveClient(player);
|
||||||
|
|
||||||
|
client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(player);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,17 +137,25 @@ namespace Ryujinx.Motion
|
||||||
{
|
{
|
||||||
if (_motionData.ContainsKey(player))
|
if (_motionData.ContainsKey(player))
|
||||||
{
|
{
|
||||||
input = _motionData[player][slot];
|
if (_motionData[player].TryGetValue(slot, out input))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
input = null;
|
input = null;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RemoveClient(int clientId)
|
||||||
|
{
|
||||||
|
_clients?.Remove(clientId);
|
||||||
|
|
||||||
|
_hosts?.Remove(clientId);
|
||||||
|
}
|
||||||
|
|
||||||
private void Send(byte[] data, int clientId)
|
private void Send(byte[] data, int clientId)
|
||||||
{
|
{
|
||||||
if (_clients.TryGetValue(clientId, out UdpClient _client))
|
if (_clients.TryGetValue(clientId, out UdpClient _client))
|
||||||
|
@ -143,28 +175,34 @@ namespace Ryujinx.Motion
|
||||||
|
|
||||||
_clientErrorStatus[clientId] = true;
|
_clientErrorStatus[clientId] = true;
|
||||||
|
|
||||||
_clients.Remove(clientId);
|
RemoveClient(clientId);
|
||||||
|
|
||||||
_hosts.Remove(clientId);
|
|
||||||
|
|
||||||
_client?.Dispose();
|
_client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(clientId);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException dex)
|
||||||
|
{
|
||||||
|
_clientErrorStatus[clientId] = true;
|
||||||
|
|
||||||
|
RemoveClient(clientId);
|
||||||
|
|
||||||
|
_client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(clientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] Receive(int clientId)
|
private byte[] Receive(int clientId, int timeout = 0)
|
||||||
{
|
{
|
||||||
if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint))
|
if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client))
|
||||||
{
|
{
|
||||||
if (_clients.TryGetValue(clientId, out UdpClient _client))
|
if (_client != null && _client.Client != null && _client.Client.Connected)
|
||||||
{
|
|
||||||
if (_client != null && _client.Client != null)
|
|
||||||
{
|
|
||||||
if (_client.Client.Connected)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
|
_client.Client.ReceiveTimeout = timeout;
|
||||||
|
|
||||||
var result = _client?.Receive(ref endPoint);
|
var result = _client?.Receive(ref endPoint);
|
||||||
|
|
||||||
if (result.Length > 0)
|
if (result.Length > 0)
|
||||||
|
@ -174,30 +212,35 @@ namespace Ryujinx.Motion
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (SocketException ex)
|
}
|
||||||
|
|
||||||
|
throw new Exception($"Client {clientId} is not registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetRetryTimer(int clientId)
|
||||||
{
|
{
|
||||||
if (!_clientErrorStatus[clientId])
|
var elapsedMs = PerformanceCounter.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
_clientRetryTimer[clientId] = elapsedMs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetRetryTimer(int clientId)
|
||||||
{
|
{
|
||||||
Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to receive data from motion source at {endPoint}. Error code {ex.ErrorCode}");
|
_clientRetryTimer[clientId] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_clientErrorStatus[clientId] = true;
|
private bool CanConnect(int clientId)
|
||||||
|
{
|
||||||
_clients.Remove(clientId);
|
return _clientRetryTimer[clientId] == 0 ? true : PerformanceCounter.ElapsedMilliseconds - 5000 > _clientRetryTimer[clientId];
|
||||||
|
|
||||||
_hosts.Remove(clientId);
|
|
||||||
|
|
||||||
_client?.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new byte[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReceiveLoop(int clientId)
|
public void ReceiveLoop(int clientId)
|
||||||
|
{
|
||||||
|
if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client))
|
||||||
|
{
|
||||||
|
if (_client != null && _client.Client != null && _client.Client.Connected)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
while (_active)
|
while (_active)
|
||||||
{
|
{
|
||||||
|
@ -209,23 +252,53 @@ namespace Ryujinx.Motion
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable CS4014
|
#pragma warning disable CS4014
|
||||||
HandleResponse(data, clientId);
|
Task.Run(() => HandleResponse(data, clientId));
|
||||||
#pragma warning restore CS4014
|
#pragma warning restore CS4014
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (SocketException ex)
|
||||||
|
{
|
||||||
|
if (!_clientErrorStatus[clientId])
|
||||||
|
{
|
||||||
|
Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to receive data from motion source at {endPoint}. Error code {ex.ErrorCode}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_clientErrorStatus[clientId] = true;
|
||||||
|
|
||||||
|
RemoveClient(clientId);
|
||||||
|
|
||||||
|
_client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(clientId);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
_clientErrorStatus[clientId] = true;
|
||||||
|
|
||||||
|
RemoveClient(clientId);
|
||||||
|
|
||||||
|
_client?.Dispose();
|
||||||
|
|
||||||
|
SetRetryTimer(clientId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma warning disable CS1998
|
#pragma warning disable CS1998
|
||||||
public async Task HandleResponse(byte[] data, int clientId)
|
public void HandleResponse(byte[] data, int clientId)
|
||||||
#pragma warning restore CS1998
|
#pragma warning restore CS1998
|
||||||
{
|
{
|
||||||
|
ResetRetryTimer(clientId);
|
||||||
|
|
||||||
MessageType type = (MessageType)BitConverter.ToUInt32(data.AsSpan().Slice(16, 4));
|
MessageType type = (MessageType)BitConverter.ToUInt32(data.AsSpan().Slice(16, 4));
|
||||||
|
|
||||||
data = data.AsSpan().Slice(16).ToArray();
|
data = data.AsSpan().Slice(16).ToArray();
|
||||||
|
|
||||||
using (MemoryStream mem = new MemoryStream(data))
|
using MemoryStream mem = new MemoryStream(data);
|
||||||
{
|
|
||||||
using (BinaryReader reader = new BinaryReader(mem))
|
using BinaryReader reader = new BinaryReader(mem);
|
||||||
{
|
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
case MessageType.Protocol:
|
case MessageType.Protocol:
|
||||||
|
@ -283,8 +356,6 @@ namespace Ryujinx.Motion
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RequestInfo(int clientId, int slot)
|
public void RequestInfo(int clientId, int slot)
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,13 +38,11 @@ namespace Ryujinx.Motion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Poll(PlayerIndex player, int slot)
|
public void Poll(InputConfig config, int slot)
|
||||||
{
|
{
|
||||||
InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == player);
|
|
||||||
|
|
||||||
Orientation = new float[9];
|
Orientation = new float[9];
|
||||||
|
|
||||||
if (!config.EnableMotion || !_motionSource.TryGetData((int)player, slot, out MotionInput input))
|
if (!config.EnableMotion || !_motionSource.TryGetData((int)config.PlayerIndex, slot, out MotionInput input))
|
||||||
{
|
{
|
||||||
Accelerometer = new Vector3();
|
Accelerometer = new Vector3();
|
||||||
Gyroscope = new Vector3();
|
Gyroscope = new Vector3();
|
||||||
|
|
|
@ -447,6 +447,8 @@ namespace Ryujinx.Ui
|
||||||
Enum.TryParse(_rSl.Label, out Key rButtonSl);
|
Enum.TryParse(_rSl.Label, out Key rButtonSl);
|
||||||
Enum.TryParse(_rSr.Label, out Key rButtonSr);
|
Enum.TryParse(_rSr.Label, out Key rButtonSr);
|
||||||
|
|
||||||
|
int.TryParse(_dsuServerPort.Buffer.Text, out int port);
|
||||||
|
|
||||||
return new KeyboardConfig
|
return new KeyboardConfig
|
||||||
{
|
{
|
||||||
Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
|
Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
|
||||||
|
@ -489,11 +491,11 @@ namespace Ryujinx.Ui
|
||||||
EnableMotion = _enableMotion.Active,
|
EnableMotion = _enableMotion.Active,
|
||||||
MirrorInput = _mirrorInput.Active,
|
MirrorInput = _mirrorInput.Active,
|
||||||
Slot = (int)_slotNumber.Value,
|
Slot = (int)_slotNumber.Value,
|
||||||
AltSlot = (int)_slotNumber.Value,
|
AltSlot = (int)_altSlotNumber.Value,
|
||||||
Sensitivity = (int)_sensitivity.Value,
|
Sensitivity = (int)_sensitivity.Value,
|
||||||
GyroDeadzone = _gyroDeadzone.Value,
|
GyroDeadzone = _gyroDeadzone.Value,
|
||||||
DsuServerHost = _dsuServerHost.Buffer.Text,
|
DsuServerHost = _dsuServerHost.Buffer.Text,
|
||||||
DsuServerPort = int.Parse(_dsuServerPort.Buffer.Text)
|
DsuServerPort = port
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,6 +527,8 @@ namespace Ryujinx.Ui
|
||||||
Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl);
|
Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl);
|
||||||
Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr);
|
Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr);
|
||||||
|
|
||||||
|
int.TryParse(_dsuServerPort.Buffer.Text, out int port);
|
||||||
|
|
||||||
return new ControllerConfig
|
return new ControllerConfig
|
||||||
{
|
{
|
||||||
Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
|
Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
|
||||||
|
@ -570,11 +574,11 @@ namespace Ryujinx.Ui
|
||||||
EnableMotion = _enableMotion.Active,
|
EnableMotion = _enableMotion.Active,
|
||||||
MirrorInput = _mirrorInput.Active,
|
MirrorInput = _mirrorInput.Active,
|
||||||
Slot = (int)_slotNumber.Value,
|
Slot = (int)_slotNumber.Value,
|
||||||
AltSlot = (int)_slotNumber.Value,
|
AltSlot = (int)_altSlotNumber.Value,
|
||||||
Sensitivity = (int)_sensitivity.Value,
|
Sensitivity = (int)_sensitivity.Value,
|
||||||
GyroDeadzone = _gyroDeadzone.Value,
|
GyroDeadzone = _gyroDeadzone.Value,
|
||||||
DsuServerHost = _dsuServerHost.Buffer.Text,
|
DsuServerHost = _dsuServerHost.Buffer.Text,
|
||||||
DsuServerPort = int.Parse(_dsuServerPort.Buffer.Text)
|
DsuServerPort = port
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.36.0 -->
|
<!-- Generated with glade 3.22.1 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.20"/>
|
<requires lib="gtk+" version="3.20"/>
|
||||||
<object class="GtkAdjustment" id="_altSlotNumber">
|
<object class="GtkAdjustment" id="_altSlotNumber">
|
||||||
|
@ -9,28 +9,28 @@
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkAdjustment" id="_controllerDeadzoneLeft">
|
<object class="GtkAdjustment" id="_controllerDeadzoneLeft">
|
||||||
<property name="upper">1</property>
|
<property name="upper">1</property>
|
||||||
<property name="value">0.05</property>
|
<property name="value">0.050000000000000003</property>
|
||||||
<property name="step_increment">0.01</property>
|
<property name="step_increment">0.01</property>
|
||||||
<property name="page_increment">0.1</property>
|
<property name="page_increment">0.10000000000000001</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkAdjustment" id="_controllerDeadzoneRight">
|
<object class="GtkAdjustment" id="_controllerDeadzoneRight">
|
||||||
<property name="upper">1</property>
|
<property name="upper">1</property>
|
||||||
<property name="value">0.05</property>
|
<property name="value">0.050000000000000003</property>
|
||||||
<property name="step_increment">0.01</property>
|
<property name="step_increment">0.01</property>
|
||||||
<property name="page_increment">0.1</property>
|
<property name="page_increment">0.10000000000000001</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkAdjustment" id="_controllerTriggerThreshold">
|
<object class="GtkAdjustment" id="_controllerTriggerThreshold">
|
||||||
<property name="upper">1</property>
|
<property name="upper">1</property>
|
||||||
<property name="value">0.5</property>
|
<property name="value">0.5</property>
|
||||||
<property name="step_increment">0.01</property>
|
<property name="step_increment">0.01</property>
|
||||||
<property name="page_increment">0.1</property>
|
<property name="page_increment">0.10000000000000001</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkAdjustment" id="_gyroDeadzone">
|
<object class="GtkAdjustment" id="_gyroDeadzone">
|
||||||
<property name="upper">100</property>
|
<property name="upper">100</property>
|
||||||
<property name="value">0.01</property>
|
<property name="value">0.01</property>
|
||||||
<property name="step_increment">0.01</property>
|
<property name="step_increment">0.01</property>
|
||||||
<property name="page_increment">0.1</property>
|
<property name="page_increment">0.10000000000000001</property>
|
||||||
<property name="page_size">0.1</property>
|
<property name="page_size">0.10000000000000001</property>
|
||||||
</object>
|
</object>
|
||||||
<object class="GtkAdjustment" id="_sensitivity">
|
<object class="GtkAdjustment" id="_sensitivity">
|
||||||
<property name="upper">1000</property>
|
<property name="upper">1000</property>
|
||||||
|
@ -50,6 +50,9 @@
|
||||||
<property name="window_position">center</property>
|
<property name="window_position">center</property>
|
||||||
<property name="default_width">1100</property>
|
<property name="default_width">1100</property>
|
||||||
<property name="default_height">600</property>
|
<property name="default_height">600</property>
|
||||||
|
<child type="titlebar">
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
@ -1803,6 +1806,7 @@
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="text" translatable="yes">0</property>
|
<property name="text" translatable="yes">0</property>
|
||||||
|
<property name="adjustment">_altSlotNumber</property>
|
||||||
<property name="climb_rate">1</property>
|
<property name="climb_rate">1</property>
|
||||||
<property name="snap_to_ticks">True</property>
|
<property name="snap_to_ticks">True</property>
|
||||||
<property name="numeric">True</property>
|
<property name="numeric">True</property>
|
||||||
|
@ -2030,8 +2034,5 @@
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child type="titlebar">
|
|
||||||
<placeholder/>
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
|
@ -512,7 +512,7 @@ namespace Ryujinx.Ui
|
||||||
|
|
||||||
currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
|
currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
|
||||||
|
|
||||||
motionDevice.Poll(inputConfig.PlayerIndex, inputConfig.Slot);
|
motionDevice.Poll(inputConfig, inputConfig.Slot);
|
||||||
|
|
||||||
SixAxisInput sixAxisInput = new SixAxisInput()
|
SixAxisInput sixAxisInput = new SixAxisInput()
|
||||||
{
|
{
|
||||||
|
@ -537,7 +537,7 @@ namespace Ryujinx.Ui
|
||||||
{
|
{
|
||||||
if (!inputConfig.MirrorInput)
|
if (!inputConfig.MirrorInput)
|
||||||
{
|
{
|
||||||
motionDevice.Poll(inputConfig.PlayerIndex, inputConfig.AltSlot);
|
motionDevice.Poll(inputConfig, inputConfig.AltSlot);
|
||||||
|
|
||||||
sixAxisInput = new SixAxisInput()
|
sixAxisInput = new SixAxisInput()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue