added peer discovery settings to NetworkConfig and updated UDP broadcasting scripts to handle peer discovery events
This commit is contained in:
@ -88,6 +88,14 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
{
|
{
|
||||||
targetIP = cfg.ipAddress;
|
targetIP = cfg.ipAddress;
|
||||||
targetPort = cfg.port;
|
targetPort = cfg.port;
|
||||||
|
|
||||||
|
// Subscribe to peer discovery if enabled
|
||||||
|
if (cfg.useAutoDiscovery && UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerDiscovered += HandlePeerDiscovered;
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerLost += HandlePeerLost;
|
||||||
|
ConvaiLogger.Info("Audio sender subscribed to peer discovery", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -130,6 +138,13 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
|
// Unsubscribe from peer discovery
|
||||||
|
if (UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerDiscovered -= HandlePeerDiscovered;
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerLost -= HandlePeerLost;
|
||||||
|
}
|
||||||
|
|
||||||
StopRecording();
|
StopRecording();
|
||||||
_cancellationTokenSource?.Cancel();
|
_cancellationTokenSource?.Cancel();
|
||||||
_cancellationTokenSource?.Dispose();
|
_cancellationTokenSource?.Dispose();
|
||||||
@ -143,6 +158,24 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
_udpClient?.Close();
|
_udpClient?.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandlePeerDiscovered(string peerIP)
|
||||||
|
{
|
||||||
|
targetIP = peerIP;
|
||||||
|
_targetEndPoint = new IPEndPoint(IPAddress.Parse(peerIP), targetPort);
|
||||||
|
ConvaiLogger.Info($"🎤 Audio sender now targeting peer at {peerIP}:{targetPort}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerLost()
|
||||||
|
{
|
||||||
|
var cfg = NetworkConfig.Instance;
|
||||||
|
if (cfg != null)
|
||||||
|
{
|
||||||
|
targetIP = cfg.fallbackBroadcastIP;
|
||||||
|
_targetEndPoint = new IPEndPoint(IPAddress.Parse(targetIP), targetPort);
|
||||||
|
ConvaiLogger.Warn($"🎤 Audio sender falling back to broadcast: {targetIP}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeNetwork()
|
private void InitializeNetwork()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@ -59,6 +59,14 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
{
|
{
|
||||||
targetIP = cfg.ipAddress;
|
targetIP = cfg.ipAddress;
|
||||||
targetPort = cfg.port;
|
targetPort = cfg.port;
|
||||||
|
|
||||||
|
// Subscribe to peer discovery if enabled
|
||||||
|
if (cfg.useAutoDiscovery && UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerDiscovered += HandlePeerDiscovered;
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerLost += HandlePeerLost;
|
||||||
|
ConvaiLogger.Info("Speech sender subscribed to peer discovery", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -73,10 +81,35 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
{
|
{
|
||||||
|
// Unsubscribe from peer discovery
|
||||||
|
if (UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerDiscovered -= HandlePeerDiscovered;
|
||||||
|
UDPPeerDiscovery.Instance.OnPeerLost -= HandlePeerLost;
|
||||||
|
}
|
||||||
|
|
||||||
CleanupNPCSubscriptions();
|
CleanupNPCSubscriptions();
|
||||||
CleanupNetwork();
|
CleanupNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandlePeerDiscovered(string peerIP)
|
||||||
|
{
|
||||||
|
targetIP = peerIP;
|
||||||
|
_targetEndPoint = new IPEndPoint(IPAddress.Parse(peerIP), targetPort);
|
||||||
|
ConvaiLogger.Info($"🔊 Speech sender now targeting peer at {peerIP}:{targetPort}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerLost()
|
||||||
|
{
|
||||||
|
var cfg = NetworkConfig.Instance;
|
||||||
|
if (cfg != null)
|
||||||
|
{
|
||||||
|
targetIP = cfg.fallbackBroadcastIP;
|
||||||
|
_targetEndPoint = new IPEndPoint(IPAddress.Parse(targetIP), targetPort);
|
||||||
|
ConvaiLogger.Warn($"🔊 Speech sender falling back to broadcast: {targetIP}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void InitializeNetwork()
|
private void InitializeNetwork()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
460
Unity-Master/Assets/Scripts/Multiplayer/UDPPeerDiscovery.cs
Normal file
460
Unity-Master/Assets/Scripts/Multiplayer/UDPPeerDiscovery.cs
Normal file
@ -0,0 +1,460 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Convai.Scripts.Runtime.LoggerSystem;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace Convai.Scripts.Runtime.Multiplayer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// UDP Peer Discovery - Finds other Quest Pro on the same hotspot network
|
||||||
|
/// Solves AP isolation problem by discovering peer IP and switching to direct unicast
|
||||||
|
/// </summary>
|
||||||
|
public class UDPPeerDiscovery : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Player Configuration")]
|
||||||
|
[SerializeField] private byte localPlayerID = 1; // 1 or 2 - must be unique per Quest Pro
|
||||||
|
[SerializeField] private bool enableDebugLogging = true;
|
||||||
|
|
||||||
|
[Header("Discovery Settings")]
|
||||||
|
[SerializeField] private float discoveryBroadcastInterval = 2.0f; // Broadcast every 2 seconds
|
||||||
|
[SerializeField] private float heartbeatInterval = 5.0f; // Heartbeat every 5 seconds
|
||||||
|
[SerializeField] private float peerTimeoutSeconds = 15.0f; // Consider peer lost after 15s
|
||||||
|
|
||||||
|
// Events
|
||||||
|
public event Action<string> OnPeerDiscovered; // Fires when peer IP is found
|
||||||
|
public event Action OnPeerLost; // Fires when peer connection is lost
|
||||||
|
public event Action<ConnectionState> OnConnectionStateChanged;
|
||||||
|
|
||||||
|
// Network components
|
||||||
|
private UdpClient _udpClient;
|
||||||
|
private bool _isRunning = false;
|
||||||
|
private int _listenPort;
|
||||||
|
private CancellationTokenSource _cancellationTokenSource;
|
||||||
|
|
||||||
|
// Peer state
|
||||||
|
private string _peerIP = "";
|
||||||
|
private byte _peerPlayerID = 0;
|
||||||
|
private DateTime _lastPeerPacketTime;
|
||||||
|
private ConnectionState _connectionState = ConnectionState.Disconnected;
|
||||||
|
|
||||||
|
// Discovery protocol constants
|
||||||
|
private const uint DISCOVERY_MAGIC = 0x44495343; // "DISC" in hex
|
||||||
|
private const byte PACKET_TYPE_REQUEST = 0x01;
|
||||||
|
private const byte PACKET_TYPE_RESPONSE = 0x02;
|
||||||
|
private const byte PACKET_TYPE_HEARTBEAT = 0x03;
|
||||||
|
|
||||||
|
// Singleton for easy access
|
||||||
|
private static UDPPeerDiscovery _instance;
|
||||||
|
public static UDPPeerDiscovery Instance => _instance;
|
||||||
|
|
||||||
|
public enum ConnectionState
|
||||||
|
{
|
||||||
|
Disconnected,
|
||||||
|
Discovering,
|
||||||
|
Connected,
|
||||||
|
Lost
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public properties
|
||||||
|
public string PeerIP => _peerIP;
|
||||||
|
public byte LocalPlayerID => localPlayerID;
|
||||||
|
public byte PeerPlayerID => _peerPlayerID;
|
||||||
|
public ConnectionState CurrentState => _connectionState;
|
||||||
|
public bool IsConnected => _connectionState == ConnectionState.Connected;
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
|
{
|
||||||
|
// Singleton pattern
|
||||||
|
if (_instance != null && _instance != this)
|
||||||
|
{
|
||||||
|
Destroy(gameObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
// Get network config
|
||||||
|
var cfg = NetworkConfig.Instance;
|
||||||
|
if (cfg != null)
|
||||||
|
{
|
||||||
|
_listenPort = cfg.port;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error("NetworkConfig not found! Using default port 1221", ConvaiLogger.LogCategory.Character);
|
||||||
|
_listenPort = 1221;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
StartDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
StopDiscovery();
|
||||||
|
_cancellationTokenSource?.Cancel();
|
||||||
|
_cancellationTokenSource?.Dispose();
|
||||||
|
|
||||||
|
if (_instance == this)
|
||||||
|
_instance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
// Check for peer timeout
|
||||||
|
if (_connectionState == ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
TimeSpan timeSinceLastPacket = DateTime.UtcNow - _lastPeerPacketTime;
|
||||||
|
if (timeSinceLastPacket.TotalSeconds > peerTimeoutSeconds)
|
||||||
|
{
|
||||||
|
HandlePeerLost();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartDiscovery()
|
||||||
|
{
|
||||||
|
if (_isRunning) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create UDP client with port reuse for shared port
|
||||||
|
_udpClient = new UdpClient();
|
||||||
|
_udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
|
||||||
|
_udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, _listenPort));
|
||||||
|
_udpClient.EnableBroadcast = true;
|
||||||
|
|
||||||
|
_isRunning = true;
|
||||||
|
SetConnectionState(ConnectionState.Discovering);
|
||||||
|
|
||||||
|
ConvaiLogger.Info($"🔍 Peer Discovery started - Player {localPlayerID} on port {_listenPort}", ConvaiLogger.LogCategory.Character);
|
||||||
|
|
||||||
|
// Start listening for discovery packets
|
||||||
|
_ = ListenForDiscoveryPackets(_cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
// Start broadcasting discovery requests
|
||||||
|
_ = BroadcastDiscoveryRequests(_cancellationTokenSource.Token);
|
||||||
|
|
||||||
|
// Start heartbeat when connected
|
||||||
|
_ = SendHeartbeats(_cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Failed to start peer discovery: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopDiscovery()
|
||||||
|
{
|
||||||
|
if (!_isRunning) return;
|
||||||
|
|
||||||
|
_isRunning = false;
|
||||||
|
_udpClient?.Close();
|
||||||
|
_udpClient?.Dispose();
|
||||||
|
_udpClient = null;
|
||||||
|
|
||||||
|
SetConnectionState(ConnectionState.Disconnected);
|
||||||
|
ConvaiLogger.Info("Peer Discovery stopped", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ListenForDiscoveryPackets(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (_isRunning && !cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await _udpClient.ReceiveAsync();
|
||||||
|
await ProcessDiscoveryPacket(result.Buffer, result.RemoteEndPoint);
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// Normal when stopping
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
if (_isRunning)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Error receiving discovery packet: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task BroadcastDiscoveryRequests(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (_isRunning && !cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Only broadcast if we're still discovering
|
||||||
|
if (_connectionState == ConnectionState.Discovering || _connectionState == ConnectionState.Lost)
|
||||||
|
{
|
||||||
|
await SendDiscoveryRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay((int)(discoveryBroadcastInterval * 1000), cancellationToken);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Error broadcasting discovery: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendHeartbeats(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
while (_isRunning && !cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay((int)(heartbeatInterval * 1000), cancellationToken);
|
||||||
|
|
||||||
|
// Only send heartbeat if connected
|
||||||
|
if (_connectionState == ConnectionState.Connected && !string.IsNullOrEmpty(_peerIP))
|
||||||
|
{
|
||||||
|
await SendHeartbeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Error sending heartbeat: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendDiscoveryRequest()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] packet = CreateDiscoveryPacket(PACKET_TYPE_REQUEST);
|
||||||
|
|
||||||
|
// Broadcast to subnet (will be blocked by AP isolation but we try anyway)
|
||||||
|
var broadcastEndPoint = new IPEndPoint(IPAddress.Broadcast, _listenPort);
|
||||||
|
await _udpClient.SendAsync(packet, packet.Length, broadcastEndPoint);
|
||||||
|
|
||||||
|
if (enableDebugLogging && UnityEngine.Random.value < 0.1f) // Log 10% of broadcasts to reduce spam
|
||||||
|
{
|
||||||
|
ConvaiLogger.DebugLog($"🔍 Broadcasting discovery request (Player {localPlayerID})", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Failed to send discovery request: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendDiscoveryResponse(IPEndPoint targetEndPoint)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
byte[] packet = CreateDiscoveryPacket(PACKET_TYPE_RESPONSE);
|
||||||
|
await _udpClient.SendAsync(packet, packet.Length, targetEndPoint);
|
||||||
|
|
||||||
|
if (enableDebugLogging)
|
||||||
|
{
|
||||||
|
ConvaiLogger.DebugLog($"📤 Sent discovery response to {targetEndPoint.Address}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Failed to send discovery response: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SendHeartbeat()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_peerIP)) return;
|
||||||
|
|
||||||
|
byte[] packet = CreateDiscoveryPacket(PACKET_TYPE_HEARTBEAT);
|
||||||
|
var peerEndPoint = new IPEndPoint(IPAddress.Parse(_peerIP), _listenPort);
|
||||||
|
await _udpClient.SendAsync(packet, packet.Length, peerEndPoint);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Warn($"Failed to send heartbeat: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] CreateDiscoveryPacket(byte packetType)
|
||||||
|
{
|
||||||
|
byte[] packet = new byte[14];
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
// Magic number
|
||||||
|
BitConverter.GetBytes(DISCOVERY_MAGIC).CopyTo(packet, offset);
|
||||||
|
offset += 4;
|
||||||
|
|
||||||
|
// Packet type
|
||||||
|
packet[offset] = packetType;
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
// Player ID
|
||||||
|
packet[offset] = localPlayerID;
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
// Timestamp
|
||||||
|
long timestamp = DateTime.UtcNow.Ticks;
|
||||||
|
BitConverter.GetBytes(timestamp).CopyTo(packet, offset);
|
||||||
|
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessDiscoveryPacket(byte[] data, IPEndPoint senderEndPoint)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Check if this is a discovery packet
|
||||||
|
if (data.Length < 14) return;
|
||||||
|
|
||||||
|
// Verify magic number
|
||||||
|
uint magic = BitConverter.ToUInt32(data, 0);
|
||||||
|
if (magic != DISCOVERY_MAGIC) return;
|
||||||
|
|
||||||
|
// Parse packet
|
||||||
|
byte packetType = data[4];
|
||||||
|
byte senderPlayerID = data[5];
|
||||||
|
long timestamp = BitConverter.ToInt64(data, 6);
|
||||||
|
|
||||||
|
// Ignore packets from ourselves
|
||||||
|
if (senderPlayerID == localPlayerID) return;
|
||||||
|
|
||||||
|
string senderIP = senderEndPoint.Address.ToString();
|
||||||
|
|
||||||
|
if (enableDebugLogging)
|
||||||
|
{
|
||||||
|
string typeStr = packetType switch
|
||||||
|
{
|
||||||
|
PACKET_TYPE_REQUEST => "REQUEST",
|
||||||
|
PACKET_TYPE_RESPONSE => "RESPONSE",
|
||||||
|
PACKET_TYPE_HEARTBEAT => "HEARTBEAT",
|
||||||
|
_ => "UNKNOWN"
|
||||||
|
};
|
||||||
|
ConvaiLogger.DebugLog($"📥 Received {typeStr} from Player {senderPlayerID} at {senderIP}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update peer info
|
||||||
|
_lastPeerPacketTime = DateTime.UtcNow;
|
||||||
|
|
||||||
|
switch (packetType)
|
||||||
|
{
|
||||||
|
case PACKET_TYPE_REQUEST:
|
||||||
|
// Respond to discovery request
|
||||||
|
await SendDiscoveryResponse(senderEndPoint);
|
||||||
|
|
||||||
|
// If we weren't connected before, this is a new peer
|
||||||
|
if (_connectionState != ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
HandlePeerDiscovered(senderIP, senderPlayerID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PACKET_TYPE_RESPONSE:
|
||||||
|
// Discovery response received - we found our peer!
|
||||||
|
if (_connectionState != ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
HandlePeerDiscovered(senderIP, senderPlayerID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PACKET_TYPE_HEARTBEAT:
|
||||||
|
// Heartbeat keeps connection alive
|
||||||
|
if (_connectionState == ConnectionState.Connected && _peerIP == senderIP)
|
||||||
|
{
|
||||||
|
// Connection still alive
|
||||||
|
}
|
||||||
|
else if (_connectionState != ConnectionState.Connected)
|
||||||
|
{
|
||||||
|
// Reconnected
|
||||||
|
HandlePeerDiscovered(senderIP, senderPlayerID);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Error($"Error processing discovery packet: {ex.Message}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerDiscovered(string peerIP, byte peerPlayerID)
|
||||||
|
{
|
||||||
|
_peerIP = peerIP;
|
||||||
|
_peerPlayerID = peerPlayerID;
|
||||||
|
_lastPeerPacketTime = DateTime.UtcNow;
|
||||||
|
|
||||||
|
SetConnectionState(ConnectionState.Connected);
|
||||||
|
|
||||||
|
ConvaiLogger.Info($"✅ Peer discovered! Player {peerPlayerID} at {peerIP}", ConvaiLogger.LogCategory.Character);
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
OnPeerDiscovered?.Invoke(peerIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerLost()
|
||||||
|
{
|
||||||
|
ConvaiLogger.Warn($"⚠️ Peer connection lost (Player {_peerPlayerID} at {_peerIP})", ConvaiLogger.LogCategory.Character);
|
||||||
|
|
||||||
|
string lostPeerIP = _peerIP;
|
||||||
|
_peerIP = "";
|
||||||
|
_peerPlayerID = 0;
|
||||||
|
|
||||||
|
SetConnectionState(ConnectionState.Lost);
|
||||||
|
|
||||||
|
// Notify listeners
|
||||||
|
OnPeerLost?.Invoke();
|
||||||
|
|
||||||
|
// Restart discovery
|
||||||
|
SetConnectionState(ConnectionState.Discovering);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetConnectionState(ConnectionState newState)
|
||||||
|
{
|
||||||
|
if (_connectionState != newState)
|
||||||
|
{
|
||||||
|
_connectionState = newState;
|
||||||
|
OnConnectionStateChanged?.Invoke(newState);
|
||||||
|
|
||||||
|
if (enableDebugLogging)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"🔄 Connection state changed to: {newState}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods for manual control
|
||||||
|
public void RestartDiscovery()
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info("Manually restarting peer discovery", ConvaiLogger.LogCategory.Character);
|
||||||
|
_peerIP = "";
|
||||||
|
_peerPlayerID = 0;
|
||||||
|
SetConnectionState(ConnectionState.Discovering);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowStatus()
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"=== Peer Discovery Status ===", ConvaiLogger.LogCategory.Character);
|
||||||
|
ConvaiLogger.Info($"Local Player ID: {localPlayerID}", ConvaiLogger.LogCategory.Character);
|
||||||
|
ConvaiLogger.Info($"Connection State: {_connectionState}", ConvaiLogger.LogCategory.Character);
|
||||||
|
ConvaiLogger.Info($"Peer IP: {(_peerIP ?? "None")}", ConvaiLogger.LogCategory.Character);
|
||||||
|
ConvaiLogger.Info($"Peer Player ID: {_peerPlayerID}", ConvaiLogger.LogCategory.Character);
|
||||||
|
ConvaiLogger.Info($"Listen Port: {_listenPort}", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a8b9c1d2e3f4a5b6c7d8e9f0a1b2c3d4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
BIN
Unity-Master/Assets/Scripts/NetworkConfig.asset
(Stored with Git LFS)
BIN
Unity-Master/Assets/Scripts/NetworkConfig.asset
(Stored with Git LFS)
Binary file not shown.
@ -6,6 +6,19 @@ public class NetworkConfig : ScriptableObject
|
|||||||
[Header("Global Network Settings")]
|
[Header("Global Network Settings")]
|
||||||
public string ipAddress = "127.0.0.1";
|
public string ipAddress = "127.0.0.1";
|
||||||
public int port = 1221; // Single port for all UDP communication
|
public int port = 1221; // Single port for all UDP communication
|
||||||
|
|
||||||
|
[Header("Peer Discovery Settings")]
|
||||||
|
[Tooltip("Enable auto-discovery to find peer Quest Pro on same network")]
|
||||||
|
public bool useAutoDiscovery = true;
|
||||||
|
|
||||||
|
[Tooltip("Fallback broadcast IP if discovery fails (default: 255.255.255.255)")]
|
||||||
|
public string fallbackBroadcastIP = "255.255.255.255";
|
||||||
|
|
||||||
|
[Tooltip("How often to broadcast discovery requests (seconds)")]
|
||||||
|
public float discoveryIntervalSeconds = 2.0f;
|
||||||
|
|
||||||
|
[Tooltip("Time before considering peer lost (seconds)")]
|
||||||
|
public float peerTimeoutSeconds = 15.0f;
|
||||||
|
|
||||||
private static NetworkConfig _instance;
|
private static NetworkConfig _instance;
|
||||||
public static NetworkConfig Instance
|
public static NetworkConfig Instance
|
||||||
|
|||||||
@ -92,6 +92,14 @@ public class UDPAvatarBroadcaster : MonoBehaviour
|
|||||||
{
|
{
|
||||||
broadcastAddress = cfg.ipAddress;
|
broadcastAddress = cfg.ipAddress;
|
||||||
broadcastPort = cfg.port;
|
broadcastPort = cfg.port;
|
||||||
|
|
||||||
|
// Subscribe to peer discovery if enabled
|
||||||
|
if (cfg.useAutoDiscovery && Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerDiscovered += HandlePeerDiscovered;
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerLost += HandlePeerLost;
|
||||||
|
Debug.Log("Avatar broadcaster subscribed to peer discovery");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -444,6 +452,13 @@ public class UDPAvatarBroadcaster : MonoBehaviour
|
|||||||
|
|
||||||
void OnDestroy()
|
void OnDestroy()
|
||||||
{
|
{
|
||||||
|
// Unsubscribe from peer discovery
|
||||||
|
if (Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerDiscovered -= HandlePeerDiscovered;
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerLost -= HandlePeerLost;
|
||||||
|
}
|
||||||
|
|
||||||
if (udpClient != null)
|
if (udpClient != null)
|
||||||
{
|
{
|
||||||
udpClient.Close();
|
udpClient.Close();
|
||||||
@ -451,6 +466,24 @@ public class UDPAvatarBroadcaster : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandlePeerDiscovered(string peerIP)
|
||||||
|
{
|
||||||
|
broadcastAddress = peerIP;
|
||||||
|
broadcastEndPoint = new IPEndPoint(IPAddress.Parse(peerIP), broadcastPort);
|
||||||
|
Debug.Log($"👤 Avatar broadcaster now targeting peer at {peerIP}:{broadcastPort}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerLost()
|
||||||
|
{
|
||||||
|
var cfg = NetworkConfig.Instance;
|
||||||
|
if (cfg != null)
|
||||||
|
{
|
||||||
|
broadcastAddress = cfg.fallbackBroadcastIP;
|
||||||
|
broadcastEndPoint = new IPEndPoint(IPAddress.Parse(broadcastAddress), broadcastPort);
|
||||||
|
Debug.LogWarning($"👤 Avatar broadcaster falling back to broadcast: {broadcastAddress}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OnApplicationPause(bool pauseStatus)
|
void OnApplicationPause(bool pauseStatus)
|
||||||
{
|
{
|
||||||
enableBroadcast = !pauseStatus;
|
enableBroadcast = !pauseStatus;
|
||||||
|
|||||||
@ -95,6 +95,14 @@ public class UDPAvatarBroadcasterAgent : MonoBehaviour
|
|||||||
{
|
{
|
||||||
broadcastAddress = cfg.ipAddress;
|
broadcastAddress = cfg.ipAddress;
|
||||||
broadcastPort = cfg.port;
|
broadcastPort = cfg.port;
|
||||||
|
|
||||||
|
// Subscribe to peer discovery if enabled
|
||||||
|
if (cfg.useAutoDiscovery && Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerDiscovered += HandlePeerDiscovered;
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerLost += HandlePeerLost;
|
||||||
|
Debug.Log("Avatar broadcaster agent subscribed to peer discovery");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -447,6 +455,13 @@ public class UDPAvatarBroadcasterAgent : MonoBehaviour
|
|||||||
|
|
||||||
void OnDestroy()
|
void OnDestroy()
|
||||||
{
|
{
|
||||||
|
// Unsubscribe from peer discovery
|
||||||
|
if (Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance != null)
|
||||||
|
{
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerDiscovered -= HandlePeerDiscovered;
|
||||||
|
Convai.Scripts.Runtime.Multiplayer.UDPPeerDiscovery.Instance.OnPeerLost -= HandlePeerLost;
|
||||||
|
}
|
||||||
|
|
||||||
if (udpClient != null)
|
if (udpClient != null)
|
||||||
{
|
{
|
||||||
udpClient.Close();
|
udpClient.Close();
|
||||||
@ -454,6 +469,24 @@ public class UDPAvatarBroadcasterAgent : MonoBehaviour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandlePeerDiscovered(string peerIP)
|
||||||
|
{
|
||||||
|
broadcastAddress = peerIP;
|
||||||
|
broadcastEndPoint = new IPEndPoint(IPAddress.Parse(peerIP), broadcastPort);
|
||||||
|
Debug.Log($"👤 Avatar broadcaster agent now targeting peer at {peerIP}:{broadcastPort}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePeerLost()
|
||||||
|
{
|
||||||
|
var cfg = NetworkConfig.Instance;
|
||||||
|
if (cfg != null)
|
||||||
|
{
|
||||||
|
broadcastAddress = cfg.fallbackBroadcastIP;
|
||||||
|
broadcastEndPoint = new IPEndPoint(IPAddress.Parse(broadcastAddress), broadcastPort);
|
||||||
|
Debug.LogWarning($"👤 Avatar broadcaster agent falling back to broadcast: {broadcastAddress}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OnApplicationPause(bool pauseStatus)
|
void OnApplicationPause(bool pauseStatus)
|
||||||
{
|
{
|
||||||
enableBroadcast = !pauseStatus;
|
enableBroadcast = !pauseStatus;
|
||||||
|
|||||||
Reference in New Issue
Block a user