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; } public EventController eventController { get; private set; } public AvatarOutfitController avatarOutfitController { get; private set; } public SystemController systemController { 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를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다."); } // EventController 찾기 eventController = FindObjectOfType(); if (eventController == null) { Debug.LogWarning("[StreamDeckServerManager] EventController를 찾을 수 없습니다. 이벤트 컨트롤 기능이 비활성화됩니다."); } // AvatarOutfitController 찾기 avatarOutfitController = FindObjectOfType(); if (avatarOutfitController == null) { Debug.LogWarning("[StreamDeckServerManager] AvatarOutfitController를 찾을 수 없습니다. 아바타 의상 컨트롤 기능이 비활성화됩니다."); } // SystemController 찾기 systemController = FindObjectOfType(); if (systemController == null) { Debug.LogWarning("[StreamDeckServerManager] SystemController를 찾을 수 없습니다. 시스템 컨트롤 기능이 비활성화됩니다."); } 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(), event_data = eventController?.GetEventListData(), current_event = eventController?.GetCurrentEventState(), avatar_outfit_data = avatarOutfitController?.GetAvatarOutfitListData(), current_avatar_outfit = avatarOutfitController?.GetCurrentAvatarOutfitState() } }; 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] 아이템 변경 알림 전송됨"); } // 이벤트 변경 시 모든 클라이언트에게 알림 public void NotifyEventChanged() { if (eventController == null) return; var updateMessage = new { type = "event_changed", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { event_data = eventController.GetEventListData(), current_event = eventController.GetCurrentEventState() } }; string json = JsonConvert.SerializeObject(updateMessage); BroadcastMessage(json); Debug.Log("[StreamDeckServerManager] 이벤트 변경 알림 전송됨"); } // 아바타 의상 변경 시 모든 클라이언트에게 알림 public void NotifyAvatarOutfitChanged() { if (avatarOutfitController == null) return; var updateMessage = new { type = "avatar_outfit_changed", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { avatar_outfit_data = avatarOutfitController.GetAvatarOutfitListData(), current_avatar_outfit = avatarOutfitController.GetCurrentAvatarOutfitState() } }; 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_drone_mode": HandleToggleDroneMode(service); break; case "get_drone_state": HandleGetDroneState(service); break; case "toggle_item": HandleToggleItem(message); break; case "set_item": HandleSetItem(message); break; case "get_item_list": HandleGetItemList(service); break; case "execute_event": HandleExecuteEvent(message); break; case "set_event": HandleSetEvent(message); break; case "get_event_list": HandleGetEventList(service); break; case "set_avatar_outfit": HandleSetAvatarOutfit(message); break; case "get_avatar_outfit_list": HandleGetAvatarOutfitList(service); break; // SystemController 명령어들 case "toggle_optitrack_markers": case "show_optitrack_markers": case "hide_optitrack_markers": case "reconnect_optitrack": case "reconnect_facial_motion": case "refresh_facial_motion_clients": case "start_motion_recording": case "stop_motion_recording": case "toggle_motion_recording": case "refresh_motion_recorders": case "capture_screenshot": case "capture_alpha_screenshot": case "open_screenshot_folder": case "refresh_magica_cloth": case "reset_magica_cloth": case "reset_magica_cloth_keep_pose": HandleSystemCommand(message); 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 HandleToggleDroneMode(StreamDeckService service) { if (cameraManager == null) return; cameraManager.ToggleDroneMode(); // 상태 응답 HandleGetDroneState(service); } private void HandleGetDroneState(StreamDeckService service) { if (cameraManager == null) return; var response = new { type = "drone_state_response", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { is_drone_mode = cameraManager.IsDroneModeActive, 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); } private void HandleExecuteEvent(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 이벤트 실행 요청 수신"); if (eventController == null) { Debug.LogError("[StreamDeckServerManager] eventController가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("event_index")) { var eventIndexToken = jObject["event_index"]; if (int.TryParse(eventIndexToken?.ToString(), out int eventIndex)) { if (eventIndex >= 0 && eventIndex < (eventController.eventGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 이벤트 {eventIndex}번 실행"); eventController.ExecuteEvent(eventIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 이벤트 인덱스: {eventIndex}, 유효 범위: 0-{(eventController.eventGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] event_index 파싱 실패: {eventIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'event_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("event_index")) { var eventIndexObj = data["event_index"]; if (int.TryParse(eventIndexObj?.ToString(), out int eventIndex)) { if (eventIndex >= 0 && eventIndex < (eventController.eventGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 이벤트 {eventIndex}번 실행"); eventController.ExecuteEvent(eventIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 이벤트 인덱스: {eventIndex}, 유효 범위: 0-{(eventController.eventGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] event_index 파싱 실패: {eventIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'event_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 이벤트 실행 실패: {ex.Message}"); } } private void HandleSetEvent(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 이벤트 설정 요청 수신"); if (eventController == null) { Debug.LogError("[StreamDeckServerManager] eventController가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("event_index")) { var eventIndexToken = jObject["event_index"]; if (int.TryParse(eventIndexToken?.ToString(), out int eventIndex)) { if (eventIndex >= 0 && eventIndex < (eventController.eventGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 이벤트 {eventIndex}번으로 설정"); eventController.Set(eventIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 이벤트 인덱스: {eventIndex}, 유효 범위: 0-{(eventController.eventGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] event_index 파싱 실패: {eventIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'event_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("event_index")) { var eventIndexObj = data["event_index"]; if (int.TryParse(eventIndexObj?.ToString(), out int eventIndex)) { if (eventIndex >= 0 && eventIndex < (eventController.eventGroups?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 이벤트 {eventIndex}번으로 설정"); eventController.Set(eventIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 이벤트 인덱스: {eventIndex}, 유효 범위: 0-{(eventController.eventGroups?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] event_index 파싱 실패: {eventIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'event_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 이벤트 설정 실패: {ex.Message}"); } } private void HandleGetEventList(StreamDeckService service) { if (eventController == null) return; var response = new { type = "event_list_response", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { event_data = eventController.GetEventListData(), current_event = eventController.GetCurrentEventState() } }; string json = JsonConvert.SerializeObject(response); service.SendMessage(json); } private void HandleSetAvatarOutfit(Dictionary message) { Debug.Log($"[StreamDeckServerManager] 아바타 의상 설정 요청 수신"); if (avatarOutfitController == null) { Debug.LogError("[StreamDeckServerManager] avatarOutfitController가 null입니다!"); return; } try { if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { if (jObject.ContainsKey("avatar_index") && jObject.ContainsKey("outfit_index")) { var avatarIndexToken = jObject["avatar_index"]; var outfitIndexToken = jObject["outfit_index"]; if (int.TryParse(avatarIndexToken?.ToString(), out int avatarIndex) && int.TryParse(outfitIndexToken?.ToString(), out int outfitIndex)) { if (avatarIndex >= 0 && avatarIndex < (avatarOutfitController.avatars?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아바타 {avatarIndex}번 의상을 {outfitIndex}번으로 설정"); avatarOutfitController.SetAvatarOutfit(avatarIndex, outfitIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아바타 인덱스: {avatarIndex}, 유효 범위: 0-{(avatarOutfitController.avatars?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] 인덱스 파싱 실패: avatar_index={avatarIndexToken}, outfit_index={outfitIndexToken}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'avatar_index' 또는 'outfit_index' 키가 없습니다"); } } else if (dataObject is Dictionary data) { if (data.ContainsKey("avatar_index") && data.ContainsKey("outfit_index")) { var avatarIndexObj = data["avatar_index"]; var outfitIndexObj = data["outfit_index"]; if (int.TryParse(avatarIndexObj?.ToString(), out int avatarIndex) && int.TryParse(outfitIndexObj?.ToString(), out int outfitIndex)) { if (avatarIndex >= 0 && avatarIndex < (avatarOutfitController.avatars?.Count ?? 0)) { Debug.Log($"[StreamDeckServerManager] 아바타 {avatarIndex}번 의상을 {outfitIndex}번으로 설정"); avatarOutfitController.SetAvatarOutfit(avatarIndex, outfitIndex); } else { Debug.LogError($"[StreamDeckServerManager] 잘못된 아바타 인덱스: {avatarIndex}, 유효 범위: 0-{(avatarOutfitController.avatars?.Count ?? 0) - 1}"); } } else { Debug.LogError($"[StreamDeckServerManager] 인덱스 파싱 실패: avatar_index={avatarIndexObj}, outfit_index={outfitIndexObj}"); } } else { Debug.LogError("[StreamDeckServerManager] data에 'avatar_index' 또는 'outfit_index' 키가 없습니다"); } } else { Debug.LogError($"[StreamDeckServerManager] data가 예상된 타입이 아닙니다: {dataObject?.GetType().Name}"); } } else { Debug.LogError("[StreamDeckServerManager] 메시지에 'data' 키가 없습니다"); } } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 아바타 의상 설정 실패: {ex.Message}"); } } private void HandleSystemCommand(Dictionary message) { string messageType = message.ContainsKey("type") ? message["type"].ToString() : null; Debug.Log($"[StreamDeckServerManager] 시스템 명령어 실행: {messageType}"); if (systemController == null) { Debug.LogError("[StreamDeckServerManager] SystemController가 null입니다!"); return; } try { // 파라미터 추출 (있을 경우) Dictionary parameters = new Dictionary(); if (message.ContainsKey("data")) { var dataObject = message["data"]; if (dataObject is Newtonsoft.Json.Linq.JObject jObject) { foreach (var prop in jObject.Properties()) { parameters[prop.Name] = prop.Value.ToString(); } } } // SystemController의 ExecuteCommand 호출 systemController.ExecuteCommand(messageType, parameters); } catch (Exception ex) { Debug.LogError($"[StreamDeckServerManager] 시스템 명령어 실행 실패: {ex.Message}"); } } private void HandleGetAvatarOutfitList(StreamDeckService service) { Debug.Log("[StreamDeckServerManager] 아바타 의상 목록 요청 처리 시작"); if (avatarOutfitController == null) { Debug.LogError("[StreamDeckServerManager] avatarOutfitController가 null입니다!"); return; } var avatarData = avatarOutfitController.GetAvatarOutfitListData(); Debug.Log($"[StreamDeckServerManager] 아바타 데이터: {JsonConvert.SerializeObject(avatarData)}"); var response = new { type = "avatar_outfit_list_response", timestamp = DateTime.UtcNow.ToString("o"), version = "1.0", data = new { avatar_outfit_data = avatarData, current_avatar_outfit = avatarOutfitController.GetCurrentAvatarOutfitState() } }; string json = JsonConvert.SerializeObject(response); Debug.Log($"[StreamDeckServerManager] 아바타 목록 응답 전송: {json}"); 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) { string timestamp = System.DateTime.Now.ToString("HH:mm:ss.fff"); Debug.Log($"[{timestamp}] [StreamDeckService] 원본 메시지 수신: {e.Data}"); Debug.Log($"[{timestamp}] [StreamDeckService] ⚠️ 지금 바로 버튼을 클릭했나요? 클릭했다면 어떤 버튼인지 확인하세요!"); // 메인 스레드에서 처리하도록 매니저에게 전달 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}"); } }