public override void OnInspectorGUI() { string currentControlName; bool deselect; // Check for key down before drawing any fields because they might consume the event. bool enterKeyDown = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return; // Load serialized object. serializedObject.Update(); // Port field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(portControlName); int newPort = EditorGUILayout.IntField(_portLabel, _oscOut.port); if (EditorGUI.EndChangeCheck()) { _portProp.intValue = newPort; if (_oscOut.isOpen) { _oscOut.Close(); // Close socket while editing } } currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName; if (enterKeyDownPort) { UnfocusAndUpdateUI(); } deselect = _prevControlName == portControlName && currentControlName != portControlName; if ((deselect || enterKeyDownPort) && !_oscOut.isOpen) { if (_oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue)) { _tempPort = _portProp.intValue; } else { _portProp.intValue = _tempPort; // Undo _oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue); } } // Mode field. EditorGUI.BeginChangeCheck(); OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, _oscOut.mode); if (EditorGUI.EndChangeCheck() && newMode != _oscOut.mode) { switch (newMode) { case OscSendMode.UnicastToSelf: _oscOut.Open(_oscOut.port); break; case OscSendMode.Unicast: _oscOut.Open(_oscOut.port, OscConst.unicastAddressDefault); break; case OscSendMode.Multicast: _oscOut.Open(_oscOut.port, OscConst.multicastAddressDefault); break; case OscSendMode.Broadcast: _oscOut.Open(_oscOut.port, IPAddress.Broadcast.ToString()); break; } UpdateStatusInEditMode(); } // IP Address field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(ipAddressControlName); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(_remoteIpAddressLabel); string newIp = EditorGUILayout.TextField(_oscOut.remoteIpAddress); // Field if (EditorGUI.EndChangeCheck()) { IPAddress ip; if (IPAddress.TryParse(newIp, out ip)) { _remoteIpAddressProp.stringValue = newIp; // Accept only valid ip addresses } if (_oscOut.isOpen) { _oscOut.Close(); // Close socket while editing } if (!Application.isPlaying) { if (_pingEnumerator != null) { _pingEnumerator = null; // Don't update ping coroutine while editing } if (_statusInEditMode != OscRemoteStatus.Unknown) { _statusInEditMode = OscRemoteStatus.Unknown; } } } GUILayout.FlexibleSpace(); Rect rect = GUILayoutUtility.GetRect(16, 5); rect.width = 5; rect.x += 3; rect.y += 7; OscRemoteStatus status = Application.isPlaying ? _oscOut.remoteStatus : _statusInEditMode; EditorGUI.DrawRect(rect, StatusToColor(status)); EditorGUILayout.EndHorizontal(); GUILayoutUtility.GetRect(1, 2); // vertical spacing currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName; if (enterKeyDownIp) { UnfocusAndUpdateUI(); } deselect = _prevControlName == ipAddressControlName && currentControlName != ipAddressControlName; if ((deselect || enterKeyDownIp) && !_oscOut.isOpen) // All this mess to check for end edit, OMG!!! Not cool. { if (_oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue)) { _tempIPAddress = _remoteIpAddressProp.stringValue; UpdateStatusInEditMode(); } else { _remoteIpAddressProp.stringValue = _tempIPAddress; // Undo } } // Is Open field. EditorGUI.BeginDisabledGroup(true); EditorGUILayout.Toggle(_isOpenLabel, _oscOut.isOpen); EditorGUI.EndDisabledGroup(); // Open On Awake field. EditorGUI.BeginDisabledGroup(Application.isPlaying); EditorGUILayout.PropertyField(_openOnAwakeProp, _openOnAwakeLabel); EditorGUI.EndDisabledGroup(); // Settings ... _settingsFoldoutProp.boolValue = EditorGUILayout.Foldout(_settingsFoldoutProp.boolValue, _settingsFoldLabel, true); if (_settingsFoldoutProp.boolValue) { EditorGUI.indentLevel++; // Udp Buffer Size field. EditorGUI.BeginChangeCheck(); int newBufferSize = EditorGUILayout.IntField(_udpBufferSizeLabel, _udpBufferSizeProp.intValue); if (EditorGUI.EndChangeCheck()) { if (newBufferSize >= OscConst.udpBufferSizeMin) { _udpBufferSizeProp.intValue = newBufferSize; _oscOut.udpBufferSize = newBufferSize; // This will reopen OscOut } } // Bundle messages automatically. EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(_bundleMessagesAutomaticallyLabel, GUILayout.Width(220)); GUILayout.FlexibleSpace(); _bundleMessagesAutomaticallyProp.boolValue = EditorGUILayout.Toggle(_bundleMessagesAutomaticallyProp.boolValue, GUILayout.Width(30)); EditorGUILayout.EndHorizontal(); if (!_bundleMessagesAutomaticallyProp.boolValue) { EditorGUILayout.HelpBox("Unbundled messages that are send successively are prone to be lost. Only disable this setting if your receiving end does not support OSC bundles.", MessageType.Warning); } EditorGUI.indentLevel--; } // Monitor ... EditorGUI.BeginDisabledGroup(!_oscOut.isOpen); _sb.Clear(); _sb.Append("Messages (").Append(_oscOut.messageCountSendLastFrame).Append(',').Append(OscDebug.GetPrettyByteSize(_oscOut.byteCountSendLastFrame)).Append(')'); GUIContent messagesFoldContent = new GUIContent(_sb.ToString(), "Messages received last update"); _messagesFoldoutProp.boolValue = EditorGUILayout.Foldout(_messagesFoldoutProp.boolValue, messagesFoldContent, true); if (_messagesFoldoutProp.boolValue) { EditorGUI.indentLevel++; if (_messageMonitorText.Length > 0) { _messageMonitorText.Remove(_messageMonitorText.Length - 1, 1); // Trim last new line temporarily. } EditorGUILayout.HelpBox(_messageMonitorText.ToString(), MessageType.None); if (_messageMonitorText.Length > 0) { _messageMonitorText.Append('\n'); } EditorGUI.indentLevel--; } EditorGUI.EndDisabledGroup(); // Apply serializedObject.ApplyModifiedProperties(); // Request OnInspectorGUI to be called every frame as long as inspector is active EditorUtility.SetDirty(target); // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode) if (!Application.isPlaying && _pingEnumerator != null) { OscHelper.UpdateCoroutineInEditMode(_pingEnumerator, ref _lastPingTime); } // Store name of focused control to detect unfocus events _prevControlName = GUI.GetNameOfFocusedControl(); }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open( int port, string remoteIpAddress = "" ) { // TODO this should be moved to a UdpSender class. // Close and stop pinging. if( _udpClient != null ) Close(); // Validate IP. IPAddress ip; if( string.IsNullOrEmpty( remoteIpAddress ) ) remoteIpAddress = IPAddress.Loopback.ToString(); if( remoteIpAddress == IPAddress.Any.ToString() || !IPAddress.TryParse( remoteIpAddress, out ip ) ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Invalid IP address " ); sb.Append( remoteIpAddress ); sb.Append( ".\n" ); Debug.LogWarning( sb.ToString() ); return false; } if( ip.AddressFamily != AddressFamily.InterNetwork ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Only IPv4 addresses are supported. " ); sb.Append( remoteIpAddress ); sb.Append( " is " ); sb.Append( ip.AddressFamily ); sb.Append( ".\n" ); Debug.LogWarning( sb.ToString() ); return false; } _remoteIpAddress = remoteIpAddress; // Detect and set transmission mode. if( _remoteIpAddress == IPAddress.Loopback.ToString() ){ _mode = OscSendMode.UnicastToSelf; } else if( _remoteIpAddress == IPAddress.Broadcast.ToString() ){ _mode = OscSendMode.Broadcast; } else if( Regex.IsMatch( _remoteIpAddress, OscConst.multicastAddressPattern ) ){ _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if( port < OscConst.portMin || port > OscConst.portMax ){ StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Open failed. Port " ); sb.Append( port ); sb.Append( " is out of range.\n" ); Debug.LogWarning( sb.ToString() ); return false; } _port = port; // Create new client and end point. _udpClient = new UdpClient(); _endPoint = new CachedIpEndPoint( ip, _port ); // Multicast senders do not need to join a multicast group, but we need to set a few options. if( _mode == OscSendMode.Multicast ) { // Set a time to live, indicating how many routers the messages is allowed to be forwarded by. _udpClient.Client.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, OscConst.timeToLive ); // Apply out multicastLoopback field. _udpClient.MulticastLoopback = _multicastLoopback; } // Set time to live to max. I haven't observed any difference, but we better be safe. _udpClient.Ttl = OscConst.timeToLive; // If an outgoing packet happen to exceeds the MTU (Maximum Transfer Unit) then throw an error instead of fragmenting. _udpClient.DontFragment = true; // Set buffer size. _udpClient.Client.SendBufferSize = _udpBufferSize; // DO NOT CONNECT UDP CLIENT! //_udpClient.Connect( _endPoint ); // Note to self about connecting UdpClient: // We do not use udpClient.Connect(). Instead we pass the IPEndPoint directly to _udpClient.Send(). // This is because a connected UdpClient purposed for sending will throw a number of (for our use) unwanted exceptions and in some cases disconnect. // 10061: SocketException: Connection refused - When we attempt to unicast to loopback address when no application is listening. // 10049: SocketException: The requested address is not valid in this context - When we attempt to broadcast while having no access to the local network. // 10051: SocketException: Network is unreachable - When we pass a unicast or broadcast target to udpClient.Connect() while having no access to a network. // Handle pinging if( Application.isPlaying ) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if( _mode == OscSendMode.Unicast ){ _pingCoroutine = PingCoroutine(); StartCoroutine( _pingCoroutine ); } } // Create cache buffer. if( _cache == null || _cache.Length != _udpBufferSize ) _cache = new byte[_udpBufferSize]; // Log if( Application.isPlaying ){ string addressTypeString = string.Empty; switch( _mode ){ case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } StringBuilder sb = OscDebug.BuildText( this ); sb.Append( "Ready to send to " ); sb.Append( addressTypeString ); sb.Append( " address " ); sb.Append( _remoteIpAddress ); sb.Append( " on port " ); sb.Append( port ); sb.Append( ".\n" ); Debug.Log( sb.ToString() ); } return true; }
public override void OnInspectorGUI() { string currentControlName; bool deselect; // Check for key down before drawing any fields because they might consume the event. bool enterKeyDown = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return; // Load serialized object. serializedObject.Update(); // Port field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(portControlName); int newPort = EditorGUILayout.IntField(_portLabel, _oscOut.port); if (EditorGUI.EndChangeCheck()) { _port.intValue = newPort; if (_oscOut.isOpen) { _oscOut.Close(); // Close socket while editing } } currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName; if (enterKeyDownPort) { UnfocusAndUpdateUI(); } deselect = _prevControlName == portControlName && currentControlName != portControlName; if ((deselect || enterKeyDownPort) && !_oscOut.isOpen) { if (_oscOut.Open(_port.intValue, _remoteIpAddress.stringValue)) { _tempPort = _port.intValue; } else { _port.intValue = _tempPort; // Undo _oscOut.Open(_port.intValue, _remoteIpAddress.stringValue); } } // Mode field. EditorGUI.BeginChangeCheck(); OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, _oscOut.mode); if (EditorGUI.EndChangeCheck() && newMode != _oscOut.mode) { switch (newMode) { case OscSendMode.UnicastToSelf: _oscOut.Open(_oscOut.port); break; case OscSendMode.Unicast: _oscOut.Open(_oscOut.port, OscConst.unicastAddressDefault); break; case OscSendMode.Multicast: _oscOut.Open(_oscOut.port, OscConst.multicastAddressDefault); break; case OscSendMode.Broadcast: _oscOut.Open(_oscOut.port, IPAddress.Broadcast.ToString()); break; } UpdateStatusInEditMode(); } // IP Address field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(ipAddressControlName); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(_remoteIpAddressLabel); string newIp = EditorGUILayout.TextField(_oscOut.remoteIpAddress); // Field if (EditorGUI.EndChangeCheck()) { IPAddress ip; if (IPAddress.TryParse(newIp, out ip)) { _remoteIpAddress.stringValue = newIp; // Accept only valid ip addresses } if (_oscOut.isOpen) { _oscOut.Close(); // Close socket while editing } if (!Application.isPlaying) { if (_pingEnumerator != null) { _pingEnumerator = null; // Don't update ping coroutine while editing } if (_statusInEditMode != OscRemoteStatus.Unknown) { _statusInEditMode = OscRemoteStatus.Unknown; } } } GUILayout.FlexibleSpace(); Rect rect = GUILayoutUtility.GetRect(16, 5); rect.width = 5; rect.x += 3; rect.y += 7; OscRemoteStatus status = Application.isPlaying ? _oscOut.remoteStatus : _statusInEditMode; EditorGUI.DrawRect(rect, StatusToColor(status)); EditorGUILayout.EndHorizontal(); GUILayoutUtility.GetRect(1, 2); // vertical spacing currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName; if (enterKeyDownIp) { UnfocusAndUpdateUI(); } deselect = _prevControlName == ipAddressControlName && currentControlName != ipAddressControlName; if ((deselect || enterKeyDownIp) && !_oscOut.isOpen) // All this mess to check for end edit, OMG!!! Not cool. { if (_oscOut.Open(_port.intValue, _remoteIpAddress.stringValue)) { _tempIPAddress = _remoteIpAddress.stringValue; UpdateStatusInEditMode(); } else { _remoteIpAddress.stringValue = _tempIPAddress; // Undo } } // Is Open field. EditorGUI.BeginDisabledGroup(true); EditorGUILayout.Toggle(_isOpenLabel, _oscOut.isOpen); EditorGUI.EndDisabledGroup(); // Open On Awake field. EditorGUI.BeginDisabledGroup(Application.isPlaying); EditorGUILayout.PropertyField(_openOnAwake, _openOnAwakeLabel); EditorGUI.EndDisabledGroup(); // Settings ... _settingsFoldout.boolValue = EditorGUILayout.Foldout(_settingsFoldout.boolValue, _settingsFoldLabel, true); if (_settingsFoldout.boolValue) { EditorGUI.indentLevel++; // Multicast loopback field. EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(_multicastLoopbackLabel, GUILayout.Width(150)); GUILayout.FlexibleSpace(); EditorGUI.BeginChangeCheck(); _multicastLoopback.boolValue = EditorGUILayout.Toggle(_multicastLoopback.boolValue, GUILayout.Width(30)); if (EditorGUI.EndChangeCheck() && _oscOut.mode == OscSendMode.Multicast) { _oscOut.multicastLoopback = _multicastLoopback.boolValue; } EditorGUILayout.EndHorizontal(); // Bundle Messages On End Of Frame field. BoolSettingsField(_bundleMessagesOnEndOfFrame, _bundleMessagesOnEndOfFrameLabel); // Split Bundles Avoiding Buffer Overflow field. BoolSettingsField(_splitBundlesAvoidingBufferOverflow, _splitBundlesAvoidingBufferOverflowLabel); // Udp Buffer Size field. (UI horror get get a end changed event). GUI.SetNextControlName(bufferSizeControlName); EditorGUI.BeginChangeCheck(); int newBufferSize = EditorGUILayout.IntField(_udpBufferSizeLabel, _udpBufferSize.intValue); if (EditorGUI.EndChangeCheck()) { _tempBufferSize = Mathf.Clamp(newBufferSize, OscConst.udpBufferSizeMin, OscConst.udpBufferSizeMax); } currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownBufferSize = enterKeyDown && currentControlName == bufferSizeControlName; if (enterKeyDownBufferSize) { UnfocusAndUpdateUI(); } deselect = _prevControlName == bufferSizeControlName && currentControlName != bufferSizeControlName; if (enterKeyDownBufferSize || deselect) { if (_tempBufferSize != _udpBufferSize.intValue) { _udpBufferSize.intValue = _tempBufferSize; _oscOut.udpBufferSize = _tempBufferSize; // This will reopen OscOut } } EditorGUI.indentLevel--; } // Messages ... EditorGUI.BeginDisabledGroup(!_oscOut.isOpen); GUIContent messagesFoldContent = new GUIContent("Messages (" + _oscOut.messageCount + ")", "Messages received since last update"); _messagesFoldout.boolValue = EditorGUILayout.Foldout(_messagesFoldout.boolValue, messagesFoldContent, true); if (_messagesFoldout.boolValue) { EditorGUI.indentLevel++; _sb.Clear(); _messageStringQueue.CopyTo(_messageStringBuffer, 0); // Copy to array so we can iterate backswards. for (int i = _messageStringBuffer.Length - 1; i >= 0; i--) { _sb.AppendLine(_messageStringBuffer[i]); } EditorGUILayout.HelpBox(_sb.ToString(), MessageType.None); EditorGUI.indentLevel--; } EditorGUI.EndDisabledGroup(); // Apply serializedObject.ApplyModifiedProperties(); // Request OnInspectorGUI to be called every frame as long as inspector is active EditorUtility.SetDirty(target); // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode) if (!Application.isPlaying && _pingEnumerator != null) { OscHelper.UpdateCoroutineInEditMode(_pingEnumerator, ref _lastPingTime); } // Store name of focused control to detect unfocus events _prevControlName = GUI.GetNameOfFocusedControl(); }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open(int port, string ipAddress = "") { // Close and stop pinging. if (_udpClient != null) { Close(); } // Validate IP. IPAddress ip; if (string.IsNullOrEmpty(ipAddress)) { ipAddress = IPAddress.Loopback.ToString(); } if (ipAddress == IPAddress.Any.ToString() || !IPAddress.TryParse(ipAddress, out ip)) { Debug.LogWarning(logPrepend + "Open failed. Invalid IP address " + ipAddress + "." + Environment.NewLine); return(false); } else if (ip.AddressFamily != AddressFamily.InterNetwork) { Debug.LogWarning(logPrepend + "Open failed. Only IPv4 addresses are supported. " + ipAddress + " is " + ip.AddressFamily + "." + Environment.NewLine); return(false); } _ipAddress = ipAddress; // Detect and set transmission mode. if (_ipAddress == IPAddress.Loopback.ToString()) { _mode = OscSendMode.UnicastToSelf; } else if (_ipAddress == IPAddress.Broadcast.ToString()) { _mode = OscSendMode.Broadcast; } else if (Regex.IsMatch(_ipAddress, OscHelper.multicastAddressPattern)) { _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if (port < OscHelper.portMin || port > OscHelper.portMax) { Debug.LogWarning(logPrepend + "Open failed. Port " + port + " is out of range." + Environment.NewLine); return(false); } _port = port; // Create new client and end point. _udpClient = new UdpClient(); _endPoint = new IPEndPoint(ip, _port); // Multicast senders do not need to join a multicast group, but we need to set a few options. if (_mode == OscSendMode.Multicast) { // Set a time to live, indicating how many routers the messages is allowed to be forwarded by. _udpClient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, OscHelper.timeToLiveMax); // Apply out multicastLoopback field. _udpClient.MulticastLoopback = _multicastLoopback; } // Set time to live to max. I haven't observed any difference, but we better be safe. _udpClient.Ttl = OscHelper.timeToLiveMax; // If an outgoing packet happen to exceeds the MTU (Maximum Transfer Unit) then throw an error instead of fragmenting. _udpClient.DontFragment = true; // Set buffer size to windows limit since we can't tell the actual limit. _udpClient.Client.SendBufferSize = 20384; // Note to self about buffer size: // We can't get the MTU when Unity is using scripting backend ".NET 2.0 Subset" (in Unity 5.3). // https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipv4interfaceproperties.mtu(v=vs.110).aspx // Also, this method gives a "NotSupportedException: This platform is not supported" // https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipv4interfaceproperties.mtu(v=vs.110).aspx // DO NOT CONNECT UDP CLIENT! //_udpClient.Connect( _endPoint ); // Note to self about connecting UdpClient: // We do not use udpClient.Connect(). Instead we pass the IPEndPoint directly to _udpClient.Send(). // This is because a connected UdpClient purposed for sending will throw a number of (for our use) unwanted exceptions and in some cases disconnect. // 10061: SocketException: Connection refused - When we attempt to unicast to loopback address when no application is listening. // 10049: SocketException: The requested address is not valid in this context - When we attempt to broadcast while having no access to the local network. // 10051: SocketException: Network is unreachable - When we pass a unicast or broadcast target to udpClient.Connect() while having no access to a network. // Handle pinging if (Application.isPlaying) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if (_mode == OscSendMode.Unicast) { _pingCoroutine = PingCoroutine(); StartCoroutine(_pingCoroutine); } } // Log if (Application.isPlaying) { string addressTypeString = string.Empty; switch (_mode) { case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } Debug.Log( logPrepend + "Ready to send to " + addressTypeString + " address " + ipAddress + " on port " + port + "." + Environment.NewLine // + //"Buffer size: " + _udpClient.Client.SendBufferSize + " bytes." ); } return(true); }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open(int port, string remoteIpAddress = "") { // Validate IP. IPAddress ip; if (string.IsNullOrEmpty(remoteIpAddress)) { remoteIpAddress = IPAddress.Loopback.ToString(); } if (remoteIpAddress == IPAddress.Any.ToString() || !IPAddress.TryParse(remoteIpAddress, out ip)) { if (OscGlobals.logWarnings) { Debug.LogWarning(string.Format("{0}Open failed. Invalid IP address {1}\n", logPrepend, remoteIpAddress)); } return(false); } if (ip.AddressFamily != AddressFamily.InterNetwork) { if (OscGlobals.logWarnings) { Debug.LogWarning(string.Format("{0}Open failed. Only IPv4 addresses are supported. {1} is {2}\n", logPrepend, remoteIpAddress, ip.AddressFamily)); } return(false); } _remoteIpAddress = remoteIpAddress; // Detect and set transmission mode. if (_remoteIpAddress == IPAddress.Loopback.ToString()) { _mode = OscSendMode.UnicastToSelf; } else if (_remoteIpAddress == IPAddress.Broadcast.ToString()) { _mode = OscSendMode.Broadcast; } else if (Regex.IsMatch(_remoteIpAddress, OscConst.multicastAddressPattern)) { _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if (port < OscConst.portMin || port > OscConst.portMax) { if (OscGlobals.logWarnings) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Open failed. Port "); sb.Append(port); sb.Append(" is out of range.\n"); Debug.LogWarning(sb.ToString()); } return(false); } _port = port; // Create new client and end point. if (_sender == null) { _sender = new UdpSender(_udpBufferSize); } _sender.SetRemoteTarget(_port, ip); // Handle pinging if (Application.isPlaying) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if (_mode == OscSendMode.Unicast) { _pingCoroutine = PingCoroutine(); StartCoroutine(_pingCoroutine); } } // Log if (Application.isPlaying) { string addressTypeString = string.Empty; switch (_mode) { case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } if (OscGlobals.logStatuses) { StringBuilder sb = OscDebug.BuildText(this); sb.Append("Ready to send to "); sb.Append(addressTypeString); sb.Append(" address "); sb.Append(_remoteIpAddress); sb.Append(" on port "); sb.Append(port); sb.Append(".\n"); Debug.Log(sb.ToString()); } } return(true); }
public override void OnInspectorGUI() { string currentControlName; bool deselect; // Check for key down before drawing any fields because they might consume the event. bool enterKeyDown = Event.current.type == EventType.keyDown && Event.current.keyCode == KeyCode.Return; // Load serialized object. serializedObject.Update(); // Port field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(portControlName); int newPort = EditorGUILayout.IntField(_portLabel, oscOut.port); if (EditorGUI.EndChangeCheck()) { _port.intValue = newPort; if (oscOut.isOpen) { oscOut.Close(); // Close socket while editing } } currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName; if (enterKeyDownPort) { UnfocusAndUpdateUI(); } deselect = prevControlName == portControlName && currentControlName != portControlName; if ((deselect || enterKeyDownPort) && !oscOut.isOpen) { if (oscOut.Open(_port.intValue, _ipAddress.stringValue)) { tempPort = _port.intValue; } else { _port.intValue = tempPort; // Undo oscOut.Open(_port.intValue, _ipAddress.stringValue); } } // Mode field. EditorGUI.BeginChangeCheck(); OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, oscOut.mode); if (EditorGUI.EndChangeCheck() && newMode != oscOut.mode) { switch (newMode) { case OscSendMode.UnicastToSelf: oscOut.Open(oscOut.port); break; case OscSendMode.Unicast: oscOut.Open(oscOut.port, OscHelper.unicastAddressDefault); break; case OscSendMode.Multicast: oscOut.Open(oscOut.port, OscHelper.multicastAddressDefault); break; case OscSendMode.Broadcast: oscOut.Open(oscOut.port, IPAddress.Broadcast.ToString()); break; } UpdateStatusInEditMode(); } // IP Address field. EditorGUI.BeginChangeCheck(); GUI.SetNextControlName(ipAddressControlName); EditorGUILayout.BeginHorizontal(); EditorGUILayout.PrefixLabel(_ipAdrressLabel); string newIp = EditorGUILayout.TextField(oscOut.ipAddress); // Field if (EditorGUI.EndChangeCheck()) { System.Net.IPAddress ip; if (System.Net.IPAddress.TryParse(newIp, out ip)) { _ipAddress.stringValue = newIp; // Accept only valid ip addresses } if (oscOut.isOpen) { oscOut.Close(); // Close socket while editing } if (!Application.isPlaying) { if (pingEnumerator != null) { pingEnumerator = null; // Don't update ping coroutine while editing } if (statusInEditMode != OscRemoteStatus.Unknown) { statusInEditMode = OscRemoteStatus.Unknown; } } } GUILayout.FlexibleSpace(); Rect rect = GUILayoutUtility.GetRect(16, 5); rect.width = 5; rect.x += 3; rect.y += 7; OscRemoteStatus status = Application.isPlaying ? oscOut.remoteStatus : statusInEditMode; EditorGUI.DrawRect(rect, StatusToColor(status)); EditorGUILayout.EndHorizontal(); GUILayoutUtility.GetRect(1, 2); // vertical spacing currentControlName = GUI.GetNameOfFocusedControl(); bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName; if (enterKeyDownIp) { UnfocusAndUpdateUI(); } deselect = prevControlName == ipAddressControlName && currentControlName != ipAddressControlName; if ((deselect || enterKeyDownIp) && !oscOut.isOpen) // All this mess to check for end edit, OMG!!! Not cool. { if (oscOut.Open(_port.intValue, _ipAddress.stringValue)) { tempIPAddress = _ipAddress.stringValue; UpdateStatusInEditMode(); } else { _ipAddress.stringValue = tempIPAddress; // Undo } } // Is Open field. EditorGUI.BeginDisabledGroup(true); EditorGUILayout.Toggle(_isOpenLabel, oscOut.isOpen); EditorGUI.EndDisabledGroup(); // Open On Awake field. EditorGUI.BeginDisabledGroup(Application.isPlaying); EditorGUILayout.PropertyField(_openOnAwake, _openOnAwakeLabel); EditorGUI.EndDisabledGroup(); EditorGUI.indentLevel++; // Settings ... _settingsFoldout.boolValue = EditorGUILayout.Foldout(_settingsFoldout.boolValue, _settingsFoldLabel); if (_settingsFoldout.boolValue) { // Multicast loopback field. EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField(_multicastLoopbackLabel, GUILayout.Width(150)); GUILayout.FlexibleSpace(); EditorGUI.BeginChangeCheck(); _multicastLoopback.boolValue = EditorGUILayout.Toggle(_multicastLoopback.boolValue, GUILayout.Width(30)); if (EditorGUI.EndChangeCheck() && oscOut.mode == OscSendMode.Multicast) { oscOut.multicastLoopback = _multicastLoopback.boolValue; } EditorGUILayout.EndHorizontal(); // Bundle Messages On End Of Frame field. BoolSettingsField(_bundleMessagesOnEndOfFrame, _bundleMessagesOnEndOfFrameLabel); } // Messages ... EditorGUI.BeginDisabledGroup(!oscOut.isOpen); GUIContent messagesFoldContent = new GUIContent("Messages (" + oscOut.messageCount + ")", "Messages received since last update"); _messagesFoldout.boolValue = EditorGUILayout.Foldout(_messagesFoldout.boolValue, messagesFoldContent); if (_messagesFoldout.boolValue) { OscMessage[] messages = messageBuffer.ToArray(); StringBuilder messagesText = new StringBuilder(); for (int m = messages.Length - 1; m >= 0; m--) { messagesText.Append((m != messages.Length - 1 ? Environment.NewLine : "") + messages[m].ToString()); } EditorGUILayout.HelpBox(messagesText.ToString(), MessageType.None); } EditorGUI.EndDisabledGroup(); EditorGUI.indentLevel--; // Apply serializedObject.ApplyModifiedProperties(); // Request OnInspectorGUI to be called every frame as long as inspector is active EditorUtility.SetDirty(target); // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode) if (!Application.isPlaying && pingEnumerator != null) { OscHelper.UpdateCoroutineInEditMode(pingEnumerator, ref lastPingTime); } // Store name of focused control to detect unfocus events prevControlName = GUI.GetNameOfFocusedControl(); }
/// <summary> /// Open to send messages to specified port and (optional) IP address. /// If no IP address is given, messages will be send locally on this device. /// Returns success status. /// </summary> public bool Open( int port, string ipAddress = "" ) { // Close and stop pinging if( _udpClient != null ) Close(); // Validate IP IPAddress ip; if( string.IsNullOrEmpty( ipAddress ) ) ipAddress = IPAddress.Loopback.ToString(); if( ipAddress == IPAddress.Any.ToString() || !IPAddress.TryParse( ipAddress, out ip ) ){ Debug.LogWarning( "[OscOut] Open failed. Invalid IP address " + ipAddress + "." + Environment.NewLine ); return false; } else if( ip.AddressFamily != AddressFamily.InterNetwork ){ Debug.LogWarning( "[OscOut] Open failed. Only IPv4 addresses are supported. " + ipAddress + " is " + ip.AddressFamily + "." + Environment.NewLine ); return false; } _ipAddress = ipAddress; // Detect and set transmission mode if( _ipAddress == IPAddress.Loopback.ToString() ){ _mode = OscSendMode.UnicastToSelf; } else if( _ipAddress == IPAddress.Broadcast.ToString() ){ _mode = OscSendMode.Broadcast; } else if( Regex.IsMatch( _ipAddress, OscHelper.multicastAddressPattern ) ){ _mode = OscSendMode.Multicast; } else { _mode = OscSendMode.Unicast; } // Validate port number range if( port < OscHelper.portMin || port > OscHelper.portMax ){ Debug.LogWarning( "[OscOut] Open failed. Port " + port + " is out of range." + Environment.NewLine ); return false; } _port = port; // Create new client and end point _udpClient = new UdpClient(); _endPoint = new IPEndPoint( ip, _port ); // Multicast senders do not need to join a multicast group, but we need to set a few options. if( _mode == OscSendMode.Multicast ) { // Set a time to live, indicating how many routers the messages is allowed to be forwarded by. _udpClient.Client.SetSocketOption( SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, OscHelper.timeToLiveMax ); // Apply out multicastLoopback field _udpClient.MulticastLoopback = _multicastLoopback; } // Set time to live to max. I haven't observed any difference, but we better be safe. _udpClient.Ttl = OscHelper.timeToLiveMax; // If an outgoing packet happen to exceeds the MTU (Maximum Transfer Unit) then throw an error instead of fragmenting. _udpClient.DontFragment = true; // Set buffer size to windows limit since we can't tell the actual limit. _udpClient.Client.SendBufferSize = OscHelper.udpPacketSizeMaxOnWindows; // Note to self about buffer size: // We can't get the MTU when Unity is using scripting backend ".NET 2.0 Subset" (in Unity 5.3). // https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipv4interfaceproperties.mtu(v=vs.110).aspx // Also, this method gives a "NotSupportedException: This platform is not supported" // https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipv4interfaceproperties.mtu(v=vs.110).aspx // DO NOT CONNECT UDP CLIENT! //_udpClient.Connect( _endPoint ); // Note to self about connecting UdpClient: // We do not use udpClient.Connect(). Instead we pass the IPEndPoint directly to _udpClient.Send(). // This is because a connected UdpClient purposed for sending will throw a number of (for our use) unwanted exceptions and in some cases disconnect. // 10061: SocketException: Connection refused - When we attempt to unicast to loopback address when no application is listening. // 10049: SocketException: The requested address is not valid in this context - When we attempt to broadcast while having no access to the local network. // 10051: SocketException: Network is unreachable - When we pass a unicast or broadcast target to udpClient.Connect() while having no access to a network. // Handle pinging if( Application.isPlaying ) { _remoteStatus = _mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; if( _mode == OscSendMode.Unicast ){ _pingCoroutine = PingCoroutine(); StartCoroutine( _pingCoroutine ); } } // Log if( Application.isPlaying ){ string addressTypeString = string.Empty; switch( _mode ){ case OscSendMode.Broadcast: addressTypeString = "broadcast"; break; case OscSendMode.Multicast: addressTypeString = "multicast"; break; case OscSendMode.Unicast: addressTypeString = "IP"; break; case OscSendMode.UnicastToSelf: addressTypeString = "local"; break; } Debug.Log( "[OscOut] Ready to send to " + addressTypeString + " address " + ipAddress + " on port " + port + "." + Environment.NewLine// + //"Buffer size: " + _udpClient.Client.SendBufferSize + " bytes." ); } return true; }