Skip to content

Commit 36edcb5

Browse files
blackspherefollowerqdot
authored andcommitted
Server-side infrastructure for versioned messages
This change also includes support for the new MessageAttributes support.
1 parent 6bc4ecc commit 36edcb5

9 files changed

Lines changed: 95 additions & 18 deletions

File tree

Buttplug.Components.WebsocketServer/ButtplugWebsocketServer.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ private async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancell
9696
try
9797
{
9898
ws.WriteString(new ButtplugJsonMessageParser(_logManager).Serialize(_logger.LogErrorMsg(
99-
ButtplugConsts.SystemMsgId, ErrorClass.ERROR_INIT, "WebSocketServer already in use!")));
99+
ButtplugConsts.SystemMsgId, ErrorClass.ERROR_INIT, "WebSocketServer already in use!"), 0));
100100
ws.Close();
101101
}
102102
catch
@@ -120,6 +120,11 @@ private async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancell
120120
EventHandler<MessageReceivedEventArgs> msgReceived = (aObject, aEvent) =>
121121
{
122122
var msg = buttplug.Serialize(aEvent.Message);
123+
if (msg == null)
124+
{
125+
return;
126+
}
127+
123128
try
124129
{
125130
if (ws != null && ws.IsConnected)
@@ -159,6 +164,11 @@ private async Task HandleConnectionAsync(WebSocket ws, CancellationToken cancell
159164
{
160165
var respMsgs = await buttplug.SendMessage(msg);
161166
var respMsg = buttplug.Serialize(respMsgs);
167+
if (respMsg == null)
168+
{
169+
continue;
170+
}
171+
162172
await ws.WriteStringAsync(respMsg, cancellation);
163173

164174
foreach (var m in respMsgs)

Buttplug.Core/ButtplugDevice.cs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public abstract class ButtplugDevice : IButtplugDevice
1313

1414
public string Identifier { get; }
1515

16+
public uint Index { get; set; }
17+
1618
public bool IsConnected
1719
{
1820
get
@@ -24,18 +26,36 @@ public bool IsConnected
2426
[CanBeNull]
2527
public event EventHandler DeviceRemoved;
2628

29+
[CanBeNull]
30+
public event EventHandler<MessageReceivedEventArgs> MessageEmitted;
31+
2732
[NotNull]
2833
protected readonly IButtplugLog BpLogger;
34+
2935
[NotNull]
30-
protected readonly Dictionary<Type, Func<ButtplugDeviceMessage, Task<ButtplugMessage>>> MsgFuncs;
36+
protected readonly Dictionary<Type, ButtplugDeviceWrapper> MsgFuncs;
37+
3138
private bool _isDisconnected;
3239

40+
public class ButtplugDeviceWrapper
41+
{
42+
public Func<ButtplugDeviceMessage, Task<ButtplugMessage>> Function;
43+
public MessageAttributes Attrs;
44+
45+
public ButtplugDeviceWrapper(Func<ButtplugDeviceMessage, Task<ButtplugMessage>> aFunction,
46+
MessageAttributes aAttrs = null)
47+
{
48+
Function = aFunction;
49+
Attrs = aAttrs ?? new MessageAttributes();
50+
}
51+
}
52+
3353
protected ButtplugDevice([NotNull] IButtplugLogManager aLogManager,
3454
[NotNull] string aName,
3555
[NotNull] string aIdentifier)
3656
{
3757
BpLogger = aLogManager.GetLogger(GetType());
38-
MsgFuncs = new Dictionary<Type, Func<ButtplugDeviceMessage, Task<ButtplugMessage>>>();
58+
MsgFuncs = new Dictionary<Type, ButtplugDeviceWrapper>();
3959
Name = aName;
4060
Identifier = aIdentifier;
4161
}
@@ -45,6 +65,16 @@ public IEnumerable<Type> GetAllowedMessageTypes()
4565
return MsgFuncs.Keys;
4666
}
4767

68+
public MessageAttributes GetMessageAttrs(Type aMsg)
69+
{
70+
if (MsgFuncs.TryGetValue(aMsg, out var wrapper))
71+
{
72+
return wrapper.Attrs ?? new MessageAttributes();
73+
}
74+
75+
return new MessageAttributes();
76+
}
77+
4878
protected void InvokeDeviceRemoved()
4979
{
5080
_isDisconnected = true;
@@ -67,7 +97,7 @@ public async Task<ButtplugMessage> ParseMessage([NotNull] ButtplugDeviceMessage
6797

6898
// We just checked whether the key exists above, so we're ok.
6999
// ReSharper disable once PossibleNullReferenceException
70-
return await MsgFuncs[aMsg.GetType()].Invoke(aMsg);
100+
return await MsgFuncs[aMsg.GetType()].Function.Invoke(aMsg);
71101
}
72102

73103
public virtual Task<ButtplugMessage> Initialize()
@@ -77,5 +107,10 @@ public virtual Task<ButtplugMessage> Initialize()
77107
}
78108

79109
public abstract void Disconnect();
110+
111+
protected void EmitMessage(ButtplugMessage aMsg)
112+
{
113+
MessageEmitted?.Invoke(this, new MessageReceivedEventArgs(aMsg));
114+
}
80115
}
81116
}

Buttplug.Core/ButtplugLog.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void Fatal(string aMsg, bool aLocalOnly)
7676

7777
public void LogException(Exception aEx, bool aLocalOnly = true, string aMsg = null)
7878
{
79-
Error((aEx?.GetType().ToString() ?? "Unknown Exception") + ": " + aMsg ?? (aEx.Message + "\n" + aEx.StackTrace), aLocalOnly);
79+
Error((aEx?.GetType().ToString() ?? "Unknown Exception") + ": " + (aMsg ?? (aEx.Message + "\n" + aEx.StackTrace)), aLocalOnly);
8080
OnLogException?.Invoke(this, new LogExceptionEventArgs(aEx, aLocalOnly, aMsg));
8181
}
8282
}

Buttplug.Core/IButtplugDevice.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Threading.Tasks;
44
using JetBrains.Annotations;
5+
using Buttplug.Core.Messages;
56

67
namespace Buttplug.Core
78
{
@@ -13,12 +14,18 @@ public interface IButtplugDevice
1314
[NotNull]
1415
string Identifier { get; }
1516

17+
[NotNull]
18+
uint Index { get; set; }
19+
1620
[NotNull]
1721
bool IsConnected { get; }
1822

1923
[CanBeNull]
2024
event EventHandler DeviceRemoved;
2125

26+
[CanBeNull]
27+
event EventHandler<MessageReceivedEventArgs> MessageEmitted;
28+
2229
[NotNull]
2330
IEnumerable<Type> GetAllowedMessageTypes();
2431

@@ -29,5 +36,8 @@ public interface IButtplugDevice
2936
Task<ButtplugMessage> Initialize();
3037

3138
void Disconnect();
39+
40+
[NotNull]
41+
MessageAttributes GetMessageAttrs(Type aMsg);
3242
}
3343
}

Buttplug.Server.Test/ButtplugMessage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public async void SendUnhandledMessage()
9191
public async void SerializeUnhandledMessage()
9292
{
9393
var logger = new ButtplugLogManager();
94-
var r = new ButtplugJsonMessageParser(logger).Serialize(new FakeMessage(1));
94+
var r = new ButtplugJsonMessageParser(logger).Serialize(new FakeMessage(1), 0);
9595

9696
// Even though the message is defined outside the core library, it should at least serialize
9797
Assert.True(r.Length > 0);

Buttplug.Server.Test/ButtplugServerTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,16 +62,16 @@ private void CheckDeviceMessages(ButtplugMessage aMsgArgs)
6262
case DeviceAdded da:
6363
Assert.True(da.DeviceName == "TestDevice");
6464
Assert.True(da.DeviceIndex == 1);
65-
Assert.True(da.DeviceMessages.Length == 1);
66-
Assert.True(da.DeviceMessages.Contains("SingleMotorVibrateCmd"));
65+
Assert.True(da.DeviceMessages.Count() == 1);
66+
Assert.True(da.DeviceMessages.Keys.Contains("SingleMotorVibrateCmd"));
6767
break;
6868
case DeviceList dl:
6969
Assert.True(dl.Devices.Length == 1);
7070
var di = dl.Devices[0];
7171
Assert.True(di.DeviceName == "TestDevice");
7272
Assert.True(di.DeviceIndex == 1);
73-
Assert.True(di.DeviceMessages.Length == 1);
74-
Assert.True(di.DeviceMessages.Contains("SingleMotorVibrateCmd"));
73+
Assert.True(di.DeviceMessages.Count() == 1);
74+
Assert.True(di.DeviceMessages.Keys.Contains("SingleMotorVibrateCmd"));
7575
break;
7676
case DeviceRemoved dr:
7777
Assert.True(dr.DeviceIndex == 1);

Buttplug.Server.Test/TestDevice.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ internal class TestDevice : ButtplugDevice
1010
public TestDevice(ButtplugLogManager aLogManager, string aName)
1111
: base(aLogManager, aName, "Test")
1212
{
13-
MsgFuncs.Add(typeof(SingleMotorVibrateCmd), HandleSingleMotorVibrateCmd);
13+
MsgFuncs.Add(typeof(SingleMotorVibrateCmd), new ButtplugDeviceWrapper(HandleSingleMotorVibrateCmd));
1414
}
1515

1616
private Task<ButtplugMessage> HandleSingleMotorVibrateCmd(ButtplugDeviceMessage aMsg)

Buttplug.Server/ButtplugServer.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public DeviceManager DeviceManager
3939
private readonly uint _maxPingTime;
4040
private bool _pingTimedOut;
4141
private bool _receivedRequestServerInfo;
42+
private string _clientName;
43+
private uint _clientMessageVersion;
4244

4345
public static string GetLicense()
4446
{
@@ -163,6 +165,7 @@ public async Task<ButtplugMessage> SendMessage([NotNull] ButtplugMessage aMsg)
163165
case RequestServerInfo rsi:
164166
_bpLogger.Debug("Got RequestServerInfo Message");
165167
_receivedRequestServerInfo = true;
168+
_clientMessageVersion = rsi?.MessageVersion ?? 0;
166169

167170
// Start the timer
168171
_pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime);
@@ -217,12 +220,12 @@ public async Task<ButtplugMessage[]> SendMessage(string aJsonMsgs)
217220

218221
public string Serialize(ButtplugMessage aMsg)
219222
{
220-
return _parser.Serialize(aMsg);
223+
return _parser.Serialize(aMsg, _clientMessageVersion);
221224
}
222225

223226
public string Serialize(ButtplugMessage[] aMsgs)
224227
{
225-
return _parser.Serialize(aMsgs);
228+
return _parser.Serialize(aMsgs, _clientMessageVersion);
226229
}
227230

228231
public ButtplugMessage[] Deserialize(string aMsg)

Buttplug.Server/DeviceManager.cs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,16 @@ public DeviceManager(IButtplugLogManager aLogManager)
3838
_managers = new List<IDeviceSubtypeManager>();
3939
}
4040

41-
private static IEnumerable<string> GetAllowedMessageTypesAsStrings([NotNull] IButtplugDevice aDevice)
41+
private static Dictionary<string, MessageAttributes>
42+
GetAllowedMessageTypesAsDictionary([NotNull] IButtplugDevice aDevice)
4243
{
43-
return from x in aDevice.GetAllowedMessageTypes() select x.Name;
44+
Dictionary<string, MessageAttributes> msgs = new Dictionary<string, MessageAttributes>();
45+
foreach (var msg in from x in aDevice.GetAllowedMessageTypes() select x)
46+
{
47+
msgs.Add(msg.Name, aDevice.GetMessageAttrs(msg));
48+
}
49+
50+
return msgs;
4451
}
4552

4653
private void DeviceAddedHandler(object aObj, DeviceAddedEventArgs aEvent)
@@ -65,12 +72,22 @@ private void DeviceAddedHandler(object aObj, DeviceAddedEventArgs aEvent)
6572
_bpLogger.Info((duplicates.Any() ? "Re-" : string.Empty) + $"Adding Device {aEvent.Device.Name} at index {deviceIndex}");
6673

6774
_devices[deviceIndex] = aEvent.Device;
75+
_devices[deviceIndex].Index = deviceIndex;
6876
aEvent.Device.DeviceRemoved += DeviceRemovedHandler;
69-
var msg = new DeviceAdded(deviceIndex, aEvent.Device.Name, GetAllowedMessageTypesAsStrings(aEvent.Device).ToArray());
77+
aEvent.Device.MessageEmitted += MessageEmittedHandler;
78+
var msg = new DeviceAdded(
79+
deviceIndex,
80+
aEvent.Device.Name,
81+
GetAllowedMessageTypesAsDictionary(aEvent.Device));
7082

7183
DeviceMessageReceived?.Invoke(this, new MessageReceivedEventArgs(msg));
7284
}
7385

86+
private void MessageEmittedHandler(object sender, MessageReceivedEventArgs e)
87+
{
88+
DeviceMessageReceived?.Invoke(this, e);
89+
}
90+
7491
private void DeviceRemovedHandler(object aObj, EventArgs aEvent)
7592
{
7693
if ((aObj as ButtplugDevice) == null)
@@ -164,8 +181,10 @@ public async Task<ButtplugMessage> SendMessage(ButtplugMessage aMsg)
164181
case RequestDeviceList _:
165182
_bpLogger.Debug("Got RequestDeviceList Message");
166183
var msgDevices = _devices.Where(aDevice => aDevice.Value.IsConnected)
167-
.Select(aDevice => new DeviceMessageInfo(aDevice.Key, aDevice.Value.Name,
168-
GetAllowedMessageTypesAsStrings(aDevice.Value).ToArray())).ToList();
184+
.Select(aDevice => new DeviceMessageInfo(
185+
aDevice.Key,
186+
aDevice.Value.Name,
187+
GetAllowedMessageTypesAsDictionary(aDevice.Value))).ToList();
169188
return new DeviceList(msgDevices.ToArray(), id);
170189

171190
// If it's a device message, it's most likely not ours.

0 commit comments

Comments
 (0)