AnimalSimulation/Assets/Scripts/DisplayServer.cs

692 lines
22 KiB
C#
Raw Permalink Blame History

using UnityEngine;
using UnityEngine.UI;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Collections.Generic;
using DG.Tweening;
public class DisplayServer : MonoBehaviour
{
private TcpListener listener;
private Thread listenThread;
private CancellationTokenSource listenCts = new CancellationTokenSource();
private volatile bool isRunning = true;
private NetworkStream clientStream;
public Transform background;
[System.Serializable]
public class PageModel
{
public string pageName;
public GameObject model;
public Vector3 originalPosition;
public List<GameObject> singleItemModels;
}
public List<PageModel> pageModels = new List<PageModel>();
private readonly Queue<string> messageQueue = new Queue<string>();
private readonly object queueLock = new object();
private string currentPage = "";
private string currentSingleItemModel = "";
private Dictionary<string, PageModel> pageModelCache;
private Dictionary<string, Transform> textPanelCache;
private Dictionary<string, Slider> sliderCache;
private bool isPlayingAnimation = false;
private bool isClientDragging = false;
private float lastSendTime = 0f;
private float lastSentProgress = 0f;
[SerializeField] private int tcpPort = 8888;
[SerializeField] private float sendInterval = 0.05f;
[SerializeField] private int resolutionWidth = 3328;
[SerializeField] private int resolutionHeight = 1352;
public Sprite textBackgroundSprite;
public Sprite noTextBackgroundSprite;
[SerializeField] private float textHiddenOffset = 33f;
void Start()
{
Screen.SetResolution(resolutionWidth, resolutionHeight, false);
pageModelCache = new Dictionary<string, PageModel>();
textPanelCache = new Dictionary<string, Transform>();
sliderCache = new Dictionary<string, Slider>();
foreach (var pm in pageModels)
{
if (!string.IsNullOrEmpty(pm.pageName) && pm.model != null)
{
pm.originalPosition = pm.model.transform.localPosition;
pageModelCache[pm.pageName] = pm;
if (pm.singleItemModels == null)
pm.singleItemModels = new List<GameObject>();
}
}
CacheUIComponents();
if (background == null)
{
return;
}
Image backgroundImage = background.GetComponent<Image>();
if (backgroundImage == null)
{
return;
}
try
{
listener = new TcpListener(IPAddress.Any, tcpPort);
listener.Start();
}
catch
{
return;
}
listenThread = new Thread(() => ListenForClients(listenCts.Token));
listenThread.Start();
ShowPage("<22><>ҳ");
SendHasAnimationStatus();
}
private void CacheUIComponents()
{
foreach (Transform child in background)
{
Transform textPanel = FindDeep(child, "TextPanel");
if (textPanel != null)
{
textPanelCache[child.name] = textPanel;
}
Slider slider = child.GetComponentInChildren<Slider>();
if (slider != null)
{
sliderCache[child.name] = slider;
}
}
}
private void ListenForClients(CancellationToken token)
{
int retryDelayMs = 2000;
while (!token.IsCancellationRequested && isRunning)
{
try
{
TcpClient client = listener.AcceptTcpClient();
clientStream = client.GetStream();
Thread clientThread = new Thread(() => HandleClientComm(client, token));
clientThread.Start();
retryDelayMs = 2000;
}
catch
{
Thread.Sleep(retryDelayMs);
retryDelayMs = Mathf.Min(retryDelayMs * 2, 10000);
}
}
}
private void HandleClientComm(TcpClient tcpClient, CancellationToken token)
{
NetworkStream stream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (!token.IsCancellationRequested && isRunning)
{
try
{
bytesRead = stream.Read(message, 0, 4096);
if (bytesRead == 0) break;
string data = Encoding.UTF8.GetString(message, 0, bytesRead);
lock (queueLock)
{
messageQueue.Enqueue(data);
}
}
catch
{
break;
}
}
stream.Close();
tcpClient.Close();
clientStream = null;
}
private void Update()
{
string msg = null;
lock (queueLock)
{
if (messageQueue.Count > 0)
{
msg = messageQueue.Dequeue();
}
}
if (!string.IsNullOrEmpty(msg))
{
if (msg.StartsWith("Page:"))
{
string pageName = msg.Replace("Page:", "");
ShowPage(pageName);
isPlayingAnimation = false;
isClientDragging = false;
lastSentProgress = 0f;
SendHasAnimationStatus();
}
else if (msg.StartsWith("Model:"))
{
string modelName = msg.Replace("Model:", "");
PlayModelAnimation(modelName);
lastSentProgress = 0f;
}
else if (msg.StartsWith("SingleItemModel:"))
{
string modelName = msg.Replace("SingleItemModel:", "");
ShowSingleItemModel(modelName);
isPlayingAnimation = false;
isClientDragging = false;
lastSentProgress = 0f;
SendHasAnimationStatus();
}
else if (msg.StartsWith("Progress:"))
{
if (float.TryParse(msg.Replace("Progress:", ""), out float progress))
{
SetAnimationProgress(progress);
isPlayingAnimation = false;
isClientDragging = true;
lastSentProgress = progress;
}
}
else if (msg == "EndDrag")
{
isClientDragging = false;
}
else if (msg.StartsWith("TextPanel:"))
{
string command = msg.Replace("TextPanel:", "");
SetTextPanelVisibility(command == "Show");
}
else if (msg.StartsWith("Background:"))
{
string command = msg.Replace("Background:", "");
SetBackgroundSprite(command == "Text");
}
else if (msg.StartsWith("ModelMove:"))
{
string command = msg.Replace("ModelMove:", "");
if (command == "Reset")
{
ResetModelPosition();
}
else if (float.TryParse(command, out float offset))
{
MoveModel(offset);
}
}
else if (msg == "RequestHasAnimation")
{
SendHasAnimationStatus();
}
}
if (isPlayingAnimation && !isClientDragging && clientStream != null && clientStream.CanWrite)
{
if (Time.time - lastSendTime >= sendInterval)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
if (!string.IsNullOrEmpty(currentSingleItemModel))
{
GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel);
if (singleItemModel != null)
{
mc = singleItemModel.GetComponent<ModelController>();
}
}
else if (pm.model != null)
{
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null && mc.IsPlayingAnimation())
{
float progress = mc.GetAnimationProgress();
if (progress > 0f && Mathf.Abs(progress - lastSentProgress) > 0.005f)
{
try
{
string progressMsg = $"Progress:{progress:F2}";
byte[] data = Encoding.UTF8.GetBytes(progressMsg);
clientStream.Write(data, 0, data.Length);
lastSentProgress = progress;
lastSendTime = Time.time;
}
catch
{
clientStream = null;
}
}
}
else
{
isPlayingAnimation = false;
}
}
}
}
}
private void SendHasAnimationStatus()
{
if (clientStream == null || !clientStream.CanWrite) return;
bool hasAnimation = false;
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
if (!string.IsNullOrEmpty(currentSingleItemModel))
{
GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel);
if (singleItemModel != null)
{
mc = singleItemModel.GetComponent<ModelController>();
}
}
else if (pm.model != null)
{
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null)
{
hasAnimation = mc.GetHasAnimation();
}
}
try
{
string msg = $"HasAnimation:{(hasAnimation ? "True" : "False")}";
byte[] data = Encoding.UTF8.GetBytes(msg);
clientStream.Write(data, 0, data.Length);
}
catch
{
clientStream = null;
}
}
private void ShowPage(string pageName)
{
if (background == null)
{
return;
}
foreach (Transform child in background)
{
bool isActive = child.name == pageName;
child.gameObject.SetActive(isActive);
}
currentPage = pageName;
currentSingleItemModel = "";
foreach (var pageModel in pageModels)
{
bool isActive = pageModel.pageName == pageName;
if (pageModel.model != null)
{
pageModel.model.SetActive(isActive && string.IsNullOrEmpty(currentSingleItemModel));
pageModel.originalPosition = pageModel.model.transform.localPosition;
ModelController mc = pageModel.model.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
if (isActive)
{
mc.OnAnimationFinished += OnModelAnimationFinished;
}
mc.ResetAnimation();
mc.ResetModelPosition();
}
}
foreach (var singleItemModel in pageModel.singleItemModels)
{
if (singleItemModel != null)
{
singleItemModel.SetActive(false);
ModelController mc = singleItemModel.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
mc.ResetAnimation();
mc.ResetModelPosition();
}
}
}
}
SetTextPanelVisibility(pageName != "<22><>ҳ");
SetBackgroundSprite(pageName != "<22><>ҳ");
}
private void ShowSingleItemModel(string modelName)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
if (pm.model != null)
{
pm.model.SetActive(false);
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
mc.ResetAnimation();
}
}
bool modelFound = false;
foreach (var singleItemModel in pm.singleItemModels)
{
if (singleItemModel != null)
{
bool isActive = singleItemModel.name == modelName;
singleItemModel.SetActive(isActive);
ModelController mc = singleItemModel.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
if (isActive)
{
mc.OnAnimationFinished += OnModelAnimationFinished;
mc.ResetAnimation();
mc.ResetModelPosition();
currentSingleItemModel = modelName;
modelFound = true;
}
}
else if (isActive)
{
currentSingleItemModel = modelName;
modelFound = true;
}
}
}
SetTextPanelVisibility(currentPage != "<22><>ҳ");
SetBackgroundSprite(currentPage != "<22><>ҳ");
}
}
private void PlayModelAnimation(string modelName)
{
if (string.IsNullOrEmpty(modelName))
{
return;
}
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
GameObject targetModel = null;
if (!string.IsNullOrEmpty(currentSingleItemModel) && modelName == currentSingleItemModel)
{
targetModel = pm.singleItemModels.Find(m => m != null && m.name == modelName);
if (targetModel != null)
{
mc = targetModel.GetComponent<ModelController>();
}
}
else if (pm.model != null && pm.model.name == modelName)
{
targetModel = pm.model;
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null)
{
Animator animator = mc.GetComponent<Animator>();
if (animator != null && animator.runtimeAnimatorController != null && mc.GetHasAnimation())
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
mc.OnAnimationFinished += OnModelAnimationFinished;
mc.PlayAnimation();
isPlayingAnimation = true;
}
else
{
isPlayingAnimation = false;
}
}
else
{
isPlayingAnimation = false;
}
}
else
{
isPlayingAnimation = false;
}
}
private void SetAnimationProgress(float progress)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
if (!string.IsNullOrEmpty(currentSingleItemModel))
{
GameObject singleItemModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel);
if (singleItemModel != null)
{
mc = singleItemModel.GetComponent<ModelController>();
}
}
else if (pm.model != null)
{
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null)
{
mc.SetAnimationProgress(progress);
}
}
}
private void MoveModel(float xOffset)
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
GameObject targetModel = null;
if (!string.IsNullOrEmpty(currentSingleItemModel))
{
targetModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel);
if (targetModel != null)
{
mc = targetModel.GetComponent<ModelController>();
}
}
else if (pm.model != null)
{
targetModel = pm.model;
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null && targetModel.activeSelf)
{
mc.MoveModel(xOffset);
}
else if (targetModel != null && targetModel.activeSelf)
{
Vector3 originalPosition = pm.originalPosition;
Vector3 targetPosition = originalPosition + new Vector3(xOffset, 0f, 0f);
targetModel.transform.DOLocalMoveX(targetPosition.x, 1f).SetEase(Ease.InOutSine);
}
}
}
private void ResetModelPosition()
{
if (pageModelCache.TryGetValue(currentPage, out PageModel pm) && pm != null)
{
ModelController mc = null;
GameObject targetModel = null;
if (!string.IsNullOrEmpty(currentSingleItemModel))
{
targetModel = pm.singleItemModels.Find(m => m != null && m.name == currentSingleItemModel);
if (targetModel != null)
{
mc = targetModel.GetComponent<ModelController>();
}
}
else if (pm.model != null)
{
targetModel = pm.model;
mc = pm.model.GetComponent<ModelController>();
}
if (mc != null && targetModel.activeSelf)
{
mc.ResetModelPosition();
}
else if (targetModel != null && targetModel.activeSelf)
{
Vector3 originalPosition = pm.originalPosition;
targetModel.transform.DOLocalMoveX(originalPosition.x, 1f).SetEase(Ease.InOutSine);
}
}
}
private void SetTextPanelVisibility(bool isVisible)
{
if (background == null || string.IsNullOrEmpty(currentPage) || currentPage == "<22><>ҳ")
{
return;
}
if (textPanelCache.TryGetValue(currentPage, out Transform textPanel))
{
textPanel.gameObject.SetActive(isVisible);
if (isVisible)
{
ResetModelPosition();
}
else
{
MoveModel(textHiddenOffset);
}
}
}
private void SetBackgroundSprite(bool hasText)
{
if (background == null)
{
return;
}
Image backgroundImage = background.GetComponent<Image>();
if (backgroundImage != null)
{
Sprite newSprite = hasText ? textBackgroundSprite : noTextBackgroundSprite;
if (newSprite != null)
{
backgroundImage.sprite = newSprite;
}
}
}
private Transform FindDeep(Transform parent, string name)
{
Transform result = parent.Find(name);
if (result != null)
return result;
foreach (Transform child in parent)
{
result = FindDeep(child, name);
if (result != null)
return result;
}
return null;
}
private void OnModelAnimationFinished()
{
isPlayingAnimation = false;
if (clientStream != null && clientStream.CanWrite)
{
try
{
string progressMsg = "Progress:1.00";
byte[] data = Encoding.UTF8.GetBytes(progressMsg);
clientStream.Write(data, 0, data.Length);
lastSentProgress = 1f;
lastSendTime = Time.time;
}
catch
{
clientStream = null;
}
}
}
private void OnApplicationQuit()
{
isRunning = false;
listenCts.Cancel();
listener?.Stop();
clientStream?.Close();
foreach (var pm in pageModels)
{
if (pm.model != null)
{
ModelController mc = pm.model.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
}
}
foreach (var singleItemModel in pm.singleItemModels)
{
if (singleItemModel != null)
{
ModelController mc = singleItemModel.GetComponent<ModelController>();
if (mc != null)
{
mc.OnAnimationFinished -= OnModelAnimationFinished;
}
}
}
}
if (listenThread != null && listenThread.IsAlive)
{
listenThread.Join(1000);
}
}
private void OnDestroy()
{
OnApplicationQuit();
}
}