From cbdd8228ceba39af7d6696b9a718da9fb44a95ba Mon Sep 17 00:00:00 2001 From: KINDNICK <68893236+KINDNICK@users.noreply.github.com> Date: Wed, 9 Jul 2025 00:01:00 +0900 Subject: [PATCH] =?UTF-8?q?Fix=20:=20=EC=8A=A4=ED=8A=B8=EB=A6=BC=EB=8D=B1?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=9E=90=EB=8F=99=ED=99=94=20=EC=84=B8?= =?UTF-8?q?=ED=8C=85=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StreamingleControllerSetupToolAdvanced.cs | 493 ++++++++++++++++++ ...amingleControllerSetupToolAdvanced.cs.meta | 2 + .../Streamdeck/StreamDeckServerManager.cs | 242 ++++++++- .../Editor/ItemControllerEditor.cs | 259 --------- .../Editor/ItemControllerEditor.cs.meta | 2 - .../Controllers/EventController.cs | 491 +++++++++++++++++ .../Controllers/EventController.cs.meta | 2 + .../images/event_icon.png | 3 + .../manifest.json | 4 +- .../plugin/index.js | 256 ++++++++- .../propertyInspector/event/index.html | 190 +++++++ .../propertyInspector/event/index.js | 308 +++++++++++ 12 files changed, 1985 insertions(+), 267 deletions(-) create mode 100644 Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs create mode 100644 Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs.meta delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs delete mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs.meta create mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs create mode 100644 Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs.meta create mode 100644 Streamdeck/com.mirabox.streamingle.sdPlugin/images/event_icon.png create mode 100644 Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.html create mode 100644 Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.js diff --git a/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs new file mode 100644 index 00000000..f0e75ba1 --- /dev/null +++ b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs @@ -0,0 +1,493 @@ +using UnityEngine; +using UnityEditor; + +namespace Streamingle.Editor +{ + public class StreamingleControllerSetupToolAdvanced : EditorWindow + { + private bool createCameraManager = true; + private bool createItemController = true; + private bool createEventController = true; + private bool createStreamDeckManager = true; + + private string parentObjectName = "Streamingle 컨트롤러들"; + private bool createParentObject = true; + + // 고급 옵션 + private bool autoConnectExistingControllers = true; + private bool setupDefaultSettings = true; + private bool createSampleData = false; + private bool moveExistingControllersToParent = true; + + // 기존 컨트롤러 참조 + private StreamDeckServerManager existingStreamDeckManager; + private CameraManager existingCameraManager; + private ItemController existingItemController; + private EventController existingEventController; + + [MenuItem("Tools/Streamingle/고급 컨트롤러 설정 도구")] + public static void ShowWindow() + { + GetWindow("고급 Streamingle 설정"); + } + + private void OnEnable() + { + FindExistingControllers(); + } + + private void OnGUI() + { + GUILayout.Label("고급 Streamingle 컨트롤러 설정 도구", EditorStyles.boldLabel); + GUILayout.Space(10); + + // 기존 컨트롤러 상태 표시 + ShowExistingControllersStatus(); + + GUILayout.Space(10); + + // 부모 오브젝트 설정 + EditorGUILayout.BeginVertical("box"); + GUILayout.Label("부모 오브젝트 설정", EditorStyles.boldLabel); + createParentObject = EditorGUILayout.Toggle("부모 오브젝트 생성", createParentObject); + if (createParentObject) + { + parentObjectName = EditorGUILayout.TextField("부모 오브젝트 이름", parentObjectName); + } + EditorGUILayout.EndVertical(); + + GUILayout.Space(10); + + // 컨트롤러 선택 + EditorGUILayout.BeginVertical("box"); + GUILayout.Label("생성할 컨트롤러들", EditorStyles.boldLabel); + + createStreamDeckManager = EditorGUILayout.Toggle("StreamDeck 서버 매니저", createStreamDeckManager); + createCameraManager = EditorGUILayout.Toggle("카메라 매니저", createCameraManager); + createItemController = EditorGUILayout.Toggle("아이템 컨트롤러", createItemController); + createEventController = EditorGUILayout.Toggle("이벤트 컨트롤러", createEventController); + + EditorGUILayout.EndVertical(); + + GUILayout.Space(10); + + // 고급 옵션 + EditorGUILayout.BeginVertical("box"); + GUILayout.Label("고급 옵션", EditorStyles.boldLabel); + + autoConnectExistingControllers = EditorGUILayout.Toggle("기존 컨트롤러 자동 연결", autoConnectExistingControllers); + setupDefaultSettings = EditorGUILayout.Toggle("기본 설정 구성", setupDefaultSettings); + createSampleData = EditorGUILayout.Toggle("샘플 데이터 생성", createSampleData); + moveExistingControllersToParent = EditorGUILayout.Toggle("기존 컨트롤러를 부모 하위로 이동", moveExistingControllersToParent); + + EditorGUILayout.EndVertical(); + + GUILayout.Space(20); + + // 버튼들 + EditorGUILayout.BeginHorizontal(); + + if (GUILayout.Button("선택된 컨트롤러들 생성", GUILayout.Height(30))) + { + CreateControllers(); + } + + if (GUILayout.Button("기존 컨트롤러들 연결", GUILayout.Height(30))) + { + ConnectExistingControllers(); + } + + EditorGUILayout.EndHorizontal(); + + GUILayout.Space(10); + + EditorGUILayout.BeginHorizontal(); + + if (GUILayout.Button("컨트롤러 상태 새로고침", GUILayout.Height(25))) + { + FindExistingControllers(); + } + + if (GUILayout.Button("기존 컨트롤러들을 부모 하위로 이동", GUILayout.Height(25))) + { + MoveExistingControllersToParent(); + } + + EditorGUILayout.EndHorizontal(); + + GUILayout.Space(10); + + // 정보 표시 + EditorGUILayout.HelpBox( + "자동 감지 및 연결 기능이 포함된 고급 설정 도구입니다.\n" + + "'기존 컨트롤러들 연결'을 사용하여 찾은 컨트롤러들을 StreamDeck 매니저에 연결하세요.\n" + + "'기존 컨트롤러들을 부모 하위로 이동'을 사용하여 발견된 컨트롤러들을 정리하세요.", + MessageType.Info + ); + } + + private void ShowExistingControllersStatus() + { + EditorGUILayout.BeginVertical("box"); + GUILayout.Label("기존 컨트롤러 상태", EditorStyles.boldLabel); + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("StreamDeck 서버 매니저:", GUILayout.Width(200)); + if (existingStreamDeckManager != null) + { + string parentInfo = GetParentInfo(existingStreamDeckManager.transform); + EditorGUILayout.LabelField($"✓ 발견됨 {parentInfo}", EditorStyles.boldLabel); + } + else + { + EditorGUILayout.LabelField("✗ 발견되지 않음", EditorStyles.boldLabel); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("카메라 매니저:", GUILayout.Width(200)); + if (existingCameraManager != null) + { + string parentInfo = GetParentInfo(existingCameraManager.transform); + EditorGUILayout.LabelField($"✓ 발견됨 {parentInfo}", EditorStyles.boldLabel); + } + else + { + EditorGUILayout.LabelField("✗ 발견되지 않음", EditorStyles.boldLabel); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("아이템 컨트롤러:", GUILayout.Width(200)); + if (existingItemController != null) + { + string parentInfo = GetParentInfo(existingItemController.transform); + EditorGUILayout.LabelField($"✓ 발견됨 {parentInfo}", EditorStyles.boldLabel); + } + else + { + EditorGUILayout.LabelField("✗ 발견되지 않음", EditorStyles.boldLabel); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.BeginHorizontal(); + GUILayout.Label("이벤트 컨트롤러:", GUILayout.Width(200)); + if (existingEventController != null) + { + string parentInfo = GetParentInfo(existingEventController.transform); + EditorGUILayout.LabelField($"✓ 발견됨 {parentInfo}", EditorStyles.boldLabel); + } + else + { + EditorGUILayout.LabelField("✗ 발견되지 않음", EditorStyles.boldLabel); + } + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.EndVertical(); + } + + private string GetParentInfo(Transform transform) + { + if (transform.parent == null) + { + return "(루트)"; + } + else + { + return $"({transform.parent.name} 하위)"; + } + } + + private void FindExistingControllers() + { + existingStreamDeckManager = FindObjectOfType(); + existingCameraManager = FindObjectOfType(); + existingItemController = FindObjectOfType(); + existingEventController = FindObjectOfType(); + } + + private void CreateControllers() + { + GameObject parentObject = null; + + // 부모 오브젝트 생성 + if (createParentObject) + { + parentObject = new GameObject(parentObjectName); + UnityEngine.Debug.Log($"부모 오브젝트 생성됨: {parentObjectName}"); + } + + // StreamDeck Server Manager 생성 + if (createStreamDeckManager && existingStreamDeckManager == null) + { + CreateStreamDeckManager(parentObject); + } + + // Camera Manager 생성 + if (createCameraManager && existingCameraManager == null) + { + CreateCameraManager(parentObject); + } + + // Item Controller 생성 + if (createItemController && existingItemController == null) + { + CreateItemController(parentObject); + } + + // Event Controller 생성 + if (createEventController && existingEventController == null) + { + CreateEventController(parentObject); + } + + // 기존 컨트롤러들을 부모 하위로 이동 + if (moveExistingControllersToParent && parentObject != null) + { + MoveExistingControllersToParent(parentObject); + } + + // 생성된 오브젝트들을 선택 + if (parentObject != null) + { + Selection.activeGameObject = parentObject; + EditorGUIUtility.PingObject(parentObject); + } + + // 기존 컨트롤러 상태 업데이트 + FindExistingControllers(); + + UnityEngine.Debug.Log("Streamingle 컨트롤러 설정 완료!"); + } + + private void MoveExistingControllersToParent(GameObject parent = null) + { + if (parent == null) + { + // 기존 부모 오브젝트 찾기 또는 생성 + parent = GameObject.Find(parentObjectName); + if (parent == null) + { + parent = new GameObject(parentObjectName); + UnityEngine.Debug.Log($"부모 오브젝트 생성됨: {parentObjectName}"); + } + } + + int movedCount = 0; + + // StreamDeck Server Manager 이동 + if (existingStreamDeckManager != null && existingStreamDeckManager.transform.parent != parent.transform) + { + existingStreamDeckManager.transform.SetParent(parent.transform); + movedCount++; + UnityEngine.Debug.Log($"StreamDeck 서버 매니저를 {parent.name} 하위로 이동"); + } + + // Camera Manager 이동 + if (existingCameraManager != null && existingCameraManager.transform.parent != parent.transform) + { + existingCameraManager.transform.SetParent(parent.transform); + movedCount++; + UnityEngine.Debug.Log($"카메라 매니저를 {parent.name} 하위로 이동"); + } + + // Item Controller 이동 + if (existingItemController != null && existingItemController.transform.parent != parent.transform) + { + existingItemController.transform.SetParent(parent.transform); + movedCount++; + UnityEngine.Debug.Log($"아이템 컨트롤러를 {parent.name} 하위로 이동"); + } + + // Event Controller 이동 + if (existingEventController != null && existingEventController.transform.parent != parent.transform) + { + existingEventController.transform.SetParent(parent.transform); + movedCount++; + UnityEngine.Debug.Log($"이벤트 컨트롤러를 {parent.name} 하위로 이동"); + } + + if (movedCount > 0) + { + UnityEngine.Debug.Log($"{movedCount}개의 컨트롤러를 {parent.name} 하위로 이동했습니다."); + + // 부모 오브젝트 선택 + Selection.activeGameObject = parent; + EditorGUIUtility.PingObject(parent); + } + else + { + UnityEngine.Debug.Log("이동할 컨트롤러가 없습니다."); + } + } + + private void ConnectExistingControllers() + { + if (existingStreamDeckManager == null) + { + UnityEngine.Debug.LogError("StreamDeck 서버 매니저를 찾을 수 없습니다! 먼저 생성해주세요."); + return; + } + + // StreamDeck Manager에 컨트롤러들 연결 + SerializedObject serializedObject = new SerializedObject(existingStreamDeckManager); + serializedObject.Update(); + + // Camera Manager 연결 + if (existingCameraManager != null) + { + var cameraManagerProperty = serializedObject.FindProperty("cameraManager"); + if (cameraManagerProperty != null) + { + cameraManagerProperty.objectReferenceValue = existingCameraManager; + } + } + + // Item Controller 연결 + if (existingItemController != null) + { + var itemControllerProperty = serializedObject.FindProperty("itemController"); + if (itemControllerProperty != null) + { + itemControllerProperty.objectReferenceValue = existingItemController; + } + } + + // Event Controller 연결 + if (existingEventController != null) + { + var eventControllerProperty = serializedObject.FindProperty("eventController"); + if (eventControllerProperty != null) + { + eventControllerProperty.objectReferenceValue = existingEventController; + } + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("기존 컨트롤러들을 StreamDeck 서버 매니저에 연결했습니다!"); + } + + private void CreateStreamDeckManager(GameObject parent) + { + GameObject streamDeckObject = new GameObject("StreamDeck 서버 매니저"); + + if (parent != null) + { + streamDeckObject.transform.SetParent(parent.transform); + } + + // StreamDeckServerManager 스크립트 추가 + var streamDeckManager = streamDeckObject.AddComponent(); + + // 기본 설정 + SerializedObject serializedObject = new SerializedObject(streamDeckManager); + serializedObject.Update(); + + // 포트 설정 (기본값: 10701) + var portProperty = serializedObject.FindProperty("port"); + if (portProperty != null) + { + portProperty.intValue = 10701; + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("StreamDeck 서버 매니저 생성됨"); + } + + private void CreateCameraManager(GameObject parent) + { + GameObject cameraObject = new GameObject("카메라 매니저"); + + if (parent != null) + { + cameraObject.transform.SetParent(parent.transform); + } + + // CameraManager 스크립트 추가 + var cameraManager = cameraObject.AddComponent(); + + // 기본 설정 + SerializedObject serializedObject = new SerializedObject(cameraManager); + serializedObject.Update(); + + // 카메라 프리셋 리스트 초기화 + var cameraPresetsProperty = serializedObject.FindProperty("cameraPresets"); + if (cameraPresetsProperty != null) + { + cameraPresetsProperty.ClearArray(); + cameraPresetsProperty.arraySize = 0; + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("카메라 매니저 생성됨"); + } + + private void CreateItemController(GameObject parent) + { + GameObject itemObject = new GameObject("아이템 컨트롤러"); + + if (parent != null) + { + itemObject.transform.SetParent(parent.transform); + } + + // ItemController 스크립트 추가 + var itemController = itemObject.AddComponent(); + + // 기본 설정 + SerializedObject serializedObject = new SerializedObject(itemController); + serializedObject.Update(); + + // 아이템 그룹 리스트 초기화 + var itemGroupsProperty = serializedObject.FindProperty("itemGroups"); + if (itemGroupsProperty != null) + { + itemGroupsProperty.ClearArray(); + itemGroupsProperty.arraySize = 0; + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("아이템 컨트롤러 생성됨"); + } + + private void CreateEventController(GameObject parent) + { + GameObject eventObject = new GameObject("이벤트 컨트롤러"); + + if (parent != null) + { + eventObject.transform.SetParent(parent.transform); + } + + // EventController 스크립트 추가 + var eventController = eventObject.AddComponent(); + + // 기본 설정 + SerializedObject serializedObject = new SerializedObject(eventController); + serializedObject.Update(); + + // 이벤트 그룹 리스트 초기화 + var eventGroupsProperty = serializedObject.FindProperty("eventGroups"); + if (eventGroupsProperty != null) + { + eventGroupsProperty.ClearArray(); + eventGroupsProperty.arraySize = 0; + } + + // 자동 찾기 비활성화 + var autoFindEventsProperty = serializedObject.FindProperty("autoFindEvents"); + if (autoFindEventsProperty != null) + { + autoFindEventsProperty.boolValue = false; + } + + serializedObject.ApplyModifiedProperties(); + + UnityEngine.Debug.Log("이벤트 컨트롤러 생성됨"); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs.meta b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs.meta new file mode 100644 index 00000000..851684f6 --- /dev/null +++ b/Assets/Scripts/Editor/StreamingleControllerSetupToolAdvanced.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 84a4d570d6f8faa44bafc90ed4780108 \ No newline at end of file diff --git a/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs b/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs index 2711d2fa..f69ea59d 100644 --- a/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs +++ b/Assets/Scripts/Streamdeck/StreamDeckServerManager.cs @@ -14,6 +14,7 @@ public class StreamDeckServerManager : MonoBehaviour private List connectedClients = new List(); public CameraManager cameraManager { get; private set; } public ItemController itemController { get; private set; } + public EventController eventController { get; private set; } // 싱글톤 패턴으로 StreamDeckService에서 접근 가능하도록 public static StreamDeckServerManager Instance { get; private set; } @@ -44,6 +45,13 @@ public class StreamDeckServerManager : MonoBehaviour Debug.LogWarning("[StreamDeckServerManager] ItemController를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다."); } + // EventController 찾기 + eventController = FindObjectOfType(); + if (eventController == null) + { + Debug.LogWarning("[StreamDeckServerManager] EventController를 찾을 수 없습니다. 이벤트 컨트롤 기능이 비활성화됩니다."); + } + StartServer(); } @@ -152,7 +160,9 @@ public class StreamDeckServerManager : MonoBehaviour camera_data = cameraManager.GetCameraListData(), current_camera = cameraManager.GetCurrentCameraState(), item_data = itemController?.GetItemListData(), - current_item = itemController?.GetCurrentItemState() + current_item = itemController?.GetCurrentItemState(), + event_data = eventController?.GetEventListData(), + current_event = eventController?.GetCurrentEventState() } }; @@ -221,6 +231,28 @@ public class StreamDeckServerManager : MonoBehaviour 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] 이벤트 변경 알림 전송됨"); + } + // 메시지 처리 로직을 여기로 이동 private void ProcessMessage(string messageData, StreamDeckService service) { @@ -250,6 +282,18 @@ public class StreamDeckServerManager : MonoBehaviour HandleGetItemList(service); break; + case "execute_event": + HandleExecuteEvent(message); + break; + + case "set_event": + HandleSetEvent(message); + break; + + case "get_event_list": + HandleGetEventList(service); + break; + case "test": // 테스트 메시지 에코 응답 var response = new @@ -587,6 +631,202 @@ public class StreamDeckServerManager : MonoBehaviour 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); + } } public class StreamDeckService : WebSocketBehavior diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs deleted file mode 100644 index 2958f92e..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs +++ /dev/null @@ -1,259 +0,0 @@ -using UnityEngine; -using UnityEditor; - -[CustomEditor(typeof(ItemController))] -public class ItemControllerEditor : Editor -{ - private ItemController itemController; - private bool showGroupSettings = true; - private bool showGroupList = true; - private bool showTestActions = true; - - private void OnEnable() - { - itemController = (ItemController)target; - } - - public override void OnInspectorGUI() - { - DrawDefaultInspector(); - - EditorGUILayout.Space(); - EditorGUILayout.LabelField("Item Controller Tools", EditorStyles.boldLabel); - - // 현재 상태 표시 - DrawCurrentState(); - - // 그룹 설정 섹션 - showGroupSettings = EditorGUILayout.Foldout(showGroupSettings, "Group Management"); - if (showGroupSettings) - { - EditorGUILayout.BeginVertical("box"); - - EditorGUILayout.LabelField("Quick Group Actions", EditorStyles.boldLabel); - - EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button("Add Selected Objects as New Group")) - { - AddSelectedObjectsAsGroup(); - } - - if (GUILayout.Button("Refresh Item Groups")) - { - itemController.InitializeItemGroups(); - } - EditorGUILayout.EndHorizontal(); - - if (GUILayout.Button("Clear All Groups")) - { - if (EditorUtility.DisplayDialog("Clear Groups", "모든 그룹을 삭제하시겠습니까?", "Yes", "No")) - { - itemController.itemGroups.Clear(); - Debug.Log("[ItemControllerEditor] 모든 그룹이 삭제되었습니다."); - } - } - - EditorGUILayout.EndVertical(); - } - - // 그룹 리스트 섹션 - showGroupList = EditorGUILayout.Foldout(showGroupList, "Current Group List"); - if (showGroupList) - { - EditorGUILayout.BeginVertical("box"); - - if (itemController.itemGroups != null && itemController.itemGroups.Count > 0) - { - for (int i = 0; i < itemController.itemGroups.Count; i++) - { - var group = itemController.itemGroups[i]; - if (group != null) - { - DrawGroupItem(i, group); - } - } - } - else - { - EditorGUILayout.LabelField("No groups registered", EditorStyles.centeredGreyMiniLabel); - } - - EditorGUILayout.EndVertical(); - } - - // 테스트 액션 섹션 - showTestActions = EditorGUILayout.Foldout(showTestActions, "Test Actions"); - if (showTestActions) - { - EditorGUILayout.BeginVertical("box"); - - EditorGUILayout.BeginHorizontal(); - if (GUILayout.Button("Activate All Groups")) - { - itemController.ActivateAllGroups(); - } - - if (GUILayout.Button("Deactivate All Groups")) - { - itemController.DeactivateAllGroups(); - } - EditorGUILayout.EndHorizontal(); - - if (itemController.itemGroups.Count > 0) - { - EditorGUILayout.LabelField("Set Active Group:", EditorStyles.miniLabel); - EditorGUILayout.BeginHorizontal(); - for (int i = 0; i < Mathf.Min(3, itemController.itemGroups.Count); i++) - { - if (GUILayout.Button($"Group {i}")) - { - itemController.Set(i); - } - } - EditorGUILayout.EndHorizontal(); - } - - EditorGUILayout.EndVertical(); - } - } - - private void DrawCurrentState() - { - EditorGUILayout.BeginVertical("box"); - EditorGUILayout.LabelField("Current State", EditorStyles.boldLabel); - - EditorGUILayout.LabelField($"Total Groups: {itemController.itemGroups?.Count ?? 0}"); - EditorGUILayout.LabelField($"Current Group: {(itemController.CurrentGroup?.groupName ?? "None")}"); - EditorGUILayout.LabelField($"Current Index: {itemController.CurrentIndex}"); - - if (itemController.CurrentGroup != null) - { - EditorGUILayout.LabelField($"Active: {itemController.CurrentGroup.IsActive()}", - itemController.CurrentGroup.IsActive() ? EditorStyles.boldLabel : EditorStyles.miniLabel); - } - - EditorGUILayout.EndVertical(); - EditorGUILayout.Space(); - } - - private void DrawGroupItem(int index, ItemController.ItemGroup group) - { - EditorGUILayout.BeginVertical("box"); - - // 그룹 헤더 - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.LabelField($"{index + 1}. {group.groupName}", EditorStyles.boldLabel); - - // 활성화 상태 토글 - bool isActive = group.IsActive(); - bool newActive = EditorGUILayout.Toggle(isActive, GUILayout.Width(20)); - if (newActive != isActive) - { - group.SetActive(newActive); - } - - // 토글 버튼 - if (GUILayout.Button("Toggle", GUILayout.Width(50))) - { - itemController.ToggleGroup(index); - } - - // 핫키 레코딩 버튼 - if (GUILayout.Button(group.isRecording ? "Stop" : "Record", GUILayout.Width(60))) - { - if (group.isRecording) - { - itemController.StopRecordingHotkey(index); - } - else - { - itemController.StartRecordingHotkey(index); - } - } - - // 삭제 버튼 - if (GUILayout.Button("X", GUILayout.Width(20))) - { - if (EditorUtility.DisplayDialog("Delete Group", $"그룹 '{group.groupName}'을 삭제하시겠습니까?", "Yes", "No")) - { - itemController.RemoveGroup(index); - return; - } - } - - EditorGUILayout.EndHorizontal(); - - // 핫키 표시 - if (group.hotkeys != null && group.hotkeys.Count > 0) - { - EditorGUILayout.LabelField($"Hotkey: {string.Join(" + ", group.hotkeys)}", EditorStyles.miniLabel); - } - else - { - EditorGUILayout.LabelField("Hotkey: 설정되지 않음", EditorStyles.miniLabel); - } - - // 아이템 오브젝트들 - EditorGUILayout.LabelField("Items:", EditorStyles.miniLabel); - if (group.itemObjects != null) - { - for (int j = 0; j < group.itemObjects.Length; j++) - { - EditorGUILayout.BeginHorizontal(); - EditorGUILayout.LabelField($" {j + 1}.", GUILayout.Width(30)); - - GameObject obj = group.itemObjects[j]; - GameObject newObj = (GameObject)EditorGUILayout.ObjectField(obj, typeof(GameObject), true); - if (newObj != obj) - { - group.itemObjects[j] = newObj; - } - - // 아이템 삭제 버튼 - if (GUILayout.Button("X", GUILayout.Width(20))) - { - var newArray = new GameObject[group.itemObjects.Length - 1]; - int newIndex = 0; - for (int k = 0; k < group.itemObjects.Length; k++) - { - if (k != j) - { - newArray[newIndex] = group.itemObjects[k]; - newIndex++; - } - } - group.itemObjects = newArray; - break; - } - - EditorGUILayout.EndHorizontal(); - } - - // 아이템 추가 버튼 - if (GUILayout.Button("Add Item")) - { - var newArray = new GameObject[group.itemObjects.Length + 1]; - group.itemObjects.CopyTo(newArray, 0); - group.itemObjects = newArray; - } - } - - EditorGUILayout.EndVertical(); - EditorGUILayout.Space(); - } - - private void AddSelectedObjectsAsGroup() - { - GameObject[] selectedObjects = Selection.gameObjects; - if (selectedObjects.Length > 0) - { - string groupName = $"Group {itemController.itemGroups.Count + 1}"; - itemController.AddGroup(groupName, selectedObjects); - Debug.Log($"[ItemControllerEditor] {selectedObjects.Length}개의 선택된 오브젝트를 새 그룹 '{groupName}'으로 추가했습니다."); - } - else - { - Debug.LogWarning("[ItemControllerEditor] 선택된 오브젝트가 없습니다."); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs.meta deleted file mode 100644 index 0939703b..00000000 --- a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/Editor/ItemControllerEditor.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 31447d4896db6c9458ce66dae88feec8 \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs new file mode 100644 index 00000000..d9d6f177 --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs @@ -0,0 +1,491 @@ +using UnityEngine; +using System.Collections.Generic; +using System.Linq; +using Streamingle; +using UnityRawInput; +using UnityEngine.Events; + +public class EventController : MonoBehaviour, IController +{ + #region Classes + [System.Serializable] + public class EventGroup + { + [Header("Event Settings")] + public string groupName = "New Event Group"; + public UnityEvent unityEvent = new UnityEvent(); + + [Header("Hotkey Settings")] + public List hotkeys = new List(); + [System.NonSerialized] public bool isRecording = false; + [System.NonSerialized] private List unityKeys = new List(); + [System.NonSerialized] private float recordStartTime; + [System.NonSerialized] private const float MAX_RECORD_TIME = 2f; + + public EventGroup(string name) + { + groupName = name; + hotkeys = new List(); + unityEvent = new UnityEvent(); + } + + public void StartRecording() + { + isRecording = true; + recordStartTime = Time.time; + hotkeys.Clear(); + } + + public void StopRecording() + { + isRecording = false; + InitializeUnityKeys(); + } + + public void UpdateRecording() + { + if (!isRecording) return; + + if (Time.time - recordStartTime > MAX_RECORD_TIME) + { + StopRecording(); + return; + } + + foreach (KeyCode keyCode in System.Enum.GetValues(typeof(KeyCode))) + { + if (Input.GetKeyDown(keyCode) && KeyMapping.TryGetRawKey(keyCode, out RawKey rawKey)) + { + if (!hotkeys.Contains(rawKey)) + { + hotkeys.Add(rawKey); + } + } + } + + bool allKeysReleased = hotkeys.Any() && hotkeys.All(key => !Input.GetKey(KeyMapping.TryGetKeyCode(key, out KeyCode keyCode) ? keyCode : KeyCode.None)); + + if (allKeysReleased) + { + StopRecording(); + } + } + + public void InitializeUnityKeys() + { + unityKeys.Clear(); + + if (hotkeys == null || !hotkeys.Any()) return; + + foreach (var hotkey in hotkeys) + { + if (KeyMapping.TryGetKeyCode(hotkey, out KeyCode keyCode) && keyCode != KeyCode.None) + { + unityKeys.Add(keyCode); + } + } + } + + public bool IsTriggered() + { + if (isRecording) return false; + + if (hotkeys == null || !hotkeys.Any()) return false; + + bool allHotkeysPressed = hotkeys.All(key => RawInput.IsKeyDown(key)); + if (allHotkeysPressed) return true; + + if (unityKeys.Any()) + { + return unityKeys.All(key => Input.GetKey(key)); + } + + return false; + } + + public void ExecuteEvent() + { + if (unityEvent != null) + { + unityEvent.Invoke(); + } + } + + public override string ToString() => + hotkeys?.Any() == true ? $"{groupName} ({string.Join(" + ", hotkeys)})" : $"{groupName} (설정되지 않음)"; + } + + private static class KeyMapping + { + private static readonly Dictionary _mapping; + + static KeyMapping() + { + _mapping = new Dictionary(RawKeySetup.KeyMapping); + } + + public static bool TryGetRawKey(KeyCode keyCode, out RawKey rawKey) + { + return _mapping.TryGetValue(keyCode, out rawKey); + } + + public static bool TryGetKeyCode(RawKey rawKey, out KeyCode keyCode) + { + var pair = _mapping.FirstOrDefault(x => x.Value == rawKey); + keyCode = pair.Key; + return keyCode != KeyCode.None; + } + + public static bool IsValidRawKey(RawKey key) + { + return _mapping.ContainsValue(key); + } + } + #endregion + + #region Events + public delegate void EventGroupExecutedEventHandler(EventGroup eventGroup); + public event EventGroupExecutedEventHandler OnEventExecuted; + #endregion + + #region Fields + [SerializeField] public List eventGroups = new List(); + + [Header("Event Control Settings")] + [SerializeField] private bool autoFindEvents = false; // 태그 기능 비활성화 + + private EventGroup currentGroup; + private StreamDeckServerManager streamDeckManager; + #endregion + + #region Properties + public EventGroup CurrentGroup => currentGroup; + public int CurrentIndex => eventGroups.IndexOf(currentGroup); + #endregion + + #region Unity Messages + private void Awake() + { + InitializeEventGroups(); + InitializeRawInput(); + + // StreamDeckServerManager 찾기 + streamDeckManager = FindObjectOfType(); + if (streamDeckManager == null) + { + Debug.LogWarning("[EventController] StreamDeckServerManager를 찾을 수 없습니다. 스트림덱 연동이 비활성화됩니다."); + } + } + + private void OnDestroy() + { + if (RawInput.IsRunning) + { + RawInput.OnKeyDown -= HandleRawKeyDown; + RawInput.Stop(); + } + } + + private void Update() + { + UpdateHotkeyRecording(); + HandleHotkeys(); + } + #endregion + + #region Initialization + public void InitializeEventGroups() + { + // 태그 기능 제거 - 수동으로 이벤트 그룹을 추가해야 함 + + // 첫 번째 그룹을 기본으로 설정 + if (eventGroups.Count > 0 && currentGroup == null) + { + currentGroup = eventGroups[0]; + } + + Debug.Log($"[EventController] {eventGroups.Count}개의 이벤트 그룹이 초기화되었습니다."); + } + + private void InitializeRawInput() + { + if (!RawInput.IsRunning) + { + RawInput.Start(); + RawInput.OnKeyDown += HandleRawKeyDown; + } + } + + private void UpdateHotkeyRecording() + { + foreach (var group in eventGroups) + { + if (group.isRecording) + { + group.UpdateRecording(); + } + } + } + + private void HandleRawKeyDown(RawKey key) + { + // 핫키 레코딩 중일 때는 처리하지 않음 + if (eventGroups.Any(g => g.isRecording)) return; + } + + private void HandleHotkeys() + { + foreach (var group in eventGroups) + { + if (group.IsTriggered()) + { + ExecuteEvent(group); + } + } + } + #endregion + + #region Public Methods + public void Set(int index) + { + if (index < 0 || index >= eventGroups.Count) + { + Debug.LogWarning($"[EventController] 유효하지 않은 인덱스: {index}"); + return; + } + + currentGroup = eventGroups[index]; + Debug.Log($"[EventController] 현재 이벤트 그룹: {currentGroup.groupName}"); + + // StreamDeck에 알림 + if (streamDeckManager != null) + { + streamDeckManager.NotifyEventChanged(); + } + } + + public void ExecuteEvent(int index) + { + if (index < 0 || index >= eventGroups.Count) + { + Debug.LogWarning($"[EventController] 유효하지 않은 인덱스: {index}"); + return; + } + + ExecuteEvent(eventGroups[index]); + } + + public void ExecuteEvent(EventGroup group) + { + if (group == null) + { + Debug.LogWarning("[EventController] 이벤트 그룹이 null입니다."); + return; + } + + Debug.Log($"[EventController] 이벤트 실행: {group.groupName}"); + group.ExecuteEvent(); + + // 이벤트 발생 알림 + OnEventExecuted?.Invoke(group); + + // StreamDeck에 알림 + if (streamDeckManager != null) + { + streamDeckManager.NotifyEventChanged(); + } + } + + public void ExecuteCurrentEvent() + { + if (currentGroup != null) + { + ExecuteEvent(currentGroup); + } + else + { + Debug.LogWarning("[EventController] 현재 이벤트 그룹이 설정되지 않았습니다."); + } + } + + public void AddGroup(string groupName, GameObject targetObject = null) + { + var newGroup = new EventGroup(groupName); + + if (targetObject != null) + { + // 대상 오브젝트에 UnityEvent 컴포넌트가 있다면 연결 + var eventComponent = targetObject.GetComponent(); + if (eventComponent != null) + { + newGroup.unityEvent = eventComponent; + } + } + + eventGroups.Add(newGroup); + Debug.Log($"[EventController] 이벤트 그룹 추가: {groupName}"); + } + + public void RemoveGroup(int index) + { + if (index < 0 || index >= eventGroups.Count) + { + Debug.LogWarning($"[EventController] 유효하지 않은 인덱스: {index}"); + return; + } + + var groupToRemove = eventGroups[index]; + eventGroups.RemoveAt(index); + + if (currentGroup == groupToRemove) + { + currentGroup = eventGroups.Count > 0 ? eventGroups[0] : null; + } + + Debug.Log($"[EventController] 이벤트 그룹 제거: {groupToRemove.groupName}"); + } + + public void StartRecordingHotkey(int groupIndex) + { + if (groupIndex < 0 || groupIndex >= eventGroups.Count) + { + Debug.LogWarning($"[EventController] 유효하지 않은 인덱스: {groupIndex}"); + return; + } + + eventGroups[groupIndex].StartRecording(); + Debug.Log($"[EventController] 핫키 레코딩 시작: {eventGroups[groupIndex].groupName}"); + } + + public void StopRecordingHotkey(int groupIndex) + { + if (groupIndex < 0 || groupIndex >= eventGroups.Count) + { + Debug.LogWarning($"[EventController] 유효하지 않은 인덱스: {groupIndex}"); + return; + } + + eventGroups[groupIndex].StopRecording(); + Debug.Log($"[EventController] 핫키 레코딩 종료: {eventGroups[groupIndex].groupName}"); + } + + private void NotifyEventChanged() + { + // StreamDeck에 이벤트 변경 알림 + if (streamDeckManager != null) + { + streamDeckManager.NotifyEventChanged(); + } + } + #endregion + + #region StreamDeck Integration + public EventListData GetEventListData() + { + var eventData = new EventPresetData[eventGroups.Count]; + + for (int i = 0; i < eventGroups.Count; i++) + { + var group = eventGroups[i]; + eventData[i] = new EventPresetData + { + index = i, + name = group.groupName, + hotkey = group.hotkeys.Any() ? string.Join(" + ", group.hotkeys) : "설정되지 않음" + }; + } + + return new EventListData + { + event_count = eventGroups.Count, + events = eventData, + current_index = CurrentIndex + }; + } + + public EventStateData GetCurrentEventState() + { + return new EventStateData + { + current_index = CurrentIndex, + event_name = currentGroup?.groupName ?? "없음", + total_events = eventGroups.Count + }; + } + + public string GetEventListJson() + { + return JsonUtility.ToJson(GetEventListData()); + } + + public string GetEventStateJson() + { + return JsonUtility.ToJson(GetCurrentEventState()); + } + #endregion + + #region Data Classes + [System.Serializable] + public class EventPresetData + { + public int index; + public string name; + public string hotkey; + } + + [System.Serializable] + public class EventListData + { + public int event_count; + public EventPresetData[] events; + public int current_index; + } + + [System.Serializable] + public class EventStateData + { + public int current_index; + public string event_name; + public int total_events; + } + #endregion + + #region IController Implementation + public string GetControllerId() + { + return "event_controller"; + } + + public string GetControllerName() + { + return "Event Controller"; + } + + public object GetControllerData() + { + return GetEventListData(); + } + + public void ExecuteAction(string actionId, object parameters) + { + switch (actionId) + { + case "execute_event": + if (parameters is int executeIndex) + { + ExecuteEvent(executeIndex); + } + break; + case "set_event": + if (parameters is int setIndex) + { + Set(setIndex); + } + break; + default: + Debug.LogWarning($"[EventController] 알 수 없는 액션: {actionId}"); + break; + } + } + #endregion +} \ No newline at end of file diff --git a/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs.meta b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs.meta new file mode 100644 index 00000000..88da11b3 --- /dev/null +++ b/Assets/Scripts/Streamingle/StreamingleControl/Controllers/EventController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 6f1686151b323be4ba49f1ec12f88471 \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/images/event_icon.png b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/event_icon.png new file mode 100644 index 00000000..dd772cfc --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/images/event_icon.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4053d5770e2feb3fe6004004f1229fa390271f4819f44072755b1e336cc88e68 +size 61607 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json b/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json index 4cef03ab..2651a4d7 100644 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f84721f6fa44719b7d51065be665cd5427e60230a6f1bf7114b5cf2179296ca -size 2331 +oid sha256:d570925d43102b294ea6e23b83fc47ab44dcd46ad3fde70694d25d64515d2953 +size 3061 diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js index f60cbc41..9a586d8b 100644 --- a/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/plugin/index.js @@ -8,6 +8,7 @@ let unitySocket = null; let isUnityConnected = false; let cameraList = []; // 카메라 목록 저장 let itemList = []; // 아이템 목록 저장 +let eventList = []; // 이벤트 목록 저장 const fs = require('fs'); @@ -85,19 +86,26 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction break; case 'willAppear': console.log('👀 버튼 나타남:', jsonObj.context); + console.log('🔍 Action UUID:', jsonObj.action); let settings = jsonObj.payload?.settings || {}; + console.log('⚙️ 초기 설정:', settings); // action UUID로 actionType 결정 if (jsonObj.action === 'com.mirabox.streamingle.item') { settings.actionType = 'item'; console.log('🎯 아이템 컨트롤러 등록:', jsonObj.context); + } else if (jsonObj.action === 'com.mirabox.streamingle.event') { + settings.actionType = 'event'; + console.log('🎯 이벤트 컨트롤러 등록:', jsonObj.context); } else { settings.actionType = 'camera'; console.log('📹 카메라 컨트롤러 등록:', jsonObj.context); } + console.log('🎯 최종 actionType:', settings.actionType); buttonContexts.set(jsonObj.context, settings); + console.log('💾 설정 저장됨:', settings); updateButtonTitle(jsonObj.context); // Unity가 이미 연결되어 있다면 Property Inspector에 상태 전송 @@ -107,6 +115,8 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction let actionUUID = 'com.mirabox.streamingle.camera'; if (actionType === 'item') { actionUUID = 'com.mirabox.streamingle.item'; + } else if (actionType === 'event') { + actionUUID = 'com.mirabox.streamingle.event'; } sendToPropertyInspector(jsonObj.context, 'unity_connected', { connected: true }, actionUUID); @@ -122,6 +132,23 @@ function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inAction handlePropertyInspectorMessage(jsonObj.payload, jsonObj.context, jsonObj.action); break; + case 'setSettings': + console.log('💾 setSettings 이벤트 수신:', jsonObj.payload, 'context:', jsonObj.context); + if (jsonObj.payload && jsonObj.context) { + const currentSettings = getCurrentSettings(jsonObj.context); + console.log('📋 저장 전 설정:', currentSettings); + + // actionType을 유지하면서 설정 업데이트 + const updatedSettings = { ...currentSettings, ...jsonObj.payload }; + setCurrentSettings(jsonObj.context, updatedSettings); + + const newSettings = getCurrentSettings(jsonObj.context); + console.log('✅ 설정 저장 완료:', newSettings); + console.log('🎯 액션 타입:', newSettings.actionType); + updateButtonTitle(jsonObj.context); + } + break; + default: console.log('❓ 알 수 없는 이벤트:', jsonObj.event); break; @@ -170,6 +197,13 @@ function connectToUnity() { unitySocket.send(message); console.log('📋 아이템 목록 요청:', message); }, 200); + + // 이벤트 목록 요청 + setTimeout(() => { + const message = JSON.stringify({ type: 'get_event_list' }); + unitySocket.send(message); + console.log('📋 이벤트 목록 요청:', message); + }, 300); }; unitySocket.onmessage = function(event) { @@ -195,6 +229,8 @@ function connectToUnity() { let actionUUID = 'com.mirabox.streamingle.camera'; if (actionType === 'item') { actionUUID = 'com.mirabox.streamingle.item'; + } else if (actionType === 'event') { + actionUUID = 'com.mirabox.streamingle.event'; } sendToPropertyInspector(context, 'unity_disconnected', { connected: false }, actionUUID); } @@ -233,6 +269,9 @@ function handleButtonClick(context) { case 'item': handleItemAction(settings); break; + case 'event': + handleEventAction(settings); + break; default: console.log('⚠️ 알 수 없는 액션 타입:', actionType); // 기본적으로 카메라 액션으로 처리 @@ -310,6 +349,44 @@ function handleItemAction(settings) { } } +// 이벤트 액션 처리 +function handleEventAction(settings) { + let eventIndex = settings.eventIndex; + const eventAction = settings.eventAction || 'execute'; // 기본값은 execute + + // 이벤트 인덱스가 설정되지 않았으면 0 사용 + if (typeof eventIndex !== 'number') { + eventIndex = 0; + console.log('⚠️ 이벤트 인덱스가 설정되지 않음, 기본값 0 사용'); + } + + console.log('🎯 이벤트 액션:', eventAction, '인덱스:', eventIndex); + + let messageType = 'execute_event'; + if (eventAction === 'set') { + messageType = 'set_event'; + } + + // Unity에 이벤트 액션 요청 + const message = JSON.stringify({ + type: messageType, + data: { + event_index: eventIndex + } + }); + + console.log('📤 Unity에 이벤트 액션 요청 전송:', message); + console.log('🔍 Unity 연결 상태:', isUnityConnected); + console.log('🔍 Unity 소켓 상태:', !!unitySocket); + + if (unitySocket && unitySocket.readyState === WebSocket.OPEN) { + unitySocket.send(message); + console.log('✅ 메시지 전송 완료'); + } else { + console.error('❌ Unity 소켓이 연결되지 않음'); + } +} + // Property Inspector 메시지 처리 function handlePropertyInspectorMessage(payload, context, actionUUID) { const command = payload.command; @@ -341,6 +418,15 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { currentIndex: currentItemIndex }, actionUUID); } + if (eventList.length > 0) { + // 현재 활성 이벤트 인덱스 찾기 + const currentEventIndex = eventList.findIndex(event => event.isActive) || 0; + console.log('📡 현재 활성 이벤트 인덱스:', currentEventIndex); + sendToPropertyInspector(context, 'event_list', { + events: eventList, + currentIndex: currentEventIndex + }, actionUUID); + } } else { // Unity가 연결되지 않은 경우에도 빈 목록 전송 console.log('📡 Unity 연결 안됨 - 빈 목록 전송'); @@ -352,6 +438,10 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { items: [], currentIndex: 0 }, actionUUID); + sendToPropertyInspector(context, 'event_list', { + events: [], + currentIndex: 0 + }, actionUUID); } break; case 'get_camera_list': @@ -370,6 +460,14 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { console.log('📤 Unity에 아이템 목록 요청 전송'); } break; + case 'get_event_list': + console.log('🎯 이벤트 목록 요청'); + if (isUnityConnected && unitySocket) { + const message = JSON.stringify({ type: 'get_event_list' }); + unitySocket.send(message); + console.log('📤 Unity에 이벤트 목록 요청 전송'); + } + break; case 'switch_camera': console.log('📹 카메라 전환 요청:', payload.cameraIndex); if (isUnityConnected && unitySocket) { @@ -403,6 +501,28 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { console.log('📤 Unity에 아이템 설정 요청 전송'); } break; + case 'execute_event': + console.log('🎯 이벤트 실행 요청:', payload.eventIndex); + if (isUnityConnected && unitySocket) { + const message = JSON.stringify({ + type: 'execute_event', + data: { event_index: payload.eventIndex } + }); + unitySocket.send(message); + console.log('📤 Unity에 이벤트 실행 요청 전송'); + } + break; + case 'set_event': + console.log('🎯 이벤트 설정 요청:', payload.eventIndex); + if (isUnityConnected && unitySocket) { + const message = JSON.stringify({ + type: 'set_event', + data: { event_index: payload.eventIndex } + }); + unitySocket.send(message); + console.log('📤 Unity에 이벤트 설정 요청 전송'); + } + break; case 'refresh_camera_list': console.log('🔄 카메라 목록 새로고침 요청'); if (isUnityConnected && unitySocket) { @@ -419,10 +539,19 @@ function handlePropertyInspectorMessage(payload, context, actionUUID) { console.log('📤 Unity에 아이템 목록 요청 전송'); } break; + case 'refresh_event_list': + console.log('🔄 이벤트 목록 새로고침 요청'); + if (isUnityConnected && unitySocket) { + const message = JSON.stringify({ type: 'get_event_list' }); + unitySocket.send(message); + console.log('📤 Unity에 이벤트 목록 요청 전송'); + } + break; case 'update_title': console.log('🏷️ 버튼 제목 업데이트 요청'); updateButtonTitle(context); break; + default: console.log('❓ 알 수 없는 Property Inspector 명령:', command); } @@ -454,6 +583,8 @@ function sendToPropertyInspector(context, event, payload, actionUUID = null) { if (actionType === 'item') { action = 'com.mirabox.streamingle.item'; + } else if (actionType === 'event') { + action = 'com.mirabox.streamingle.event'; } } @@ -509,6 +640,18 @@ function handleUnityMessage(data) { } } + // 이벤트 데이터 처리 + if (data.data.event_data) { + let events = data.data.event_data.events || data.data.event_data; + if (Array.isArray(events)) { + eventList = events; + console.log('🎯 이벤트 목록 저장됨:', eventList.length, '개'); + } else { + eventList = []; + console.log('⚠️ 이벤트 데이터가 배열이 아님:', events); + } + } + updateAllButtonTitles(); // Property Inspector들에게 Unity 연결 상태 알림 @@ -528,6 +671,8 @@ function handleUnityMessage(data) { let actionUUID = 'com.mirabox.streamingle.camera'; if (actionType === 'item') { actionUUID = 'com.mirabox.streamingle.item'; + } else if (actionType === 'event') { + actionUUID = 'com.mirabox.streamingle.event'; } console.log('🔍 컨텍스트 분석:', context, 'Action Type:', actionType, 'Action UUID:', actionUUID); @@ -556,6 +701,17 @@ function handleUnityMessage(data) { items: itemList, currentIndex: currentItemIndex }, actionUUID); + } else if (actionUUID === 'com.mirabox.streamingle.event') { + // 이벤트 컨트롤러에는 이벤트 데이터만 전송 + let currentEventIndex = 0; + if (typeof data.data.event_data?.current_index === 'number' && data.data.event_data.current_index >= 0) { + currentEventIndex = data.data.event_data.current_index; + } + console.log('🎯 이벤트 컨트롤러에 이벤트 데이터 전송:', context, '이벤트 수:', eventList.length); + sendToPropertyInspector(context, 'event_list', { + events: eventList, + currentIndex: currentEventIndex + }, actionUUID); } } console.log('✅ Property Inspector들에게 Unity 연결 알림 전송 완료'); @@ -716,6 +872,76 @@ function handleUnityMessage(data) { } break; + case 'event_changed': + console.log('🎯 이벤트 변경 알림'); + console.log('📋 이벤트 데이터 구조:', JSON.stringify(data.data, null, 2)); + if (data.data && data.data.event_data) { + let events = data.data.event_data.events || data.data.event_data; + + console.log('🎯 추출된 이벤트 배열:', events); + console.log('🎯 이벤트 배열 타입:', Array.isArray(events) ? 'Array' : typeof events); + + if (Array.isArray(events)) { + eventList = events; + console.log('🎯 이벤트 목록 업데이트됨:', eventList.length, '개'); + console.log('🎯 이벤트 목록 내용:', JSON.stringify(eventList, null, 2)); + updateAllButtonTitles(); + + // Property Inspector들에게 이벤트 목록 전송 (이벤트 컨트롤러만) + for (const context of buttonContexts.keys()) { + const settings = getCurrentSettings(context); + const actionType = settings.actionType || 'camera'; + + console.log('🔍 컨텍스트 분석:', context, 'Action Type:', actionType); + + if (actionType === 'event') { + console.log('🎯 이벤트 컨트롤러 발견 - Property Inspector에 데이터 전송'); + sendToPropertyInspector(context, 'event_list', { + events: eventList, + currentIndex: data.data.event_data?.current_index || 0 + }, 'com.mirabox.streamingle.event'); + } else { + console.log('⚠️ 이벤트 컨트롤러가 아님 - Action Type:', actionType); + } + } + } else { + console.log('⚠️ 이벤트 목록 응답에서 이벤트 데이터가 배열이 아님'); + console.log('📋 events:', events); + } + } + break; + + case 'event_list_response': + console.log('🎯 이벤트 목록 수신'); + + if (data.data && data.data.event_data) { + let events = data.data.event_data.events || data.data.event_data; + + if (Array.isArray(events)) { + eventList = events; + const currentIndex = data.data.current_event ?? data.data.event_data?.current_index ?? 0; + console.log('🎯 이벤트 목록 업데이트됨:', eventList.length, '개'); + updateAllButtonTitles(); + + // Property Inspector들에게 이벤트 목록 전송 (이벤트 컨트롤러만) + for (const context of buttonContexts.keys()) { + const settings = getCurrentSettings(context); + const actionType = settings.actionType || 'camera'; + + if (actionType === 'event') { + sendToPropertyInspector(context, 'event_list', { + events: eventList, + currentIndex: currentIndex + }, 'com.mirabox.streamingle.event'); + } + } + } else { + console.log('⚠️ 이벤트 목록 응답에서 이벤트 데이터가 배열이 아님'); + console.log('📋 events:', events); + } + } + break; + default: console.log('❓ 알 수 없는 Unity 메시지 타입:', data.type); } @@ -740,6 +966,10 @@ function updateButtonTitle(context) { let title = 'Camera'; let isActive = true; // 아이템 활성화 상태 (기본값: 활성) + console.log('🏷️ 버튼 제목 업데이트 시작 - Context:', context); + console.log('🏷️ 설정:', settings); + console.log('🏷️ 액션 타입:', actionType); + if (actionType === 'camera') { const cameraIndex = typeof settings.cameraIndex === 'number' ? settings.cameraIndex : 0; title = `카메라 ${cameraIndex + 1}`; @@ -787,6 +1017,25 @@ function updateButtonTitle(context) { } } } + } else if (actionType === 'event') { + const eventIndex = typeof settings.eventIndex === 'number' ? settings.eventIndex : 0; + title = `이벤트 ${eventIndex + 1}`; + + // 이벤트 목록에서 이름 찾기 + if (eventList && eventList.length > eventIndex) { + const event = eventList[eventIndex]; + if (event && (event.name || event.groupName)) { + // 이벤트 이름에서 불필요한 부분 제거하고 짧게 만들기 + let eventName = event.name || event.groupName; + let shortName = eventName + .replace('Event', '') + .replace('Group', '') + .replace('_', ' ') + .substring(0, 10); // 최대 10글자 + + title = shortName || `이벤트 ${eventIndex + 1}`; + } + } } // StreamDock에 제목 업데이트 요청 @@ -807,12 +1056,13 @@ function updateButtonTitle(context) { websocket.send(JSON.stringify(message)); console.log('🏷️ 버튼 제목 업데이트:', title, '(액션 타입:', actionType, ', 활성:', isActive, ')'); - // 아이템이나 카메라가 비활성화되어 있으면 아이콘을 어둡게 표시 + // 아이템이나 카메라가 비활성화되어 있으면 아이콘을 어둡게 표시 (이벤트는 제외) if ((actionType === 'item' || actionType === 'camera') && !isActive) { setButtonState(context, false); // 비활성 상태로 설정 - } else { + } else if (actionType === 'item' || actionType === 'camera') { setButtonState(context, true); // 활성 상태로 설정 } + // 이벤트 컨트롤러는 활성/비활성 상태 변경 없음 (항상 활성) } // 버튼 상태 설정 (활성/비활성) @@ -825,7 +1075,7 @@ function setButtonState(context, isActive) { const settings = getCurrentSettings(context); const actionType = settings.actionType || 'camera'; - // 아이템 컨트롤러와 카메라 컨트롤러 모두 상태 변경 적용 + // 아이템 컨트롤러와 카메라 컨트롤러만 상태 변경 적용 (이벤트 컨트롤러는 제외) if (actionType === 'item' || actionType === 'camera') { // 방법 1: setState 이벤트 사용 const stateMessage = { diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.html b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.html new file mode 100644 index 00000000..c1db5b00 --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.html @@ -0,0 +1,190 @@ + + + + + Streamingle Event Inspector + + + + +
+
+ Unity 연결 안됨 +
+ + +
+ +
+ +
+
현재 이벤트 그룹: -
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.js b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.js new file mode 100644 index 00000000..3858a5d5 --- /dev/null +++ b/Streamdeck/com.mirabox.streamingle.sdPlugin/propertyInspector/event/index.js @@ -0,0 +1,308 @@ +// Event Controller Property Inspector +let websocket = null; +let uuid = null; +let settings = {}; +let eventList = []; +let isUnityConnected = false; + +// DOM 요소들 +let eventSelect = null; +let statusDot = null; +let connectionStatus = null; +let currentEvent = null; +let refreshButton = null; +let executeButton = null; +let autoExecute = null; +let logArea = null; + +function logToScreen(msg, color = "#fff") { + let logDiv = document.getElementById('logArea'); + if (!logDiv) return; + const line = document.createElement('div'); + line.style.color = color; + line.textContent = `[${new Date().toLocaleTimeString()}] ${msg}`; + logDiv.appendChild(line); + logDiv.scrollTop = logDiv.scrollHeight; +} + +console.log = function(...args) { + logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#0f0'); +}; +console.error = function(...args) { + logToScreen(args.map(a => (typeof a === 'object' ? JSON.stringify(a) : a)).join(' '), '#f55'); +}; + +// DOM 초기화 +window.addEventListener('DOMContentLoaded', function() { + eventSelect = document.getElementById('event-select'); + statusDot = document.getElementById('statusDot'); + connectionStatus = document.getElementById('connection-status'); + currentEvent = document.getElementById('current-event'); + refreshButton = document.getElementById('refresh-button'); + executeButton = document.getElementById('execute-button'); + autoExecute = document.getElementById('autoExecute'); + logArea = document.getElementById('logArea'); + + if (refreshButton) refreshButton.addEventListener('click', onRefreshClicked); + if (executeButton) executeButton.addEventListener('click', onExecuteClicked); + if (eventSelect) eventSelect.addEventListener('change', onEventSelectionChanged); + if (autoExecute) autoExecute.addEventListener('change', onAutoExecuteChanged); + + console.log('✅ Event Property Inspector 준비 완료'); +}); + +// StreamDeck 연결 +window.connectElgatoStreamDeckSocket = function(inPort, inUUID, inEvent, inInfo, inActionInfo) { + uuid = inUUID; + console.log('🔌 StreamDeck 연결 시작:', inPort, inUUID); + + try { + if (inActionInfo) { + const actionInfo = JSON.parse(inActionInfo); + settings = actionInfo.payload?.settings || {}; + console.log('⚙️ 초기 설정:', settings); + } + } catch (e) { + console.error('ActionInfo 파싱 오류:', e); + } + + if (!websocket) { + websocket = new WebSocket('ws://localhost:' + inPort); + + websocket.onopen = function() { + console.log('✅ StreamDeck 연결됨'); + websocket.send(JSON.stringify({ event: inEvent, uuid: inUUID })); + + // Unity 상태 요청 + setTimeout(() => { + sendToPlugin('get_unity_status'); + }, 500); + }; + + websocket.onmessage = function(evt) { + try { + const jsonObj = JSON.parse(evt.data); + handleMessage(jsonObj); + } catch (e) { + console.error('메시지 파싱 오류:', e); + } + }; + + websocket.onclose = function() { + console.log('❌ StreamDeck 연결 끊어짐'); + websocket = null; + }; + + websocket.onerror = function(e) { + console.error('WebSocket 오류:', e); + }; + } +}; + +function sendToPlugin(command, data = {}) { + if (!websocket) return; + const message = { + action: 'com.mirabox.streamingle.event', + event: 'sendToPlugin', + context: uuid, + payload: { command, ...data } + }; + websocket.send(JSON.stringify(message)); + console.log('📤 Plugin으로 메시지 전송:', command, data); +} + +function handleMessage(jsonObj) { + console.log('📨 메시지 수신:', jsonObj.event); + + if (jsonObj.event === 'sendToPropertyInspector' && jsonObj.payload && jsonObj.payload.event) { + const innerEvent = jsonObj.payload.event; + console.log('📨 Property Inspector 이벤트:', innerEvent); + + switch (innerEvent) { + case 'unity_connected': + updateUnityConnection(true); + break; + case 'unity_disconnected': + updateUnityConnection(false); + break; + case 'event_list': + updateEventList(jsonObj.payload.events, jsonObj.payload.currentIndex); + break; + case 'event_changed': + updateEventState(jsonObj.payload.currentIndex); + break; + case 'camera_list': + console.log('📹 카메라 목록 수신 (이벤트 Property Inspector에서는 무시)'); + break; + case 'item_list': + console.log('🎯 아이템 목록 수신 (이벤트 Property Inspector에서는 무시)'); + break; + } + } + + if (jsonObj.event === 'didReceiveSettings' && jsonObj.payload && jsonObj.context) { + settings = jsonObj.payload.settings || {}; + console.log('⚙️ 설정 수신:', settings); + + if (settings.eventIndex !== undefined && eventSelect) { + eventSelect.value = settings.eventIndex; + console.log('🎯 이벤트 인덱스 설정됨:', settings.eventIndex); + } + + if (settings.autoExecute !== undefined && autoExecute) { + autoExecute.checked = settings.autoExecute; + } + + updateCurrentEvent(); + } +} + +function updateUnityConnection(connected) { + isUnityConnected = connected; + + if (statusDot) { + statusDot.className = 'dot ' + (connected ? 'green' : 'red'); + } + + if (connectionStatus) { + connectionStatus.textContent = connected ? 'Unity 연결됨' : 'Unity 연결 안됨'; + connectionStatus.className = connected ? 'connected' : 'disconnected'; + } + + if (eventSelect) { + eventSelect.disabled = !connected; + } + + if (refreshButton) { + refreshButton.disabled = !connected; + } + + if (executeButton) { + executeButton.disabled = !connected; + } + + console.log('🔗 Unity 연결 상태 변경:', connected); +} + +function updateEventList(events, currentIndex) { + eventList = events || []; + console.log('🎯 이벤트 목록 업데이트:', eventList.length, '개'); + + if (!eventSelect) return; + + eventSelect.innerHTML = ''; + + if (eventList.length === 0) { + const option = document.createElement('option'); + option.value = ''; + option.textContent = '이벤트 그룹이 없습니다'; + eventSelect.appendChild(option); + } else { + eventList.forEach((event, index) => { + const option = document.createElement('option'); + option.value = index; + option.textContent = event.name || event.groupName || `이벤트 그룹 ${index + 1}`; + eventSelect.appendChild(option); + }); + } + + // 현재 선택된 이벤트 설정 + if (settings.eventIndex !== undefined) { + eventSelect.value = settings.eventIndex; + } else if (currentIndex !== undefined) { + eventSelect.value = currentIndex; + } + + updateCurrentEvent(); +} + +function updateCurrentEvent() { + if (!currentEvent) return; + + const selectedIndex = eventSelect ? parseInt(eventSelect.value) : -1; + if (selectedIndex >= 0 && selectedIndex < eventList.length) { + const selectedEvent = eventList[selectedIndex]; + currentEvent.textContent = `현재 이벤트 그룹: ${selectedEvent.name || selectedEvent.groupName || `이벤트 그룹 ${selectedIndex + 1}`}`; + } else { + currentEvent.textContent = '현재 이벤트 그룹: -'; + } +} + +function updateEventState(currentIndex) { + console.log('🎯 이벤트 상태 업데이트:', currentIndex); + + if (eventSelect && currentIndex !== undefined) { + eventSelect.value = currentIndex; + updateCurrentEvent(); + } +} + +function onEventSelectionChanged() { + if (!eventSelect) return; + + const selectedIndex = parseInt(eventSelect.value); + console.log('🎯 이벤트 선택 변경:', selectedIndex); + + if (selectedIndex >= 0) { + settings.eventIndex = selectedIndex; + + // StreamDeck에 설정 저장 (카메라 컨트롤러와 동일한 방식) + if (websocket && uuid) { + websocket.send(JSON.stringify({ + action: 'com.mirabox.streamingle.event', + event: 'setSettings', + context: uuid, + payload: { + eventIndex: selectedIndex, + actionType: 'event' // actionType 명시적으로 설정 + } + })); + console.log('💾 설정 저장됨:', { eventIndex: selectedIndex, actionType: 'event' }); + } + + // Unity에 이벤트 설정 요청 (자동 실행 여부와 관계없이) + if (isUnityConnected) { + console.log('📤 Unity에 이벤트 설정 전송:', selectedIndex); + sendToPlugin('set_event', { event_index: selectedIndex }); + } else { + console.log('⚠️ Unity가 연결되지 않아 이벤트 설정을 전송할 수 없습니다'); + } + + updateCurrentEvent(); + } +} + +function onAutoExecuteChanged() { + if (!autoExecute) return; + + settings.autoExecute = autoExecute.checked; + console.log('⚙️ 자동 실행 설정 변경:', settings.autoExecute); + + // StreamDeck에 설정 저장 (카메라 컨트롤러와 동일한 방식) + if (websocket && uuid) { + websocket.send(JSON.stringify({ + action: 'com.mirabox.streamingle.event', + event: 'setSettings', + context: uuid, + payload: { autoExecute: settings.autoExecute } + })); + console.log('💾 자동 실행 설정 저장됨:', { autoExecute: settings.autoExecute }); + } +} + +function onRefreshClicked() { + console.log('🔄 이벤트 목록 새로고침 요청'); + sendToPlugin('get_event_list'); +} + +function onExecuteClicked() { + if (!eventSelect) return; + + const selectedIndex = parseInt(eventSelect.value); + if (selectedIndex >= 0) { + console.log('▶️ 이벤트 실행 요청:', selectedIndex); + sendToPlugin('execute_event', { event_index: selectedIndex }); + } else { + console.log('⚠️ 이벤트가 선택되지 않았습니다'); + } +} \ No newline at end of file