Streamingle_URP/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs
user 34564f0f50 ADD: MagicaCloth 시뮬레이션 전체 리프레시 기능 추가
SystemController에 MagicaCloth2 시뮬레이션 리셋 기능 구현:
- RefreshAllMagicaCloth(): 씬의 모든 MagicaCloth 시뮬레이션 리셋
- ResetAllMagicaCloth(): 초기 상태로 완전 리셋
- ResetAllMagicaClothKeepPose(): 현재 포즈 유지하며 리셋
- #if MAGICACLOTH2 조건부 컴파일로 의존성 처리

StreamDeck 플러그인 연동:
- manifest.json에 MagicaCloth Refresh 액션 추가
- plugin/index.js에 버튼 핸들러 구현
- StreamDeckServerManager에 메시지 타입 등록
- magica_cloth_reload.png 아이콘 추가

WebSocket 명령어:
- refresh_magica_cloth, reset_magica_cloth: 전체 리셋
- reset_magica_cloth_keep_pose: 포즈 유지 리셋

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 22:50:04 +09:00

1114 lines
42 KiB
C#

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<StreamDeckService> connectedClients = new List<StreamDeckService>();
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<System.Action> mainThreadActions = new Queue<System.Action>();
private readonly object lockObject = new object();
void Awake()
{
Instance = this;
}
void Start()
{
// CameraManager 찾기
cameraManager = FindObjectOfType<CameraManager>();
if (cameraManager == null)
{
Debug.LogError("[StreamDeckServerManager] CameraManager를 찾을 수 없습니다!");
return;
}
// ItemController 찾기
itemController = FindObjectOfType<ItemController>();
if (itemController == null)
{
Debug.LogWarning("[StreamDeckServerManager] ItemController를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다.");
}
// EventController 찾기
eventController = FindObjectOfType<EventController>();
if (eventController == null)
{
Debug.LogWarning("[StreamDeckServerManager] EventController를 찾을 수 없습니다. 이벤트 컨트롤 기능이 비활성화됩니다.");
}
// AvatarOutfitController 찾기
avatarOutfitController = FindObjectOfType<AvatarOutfitController>();
if (avatarOutfitController == null)
{
Debug.LogWarning("[StreamDeckServerManager] AvatarOutfitController를 찾을 수 없습니다. 아바타 의상 컨트롤 기능이 비활성화됩니다.");
}
// SystemController 찾기
systemController = FindObjectOfType<SystemController>();
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<StreamDeckService>("/");
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<Dictionary<string, object>>(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 "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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> 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<string, object> parameters = new Dictionary<string, object>();
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}");
}
}