created centralized UDP Listener
This commit is contained in:
@ -2,9 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
public class UDPAvatarReceiver : MonoBehaviour
|
||||
@ -38,8 +35,6 @@ public class UDPAvatarReceiver : MonoBehaviour
|
||||
[SerializeField] private bool showDebugInfo = false;
|
||||
[SerializeField] private bool logReceivedPackets = false;
|
||||
|
||||
private UdpClient udpClient;
|
||||
private Thread udpListenerThread;
|
||||
private bool threadRunning = false;
|
||||
private int listenPort;
|
||||
private Dictionary<string, Transform> boneCache;
|
||||
@ -205,122 +200,63 @@ public class UDPAvatarReceiver : MonoBehaviour
|
||||
{
|
||||
try
|
||||
{
|
||||
if (allowPortSharing)
|
||||
// Wait for shared listener to be ready
|
||||
if (Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance == null)
|
||||
{
|
||||
// Create UDP client with port reuse for local testing
|
||||
udpClient = new UdpClient();
|
||||
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||
udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, listenPort));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard UDP client binding
|
||||
udpClient = new UdpClient(listenPort);
|
||||
Debug.LogError("SharedUDPListener not found! Make sure it's in the scene.");
|
||||
enableReceiver = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Subscribe to shared listener
|
||||
Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance.OnPacketReceived += HandlePacketReceived;
|
||||
threadRunning = true;
|
||||
udpListenerThread = new Thread(new ThreadStart(UDPListenerThread));
|
||||
udpListenerThread.IsBackground = true;
|
||||
udpListenerThread.Start();
|
||||
|
||||
if (showDebugInfo)
|
||||
Debug.Log($"UDP Avatar Receiver started on port {listenPort} (filtering binary avatar data only, Port sharing: {allowPortSharing})");
|
||||
Debug.Log($"UDP Avatar Receiver subscribed to shared listener on port {listenPort} (filtering avatar magic 0x{AVATAR_MAGIC:X})");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (allowPortSharing)
|
||||
Debug.LogError($"Failed to subscribe to shared UDP listener: {e.Message}");
|
||||
enableReceiver = false;
|
||||
}
|
||||
}
|
||||
|
||||
void HandlePacketReceived(byte[] data, IPEndPoint senderEndPoint)
|
||||
{
|
||||
// Check if this is avatar data (by magic number)
|
||||
if (!IsAvatarData(data)) return;
|
||||
|
||||
// Process avatar packet
|
||||
CompactAvatarData avatarData = DeserializeCompactData(data);
|
||||
|
||||
// Check if this is from the target player (0 means accept from any player)
|
||||
if (targetPlayerID == 0 || avatarData.playerID == targetPlayerID)
|
||||
{
|
||||
// Check for packet loss
|
||||
if (avatarData.sequenceNumber > lastSequenceNumber + 1)
|
||||
{
|
||||
Debug.LogWarning($"Failed to start UDP listener with port sharing: {e.Message}");
|
||||
Debug.LogWarning("Trying with different port...");
|
||||
TryAlternativePort();
|
||||
packetsDropped += (int)(avatarData.sequenceNumber - lastSequenceNumber - 1);
|
||||
}
|
||||
else
|
||||
|
||||
lastSequenceNumber = avatarData.sequenceNumber;
|
||||
packetsReceived++;
|
||||
|
||||
// Store the new data (thread-safe)
|
||||
lock (dataLock)
|
||||
{
|
||||
Debug.LogError($"Failed to start UDP listener: {e.Message}");
|
||||
enableReceiver = false;
|
||||
lastReceivedData = avatarData;
|
||||
hasNewData = true;
|
||||
}
|
||||
|
||||
if (logReceivedPackets && packetsReceived % 30 == 0)
|
||||
{
|
||||
string modeStr = avatarData.isFullDataMode ? "FULL" : "OPT";
|
||||
Debug.Log($"Received {modeStr} packet #{avatarData.sequenceNumber} from player {avatarData.playerID}, size: {data.Length} bytes, bones: {avatarData.bones?.Length ?? 0}, blend shapes: {avatarData.blendShapes?.Length ?? 0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TryAlternativePort()
|
||||
{
|
||||
// Try a few alternative ports for local testing
|
||||
int[] alternativePorts = { 8081, 8082, 8083, 8084, 8085 };
|
||||
|
||||
foreach (int port in alternativePorts)
|
||||
{
|
||||
try
|
||||
{
|
||||
udpClient = new UdpClient(port);
|
||||
threadRunning = true;
|
||||
udpListenerThread = new Thread(new ThreadStart(UDPListenerThread));
|
||||
udpListenerThread.IsBackground = true;
|
||||
udpListenerThread.Start();
|
||||
|
||||
Debug.Log($"UDP listener started on alternative port {port}");
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Try next port
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.LogError("Failed to start UDP listener on any available port");
|
||||
enableReceiver = false;
|
||||
}
|
||||
|
||||
void UDPListenerThread()
|
||||
{
|
||||
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
|
||||
|
||||
while (threadRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] data = udpClient.Receive(ref remoteEndPoint);
|
||||
|
||||
if (data.Length > 0 && IsAvatarData(data))
|
||||
{
|
||||
CompactAvatarData avatarData = DeserializeCompactData(data);
|
||||
|
||||
// Check if this is from the target player (0 means accept from any player)
|
||||
if (targetPlayerID == 0 || avatarData.playerID == targetPlayerID)
|
||||
{
|
||||
// Check for packet loss
|
||||
if (avatarData.sequenceNumber > lastSequenceNumber + 1)
|
||||
{
|
||||
packetsDropped += (int)(avatarData.sequenceNumber - lastSequenceNumber - 1);
|
||||
}
|
||||
|
||||
lastSequenceNumber = avatarData.sequenceNumber;
|
||||
packetsReceived++;
|
||||
|
||||
// Store the new data (thread-safe)
|
||||
lock (dataLock)
|
||||
{
|
||||
lastReceivedData = avatarData;
|
||||
hasNewData = true;
|
||||
}
|
||||
|
||||
if (logReceivedPackets && packetsReceived % 30 == 0)
|
||||
{
|
||||
string modeStr = avatarData.isFullDataMode ? "FULL" : "OPT";
|
||||
Debug.Log($"Received {modeStr} packet #{avatarData.sequenceNumber} from player {avatarData.playerID}, size: {data.Length} bytes, bones: {avatarData.bones?.Length ?? 0}, blend shapes: {avatarData.blendShapes?.Length ?? 0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (threadRunning) // Only log errors if we're supposed to be running
|
||||
{
|
||||
Debug.LogError($"UDP receive error: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsAvatarData(byte[] data)
|
||||
{
|
||||
@ -618,17 +554,10 @@ public class UDPAvatarReceiver : MonoBehaviour
|
||||
{
|
||||
threadRunning = false;
|
||||
|
||||
if (udpClient != null)
|
||||
// Unsubscribe from shared listener
|
||||
if (Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance != null)
|
||||
{
|
||||
udpClient.Close();
|
||||
udpClient.Dispose();
|
||||
udpClient = null;
|
||||
}
|
||||
|
||||
if (udpListenerThread != null)
|
||||
{
|
||||
udpListenerThread.Join(1000); // Wait up to 1 second for thread to finish
|
||||
udpListenerThread = null;
|
||||
Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance.OnPacketReceived -= HandlePacketReceived;
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,9 +617,9 @@ public class UDPAvatarReceiver : MonoBehaviour
|
||||
|
||||
int GetActualListenPort()
|
||||
{
|
||||
if (udpClient?.Client?.LocalEndPoint != null)
|
||||
if (Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance != null)
|
||||
{
|
||||
return ((IPEndPoint)udpClient.Client.LocalEndPoint).Port;
|
||||
return Convai.Scripts.Runtime.Multiplayer.SharedUDPListener.Instance.ListenPort;
|
||||
}
|
||||
return listenPort;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user