void UpdateStatusInEditMode() { switch (_oscOut.mode) { case OscSendMode.UnicastToSelf: _statusInEditMode = OscRemoteStatus.Connected; _pingEnumerator = null; break; case OscSendMode.Unicast: _statusInEditMode = OscRemoteStatus.Unknown; if (!Application.isPlaying) { _pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref _lastPingTime); } break; case OscSendMode.Multicast: _statusInEditMode = OscRemoteStatus.Unknown; _pingEnumerator = null; break; case OscSendMode.Broadcast: _statusInEditMode = OscRemoteStatus.Unknown; _pingEnumerator = null; break; } }
// OnEnable is only called when Application.isPlaying. void OnDisable() { if (isOpen) { Close(); wasClosedOnDisable = true; } _remoteStatus = OscRemoteStatus.Unknown; }
IEnumerator PingCoroutine() { while( true ) { Ping ping = new Ping( _remoteIpAddress ); yield return _pingYield; _remoteStatus = ( ping.isDone && ping.time >= 0 ) ? OscRemoteStatus.Connected : OscRemoteStatus.Disconnected; } }
// OnEnable is only called when Application.isPlaying. void OnDisable() { if( isOpen ) { Close(); _wasClosedOnDisable = true; } _remoteStatus = OscRemoteStatus.Unknown; if( _endOfFrameCoroutine != null ) StopCoroutine( _endOfFrameCoroutine ); }
IEnumerator PingCoroutine() { while (true) { Ping ping = new Ping(_ipAddress); yield return(new WaitForSeconds(_pingInterval)); _remoteStatus = (ping.isDone && ping.time >= 0) ? OscRemoteStatus.Connected : OscRemoteStatus.Disconnected; } }
/// <summary> /// Close and stop sending messages. /// </summary> public void Close() { if (_pingCoroutine != null) { StopCoroutine(_pingCoroutine); _pingCoroutine = null; } _remoteStatus = OscRemoteStatus.Unknown; _sender = null; _wasClosedOnDisable = false; }
// This coroutine is only run in Edit Mode. IEnumerator PingCoroutine() { while (true) { Ping ping = new Ping(_oscOut.remoteIpAddress); yield return(new WaitForSeconds(pingInterval)); //Debug.Log( "Ping time " + ping.time ); _statusInEditMode = (ping.isDone && ping.time >= 0) ? OscRemoteStatus.Connected : OscRemoteStatus.Disconnected; } }
static Color StatusToColor(OscRemoteStatus status) { switch (status) { case OscRemoteStatus.Unknown: return(Color.yellow); case OscRemoteStatus.Connected: return(Color.green); case OscRemoteStatus.Disconnected: return(Color.red); default: return(Color.gray); } }
/// <summary> /// Close and stop sending messages. /// </summary> public void Close() { if( _pingCoroutine != null ){ StopCoroutine( _pingCoroutine ); _pingCoroutine = null; } _remoteStatus = OscRemoteStatus.Unknown; if( _udpClient == null ) return; _udpClient.Close(); _udpClient = null; _wasClosedOnDisable = false; }
void OnEnable() { oscOut = target as OscOut; if (messageBuffer == null) { messageBuffer = new Queue <OscMessage>(messageBufferCapacity); } // Get serialized properties. _openOnAwake = serializedObject.FindProperty("_openOnAwake"); _ipAddress = serializedObject.FindProperty("_ipAddress"); _port = serializedObject.FindProperty("_port"); _multicastLoopback = serializedObject.FindProperty("_multicastLoopback"); _bundleMessagesOnEndOfFrame = serializedObject.FindProperty("_bundleMessagesOnEndOfFrame"); _settingsFoldout = serializedObject.FindProperty("_settingsFoldout"); _messagesFoldout = serializedObject.FindProperty("_messagesFoldout"); // Store socket info for change check workaround. tempIPAddress = _ipAddress.stringValue; tempPort = _port.intValue; // Ensure that OscOut scripts will be executed early, so that if Open On Awake is enabled the socket will open before other scripts are called. MonoScript script = MonoScript.FromMonoBehaviour(target as MonoBehaviour); if (MonoImporter.GetExecutionOrder(script) != executionOrderNum) { MonoImporter.SetExecutionOrder(script, executionOrderNum); } // When object is selected in Edit Mode then we start listening. if (oscOut.enabled && !Application.isPlaying && !oscOut.isOpen) { oscOut.Open(oscOut.port, oscOut.ipAddress); statusInEditMode = oscOut.mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; } // Subscribe to OSC messages oscOut.onAnyMessage.AddListener(OnOSCMessage); // If in Edit Mode, then start a coroutine that will update the connection status. Unity can't start coroutines in Runtime. if (!Application.isPlaying && oscOut.mode == OscSendMode.Unicast) { pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref lastPingTime); } }
void OnEnable() { _oscOut = target as OscOut; // Get serialized properties. _openOnAwake = serializedObject.FindProperty("_openOnAwake"); _remoteIpAddress = serializedObject.FindProperty("_remoteIpAddress"); _port = serializedObject.FindProperty("_port"); _multicastLoopback = serializedObject.FindProperty("_multicastLoopback"); _bundleMessagesOnEndOfFrame = serializedObject.FindProperty("_bundleMessagesOnEndOfFrame"); _splitBundlesAvoidingBufferOverflow = serializedObject.FindProperty("_splitBundlesAvoidingBufferOverflow"); _udpBufferSize = serializedObject.FindProperty("_udpBufferSize"); _settingsFoldout = serializedObject.FindProperty("_settingsFoldout"); _messagesFoldout = serializedObject.FindProperty("_messagesFoldout"); // Store socket info for change check workaround. _tempIPAddress = _remoteIpAddress.stringValue; _tempPort = _port.intValue; // Ensure that OscOut scripts will be executed early, so that if Open On Awake is enabled the socket will open before other scripts are called. MonoScript script = MonoScript.FromMonoBehaviour(target as MonoBehaviour); if (MonoImporter.GetExecutionOrder(script) != executionOrderNum) { MonoImporter.SetExecutionOrder(script, executionOrderNum); } // When object is selected in Edit Mode then we start listening. if (_oscOut.enabled && !Application.isPlaying && !_oscOut.isOpen) { _oscOut.Open(_oscOut.port, _oscOut.remoteIpAddress); _statusInEditMode = _oscOut.mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; } // Subscribe to OSC messages OscEditorUI.AddInspectorMessageListener(_oscOut, OnOSCMessage, ref _inspectorMessageEventObject); // If in Edit Mode, then start a coroutine that will update the connection status. Unity can't start coroutines in Runtime. if (!Application.isPlaying && _oscOut.mode == OscSendMode.Unicast) { _pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref _lastPingTime); } }
/// <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; }
// OnEnable is only called when Application.isPlaying void OnDisable() { if( isOpen ){ Close(); wasClosedOnDisable = true; } _remoteStatus = OscRemoteStatus.Unknown; }
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(); }
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(); }
// This coroutine is only run in Edit Mode. IEnumerator PingCoroutine() { while( true ) { Ping ping = new Ping( oscOut.ipAddress ); yield return new WaitForSeconds( pingInterval ); //Debug.Log( "Ping time " + ping.time ); statusInEditMode = ( ping.isDone && ping.time >= 0 ) ? OscRemoteStatus.Connected : OscRemoteStatus.Disconnected; } }
void OnEnable() { oscOut = target as OscOut; if( messageBuffer == null ) messageBuffer = new Queue<OscMessage>( messageBufferCapacity ); // Get serialized properties _openOnAwake = serializedObject.FindProperty("_openOnAwake"); _ipAddress = serializedObject.FindProperty("_ipAddress"); _port = serializedObject.FindProperty("_port"); _multicastLoopback = serializedObject.FindProperty("_multicastLoopback"); _bundleMessagesOnEndOfFrame = serializedObject.FindProperty("_bundleMessagesOnEndOfFrame"); _settingsFoldout = serializedObject.FindProperty("_settingsFoldout"); _messagesFoldout = serializedObject.FindProperty("_messagesFoldout"); // Store socket info for change check workaround tempIPAddress = _ipAddress.stringValue; tempPort = _port.intValue; // Ensure that OscOut scripts will be executed early, so that if Open On Awake is enabled the socket will open before other scripts are called. MonoScript script = MonoScript.FromMonoBehaviour( target as MonoBehaviour ); if( MonoImporter.GetExecutionOrder( script ) != executionOrderNum ) MonoImporter.SetExecutionOrder( script, executionOrderNum ); // When object is selected in Edit Mode then we start listening if( oscOut.enabled && !Application.isPlaying && !oscOut.isOpen ){ oscOut.Open( oscOut.port, oscOut.ipAddress ); statusInEditMode = oscOut.mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown; } // Subscribe to OSC messages oscOut.onAnyMessage.AddListener( OnOSCMessage ); // If in Edit Mode, then start a coroutine that will update the connection status. Unity can't start coroutines in Runtime. if( !Application.isPlaying && oscOut.mode == OscSendMode.Unicast ){ pingEnumerator = OscHelper.StartCoroutineInEditMode( PingCoroutine(), ref lastPingTime ); } }
Color StatusToColor( OscRemoteStatus status ) { switch( status ) { case OscRemoteStatus.Unknown: return Color.yellow; case OscRemoteStatus.Connected: return Color.green; case OscRemoteStatus.Disconnected: return Color.red; default: return Color.gray; } }
/// <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(); }
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; oscOut.Open( oscOut.port, oscOut.ipAddress ); } 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(); }
void UpdateStatusInEditMode() { switch( oscOut.mode ){ case OscSendMode.UnicastToSelf: statusInEditMode = OscRemoteStatus.Connected; pingEnumerator = null; break; case OscSendMode.Unicast: statusInEditMode = OscRemoteStatus.Unknown; if( !Application.isPlaying ) pingEnumerator = OscHelper.StartCoroutineInEditMode( PingCoroutine(), ref lastPingTime ); break; case OscSendMode.Multicast: statusInEditMode = OscRemoteStatus.Unknown; pingEnumerator = null; break; case OscSendMode.Broadcast: statusInEditMode = OscRemoteStatus.Unknown; pingEnumerator = null; break; } }
/// <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; }
/// <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> /// Close and stop sending messages. /// </summary> public void Close() { if( _pingCoroutine != null ){ StopCoroutine( _pingCoroutine ); _pingCoroutine = null; } _remoteStatus = OscRemoteStatus.Unknown; if( _udpClient == null ) return; _udpClient.Close(); _udpClient = null; wasClosedOnDisable = false; }
IEnumerator PingCoroutine() { while( true ) { Ping ping = new Ping( _ipAddress ); yield return new WaitForSeconds( _pingInterval ); _remoteStatus = ( ping.isDone && ping.time >= 0 ) ? OscRemoteStatus.Connected : OscRemoteStatus.Disconnected; } }