using UnityEngine; using WebSocketSharp.Server; using System.Collections.Generic; using Newtonsoft.Json; using System; using System.Collections; public class StreamDeckServerManager : MonoBehaviour { [Header("서버 설정")] public int port = 10701; private WebSocketServer server; private List connectedClients = new List(); public CameraManager cameraManager { get; private set; } public ItemController itemController { get; private set; } // 싱글톤 패턴으로 StreamDeckService에서 접근 가능하도록 public static StreamDeckServerManager Instance { get; private set; } // 메인 스레드에서 처리할 작업 큐 private readonly Queue mainThreadActions = new Queue(); private readonly object lockObject = new object(); void Awake() { Instance = this; } void Start() { // CameraManager 찾기 cameraManager = FindObjectOfType(); if (cameraManager == null) { Debug.LogError("[StreamDeckServerManager] CameraManager를 찾을 수 없습니다!"); return; } // ItemController 찾기 itemController = FindObjectOfType(); if (itemController == null) { Debug.LogWarning("[StreamDeckServerManager] ItemController를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다."); } StartServer(); } void Update() { // 메인 스레드에서 대기 중인 작업들을 처리 lock (lockObject) { while (mainThreadActions.Count > 0) { var action = mainThreadActions.Dequeue(); try { action?.Invoke(); } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 메인 스레드 작업 실행 오류: {ex.Message}"); } } } } void OnApplicationQuit() { StopServer(); } private void StartServer() { try { server = new WebSocketServer($"ws://127.0.0.1:{port}"); server.AddWebSocketService("/"); server.Start(); Debug.Log($"[StreamDeckServerManager] WebSocket 서버 시작됨, 포트: {port}"); } catch (Exception e) { Debug.LogError($"[StreamDeckServerManager] 서버 시작 실패: {e.Message}"); } } private void StopServer() { if (server != null) { server.Stop(); server = null; Debug.Log("[StreamDeckServerManager] 서버 중지됨"); } } public void OnClientConnected(StreamDeckService service) { // 메인 스레드에서 처리하도록 큐에 추가 lock (lockObject) { mainThreadActions.Enqueue(() => { connectedClients.Add(service); Debug.Log($"[StreamDeckServerManager] 클라이언트 연결됨. 총 연결: {connectedClients.Count}"); SendInitialCameraData(service); }); } } // 메시지 처리도 메인 스레드로 전달 public void ProcessMessageOnMainThread(string messageData, StreamDeckService service) { lock (lockObject) { mainThreadActions.Enqueue(() => { try { ProcessMessage(messageData, service); } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 메시지 처리 오류: {ex.Message}"); } }); } } public void OnClientDisconnected(StreamDeckService service) { connectedClients.Remove(service); Debug.Log($"[StreamDeckServerManager] 클라이언트 연결 해제됨. 총 연결: {connectedClients.Count}"); } private void SendInitialCameraData(StreamDeckService service) { if (cameraManager == null) return; var initialData = new { type = "connection_established", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { session_id = Guid.NewGuid().ToString(), message = "유니티 서버에 연결되었습니다!", camera_data = cameraManager.GetCameraListData(), current_camera = cameraManager.GetCurrentCameraState(), item_data = itemController?.GetItemListData(), current_item = itemController?.GetCurrentItemState() } }; string json = JsonConvert.SerializeObject(initialData); service.SendMessage(json); Debug.Log("[StreamDeckServerManager] 초기 데이터 전송됨 (카메라 + 아이템)"); } public void BroadcastMessage(string message) { foreach (var client in connectedClients.ToArray()) { try { client.SendMessage(message); } catch (Exception e) { Debug.LogError($"[StreamDeckServerManager] 메시지 전송 실패: {e.Message}"); connectedClients.Remove(client); } } } // 카메라 변경 시 모든 클라이언트에게 알림 public void NotifyCameraChanged() { if (cameraManager == null) return; var updateMessage = new { type = "camera_changed", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { camera_data = cameraManager.GetCameraListData(), current_camera = cameraManager.GetCurrentCameraState() } }; string json = JsonConvert.SerializeObject(updateMessage); BroadcastMessage(json); Debug.Log("[StreamDeckServerManager] 카메라 변경 알림 전송됨"); } // 아이템 변경 시 모든 클라이언트에게 알림 public void NotifyItemChanged() { if (itemController == null) return; var updateMessage = new { type = "item_changed", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { item_data = itemController.GetItemListData(), current_item = itemController.GetCurrentItemState() } }; string json = JsonConvert.SerializeObject(updateMessage); BroadcastMessage(json); Debug.Log("[StreamDeckServerManager] 아이템 변경 알림 전송됨"); } // 메시지 처리 로직을 여기로 이동 private void ProcessMessage(string messageData, StreamDeckService service) { // JSON 파싱 시도 var message = JsonConvert.DeserializeObject>(messageData); string messageType = message.ContainsKey("type") ? message["type"].ToString() : null; switch (messageType) { case "switch_camera": HandleSwitchCamera(message); break; case "get_camera_list": HandleGetCameraList(service); break; case "toggle_item": HandleToggleItem(message); break; case "set_item": HandleSetItem(message); break; case "get_item_list": HandleGetItemList(service); break; case "test": // 테스트 메시지 에코 응답 var response = new { type = "echo", timestamp = DateTime.UtcNow.ToString("o"), data = new { received_message = messageData } }; string json = JsonConvert.SerializeObject(response); service.SendMessage(json); break; default: Debug.Log($"[StreamDeckServerManager] 알 수 없는 메시지 타입: {messageType}"); break; } } private void HandleSwitchCamera(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 카메라 전환 요청 수신"); if (cameraManager == null) { Debug.LogError("[StreamDeckServerManager] cameraManager가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("camera_index")) { var cameraIndexToken = jObject["camera_index"]; if (int.TryParse(cameraIndexToken?.ToString(), out int cameraIndex)) { if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환"); cameraManager.Set(cameraIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 카메라 인덱스: {cameraIndex}, 유효 범위: 0-{(cameraManager.cameraPresets?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] camera_index 파싱 실패: {cameraIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'camera_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("camera_index")) { var cameraIndexObj = data["camera_index"]; if (int.TryParse(cameraIndexObj?.ToString(), out int cameraIndex)) { if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환"); cameraManager.Set(cameraIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 카메라 인덱스: {cameraIndex}, 유효 범위: 0-{(cameraManager.cameraPresets?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] camera_index 파싱 실패: {cameraIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'camera_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 카메라 전환 실패: {ex.Message}"); } } private void HandleGetCameraList(StreamDeckService service) { if (cameraManager == null) return; var response = new { type = "camera_list_response", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { camera_data = cameraManager.GetCameraListData(), current_camera = cameraManager.GetCurrentCameraState() } }; string json = JsonConvert.SerializeObject(response); service.SendMessage(json); } private void HandleToggleItem(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 아이템 토글 요청 수신"); if (itemController == null) { Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("item_index")) { var itemIndexToken = jObject["item_index"]; if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex)) { if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글"); itemController.ToggleGroup(itemIndex); // 아이템 변경 알림 전송 NotifyItemChanged(); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아이템 인덱스: {itemIndex}, 유효 범위: 0-{(itemController.itemGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] item_index 파싱 실패: {itemIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'item_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("item_index")) { var itemIndexObj = data["item_index"]; if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex)) { if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글"); itemController.ToggleGroup(itemIndex); // 아이템 변경 알림 전송 NotifyItemChanged(); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아이템 인덱스: {itemIndex}, 유효 범위: 0-{(itemController.itemGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] item_index 파싱 실패: {itemIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'item_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 아이템 토글 실패: {ex.Message}"); } } private void HandleSetItem(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 아이템 설정 요청 수신"); if (itemController == null) { Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("item_index")) { var itemIndexToken = jObject["item_index"]; if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex)) { if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번으로 설정"); itemController.Set(itemIndex); // 아이템 변경 알림 전송 NotifyItemChanged(); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아이템 인덱스: {itemIndex}, 유효 범위: 0-{(itemController.itemGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] item_index 파싱 실패: {itemIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'item_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("item_index")) { var itemIndexObj = data["item_index"]; if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex)) { if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아이템 {itemIndex}번으로 설정"); itemController.Set(itemIndex); // 아이템 변경 알림 전송 NotifyItemChanged(); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아이템 인덱스: {itemIndex}, 유효 범위: 0-{(itemController.itemGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] item_index 파싱 실패: {itemIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'item_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 아이템 설정 실패: {ex.Message}"); } } private void HandleGetItemList(StreamDeckService service) { if (itemController == null) return; var response = new { type = "item_list_response", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { item_data = itemController.GetItemListData(), current_item = itemController.GetCurrentItemState() } }; string json = JsonConvert.SerializeObject(response); service.SendMessage(json); } } public class StreamDeckService : WebSocketBehavior { private StreamDeckServerManager serverManager; protected override void OnOpen() { serverManager = StreamDeckServerManager.Instance; if (serverManager != null) { serverManager.OnClientConnected(this); } Debug.Log("[StreamDeckService] WebSocket 연결 열림"); } protected override void OnMessage(WebSocketSharp.MessageEventArgs e) { Debug.Log($"[StreamDeckService] 원본 메시지 수신: {e.Data}"); // 메인 스레드에서 처리하도록 매니저에게 전달 if (serverManager != null) { serverManager.ProcessMessageOnMainThread(e.Data, this); } } // WebSocketSharp의 Send 메서드 래퍼 public void SendMessage(string message) { try { Send(message); } catch (Exception ex) { Debug.LogError($"[StreamDeckService] 메시지 전송 실패: {ex.Message}"); } } protected override void OnClose(WebSocketSharp.CloseEventArgs e) { if (serverManager != null) { serverManager.OnClientDisconnected(this); } Debug.Log($"[StreamDeckService] WebSocket 연결 닫힘: {e.Reason}"); } protected override void OnError(WebSocketSharp.ErrorEventArgs e) { Debug.LogError($"[StreamDeckService] WebSocket 오류: {e.Message}"); } }