Fix : 카메라 컨트롤러 활성 비활성 디스플레이 기능 추가 패치
This commit is contained in:
parent
650c3927bc
commit
624e117a2a
@ -9,24 +9,24 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
{
|
{
|
||||||
[Header("서버 설정")]
|
[Header("서버 설정")]
|
||||||
public int port = 10701;
|
public int port = 10701;
|
||||||
|
|
||||||
private WebSocketServer server;
|
private WebSocketServer server;
|
||||||
private List<StreamDeckService> connectedClients = new List<StreamDeckService>();
|
private List<StreamDeckService> connectedClients = new List<StreamDeckService>();
|
||||||
public CameraManager cameraManager { get; private set; }
|
public CameraManager cameraManager { get; private set; }
|
||||||
public ItemController itemController { get; private set; }
|
public ItemController itemController { get; private set; }
|
||||||
|
|
||||||
// 싱글톤 패턴으로 StreamDeckService에서 접근 가능하도록
|
// 싱글톤 패턴으로 StreamDeckService에서 접근 가능하도록
|
||||||
public static StreamDeckServerManager Instance { get; private set; }
|
public static StreamDeckServerManager Instance { get; private set; }
|
||||||
|
|
||||||
// 메인 스레드에서 처리할 작업 큐
|
// 메인 스레드에서 처리할 작업 큐
|
||||||
private readonly Queue<System.Action> mainThreadActions = new Queue<System.Action>();
|
private readonly Queue<System.Action> mainThreadActions = new Queue<System.Action>();
|
||||||
private readonly object lockObject = new object();
|
private readonly object lockObject = new object();
|
||||||
|
|
||||||
void Awake()
|
void Awake()
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Start()
|
void Start()
|
||||||
{
|
{
|
||||||
// CameraManager 찾기
|
// CameraManager 찾기
|
||||||
@ -36,17 +36,17 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.LogError("[StreamDeckServerManager] CameraManager를 찾을 수 없습니다!");
|
Debug.LogError("[StreamDeckServerManager] CameraManager를 찾을 수 없습니다!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemController 찾기
|
// ItemController 찾기
|
||||||
itemController = FindObjectOfType<ItemController>();
|
itemController = FindObjectOfType<ItemController>();
|
||||||
if (itemController == null)
|
if (itemController == null)
|
||||||
{
|
{
|
||||||
Debug.LogWarning("[StreamDeckServerManager] ItemController를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다.");
|
Debug.LogWarning("[StreamDeckServerManager] ItemController를 찾을 수 없습니다. 아이템 컨트롤 기능이 비활성화됩니다.");
|
||||||
}
|
}
|
||||||
|
|
||||||
StartServer();
|
StartServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Update()
|
void Update()
|
||||||
{
|
{
|
||||||
// 메인 스레드에서 대기 중인 작업들을 처리
|
// 메인 스레드에서 대기 중인 작업들을 처리
|
||||||
@ -66,12 +66,12 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnApplicationQuit()
|
void OnApplicationQuit()
|
||||||
{
|
{
|
||||||
StopServer();
|
StopServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StartServer()
|
private void StartServer()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -86,7 +86,7 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.LogError($"[StreamDeckServerManager] 서버 시작 실패: {e.Message}");
|
Debug.LogError($"[StreamDeckServerManager] 서버 시작 실패: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopServer()
|
private void StopServer()
|
||||||
{
|
{
|
||||||
if (server != null)
|
if (server != null)
|
||||||
@ -96,26 +96,28 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.Log("[StreamDeckServerManager] 서버 중지됨");
|
Debug.Log("[StreamDeckServerManager] 서버 중지됨");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnClientConnected(StreamDeckService service)
|
public void OnClientConnected(StreamDeckService service)
|
||||||
{
|
{
|
||||||
// 메인 스레드에서 처리하도록 큐에 추가
|
// 메인 스레드에서 처리하도록 큐에 추가
|
||||||
lock (lockObject)
|
lock (lockObject)
|
||||||
{
|
{
|
||||||
mainThreadActions.Enqueue(() => {
|
mainThreadActions.Enqueue(() =>
|
||||||
|
{
|
||||||
connectedClients.Add(service);
|
connectedClients.Add(service);
|
||||||
Debug.Log($"[StreamDeckServerManager] 클라이언트 연결됨. 총 연결: {connectedClients.Count}");
|
Debug.Log($"[StreamDeckServerManager] 클라이언트 연결됨. 총 연결: {connectedClients.Count}");
|
||||||
SendInitialCameraData(service);
|
SendInitialCameraData(service);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 메시지 처리도 메인 스레드로 전달
|
// 메시지 처리도 메인 스레드로 전달
|
||||||
public void ProcessMessageOnMainThread(string messageData, StreamDeckService service)
|
public void ProcessMessageOnMainThread(string messageData, StreamDeckService service)
|
||||||
{
|
{
|
||||||
lock (lockObject)
|
lock (lockObject)
|
||||||
{
|
{
|
||||||
mainThreadActions.Enqueue(() => {
|
mainThreadActions.Enqueue(() =>
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ProcessMessage(messageData, service);
|
ProcessMessage(messageData, service);
|
||||||
@ -127,17 +129,17 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnClientDisconnected(StreamDeckService service)
|
public void OnClientDisconnected(StreamDeckService service)
|
||||||
{
|
{
|
||||||
connectedClients.Remove(service);
|
connectedClients.Remove(service);
|
||||||
Debug.Log($"[StreamDeckServerManager] 클라이언트 연결 해제됨. 총 연결: {connectedClients.Count}");
|
Debug.Log($"[StreamDeckServerManager] 클라이언트 연결 해제됨. 총 연결: {connectedClients.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendInitialCameraData(StreamDeckService service)
|
private void SendInitialCameraData(StreamDeckService service)
|
||||||
{
|
{
|
||||||
if (cameraManager == null) return;
|
if (cameraManager == null) return;
|
||||||
|
|
||||||
var initialData = new
|
var initialData = new
|
||||||
{
|
{
|
||||||
type = "connection_established",
|
type = "connection_established",
|
||||||
@ -153,12 +155,12 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
current_item = itemController?.GetCurrentItemState()
|
current_item = itemController?.GetCurrentItemState()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(initialData);
|
string json = JsonConvert.SerializeObject(initialData);
|
||||||
service.SendMessage(json);
|
service.SendMessage(json);
|
||||||
Debug.Log("[StreamDeckServerManager] 초기 데이터 전송됨 (카메라 + 아이템)");
|
Debug.Log("[StreamDeckServerManager] 초기 데이터 전송됨 (카메라 + 아이템)");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BroadcastMessage(string message)
|
public void BroadcastMessage(string message)
|
||||||
{
|
{
|
||||||
foreach (var client in connectedClients.ToArray())
|
foreach (var client in connectedClients.ToArray())
|
||||||
@ -174,12 +176,12 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 카메라 변경 시 모든 클라이언트에게 알림
|
// 카메라 변경 시 모든 클라이언트에게 알림
|
||||||
public void NotifyCameraChanged()
|
public void NotifyCameraChanged()
|
||||||
{
|
{
|
||||||
if (cameraManager == null) return;
|
if (cameraManager == null) return;
|
||||||
|
|
||||||
var updateMessage = new
|
var updateMessage = new
|
||||||
{
|
{
|
||||||
type = "camera_changed",
|
type = "camera_changed",
|
||||||
@ -191,17 +193,17 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
current_camera = cameraManager.GetCurrentCameraState()
|
current_camera = cameraManager.GetCurrentCameraState()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(updateMessage);
|
string json = JsonConvert.SerializeObject(updateMessage);
|
||||||
BroadcastMessage(json);
|
BroadcastMessage(json);
|
||||||
Debug.Log("[StreamDeckServerManager] 카메라 변경 알림 전송됨");
|
Debug.Log("[StreamDeckServerManager] 카메라 변경 알림 전송됨");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 아이템 변경 시 모든 클라이언트에게 알림
|
// 아이템 변경 시 모든 클라이언트에게 알림
|
||||||
public void NotifyItemChanged()
|
public void NotifyItemChanged()
|
||||||
{
|
{
|
||||||
if (itemController == null) return;
|
if (itemController == null) return;
|
||||||
|
|
||||||
var updateMessage = new
|
var updateMessage = new
|
||||||
{
|
{
|
||||||
type = "item_changed",
|
type = "item_changed",
|
||||||
@ -213,41 +215,41 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
current_item = itemController.GetCurrentItemState()
|
current_item = itemController.GetCurrentItemState()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(updateMessage);
|
string json = JsonConvert.SerializeObject(updateMessage);
|
||||||
BroadcastMessage(json);
|
BroadcastMessage(json);
|
||||||
Debug.Log("[StreamDeckServerManager] 아이템 변경 알림 전송됨");
|
Debug.Log("[StreamDeckServerManager] 아이템 변경 알림 전송됨");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 메시지 처리 로직을 여기로 이동
|
// 메시지 처리 로직을 여기로 이동
|
||||||
private void ProcessMessage(string messageData, StreamDeckService service)
|
private void ProcessMessage(string messageData, StreamDeckService service)
|
||||||
{
|
{
|
||||||
// JSON 파싱 시도
|
// JSON 파싱 시도
|
||||||
var message = JsonConvert.DeserializeObject<Dictionary<string, object>>(messageData);
|
var message = JsonConvert.DeserializeObject<Dictionary<string, object>>(messageData);
|
||||||
string messageType = message.ContainsKey("type") ? message["type"].ToString() : null;
|
string messageType = message.ContainsKey("type") ? message["type"].ToString() : null;
|
||||||
|
|
||||||
switch (messageType)
|
switch (messageType)
|
||||||
{
|
{
|
||||||
case "switch_camera":
|
case "switch_camera":
|
||||||
HandleSwitchCamera(message);
|
HandleSwitchCamera(message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "get_camera_list":
|
case "get_camera_list":
|
||||||
HandleGetCameraList(service);
|
HandleGetCameraList(service);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "toggle_item":
|
case "toggle_item":
|
||||||
HandleToggleItem(message);
|
HandleToggleItem(message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "set_item":
|
case "set_item":
|
||||||
HandleSetItem(message);
|
HandleSetItem(message);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "get_item_list":
|
case "get_item_list":
|
||||||
HandleGetItemList(service);
|
HandleGetItemList(service);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "test":
|
case "test":
|
||||||
// 테스트 메시지 에코 응답
|
// 테스트 메시지 에코 응답
|
||||||
var response = new
|
var response = new
|
||||||
@ -259,48 +261,45 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
received_message = messageData
|
received_message = messageData
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(response);
|
string json = JsonConvert.SerializeObject(response);
|
||||||
service.SendMessage(json);
|
service.SendMessage(json);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Debug.Log($"[StreamDeckServerManager] 알 수 없는 메시지 타입: {messageType}");
|
Debug.Log($"[StreamDeckServerManager] 알 수 없는 메시지 타입: {messageType}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSwitchCamera(Dictionary<string, object> message)
|
private void HandleSwitchCamera(Dictionary<string, object> message)
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 카메라 전환 요청 수신");
|
Debug.Log($"[StreamDeckServerManager] 카메라 전환 요청 수신");
|
||||||
|
|
||||||
if (cameraManager == null)
|
if (cameraManager == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("[StreamDeckServerManager] cameraManager가 null입니다!");
|
Debug.LogError("[StreamDeckServerManager] cameraManager가 null입니다!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (message.ContainsKey("data"))
|
if (message.ContainsKey("data"))
|
||||||
{
|
{
|
||||||
var dataObject = message["data"];
|
var dataObject = message["data"];
|
||||||
|
|
||||||
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
||||||
{
|
{
|
||||||
if (jObject.ContainsKey("camera_index"))
|
if (jObject.ContainsKey("camera_index"))
|
||||||
{
|
{
|
||||||
var cameraIndexToken = jObject["camera_index"];
|
var cameraIndexToken = jObject["camera_index"];
|
||||||
|
|
||||||
if (int.TryParse(cameraIndexToken?.ToString(), out int cameraIndex))
|
if (int.TryParse(cameraIndexToken?.ToString(), out int cameraIndex))
|
||||||
{
|
{
|
||||||
if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0))
|
if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환");
|
Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환");
|
||||||
cameraManager.Set(cameraIndex);
|
cameraManager.Set(cameraIndex);
|
||||||
|
|
||||||
// 카메라 변경 알림 전송
|
|
||||||
NotifyCameraChanged();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -322,17 +321,14 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
if (data.ContainsKey("camera_index"))
|
if (data.ContainsKey("camera_index"))
|
||||||
{
|
{
|
||||||
var cameraIndexObj = data["camera_index"];
|
var cameraIndexObj = data["camera_index"];
|
||||||
|
|
||||||
if (int.TryParse(cameraIndexObj?.ToString(), out int cameraIndex))
|
if (int.TryParse(cameraIndexObj?.ToString(), out int cameraIndex))
|
||||||
{
|
{
|
||||||
if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0))
|
if (cameraIndex >= 0 && cameraIndex < (cameraManager.cameraPresets?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환");
|
Debug.Log($"[StreamDeckServerManager] 카메라 {cameraIndex}번으로 전환");
|
||||||
cameraManager.Set(cameraIndex);
|
cameraManager.Set(cameraIndex);
|
||||||
|
}
|
||||||
// 카메라 변경 알림 전송
|
|
||||||
NotifyCameraChanged();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogError($"[StreamDeckServerManager] 잘못된 카메라 인덱스: {cameraIndex}, 유효 범위: 0-{(cameraManager.cameraPresets?.Count ?? 0) - 1}");
|
Debug.LogError($"[StreamDeckServerManager] 잘못된 카메라 인덱스: {cameraIndex}, 유효 범위: 0-{(cameraManager.cameraPresets?.Count ?? 0) - 1}");
|
||||||
@ -363,11 +359,11 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.LogError($"[StreamDeckServerManager] 카메라 전환 실패: {ex.Message}");
|
Debug.LogError($"[StreamDeckServerManager] 카메라 전환 실패: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleGetCameraList(StreamDeckService service)
|
private void HandleGetCameraList(StreamDeckService service)
|
||||||
{
|
{
|
||||||
if (cameraManager == null) return;
|
if (cameraManager == null) return;
|
||||||
|
|
||||||
var response = new
|
var response = new
|
||||||
{
|
{
|
||||||
type = "camera_list_response",
|
type = "camera_list_response",
|
||||||
@ -379,40 +375,40 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
current_camera = cameraManager.GetCurrentCameraState()
|
current_camera = cameraManager.GetCurrentCameraState()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(response);
|
string json = JsonConvert.SerializeObject(response);
|
||||||
service.SendMessage(json);
|
service.SendMessage(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleToggleItem(Dictionary<string, object> message)
|
private void HandleToggleItem(Dictionary<string, object> message)
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 토글 요청 수신");
|
Debug.Log($"[StreamDeckServerManager] 아이템 토글 요청 수신");
|
||||||
|
|
||||||
if (itemController == null)
|
if (itemController == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!");
|
Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (message.ContainsKey("data"))
|
if (message.ContainsKey("data"))
|
||||||
{
|
{
|
||||||
var dataObject = message["data"];
|
var dataObject = message["data"];
|
||||||
|
|
||||||
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
||||||
{
|
{
|
||||||
if (jObject.ContainsKey("item_index"))
|
if (jObject.ContainsKey("item_index"))
|
||||||
{
|
{
|
||||||
var itemIndexToken = jObject["item_index"];
|
var itemIndexToken = jObject["item_index"];
|
||||||
|
|
||||||
if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex))
|
if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex))
|
||||||
{
|
{
|
||||||
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글");
|
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글");
|
||||||
itemController.ToggleGroup(itemIndex);
|
itemController.ToggleGroup(itemIndex);
|
||||||
|
|
||||||
// 아이템 변경 알림 전송
|
// 아이템 변경 알림 전송
|
||||||
NotifyItemChanged();
|
NotifyItemChanged();
|
||||||
}
|
}
|
||||||
@ -436,14 +432,14 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
if (data.ContainsKey("item_index"))
|
if (data.ContainsKey("item_index"))
|
||||||
{
|
{
|
||||||
var itemIndexObj = data["item_index"];
|
var itemIndexObj = data["item_index"];
|
||||||
|
|
||||||
if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex))
|
if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex))
|
||||||
{
|
{
|
||||||
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글");
|
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번 토글");
|
||||||
itemController.ToggleGroup(itemIndex);
|
itemController.ToggleGroup(itemIndex);
|
||||||
|
|
||||||
// 아이템 변경 알림 전송
|
// 아이템 변경 알림 전송
|
||||||
NotifyItemChanged();
|
NotifyItemChanged();
|
||||||
}
|
}
|
||||||
@ -477,36 +473,36 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.LogError($"[StreamDeckServerManager] 아이템 토글 실패: {ex.Message}");
|
Debug.LogError($"[StreamDeckServerManager] 아이템 토글 실패: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSetItem(Dictionary<string, object> message)
|
private void HandleSetItem(Dictionary<string, object> message)
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 설정 요청 수신");
|
Debug.Log($"[StreamDeckServerManager] 아이템 설정 요청 수신");
|
||||||
|
|
||||||
if (itemController == null)
|
if (itemController == null)
|
||||||
{
|
{
|
||||||
Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!");
|
Debug.LogError("[StreamDeckServerManager] itemController가 null입니다!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (message.ContainsKey("data"))
|
if (message.ContainsKey("data"))
|
||||||
{
|
{
|
||||||
var dataObject = message["data"];
|
var dataObject = message["data"];
|
||||||
|
|
||||||
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
if (dataObject is Newtonsoft.Json.Linq.JObject jObject)
|
||||||
{
|
{
|
||||||
if (jObject.ContainsKey("item_index"))
|
if (jObject.ContainsKey("item_index"))
|
||||||
{
|
{
|
||||||
var itemIndexToken = jObject["item_index"];
|
var itemIndexToken = jObject["item_index"];
|
||||||
|
|
||||||
if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex))
|
if (int.TryParse(itemIndexToken?.ToString(), out int itemIndex))
|
||||||
{
|
{
|
||||||
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번으로 설정");
|
Debug.Log($"[StreamDeckServerManager] 아이템 그룹 {itemIndex}번으로 설정");
|
||||||
itemController.Set(itemIndex);
|
itemController.Set(itemIndex);
|
||||||
|
|
||||||
// 아이템 변경 알림 전송
|
// 아이템 변경 알림 전송
|
||||||
NotifyItemChanged();
|
NotifyItemChanged();
|
||||||
}
|
}
|
||||||
@ -530,14 +526,14 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
if (data.ContainsKey("item_index"))
|
if (data.ContainsKey("item_index"))
|
||||||
{
|
{
|
||||||
var itemIndexObj = data["item_index"];
|
var itemIndexObj = data["item_index"];
|
||||||
|
|
||||||
if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex))
|
if (int.TryParse(itemIndexObj?.ToString(), out int itemIndex))
|
||||||
{
|
{
|
||||||
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
if (itemIndex >= 0 && itemIndex < (itemController.itemGroups?.Count ?? 0))
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckServerManager] 아이템 {itemIndex}번으로 설정");
|
Debug.Log($"[StreamDeckServerManager] 아이템 {itemIndex}번으로 설정");
|
||||||
itemController.Set(itemIndex);
|
itemController.Set(itemIndex);
|
||||||
|
|
||||||
// 아이템 변경 알림 전송
|
// 아이템 변경 알림 전송
|
||||||
NotifyItemChanged();
|
NotifyItemChanged();
|
||||||
}
|
}
|
||||||
@ -571,11 +567,11 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
Debug.LogError($"[StreamDeckServerManager] 아이템 설정 실패: {ex.Message}");
|
Debug.LogError($"[StreamDeckServerManager] 아이템 설정 실패: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleGetItemList(StreamDeckService service)
|
private void HandleGetItemList(StreamDeckService service)
|
||||||
{
|
{
|
||||||
if (itemController == null) return;
|
if (itemController == null) return;
|
||||||
|
|
||||||
var response = new
|
var response = new
|
||||||
{
|
{
|
||||||
type = "item_list_response",
|
type = "item_list_response",
|
||||||
@ -587,7 +583,7 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
current_item = itemController.GetCurrentItemState()
|
current_item = itemController.GetCurrentItemState()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string json = JsonConvert.SerializeObject(response);
|
string json = JsonConvert.SerializeObject(response);
|
||||||
service.SendMessage(json);
|
service.SendMessage(json);
|
||||||
}
|
}
|
||||||
@ -596,7 +592,7 @@ public class StreamDeckServerManager : MonoBehaviour
|
|||||||
public class StreamDeckService : WebSocketBehavior
|
public class StreamDeckService : WebSocketBehavior
|
||||||
{
|
{
|
||||||
private StreamDeckServerManager serverManager;
|
private StreamDeckServerManager serverManager;
|
||||||
|
|
||||||
protected override void OnOpen()
|
protected override void OnOpen()
|
||||||
{
|
{
|
||||||
serverManager = StreamDeckServerManager.Instance;
|
serverManager = StreamDeckServerManager.Instance;
|
||||||
@ -606,18 +602,18 @@ public class StreamDeckService : WebSocketBehavior
|
|||||||
}
|
}
|
||||||
Debug.Log("[StreamDeckService] WebSocket 연결 열림");
|
Debug.Log("[StreamDeckService] WebSocket 연결 열림");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnMessage(WebSocketSharp.MessageEventArgs e)
|
protected override void OnMessage(WebSocketSharp.MessageEventArgs e)
|
||||||
{
|
{
|
||||||
Debug.Log($"[StreamDeckService] 원본 메시지 수신: {e.Data}");
|
Debug.Log($"[StreamDeckService] 원본 메시지 수신: {e.Data}");
|
||||||
|
|
||||||
// 메인 스레드에서 처리하도록 매니저에게 전달
|
// 메인 스레드에서 처리하도록 매니저에게 전달
|
||||||
if (serverManager != null)
|
if (serverManager != null)
|
||||||
{
|
{
|
||||||
serverManager.ProcessMessageOnMainThread(e.Data, this);
|
serverManager.ProcessMessageOnMainThread(e.Data, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WebSocketSharp의 Send 메서드 래퍼
|
// WebSocketSharp의 Send 메서드 래퍼
|
||||||
public void SendMessage(string message)
|
public void SendMessage(string message)
|
||||||
{
|
{
|
||||||
@ -630,7 +626,7 @@ public class StreamDeckService : WebSocketBehavior
|
|||||||
Debug.LogError($"[StreamDeckService] 메시지 전송 실패: {ex.Message}");
|
Debug.LogError($"[StreamDeckService] 메시지 전송 실패: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose(WebSocketSharp.CloseEventArgs e)
|
protected override void OnClose(WebSocketSharp.CloseEventArgs e)
|
||||||
{
|
{
|
||||||
if (serverManager != null)
|
if (serverManager != null)
|
||||||
@ -639,9 +635,9 @@ public class StreamDeckService : WebSocketBehavior
|
|||||||
}
|
}
|
||||||
Debug.Log($"[StreamDeckService] WebSocket 연결 닫힘: {e.Reason}");
|
Debug.Log($"[StreamDeckService] WebSocket 연결 닫힘: {e.Reason}");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
Debug.LogError($"[StreamDeckService] WebSocket 오류: {e.Message}");
|
Debug.LogError($"[StreamDeckService] WebSocket 오류: {e.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,90 @@
|
|||||||
const { Plugins, Actions, log, EventEmitter } = require('./utils/plugin');
|
const { Plugins, Actions, log, EventEmitter } = require('./utils/plugin');
|
||||||
const { execSync } = require('child_process');
|
const { execSync } = require('child_process');
|
||||||
|
const WebSocket = require('ws');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
const plugin = new Plugins('demo');
|
const plugin = new Plugins('demo');
|
||||||
|
|
||||||
|
// 로그 파일 경로 설정
|
||||||
|
const logFilePath = path.join(__dirname, 'streamdeck_plugin.log');
|
||||||
|
|
||||||
|
// 로그 함수 - 기존 log 시스템과 함께 사용
|
||||||
|
function writeLog(message) {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const logMessage = `[${timestamp}] ${message}`;
|
||||||
|
|
||||||
|
// 기존 log 시스템 사용
|
||||||
|
log.info(logMessage);
|
||||||
|
|
||||||
|
// 파일에도 기록
|
||||||
|
try {
|
||||||
|
fs.appendFileSync(logFilePath, logMessage + '\n');
|
||||||
|
} catch (err) {
|
||||||
|
log.error('로그 파일 쓰기 실패:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const counters = {};
|
||||||
|
let unityWebSocket = null;
|
||||||
|
let reconnectTimer = null;
|
||||||
|
|
||||||
|
// 유니티 WebSocket 서버 연결 함수
|
||||||
|
function connectToUnity() {
|
||||||
|
if (unityWebSocket) {
|
||||||
|
unityWebSocket.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
unityWebSocket = new WebSocket('ws://localhost:10701/');
|
||||||
|
|
||||||
|
unityWebSocket.on('open', function() {
|
||||||
|
writeLog('유니티 WebSocket 서버에 연결됨');
|
||||||
|
});
|
||||||
|
|
||||||
|
unityWebSocket.on('message', function(data) {
|
||||||
|
writeLog('유니티로부터 메시지 수신: ' + data.toString());
|
||||||
|
});
|
||||||
|
|
||||||
|
unityWebSocket.on('error', function(err) {
|
||||||
|
writeLog('유니티 WebSocket 연결 오류: ' + err.message);
|
||||||
|
scheduleReconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
unityWebSocket.on('close', function() {
|
||||||
|
writeLog('유니티 WebSocket 연결 종료');
|
||||||
|
scheduleReconnect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 재연결 스케줄링
|
||||||
|
function scheduleReconnect() {
|
||||||
|
if (reconnectTimer) {
|
||||||
|
clearTimeout(reconnectTimer);
|
||||||
|
}
|
||||||
|
reconnectTimer = setTimeout(() => {
|
||||||
|
writeLog('유니티 WebSocket 서버 재연결 시도...');
|
||||||
|
connectToUnity();
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 유니티로 메시지 전송
|
||||||
|
function sendToUnity(message) {
|
||||||
|
if (unityWebSocket && unityWebSocket.readyState === WebSocket.OPEN) {
|
||||||
|
unityWebSocket.send(message);
|
||||||
|
writeLog('유니티 WebSocket 서버로 메시지 전송: ' + message);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
writeLog('유니티 WebSocket 서버 연결이 없거나 열려있지 않음');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
plugin.didReceiveGlobalSettings = ({ payload: { settings } }) => {
|
plugin.didReceiveGlobalSettings = ({ payload: { settings } }) => {
|
||||||
log.info('didReceiveGlobalSettings', settings);
|
log.info('didReceiveGlobalSettings', settings);
|
||||||
|
writeLog('플러그인 설정 수신됨');
|
||||||
|
|
||||||
|
// 플러그인 시작 시 유니티 WebSocket 서버 연결
|
||||||
|
connectToUnity();
|
||||||
};
|
};
|
||||||
|
|
||||||
const createSvg = (text) => `<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg">
|
const createSvg = (text) => `<svg width="144" height="144" xmlns="http://www.w3.org/2000/svg">
|
||||||
@ -20,22 +99,30 @@ plugin.demo = new Actions({
|
|||||||
default: {
|
default: {
|
||||||
},
|
},
|
||||||
async _willAppear({ context, payload }) {
|
async _willAppear({ context, payload }) {
|
||||||
// log.info("demo: ", context);
|
// 버튼이 처음 나타날 때 카운터 초기화 및 이미지 표시
|
||||||
let n = 0;
|
counters[context] = 0;
|
||||||
timers[context] = setInterval(async () => {
|
const svg = createSvg(counters[context]);
|
||||||
const svg = createSvg(++n);
|
plugin.setImage(context, `data:image/svg+xml;charset=utf8,${svg}`);
|
||||||
plugin.setImage(context, `data:image/svg+xml;charset=utf8,${svg}`);
|
writeLog(`버튼 나타남, context: ${context}`);
|
||||||
}, 1000);
|
|
||||||
},
|
},
|
||||||
_willDisappear({ context }) {
|
_willDisappear({ context }) {
|
||||||
// log.info('willDisAppear', context)
|
delete counters[context];
|
||||||
timers[context] && clearInterval(timers[context]);
|
writeLog(`버튼 사라짐, context: ${context}`);
|
||||||
},
|
},
|
||||||
_propertyInspectorDidAppear({ context }) {
|
_propertyInspectorDidAppear({ context }) {
|
||||||
},
|
},
|
||||||
sendToPlugin({ payload, context }) {
|
sendToPlugin({ payload, context }) {
|
||||||
},
|
},
|
||||||
keyUp({ context, payload }) {
|
keyUp({ context, payload }) {
|
||||||
|
// 카운터 증가
|
||||||
|
if (counters[context] === undefined) counters[context] = 0;
|
||||||
|
counters[context]++;
|
||||||
|
const svg = createSvg(counters[context]);
|
||||||
|
plugin.setImage(context, `data:image/svg+xml;charset=utf8,${svg}`);
|
||||||
|
writeLog(`버튼 클릭됨, context: ${context}, count: ${counters[context]}`);
|
||||||
|
|
||||||
|
// 유니티 WebSocket 서버로 메시지 전송
|
||||||
|
sendToUnity('Hello Unity from StreamDeck!');
|
||||||
},
|
},
|
||||||
dialDown({ context, payload }) {},
|
dialDown({ context, payload }) {},
|
||||||
dialRotate({ context, payload }) {}
|
dialRotate({ context, payload }) {}
|
||||||
|
|||||||
BIN
Streamdeck/com.mirabox.streamingle.sdPlugin/images/camera_icon_inactive.png
(Stored with Git LFS)
Normal file
BIN
Streamdeck/com.mirabox.streamingle.sdPlugin/images/camera_icon_inactive.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json
(Stored with Git LFS)
BIN
Streamdeck/com.mirabox.streamingle.sdPlugin/manifest.json
(Stored with Git LFS)
Binary file not shown.
@ -9,6 +9,18 @@ let isUnityConnected = false;
|
|||||||
let cameraList = []; // 카메라 목록 저장
|
let cameraList = []; // 카메라 목록 저장
|
||||||
let itemList = []; // 아이템 목록 저장
|
let itemList = []; // 아이템 목록 저장
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
function getBase64Image(filePath) {
|
||||||
|
try {
|
||||||
|
const data = fs.readFileSync(filePath);
|
||||||
|
return 'data:image/png;base64,' + data.toString('base64');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('이미지 파일 읽기 실패:', filePath, e);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StreamDock 연결 함수 (브라우저 기반)
|
// StreamDock 연결 함수 (브라우저 기반)
|
||||||
function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inActionInfo) {
|
function connectElgatoStreamDeckSocket(inPort, inUUID, inEvent, inInfo, inActionInfo) {
|
||||||
console.log('🔌 StreamDock 연결 시작 (브라우저)');
|
console.log('🔌 StreamDock 연결 시작 (브라우저)');
|
||||||
@ -576,6 +588,12 @@ function handleUnityMessage(data) {
|
|||||||
cameras: cameraList,
|
cameras: cameraList,
|
||||||
currentIndex: currentIndex
|
currentIndex: currentIndex
|
||||||
}, 'com.mirabox.streamingle.camera');
|
}, 'com.mirabox.streamingle.camera');
|
||||||
|
|
||||||
|
// 카메라 상태에 따라 버튼 상태도 업데이트
|
||||||
|
const cameraIndex = settings.cameraIndex || 0;
|
||||||
|
const isActive = (cameraIndex === currentIndex);
|
||||||
|
setButtonState(context, isActive);
|
||||||
|
console.log('🎨 카메라 변경으로 상태 업데이트:', context, '카메라 인덱스:', cameraIndex, '활성:', isActive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -586,7 +604,41 @@ function handleUnityMessage(data) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'camera_changed':
|
case 'camera_changed':
|
||||||
console.log('🎯 카메라 변경 알림');
|
console.log('📹 카메라 변경 알림');
|
||||||
|
if (data.data && data.data.camera_data) {
|
||||||
|
let cameras = data.data.camera_data.presets || data.data.camera_data;
|
||||||
|
|
||||||
|
if (Array.isArray(cameras)) {
|
||||||
|
cameraList = cameras;
|
||||||
|
console.log('📹 카메라 목록 업데이트됨:', cameraList.length, '개');
|
||||||
|
updateAllButtonTitles();
|
||||||
|
|
||||||
|
// Property Inspector들에게 카메라 목록 전송 (카메라 컨트롤러만)
|
||||||
|
for (const context of buttonContexts.keys()) {
|
||||||
|
const settings = getCurrentSettings(context);
|
||||||
|
const actionType = settings.actionType || 'camera';
|
||||||
|
|
||||||
|
if (actionType === 'camera') {
|
||||||
|
sendToPropertyInspector(context, 'camera_list', {
|
||||||
|
cameras: cameraList,
|
||||||
|
currentIndex: data.data.camera_data?.current_index || 0
|
||||||
|
}, 'com.mirabox.streamingle.camera');
|
||||||
|
|
||||||
|
// 카메라 상태에 따라 버튼 상태도 업데이트
|
||||||
|
const cameraIndex = settings.cameraIndex || 0;
|
||||||
|
let isActive = false;
|
||||||
|
if (cameraList && cameraList[cameraIndex]) {
|
||||||
|
isActive = cameraList[cameraIndex].isActive === true;
|
||||||
|
}
|
||||||
|
setButtonState(context, isActive);
|
||||||
|
console.log('🎨 카메라 변경으로 상태 업데이트:', context, '카메라 인덱스:', cameraIndex, 'isActive:', isActive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ 카메라 변경 응답에서 카메라 데이터가 배열이 아님');
|
||||||
|
console.log('📋 cameras:', cameras);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'item_changed':
|
case 'item_changed':
|
||||||
@ -705,6 +757,11 @@ function updateButtonTitle(context) {
|
|||||||
|
|
||||||
title = shortName || `카메라 ${cameraIndex + 1}`;
|
title = shortName || `카메라 ${cameraIndex + 1}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 카메라 활성화 상태 확인
|
||||||
|
if (typeof camera.isActive === 'boolean') {
|
||||||
|
isActive = camera.isActive;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (actionType === 'item') {
|
} else if (actionType === 'item') {
|
||||||
const itemIndex = typeof settings.itemIndex === 'number' ? settings.itemIndex : 0;
|
const itemIndex = typeof settings.itemIndex === 'number' ? settings.itemIndex : 0;
|
||||||
@ -750,8 +807,8 @@ function updateButtonTitle(context) {
|
|||||||
websocket.send(JSON.stringify(message));
|
websocket.send(JSON.stringify(message));
|
||||||
console.log('🏷️ 버튼 제목 업데이트:', title, '(액션 타입:', actionType, ', 활성:', isActive, ')');
|
console.log('🏷️ 버튼 제목 업데이트:', title, '(액션 타입:', actionType, ', 활성:', isActive, ')');
|
||||||
|
|
||||||
// 아이템이 비활성화되어 있으면 아이콘을 어둡게 표시
|
// 아이템이나 카메라가 비활성화되어 있으면 아이콘을 어둡게 표시
|
||||||
if (actionType === 'item' && !isActive) {
|
if ((actionType === 'item' || actionType === 'camera') && !isActive) {
|
||||||
setButtonState(context, false); // 비활성 상태로 설정
|
setButtonState(context, false); // 비활성 상태로 설정
|
||||||
} else {
|
} else {
|
||||||
setButtonState(context, true); // 활성 상태로 설정
|
setButtonState(context, true); // 활성 상태로 설정
|
||||||
@ -768,8 +825,8 @@ function setButtonState(context, isActive) {
|
|||||||
const settings = getCurrentSettings(context);
|
const settings = getCurrentSettings(context);
|
||||||
const actionType = settings.actionType || 'camera';
|
const actionType = settings.actionType || 'camera';
|
||||||
|
|
||||||
// 아이템 컨트롤러만 상태 변경 적용
|
// 아이템 컨트롤러와 카메라 컨트롤러 모두 상태 변경 적용
|
||||||
if (actionType === 'item') {
|
if (actionType === 'item' || actionType === 'camera') {
|
||||||
// 방법 1: setState 이벤트 사용
|
// 방법 1: setState 이벤트 사용
|
||||||
const stateMessage = {
|
const stateMessage = {
|
||||||
event: 'setState',
|
event: 'setState',
|
||||||
@ -783,19 +840,25 @@ function setButtonState(context, isActive) {
|
|||||||
websocket.send(JSON.stringify(stateMessage));
|
websocket.send(JSON.stringify(stateMessage));
|
||||||
console.log('🎨 버튼 상태 업데이트 (setState):', context, '(활성:', isActive, ', 상태:', isActive ? 0 : 1, ')');
|
console.log('🎨 버튼 상태 업데이트 (setState):', context, '(활성:', isActive, ', 상태:', isActive ? 0 : 1, ')');
|
||||||
|
|
||||||
// 방법 2: setImage 이벤트로 아이콘 직접 변경
|
// 방법 2: setImage 이벤트로 아이콘 직접 변경 (base64)
|
||||||
const imageName = isActive ? 'item_icon.png' : 'item_icon_inactive.png';
|
let imagePath;
|
||||||
|
if (actionType === 'item') {
|
||||||
|
imagePath = isActive ? 'images/item_icon.png' : 'images/item_icon_inactive.png';
|
||||||
|
} else if (actionType === 'camera') {
|
||||||
|
imagePath = isActive ? 'images/camera_icon.png' : 'images/camera_icon_inactive.png';
|
||||||
|
}
|
||||||
|
const imageBase64 = getBase64Image(imagePath);
|
||||||
const imageMessage = {
|
const imageMessage = {
|
||||||
event: 'setImage',
|
event: 'setImage',
|
||||||
context: context,
|
context: context,
|
||||||
payload: {
|
payload: {
|
||||||
image: imageName,
|
image: imageBase64,
|
||||||
target: 0 // hardware and software
|
target: 0 // hardware and software
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
websocket.send(JSON.stringify(imageMessage));
|
websocket.send(JSON.stringify(imageMessage));
|
||||||
console.log('🖼️ 버튼 아이콘 업데이트 (setImage):', context, '(활성:', isActive, ', 이미지:', imageName, ')');
|
console.log('🖼️ 버튼 아이콘 업데이트 (setImage):', context, '(활성:', isActive, ', 이미지:', imagePath, ')');
|
||||||
|
|
||||||
// 추가 디버깅을 위한 로그
|
// 추가 디버깅을 위한 로그
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user