using UnityEngine; using UnityEngine.Events; using System; using System.Collections; using System.Text; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; [System.Serializable] public class StreamDockEvent : UnityEvent { } public class SimpleStreamDockCommunicator : MonoBehaviour { [Header("연결 설정")] [SerializeField] private string serverUrl = "ws://localhost:15732"; [SerializeField] private bool autoConnect = true; [SerializeField] private float reconnectInterval = 5f; [Header("이벤트")] public StreamDockEvent OnStreamDockMessageReceived; public UnityEvent OnConnected; public UnityEvent OnDisconnected; // 내부 변수 private ClientWebSocket webSocket; private CancellationTokenSource cancellationTokenSource; private bool isConnecting = false; private bool isConnected = false; // 프로퍼티 public bool IsConnected => isConnected; void Start() { if (autoConnect) { ConnectToStreamDock(); } } void OnDestroy() { DisconnectFromStreamDock(); } /// /// StreamDock에 연결 /// public async void ConnectToStreamDock() { if (isConnecting || isConnected) return; isConnecting = true; try { webSocket = new ClientWebSocket(); cancellationTokenSource = new CancellationTokenSource(); Debug.Log($"StreamDock에 연결 중... {serverUrl}"); await webSocket.ConnectAsync(new Uri(serverUrl), cancellationTokenSource.Token); isConnected = true; isConnecting = false; Debug.Log("StreamDock에 연결되었습니다!"); OnConnected?.Invoke(); // 메시지 수신 시작 _ = ReceiveMessages(); } catch (Exception e) { Debug.LogError($"StreamDock 연결 실패: {e.Message}"); isConnecting = false; OnDisconnected?.Invoke(); // 재연결 시도 StartCoroutine(TryReconnect()); } } /// /// StreamDock 연결 해제 /// public async void DisconnectFromStreamDock() { if (!isConnected) return; try { cancellationTokenSource?.Cancel(); if (webSocket != null && webSocket.State == WebSocketState.Open) { await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Unity 종료", CancellationToken.None); } } catch (Exception e) { Debug.LogError($"연결 해제 중 오류: {e.Message}"); } finally { isConnected = false; webSocket?.Dispose(); webSocket = null; OnDisconnected?.Invoke(); } } /// /// StreamDock으로 메시지 전송 /// public async void SendMessageToStreamDock(string eventType, object data = null) { if (!isConnected) { Debug.LogWarning("StreamDock에 연결되지 않았습니다."); return; } try { var message = new { type = eventType, data = data, timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() }; string jsonMessage = JsonUtility.ToJson(message); byte[] buffer = Encoding.UTF8.GetBytes(jsonMessage); await webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationTokenSource.Token); Debug.Log($"StreamDock으로 메시지 전송: {eventType}"); } catch (Exception e) { Debug.LogError($"메시지 전송 실패: {e.Message}"); } } /// /// 메시지 수신 처리 /// private async Task ReceiveMessages() { var buffer = new byte[4096]; try { while (webSocket.State == WebSocketState.Open) { var result = await webSocket.ReceiveAsync(new ArraySegment(buffer), cancellationTokenSource.Token); if (result.MessageType == WebSocketMessageType.Text) { string message = Encoding.UTF8.GetString(buffer, 0, result.Count); ProcessReceivedMessage(message); } else if (result.MessageType == WebSocketMessageType.Close) { Debug.Log("StreamDock에서 연결을 종료했습니다."); break; } } } catch (Exception e) { Debug.LogError($"메시지 수신 중 오류: {e.Message}"); } finally { isConnected = false; OnDisconnected?.Invoke(); StartCoroutine(TryReconnect()); } } /// /// 수신된 메시지 처리 /// private void ProcessReceivedMessage(string message) { try { //Debug.Log($"StreamDock에서 메시지 수신: {message}"); // JSON 파싱 (간단한 구조) if (message.Contains("type")) { // UnityEvent 호출 OnStreamDockMessageReceived?.Invoke("streamdock_message", message); // 특정 이벤트 타입 처리 if (message.Contains("streamdock_button_clicked")) { HandleButtonClick(message); } else if (message.Contains("dial_rotate")) { HandleDialRotate(message); } else if (message.Contains("dial_press")) { HandleDialPress(message); } } } catch (Exception e) { Debug.LogError($"메시지 처리 중 오류: {e.Message}"); } } /// /// 버튼 클릭 이벤트 처리 /// private void HandleButtonClick(string message) { Debug.Log("StreamDock 버튼이 클릭되었습니다!"); OnStreamDockMessageReceived?.Invoke("button_clicked", message); } /// /// 다이얼 회전 이벤트 처리 /// private void HandleDialRotate(string message) { Debug.Log("StreamDock 다이얼이 회전했습니다!"); OnStreamDockMessageReceived?.Invoke("dial_rotate", message); } /// /// 다이얼 누름 이벤트 처리 /// private void HandleDialPress(string message) { Debug.Log("StreamDock 다이얼이 눌렸습니다!"); OnStreamDockMessageReceived?.Invoke("dial_press", message); } /// /// 재연결 시도 /// private IEnumerator TryReconnect() { yield return new WaitForSeconds(reconnectInterval); if (!isConnected && !isConnecting) { Debug.Log("StreamDock 재연결 시도..."); ConnectToStreamDock(); } } // 테스트용 메서드들 [ContextMenu("테스트 메시지 전송")] public void SendTestMessage() { SendMessageToStreamDock("test_message", new { message = "Unity에서 테스트 메시지" }); } [ContextMenu("커스텀 이벤트 전송")] public void SendCustomEvent() { SendMessageToStreamDock("custom_event", new { action = "test_action", value = 123 }); } }