superstar_v2/TCPServer.cs

389 lines
14 KiB
C#
Raw Normal View History

using System;
2025-04-07 16:54:10 +08:00
using System.Net;
using System.Net.Sockets;
2025-04-07 16:54:10 +08:00
using System.Text;
using System.Text.RegularExpressions;
using ActionString = System.Action<string>; // 现在可以在代码中使用 ActionString
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Drawing;
2025-04-07 16:54:10 +08:00
using DBObj;
using OverlayFormObj;
2025-04-07 16:54:10 +08:00
namespace DualScreenDemo
{
public class TCPServer
{
private TcpListener listener;
private const int Port = 1000;
2025-04-07 16:54:10 +08:00
private readonly string hostNameSuffix;
private readonly Room _room;
public static event ActionString OnDisplayBarrage;
public static event ActionString OnDisplaySticker;
2025-04-07 16:54:10 +08:00
public TCPServer(Room room)
2025-04-07 16:54:10 +08:00
{
_room = room;
2025-04-07 16:54:10 +08:00
listener = new TcpListener(IPAddress.Any, Port);
hostNameSuffix = GetHostNameSuffix();
_ = Task.Run(() => Start());
2025-04-07 16:54:10 +08:00
}
// ================================
// 啟動 TCP Server
// ================================
public void Start()
2025-04-07 16:54:10 +08:00
{
listener.Start();
Console.WriteLine($"Server started on port {Port}.");
_ = Task.Run(async () =>
2025-04-07 16:54:10 +08:00
{
try
2025-04-07 16:54:10 +08:00
{
while (true)
2025-04-07 16:54:10 +08:00
{
Console.WriteLine("Waiting for connections...");
TcpClient client = await listener.AcceptTcpClientAsync();
Console.WriteLine("Client connected!");
_ = Task.Run(() => HandleClient(client));
2025-04-07 16:54:10 +08:00
}
}
catch (Exception ex)
{
Console.WriteLine($"[Server Error] {ex.Message}");
}
finally
{
listener.Stop();
}
});
2025-04-07 16:54:10 +08:00
}
// ================================
// 處理 Client 連線
// ================================
private async Task HandleClient(TcpClient client)
2025-04-07 16:54:10 +08:00
{
using (client)
2025-04-07 16:54:10 +08:00
{
try
{
NetworkStream stream = client.GetStream();
byte[] buffer = new byte[1024];
while (client.Connected)
2025-04-07 16:54:10 +08:00
{
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);
// 先判斷是否是 Chat 指令 (格式帶有 ",")
if (request.Contains(","))
await HandleChatCommand(request, stream);
else if (request.Length >= 5)
2025-04-07 16:54:10 +08:00
{
string requestHostSuffix = request.Substring(0, 3);
string command = request.Substring(4);
if (requestHostSuffix.Equals(hostNameSuffix, StringComparison.OrdinalIgnoreCase))
await HandleRoomCommand(command, stream);
else
await HandleOverlayCommand(request, stream);
2025-04-07 16:54:10 +08:00
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[Client Error] {ex.Message}");
2025-04-07 16:54:10 +08:00
}
}
Console.WriteLine("Client disconnected.");
}
// ================================
// 房間控制指令
// ================================
private async Task HandleRoomCommand(string command, NetworkStream stream)
{
switch (command.ToLowerInvariant())
{
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;
2025-04-07 16:54:10 +08:00
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;
default:
Console.WriteLine($"[Unknown Command] {command}");
break;
2025-04-07 16:54:10 +08:00
}
await SendOkResponse(stream);
2025-04-07 16:54:10 +08:00
}
// ================================
// 跑馬燈/公告指令
// ================================
private async Task HandleOverlayCommand(string message, NetworkStream stream)
2025-04-07 16:54:10 +08:00
{
if (!IsFormReady(OverlayForm.MainForm)) return;
Match match = Regex.Match(message, @"^(全部|\d{4})\((白色|紅色|綠色|黑色|藍色)\)-");
_ = SafeInvoke(OverlayForm.MainForm, () =>
{
if (match.Success)
2025-04-07 16:54:10 +08:00
{
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("系統公告: ");
}
});
2025-04-07 16:54:10 +08:00
await SendOkResponse(stream);
}
2025-04-07 16:54:10 +08:00
// ================================
// Chat 指令 (send-sticker, message)
// ================================
private async Task HandleChatCommand(string message, NetworkStream stream)
{
try
{
string[] parts = message.Split(',', 2); // 只切一次
if (parts.Length < 2)
{
Console.WriteLine($"[ChatCommand Error] 格式錯誤: {message}");
return;
}
2025-04-07 16:54:10 +08:00
string cmd = parts[0].Trim().ToLowerInvariant();
string content = parts[1].Trim();
2025-04-07 16:54:10 +08:00
switch (cmd)
{
case "send-sticker":
_ = SafeInvoke(OverlayForm.MainForm, () => OnDisplaySticker?.Invoke(content));
break;
2025-04-07 16:54:10 +08:00
case "message":
_ = SafeInvoke(OverlayForm.MainForm, () => OnDisplayBarrage?.Invoke(content) );
break;
2025-04-07 16:54:10 +08:00
default:
Console.WriteLine($"[Unknown ChatCommand] {cmd}");
break;
2025-04-07 16:54:10 +08:00
}
await SendOkResponse(stream);
2025-04-07 16:54:10 +08:00
}
catch (Exception ex)
2025-04-07 16:54:10 +08:00
{
Console.WriteLine($"[ChatCommand Error] {ex.Message}");
2025-04-07 16:54:10 +08:00
}
}
// ================================
// 輔助方法
// ================================
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(() =>
2025-04-07 16:54:10 +08:00
{
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;
}
});
2025-04-07 16:54:10 +08:00
}
private void ToggleMute()
2025-04-07 16:54:10 +08:00
{
PrimaryForm.Instance.Invoke(() =>
2025-04-07 16:54:10 +08:00
{
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);
2025-04-07 16:54:10 +08:00
}
}
private Color GetColorFromString(string colorName) => colorName switch
{
"白色" => Color.White,
"紅色" => Color.Red,
"綠色" => Color.LightGreen,
"黑色" => Color.Black,
"藍色" => Color.LightBlue,
_ => Color.Black
};
2025-04-07 16:54:10 +08:00
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");
2025-04-07 16:54:10 +08:00
}
}
}