using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using ActionString = System.Action; // 现在可以在代码中使用 ActionString using System.Threading.Tasks; using System.Windows.Forms; using System.Drawing; using DBObj; using OverlayFormObj; namespace DualScreenDemo { public class TCPServer { private TcpListener listener; private const int Port = 1000; private readonly string hostNameSuffix; private readonly Room _room; public static event ActionString OnDisplayBarrage; public static event ActionString OnDisplaySticker; public TCPServer(Room room) { _room = room; listener = new TcpListener(IPAddress.Any, Port); hostNameSuffix = GetHostNameSuffix(); _ = Task.Run(() => Start()); } // ================================ // 啟動 TCP Server // ================================ public void Start() { listener.Start(); Console.WriteLine($"Server started on port {Port}."); _ = Task.Run(async () => { try { while (true) { Console.WriteLine("Waiting for connections..."); TcpClient client = await listener.AcceptTcpClientAsync(); Console.WriteLine("Client connected!"); _ = Task.Run(() => HandleClient(client)); } } catch (Exception ex) { Console.WriteLine($"[Server Error] {ex.Message}"); } finally { listener.Stop(); } }); } // ================================ // 處理 Client 連線 // ================================ private async Task HandleClient(TcpClient client) { using (client) { try { NetworkStream stream = client.GetStream(); byte[] buffer = new byte[1024]; while (client.Connected) { int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (bytesRead == 0) break; string request = Encoding.UTF8.GetString(buffer, 0, bytesRead).Trim(); Console.WriteLine("Received: " + request); if (request.Length >= 5) { if (request.Substring(0, 3).Equals(hostNameSuffix, StringComparison.OrdinalIgnoreCase)) await HandleRoomCommand(request, stream); else await HandleOverlayCommand(request, stream); } } } catch (Exception ex) { Console.WriteLine($"[Client Error] {ex.Message}"); } } Console.WriteLine("Client disconnected."); } // ================================ // 房間控制指令 // ================================ private async Task HandleRoomCommand(string request ,NetworkStream stream) { string[] parts = request.Split(',', 3); // 最多拆三段 string cmdPrefix = parts[0].Trim(); string cmd = parts[1].Trim().ToLowerInvariant(); string content = parts.Length > 2 ? parts[2] : ""; switch (cmd) { case "x": _ = SafeInvoke(VideoPlayerForm.Instance, async () => { _room.set("closed"); try { await HttpServer.RestartServer(); } catch (Exception ex) { Console.WriteLine($"RestartServer Error: {ex.Message}"); } }); break; case "o": _room.set("active"); break; case "f": _ = SafeInvoke(PrimaryForm.Instance, () => _room.set("fire")); break; case "pause": TogglePause(); break; case "volume_up": OverlayForm.MainForm.Invoke(() => OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↑", "a2 b3 a4")); break; case "volume_down": OverlayForm.MainForm.Invoke(() => OverlayForm.MainForm.ShowTopRightLabelTime("音量 ↓", "a2 b4 a4")); break; case "mic_up": OverlayForm.MainForm.Invoke(() => OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↑", "a2 b5 a4")); break; case "mic_down": OverlayForm.MainForm.Invoke(() => OverlayForm.MainForm.ShowTopRightLabelTime("麥克風 ↓", "a2 b6 a4")); break; case "mute": ToggleMute(); break; case "original_song": PrimaryForm.Instance.Invoke(() => PrimaryForm.Instance.videoPlayerForm.ToggleVocalRemoval()); break; case "service": OverlayForm.MainForm.Invoke(() => OverlayForm.MainForm.ShowTopRightLabel("服務鈴", "a2 53 a4")); break; case "replay": PrimaryForm.Instance.Invoke(() => PrimaryForm.Instance.videoPlayerForm.ReplayCurrentSong()); break; case "male_key": SendSerialCommand(new byte[] { 0xA2, 0x7F, 0xA4 }, new byte[] { 0xA2, 0xB2, 0xA4 }, 2); break; case "female_key": SendSerialCommand(new byte[] { 0xA2, 0x7F, 0xA4 }, new byte[] { 0xA2, 0xB1, 0xA4 }, 2); break; case "cut": PrimaryForm.Instance.Invoke(() => PrimaryForm.Instance.videoPlayerForm.PlayNextSong()); break; case "lower_key": SendSerialCommand(new byte[] { 0xA2, 0xB2, 0xA4 }); break; case "standard_key": SendSerialCommand(new byte[] { 0xA2, 0x7F, 0xA4 }); break; case "raise_key": SendSerialCommand(new byte[] { 0xA2, 0xB1, 0xA4 }); break; case "send-sticker": _ = SafeInvoke(OverlayForm.MainForm, () => OnDisplaySticker?.Invoke(content)); break; case "message": _ = SafeInvoke(OverlayForm.MainForm, () => OnDisplayBarrage?.Invoke(content)); break; default: Console.WriteLine($"[Unknown Command] {request}"); break; } await SendOkResponse(stream); } // ================================ // 跑馬燈/公告指令 // ================================ private async Task HandleOverlayCommand(string message, NetworkStream stream) { if (!IsFormReady(OverlayForm.MainForm)) return; Match match = Regex.Match(message, @"^(全部|\d{4})\((白色|紅色|綠色|黑色|藍色)\)-"); _ = SafeInvoke(OverlayForm.MainForm, () => { if (match.Success) { string marqueeMessage = message.Substring(match.Value.Length).Trim(); Color textColor = GetColorFromString(match.Groups[2].Value); OverlayForm.MainForm.UpdateMarqueeText(marqueeMessage, OverlayForm.MarqueeStartPosition.Right, textColor); } else { OverlayForm.MainForm.UpdateMarqueeTextSecondLine("系統公告: "); } }); await SendOkResponse(stream); } // ================================ // 輔助方法 // ================================ private async Task SendOkResponse(NetworkStream stream) { byte[] okResponse = Encoding.UTF8.GetBytes("OK\n"); await stream.WriteAsync(okResponse, 0, okResponse.Length); } private void TogglePause() { PrimaryForm.Instance.Invoke(() => { if (VideoPlayerForm.Instance.isPaused) { VideoPlayerForm.Instance.Play(); PrimaryForm.Instance.pauseButton.Visible = true; PrimaryForm.Instance.playButton.Visible = false; PrimaryForm.Instance.syncPauseButton.Visible = true; PrimaryForm.Instance.syncPlayButton.Visible = false; } else { VideoPlayerForm.Instance.Pause(); PrimaryForm.Instance.pauseButton.Visible = false; PrimaryForm.Instance.playButton.Visible = true; PrimaryForm.Instance.syncPauseButton.Visible = false; PrimaryForm.Instance.syncPlayButton.Visible = true; } }); } private void ToggleMute() { PrimaryForm.Instance.Invoke(() => { if (PrimaryForm.Instance.videoPlayerForm.isMuted) { PrimaryForm.Instance.videoPlayerForm.Mute(false); PrimaryForm.Instance.videoPlayerForm.isMuted = false; OverlayForm.MainForm.HideMuteLabel(); } else { PrimaryForm.Instance.videoPlayerForm.Mute(true); PrimaryForm.Instance.videoPlayerForm.isMuted = true; OverlayForm.MainForm.ShowMuteLabel(); } }); } private void SendSerialCommand(byte[] prefix, byte[]? repeat = null, int repeatCount = 1) { if (SerialPortManager.mySerialPort == null || !SerialPortManager.mySerialPort.IsOpen) { Console.WriteLine("[SerialPort] Port not open."); return; } SerialPortManager.mySerialPort.Write(prefix, 0, prefix.Length); if (repeat != null) { for (int i = 0; i < repeatCount; i++) SerialPortManager.mySerialPort.Write(repeat, 0, repeat.Length); } } private Color GetColorFromString(string colorName) => colorName switch { "白色" => Color.White, "紅色" => Color.Red, "綠色" => Color.LightGreen, "黑色" => Color.Black, "藍色" => Color.LightBlue, _ => Color.Black }; private string GetHostNameSuffix() { string hostName = Dns.GetHostName(); return hostName.Length > 3 ? hostName[^3..] : hostName; } // ================================ // 表單安全呼叫 // ================================ private bool IsFormReady(Form form) { if (form == null) return false; try { return form.IsHandleCreated && !form.IsDisposed; } catch { return false; } } private async Task SafeInvoke(Form form, Action action, int maxRetries = 10) { if (form == null) return; for (int i = 0; i < maxRetries; i++) { try { if (IsFormReady(form)) { if (form.InvokeRequired) form.Invoke(action); else action(); return; } } catch (Exception ex) { Console.WriteLine($"Invoke attempt {i + 1} failed: {ex.Message}"); } await Task.Delay(500); } Console.WriteLine("Failed to invoke action after maximum retries"); } } }