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