Example #1
0
        private void DeviceAddedHandler(object aObj, DeviceAddedEventArgs aEvent)
        {
            // Devices can be turned off by the time they get to this point, at which point they end up null. Make sure the device isn't null.
            if (aEvent.Device == null)
            {
                return;
            }

            var duplicates = from x in _devices
                             where x.Value.Identifier == aEvent.Device.Identifier
                             select x;

            if (duplicates.Any() && (duplicates.Count() > 1 || duplicates.First().Value.IsConnected))
            {
                _bpLogger.Debug($"Already have device {aEvent.Device.Name} in Devices list");
                return;
            }

            // If we get to 4 billion devices connected, this may be a problem.
            var deviceIndex = duplicates.Any() ? duplicates.First().Key : (uint)Interlocked.Increment(ref _deviceIndexCounter);

            _bpLogger.Info((duplicates.Any() ? "Re-" : string.Empty) + $"Adding Device {aEvent.Device.Name} at index {deviceIndex}");

            _devices[deviceIndex]        = aEvent.Device;
            aEvent.Device.DeviceRemoved += DeviceRemovedHandler;
            var msg = new DeviceAdded(deviceIndex, aEvent.Device.Name, GetAllowedMessageTypesAsStrings(aEvent.Device).ToArray());

            DeviceMessageReceived?.Invoke(this, new MessageReceivedEventArgs(msg));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class.
        /// </summary>
        /// <param name="aLogManager">Log manager</param>
        public ButtplugJsonMessageParser(IButtplugLogManager aLogManager = null)
        {
            _bpLogger = aLogManager.GetLogger(GetType());
            _bpLogger?.Info($"Setting up {GetType().Name}");

            IEnumerable <Type> allTypes;

            // Some classes in the library may not load on certain platforms due to missing symbols.
            // If this is the case, we should still find messages even though an exception was thrown.
            try
            {
                allTypes = Assembly.GetAssembly(typeof(ButtplugMessage)).GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                allTypes = e.Types;
            }

            var messageClasses = allTypes.Where(t => t != null && t.IsClass && t.Namespace == "Buttplug4Net35.Messages" && typeof(ButtplugMessage).IsAssignableFrom(t));

            var enumerable = messageClasses as Type[] ?? messageClasses.ToArray();

            _bpLogger?.Debug($"Message type count: {enumerable.Length}");
            _messageTypes = new Dictionary <string, Type>();
            enumerable.ToList().ForEach(aMessageType =>
            {
                _bpLogger?.Debug($"- {aMessageType.Name}");
                _messageTypes.Add(aMessageType.Name, aMessageType);
            });
        }
        public async Task <ButtplugMessage> SendMessage([NotNull] ButtplugMessage aMsg)
        {
            _bpLogger.Trace($"Got Message {aMsg.Id} of type {aMsg.GetType().Name} to send");
            var id = aMsg.Id;

            if (id == 0)
            {
                return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG,
                                            "Message Id 0 is reserved for outgoing system messages. Please use another Id."));
            }

            if (aMsg is IButtplugMessageOutgoingOnly)
            {
                return(_bpLogger.LogWarnMsg(id, Error.ErrorClass.ERROR_MSG,
                                            $"Message of type {aMsg.GetType().Name} cannot be sent to server"));
            }

            if (_pingTimedOut)
            {
                return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_PING, "Ping timed out."));
            }

            // If we get a message that's not RequestServerInfo first, return an error.
            if (!_receivedRequestServerInfo && !(aMsg is RequestServerInfo))
            {
                return(_bpLogger.LogErrorMsg(id, Error.ErrorClass.ERROR_INIT,
                                             "RequestServerInfo must be first message received by server!"));
            }

            switch (aMsg)
            {
            case RequestLog m:
                _bpLogger.Debug("Got RequestLog Message");
                BpLogManager.Level = m.LogLevel;
                return(new Ok(id));

            case Ping _:
                // Start the timer
                _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime);

                return(new Ok(id));

            case RequestServerInfo rsi:
                _bpLogger.Debug("Got RequestServerInfo Message");
                _receivedRequestServerInfo = true;
                _clientMessageVersion      = rsi.MessageVersion;

                // Start the timer
                _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime);
                ClientConnected?.Invoke(this, new MessageReceivedEventArgs(rsi));
                return(new ServerInfo(_serverName, 1, _maxPingTime, id));

            case Test m:
                return(new Test(m.TestString, id));
            }

            return(await _deviceManager.SendMessage(aMsg));
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class.
        /// </summary>
        /// <param name="aLogManager">Log manager</param>
        public ButtplugJsonMessageParser(IButtplugLogManager aLogManager = null)
        {
            _bpLogger = aLogManager.GetLogger(GetType());
            _bpLogger?.Info($"Setting up {GetType().Name}");
            _serializer = new JsonSerializer {
                MissingMemberHandling = MissingMemberHandling.Error
            };
            IEnumerable <Type> allTypes;

            // Some classes in the library may not load on certain platforms due to missing symbols.
            // If this is the case, we should still find messages even though an exception was thrown.
            try
            {
                allTypes = Assembly.GetAssembly(typeof(ButtplugMessage)).GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                allTypes = e.Types;
            }

            var messageClasses = allTypes.Where(t => t != null && t.IsClass && t.Namespace == "B******g.Core.Messages" && typeof(ButtplugMessage).IsAssignableFrom(t));

            var enumerable = messageClasses as Type[] ?? messageClasses.ToArray();

            _bpLogger?.Debug($"Message type count: {enumerable.Length}");
            _messageTypes = new Dictionary <string, Type>();
            enumerable.ToList().ForEach(aMessageType =>
            {
                _bpLogger?.Debug($"- {aMessageType.Name}");
                _messageTypes.Add(aMessageType.Name, aMessageType);
            });

            // Load the schema for validation
            var          assembly     = Assembly.GetExecutingAssembly();
            const string resourceName = "B******g.Core.b******g-schema.json";
            Stream       stream       = null;

            try
            {
                stream = assembly.GetManifestResourceStream(resourceName);
                using (var reader = new StreamReader(stream))
                {
                    stream = null;
                    var result = reader.ReadToEnd();
                    _schema = JsonSchema4.FromJsonAsync(result).GetAwaiter().GetResult();
                }
            }
            catch (Exception e)
            {
                _bpLogger.LogException(e);
                throw e;
            }
            finally
            {
                stream?.Dispose();
            }
        }
Example #5
0
        public bool MayBeDevice(string advertName, List <Guid> advertGUIDs)
        {
            if (_deviceInfo.NamePrefixes.Any())
            {
                foreach (var deviceInfoNamePrefix in _deviceInfo.NamePrefixes)
                {
                    if (advertName.IndexOf(deviceInfoNamePrefix) != 0)
                    {
                        continue;
                    }

                    _bpLogger.Debug($"Found {advertName} via NamePrefix {deviceInfoNamePrefix}");
                    return(true);
                }
            }

            if ((_deviceInfo.Names.Any() && !_deviceInfo.Names.Contains(advertName)) || !_deviceInfo.Names.Any())
            {
                _bpLogger.Trace($"Dropping query for {advertName}.");
                return(false);
            }

            if (_deviceInfo.Names.Any() && !advertGUIDs.Any())
            {
                _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType());
                return(true);
            }

            _bpLogger.Trace("Found " + advertName + " for " + _deviceInfo.GetType() + " with services " + advertGUIDs);
            foreach (var s in _deviceInfo.Services)
            {
                _bpLogger.Trace("Expecting " + s);
            }

            foreach (var s in advertGUIDs)
            {
                _bpLogger.Trace("Got " + s);
            }

            // Intersect doesn't intersect until the enumerator is called
            var sv = _deviceInfo.Services.Intersect(advertGUIDs);

            foreach (var s in sv)
            {
                _bpLogger.Trace("Matched " + s);
                return(true);
            }

            return(false);
        }
        public ButtplugServer(ButtplugServerOptions aOptions)
        {
            ButtplugUtils.ArgumentNotNull(aOptions, nameof(aOptions));
            _clientName   = null;
            _serverName   = aOptions.ServerName;
            _maxPingTime  = aOptions.MaxPingTime;
            _pingTimedOut = false;
            if (_maxPingTime != 0)
            {
                // Create a new timer that wont fire any events just yet
                _pingTimer = new Timer(PingTimeoutHandler, null, Timeout.Infinite, Timeout.Infinite);
            }

            BpLogManager = new ButtplugLogManager();
            _bpLogger    = BpLogManager.GetLogger(GetType());
            _bpLogger.Debug("Setting up ButtplugServer");
            _parser        = new ButtplugJsonMessageParser(BpLogManager);
            _deviceManager = aOptions.DeviceManager ?? new DeviceManager(BpLogManager, aOptions.SubtypeManagerSearchPaths);

            _bpLogger.Info("Finished setting up ButtplugServer");
            _deviceManager.DeviceMessageReceived += DeviceMessageReceivedHandler;
            _deviceManager.ScanningFinished      += ScanningFinishedHandler;

            if (!DeviceConfigurationManager.HasManager)
            {
                DeviceConfigurationManager.LoadBaseConfigurationFromResource();
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class.
        /// </summary>
        /// <param name="aLogManager">Log manager, passed from the parser owner.</param>
        public ButtplugJsonMessageParser([NotNull] IButtplugLogManager aLogManager)
        {
            // Set up logging.
            if (aLogManager == null)
            {
                throw new ArgumentNullException(nameof(aLogManager));
            }

            _bpLogger = aLogManager.GetLogger(GetType());
            _bpLogger?.Info($"Setting up {GetType().Name}");

            _serializer = new JsonSerializer {
                MissingMemberHandling = MissingMemberHandling.Error
            };
            _messageTypes = new Dictionary <string, Type>();
            foreach (var aMessageType in ButtplugUtils.GetAllMessageTypes())
            {
                _bpLogger?.Debug($"- {aMessageType.Name}");
                _messageTypes.Add(aMessageType.Name, aMessageType);
            }

            // If we can't find any message types in our assembly, the system is basically useless.
            if (!_messageTypes.Any())
            {
                throw new ButtplugMessageException(_bpLogger, "No message types available.");
            }

            // Load the schema for validation. Schema file is an embedded resource in the library.
            var jsonSchemaString = ButtplugUtils.GetStringFromFileResource("B******g.b******g-schema.json");

            _schema = JsonSchema.FromJsonAsync(jsonSchemaString)?.GetAwaiter().GetResult() ?? throw new InvalidOperationException();
        }
Example #8
0
        public bool MayBeDevice(int aVendorId, int aProductId)
        {
            if (_deviceInfo.VendorId != aVendorId || _deviceInfo.ProductId != aProductId)
            {
                return(false);
            }

            _bpLogger.Debug("Matched " + _deviceInfo.Name);
            return(true);
        }
Example #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ButtplugJsonMessageParser"/> class.
        /// </summary>
        /// <param name="aLogManager">Log manager, passed from the parser owner.</param>
        public ButtplugJsonMessageParser([NotNull] IButtplugLogManager aLogManager)
        {
            // Set up logging.
            if (aLogManager == null)
            {
                throw new ArgumentNullException(nameof(aLogManager));
            }
            _bpLogger = aLogManager.GetLogger(GetType());
            _bpLogger?.Info($"Setting up {GetType().Name}");

            _serializer = new JsonSerializer {
                MissingMemberHandling = MissingMemberHandling.Error
            };
            _messageTypes = new Dictionary <string, Type>();
            foreach (var aMessageType in ButtplugUtils.GetAllMessageTypes())
            {
                _bpLogger?.Debug($"- {aMessageType.Name}");
                _messageTypes.Add(aMessageType.Name, aMessageType);
            }

            // If we can't find any message types in our assembly, the system is basically useless.
            if (!_messageTypes.Any())
            {
                throw new ButtplugMessageException(_bpLogger, "No message types available.");
            }

            // Load the schema for validation. Schema file is an embedded resource in the library.
            var          assembly     = Assembly.GetExecutingAssembly();
            const string resourceName = "B******g.b******g-schema.json";
            var          stream       = assembly.GetManifestResourceStream(resourceName);

            try
            {
                using (var reader = new StreamReader(stream ?? throw new InvalidOperationException()))
                {
                    stream = null;
                    var result = reader.ReadToEnd();
                    _schema = JsonSchema4.FromJsonAsync(result)?.GetAwaiter().GetResult() ?? throw new InvalidOperationException();
                }
            }
            finally
            {
                // Always make sure we dispose of the resource stream, even if we throw. All
                // exceptions should be rethrown though.
                stream?.Dispose();
            }
        }
        public ButtplugServer([NotNull] string aServerName, uint aMaxPingTime, DeviceManager aDeviceManager = null)
        {
            _serverName   = aServerName;
            _maxPingTime  = aMaxPingTime;
            _pingTimedOut = false;
            if (aMaxPingTime != 0)
            {
                // Create a new timer that wont fire any events just yet
                _pingTimer = new Timer(PingTimeoutHandler, null, Timeout.Infinite, Timeout.Infinite);
            }

            BpLogManager = new ButtplugLogManager();
            _bpLogger    = BpLogManager.GetLogger(GetType());
            _bpLogger.Debug("Setting up ButtplugServer");
            _parser        = new ButtplugJsonMessageParser(BpLogManager);
            _deviceManager = aDeviceManager ?? new DeviceManager(BpLogManager);

            _bpLogger.Info("Finished setting up ButtplugServer");
            _deviceManager.DeviceMessageReceived += DeviceMessageReceivedHandler;
            _deviceManager.ScanningFinished      += ScanningFinishedHandler;
            BpLogManager.LogMessageReceived      += LogMessageReceivedHandler;
        }
        public bool MayBeDevice(string advertName, List <Guid> advertGUIDs)
        {
            if (_deviceInfo.Names.Any() && !_deviceInfo.Names.Contains(advertName))
            {
                return(false);
            }

            if (_deviceInfo.Names.Any() && !advertGUIDs.Any())
            {
                _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType().ToString());
                _bpLogger.Debug("No advertised services?");
                return(true);
            }

            _bpLogger.Debug("Found " + advertName + " for " + _deviceInfo.GetType().ToString());
            foreach (var s in _deviceInfo.Services)
            {
                _bpLogger.Debug("Expecting " + s.ToString());
            }

            foreach (var s in advertGUIDs)
            {
                _bpLogger.Debug("Got " + s.ToString());
            }

            // Intersect doesn't intersect until the enumerator is called
            var sv = _deviceInfo.Services.Intersect(advertGUIDs);

            foreach (var s in sv)
            {
                _bpLogger.Debug("Matched " + s.ToString());
                return(true);
            }

            return(false);
        }
Example #12
0
        public async Task <ButtplugMessage> SendMessageAsync([NotNull] ButtplugMessage aMsg, CancellationToken aToken = default(CancellationToken))
        {
            var combinedToken = CancellationTokenSource.CreateLinkedTokenSource(_internalToken.Token, aToken);

            _bpLogger.Trace($"Got Message {aMsg.Id} of type {aMsg.GetType().Name} to send");
            var id = aMsg.Id;

            ButtplugUtils.ArgumentNotNull(aMsg, nameof(aMsg));

            if (id == ButtplugConsts.SystemMsgId)
            {
                throw new ButtplugMessageException(_bpLogger, "Message Id 0 is reserved for outgoing system messages. Please use another Id.", id);
            }

            if (aMsg is IButtplugMessageOutgoingOnly)
            {
                throw new ButtplugMessageException(_bpLogger, $"Message of type {aMsg.GetType().Name} cannot be sent to server", id);
            }

            if (_pingTimedOut)
            {
                throw new ButtplugPingException(_bpLogger, "Ping timed out.", id);
            }

            // If we get a message that's not RequestServerInfo first, return an error.
            if (!_receivedRequestServerInfo && !(aMsg is RequestServerInfo))
            {
                throw new ButtplugHandshakeException(_bpLogger, "RequestServerInfo must be first message received by server!", id);
            }

            _bpLogger.Debug($"Got {aMsg.Name} message.");

            switch (aMsg)
            {
            case RequestLog m:
                BpLogManager.Level = m.LogLevel;
                return(new Ok(id));

            case Ping _:
                // Start the timer
                _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime);

                return(new Ok(id));

            case RequestServerInfo rsi:
                if (_receivedRequestServerInfo)
                {
                    throw new ButtplugHandshakeException(_bpLogger, "Already received RequestServerInfo, cannot be sent twice.", id);
                }

                _receivedRequestServerInfo = true;
                _clientSpecVersion         = rsi.MessageVersion;
                _deviceManager.SpecVersion = _clientSpecVersion;

                // Start the timer
                _pingTimer?.Change((int)_maxPingTime, (int)_maxPingTime);
                _clientName = rsi.ClientName;
                ClientConnected?.Invoke(this, EventArgs.Empty);
                return(new ServerInfo(_serverName, ButtplugConsts.CurrentSpecVersion, _maxPingTime, id));

            case Core.Messages.Test m:
                return(new Core.Messages.Test(m.TestString, id));
            }

            return(await _deviceManager.SendMessageAsync(aMsg, combinedToken.Token).ConfigureAwait(false));
        }
 protected DeviceSubtypeManager([NotNull] IButtplugLogManager aLogManager)
 {
     LogManager = aLogManager;
     BpLogger   = aLogManager.GetLogger(GetType());
     BpLogger.Debug($"Setting up Device Manager {GetType().Name}");
 }
        public ButtplugMessage[] Deserialize(string aJsonMsg)
        {
            _bpLogger.Trace($"Got JSON Message: {aJsonMsg}");

            var    res = new List <ButtplugMessage>();
            JArray a;

            try
            {
                a = JArray.Parse(aJsonMsg);
            }
            catch (JsonReaderException e)
            {
                _bpLogger.Debug($"Not valid JSON: {aJsonMsg}");
                _bpLogger.Debug(e.Message);
                res.Add(new Error("Not valid JSON", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId));
                return(res.ToArray());
            }

            var errors = _schema.Validate(a);

            if (errors.Any())
            {
                res.Add(new Error("Message does not conform to schema: " + string.Join(", ", errors.Select(aErr => aErr.ToString()).ToArray()), ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId));
                return(res.ToArray());
            }

            if (!a.Any())
            {
                res.Add(new Error("No messages in array", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId));
                return(res.ToArray());
            }

            // JSON input is an array of messages.
            // We currently only handle the first one.
            foreach (var o in a.Children <JObject>())
            {
                if (!o.Properties().Any())
                {
                    res.Add(new Error("No message name available", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId));
                    continue;
                }

                var msgName = o.Properties().First().Name;
                if (!_messageTypes.Keys.Any() || !_messageTypes.Keys.Contains(msgName))
                {
                    res.Add(new Error($"{msgName} is not a valid message class", ErrorClass.ERROR_MSG, ButtplugConsts.SystemMsgId));
                    continue;
                }

                var s = new JsonSerializer {
                    MissingMemberHandling = MissingMemberHandling.Error
                };

                // This specifically could fail due to object conversion.
                try
                {
                    var r = o[msgName].Value <JObject>();
                    res.Add((ButtplugMessage)r.ToObject(_messageTypes[msgName], s));
                    _bpLogger.Trace($"Message successfully parsed as {msgName} type");
                }
                catch (InvalidCastException e)
                {
                    res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"Could not create message for JSON {aJsonMsg}: {e.Message}"));
                }
                catch (JsonSerializationException e)
                {
                    res.Add(_bpLogger.LogErrorMsg(ButtplugConsts.SystemMsgId, ErrorClass.ERROR_MSG, $"Could not create message for JSON {aJsonMsg}: {e.Message}"));
                }
            }

            return(res.ToArray());
        }