enhanced UDP audio sender/receiver scripts with metrics for packet tracking and improved NPC assignment logic
This commit is contained in:
BIN
Unity-Master/Assets/Resources/OVROverlayCanvasSettings.asset
(Stored with Git LFS)
Normal file
BIN
Unity-Master/Assets/Resources/OVROverlayCanvasSettings.asset
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 44c8d4b7ac986c847a36bd9a8d84f4b6
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 11400000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
BIN
Unity-Master/Assets/Scenes/VR_Player1.unity
(Stored with Git LFS)
BIN
Unity-Master/Assets/Scenes/VR_Player1.unity
(Stored with Git LFS)
Binary file not shown.
BIN
Unity-Master/Assets/Scenes/VR_Player2.unity
(Stored with Git LFS)
BIN
Unity-Master/Assets/Scenes/VR_Player2.unity
(Stored with Git LFS)
Binary file not shown.
@ -28,6 +28,14 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
// Events
|
// Events
|
||||||
public Action<bool> OnAudioReceiving;
|
public Action<bool> OnAudioReceiving;
|
||||||
|
|
||||||
|
// Metrics for debug UI
|
||||||
|
private int _totalPacketsReceived = 0;
|
||||||
|
private DateTime _lastPacketReceivedTime;
|
||||||
|
public int TotalPacketsReceived => _totalPacketsReceived;
|
||||||
|
public float TimeSinceLastReceive => _lastPacketReceivedTime != default ?
|
||||||
|
(float)(DateTime.UtcNow - _lastPacketReceivedTime).TotalSeconds : -1f;
|
||||||
|
public int ListenPort => listenPort;
|
||||||
|
|
||||||
// Network components
|
// Network components
|
||||||
private UdpClient _udpListener;
|
private UdpClient _udpListener;
|
||||||
private IPEndPoint _remoteEndPoint;
|
private IPEndPoint _remoteEndPoint;
|
||||||
@ -107,6 +115,17 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
}
|
}
|
||||||
StartListening();
|
StartListening();
|
||||||
|
|
||||||
|
// Immediately try to assign an enabled NPC
|
||||||
|
if (useActiveNPC && targetNPC == null)
|
||||||
|
{
|
||||||
|
var currentActiveNPC = FindEnabledConvaiNPC();
|
||||||
|
if (currentActiveNPC != null)
|
||||||
|
{
|
||||||
|
targetNPC = currentActiveNPC;
|
||||||
|
ConvaiLogger.Info($"🔄 UDP Audio Receiver assigned target NPC on enable: {targetNPC.characterName} (on {targetNPC.gameObject.name})", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestroy()
|
private void OnDestroy()
|
||||||
@ -135,6 +154,47 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
{
|
{
|
||||||
StopTalkingSimulation();
|
StopTalkingSimulation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Continuously update target NPC if using active NPC mode
|
||||||
|
if (useActiveNPC)
|
||||||
|
{
|
||||||
|
var currentActiveNPC = FindEnabledConvaiNPC();
|
||||||
|
|
||||||
|
// Update whenever the active NPC changes (including null → NPC or NPC → different NPC)
|
||||||
|
if (currentActiveNPC != targetNPC)
|
||||||
|
{
|
||||||
|
targetNPC = currentActiveNPC;
|
||||||
|
|
||||||
|
if (targetNPC != null)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"🔄 UDP Audio Receiver updated target NPC to: {targetNPC.characterName} (on {targetNPC.gameObject.name})", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"🔄 UDP Audio Receiver cleared target NPC", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an enabled ConvaiNPC in the scene (doesn't rely on ConvaiNPCManager raycasting)
|
||||||
|
/// </summary>
|
||||||
|
private ConvaiNPC FindEnabledConvaiNPC()
|
||||||
|
{
|
||||||
|
// Find all ConvaiNPC components in the scene (including inactive GameObjects)
|
||||||
|
var allNPCs = FindObjectsOfType<ConvaiNPC>(true);
|
||||||
|
|
||||||
|
// Return the first one that's on an active GameObject
|
||||||
|
foreach (var npc in allNPCs)
|
||||||
|
{
|
||||||
|
if (npc.gameObject.activeInHierarchy && npc.enabled)
|
||||||
|
{
|
||||||
|
return npc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeNetwork()
|
private void InitializeNetwork()
|
||||||
@ -151,10 +211,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
private void InitializeConvai()
|
private void InitializeConvai()
|
||||||
{
|
{
|
||||||
// Get target NPC
|
// Get target NPC by finding enabled NPCs in the scene
|
||||||
if (useActiveNPC)
|
if (useActiveNPC)
|
||||||
{
|
{
|
||||||
targetNPC = ConvaiNPCManager.Instance?.GetActiveConvaiNPC();
|
targetNPC = FindEnabledConvaiNPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetNPC == null)
|
if (targetNPC == null)
|
||||||
@ -163,7 +223,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConvaiLogger.Info($"UDP Audio Receiver V2 initialized with NPC: {targetNPC.characterName}", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info($"UDP Audio Receiver V2 initialized with NPC: {targetNPC.characterName} (on {targetNPC.gameObject.name})", ConvaiLogger.LogCategory.Character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,6 +298,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
var packet = packetData.Value;
|
var packet = packetData.Value;
|
||||||
_lastPacketTime = Time.time;
|
_lastPacketTime = Time.time;
|
||||||
|
|
||||||
|
// Update metrics
|
||||||
|
_totalPacketsReceived++;
|
||||||
|
_lastPacketReceivedTime = DateTime.UtcNow;
|
||||||
|
|
||||||
if (enableDebugLogging)
|
if (enableDebugLogging)
|
||||||
{
|
{
|
||||||
if (packet.isEndSignal)
|
if (packet.isEndSignal)
|
||||||
@ -299,10 +363,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
if (_isReceivingAudio) return;
|
if (_isReceivingAudio) return;
|
||||||
|
|
||||||
MainThreadDispatcher.Instance.RunOnMainThread(() => {
|
MainThreadDispatcher.Instance.RunOnMainThread(() => {
|
||||||
// Update target NPC if using active NPC
|
// Update target NPC by finding enabled NPCs in the scene
|
||||||
if (useActiveNPC)
|
if (useActiveNPC)
|
||||||
{
|
{
|
||||||
targetNPC = ConvaiNPCManager.Instance?.GetActiveConvaiNPC();
|
targetNPC = FindEnabledConvaiNPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetNPC == null)
|
if (targetNPC == null)
|
||||||
@ -324,7 +388,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
// This is the KEY! Simulate a talk key press to trigger normal Convai flow
|
// This is the KEY! Simulate a talk key press to trigger normal Convai flow
|
||||||
ConvaiInputManager.Instance.talkKeyInteract?.Invoke(true);
|
ConvaiInputManager.Instance.talkKeyInteract?.Invoke(true);
|
||||||
|
|
||||||
ConvaiLogger.Info($"🎤 Started talking simulation for {targetNPC.characterName} (remote player audio)", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info($"🎤 Started talking simulation for {targetNPC.characterName} (on {targetNPC.gameObject.name}) (remote player audio)", ConvaiLogger.LogCategory.Character);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -70,6 +70,16 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
public event Action<bool> OnRecordingStateChanged;
|
public event Action<bool> OnRecordingStateChanged;
|
||||||
|
|
||||||
|
// Metrics for debug UI
|
||||||
|
private int _totalPacketsSent = 0;
|
||||||
|
private DateTime _lastPacketSentTime;
|
||||||
|
public int TotalPacketsSent => _totalPacketsSent;
|
||||||
|
public float TimeSinceLastSend => _lastPacketSentTime != default ?
|
||||||
|
(float)(DateTime.UtcNow - _lastPacketSentTime).TotalSeconds : -1f;
|
||||||
|
public string CurrentTargetIP => targetIP;
|
||||||
|
public int CurrentTargetPort => targetPort;
|
||||||
|
public bool UsingDiscovery => NetworkConfig.Instance?.useAutoDiscovery ?? false;
|
||||||
|
|
||||||
[Header("Recording Storage")]
|
[Header("Recording Storage")]
|
||||||
[SerializeField] private bool saveLocalAudio = true;
|
[SerializeField] private bool saveLocalAudio = true;
|
||||||
[SerializeField] private int localSampleRate = 16000;
|
[SerializeField] private int localSampleRate = 16000;
|
||||||
@ -609,6 +619,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
// Send the packet
|
// Send the packet
|
||||||
await _udpClient.SendAsync(packet, packet.Length, _targetEndPoint);
|
await _udpClient.SendAsync(packet, packet.Length, _targetEndPoint);
|
||||||
|
|
||||||
|
// Update metrics
|
||||||
|
_totalPacketsSent++;
|
||||||
|
_lastPacketSentTime = DateTime.UtcNow;
|
||||||
|
|
||||||
if (enableDebugLogging && _packetSequence % 10 == 0) // Log every 10th packet
|
if (enableDebugLogging && _packetSequence % 10 == 0) // Log every 10th packet
|
||||||
{
|
{
|
||||||
ConvaiLogger.DebugLog($"Sent packet {_packetSequence} with {currentChunkSamples} samples", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.DebugLog($"Sent packet {_packetSequence} with {currentChunkSamples} samples", ConvaiLogger.LogCategory.Character);
|
||||||
|
|||||||
@ -54,6 +54,14 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
public Action<string> OnTranscriptReceived;
|
public Action<string> OnTranscriptReceived;
|
||||||
public Action<AudioClip> OnAudioClipReceived;
|
public Action<AudioClip> OnAudioClipReceived;
|
||||||
|
|
||||||
|
// Metrics for debug UI
|
||||||
|
private int _totalClipsReceived = 0;
|
||||||
|
private DateTime _lastClipReceivedTime;
|
||||||
|
public int TotalClipsReceived => _totalClipsReceived;
|
||||||
|
public float TimeSinceLastReceive => _lastClipReceivedTime != default ?
|
||||||
|
(float)(DateTime.UtcNow - _lastClipReceivedTime).TotalSeconds : -1f;
|
||||||
|
public int ListenPort => listenPort;
|
||||||
|
|
||||||
// Data structures
|
// Data structures
|
||||||
private struct SpeechPacket
|
private struct SpeechPacket
|
||||||
{
|
{
|
||||||
@ -446,6 +454,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
OnAudioClipReceived?.Invoke(clip);
|
OnAudioClipReceived?.Invoke(clip);
|
||||||
|
|
||||||
|
// Update metrics
|
||||||
|
_totalClipsReceived++;
|
||||||
|
_lastClipReceivedTime = DateTime.UtcNow;
|
||||||
|
|
||||||
if (enableDebugLogging)
|
if (enableDebugLogging)
|
||||||
ConvaiLogger.DebugLog($"✅ Reconstructed audio clip {sequence}: {clip.length:F2}s, '{incomingClip.transcript}'", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.DebugLog($"✅ Reconstructed audio clip {sequence}: {clip.length:F2}s, '{incomingClip.transcript}'", ConvaiLogger.LogCategory.Character);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,6 +51,16 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
public Action<bool> OnSpeechTransmission;
|
public Action<bool> OnSpeechTransmission;
|
||||||
public Action<string> OnSpeechSent;
|
public Action<string> OnSpeechSent;
|
||||||
|
|
||||||
|
// Metrics for debug UI
|
||||||
|
private int _totalClipsSent = 0;
|
||||||
|
private DateTime _lastClipSentTime;
|
||||||
|
public int TotalClipsSent => _totalClipsSent;
|
||||||
|
public float TimeSinceLastSend => _lastClipSentTime != default ?
|
||||||
|
(float)(DateTime.UtcNow - _lastClipSentTime).TotalSeconds : -1f;
|
||||||
|
public string CurrentTargetIP => targetIP;
|
||||||
|
public int CurrentTargetPort => targetPort;
|
||||||
|
public bool UsingDiscovery => NetworkConfig.Instance?.useAutoDiscovery ?? false;
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
// Get network config from global instance
|
// Get network config from global instance
|
||||||
@ -92,6 +102,53 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
CleanupNetwork();
|
CleanupNetwork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
// Continuously update source NPC if using active NPC mode
|
||||||
|
if (useActiveNPC)
|
||||||
|
{
|
||||||
|
var currentActiveNPC = FindEnabledConvaiNPC();
|
||||||
|
if (currentActiveNPC != sourceNPC)
|
||||||
|
{
|
||||||
|
// Cleanup old subscriptions
|
||||||
|
CleanupNPCSubscriptions();
|
||||||
|
|
||||||
|
// Update to new NPC
|
||||||
|
sourceNPC = currentActiveNPC;
|
||||||
|
SubscribeToNPCEvents();
|
||||||
|
|
||||||
|
if (sourceNPC != null)
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"🔄 UDP Speech Sender updated source NPC to: {sourceNPC.characterName} (on {sourceNPC.gameObject.name})", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConvaiLogger.Info($"🔄 UDP Speech Sender cleared source NPC", ConvaiLogger.LogCategory.Character);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds an enabled ConvaiNPC in the scene (doesn't rely on ConvaiNPCManager raycasting)
|
||||||
|
/// </summary>
|
||||||
|
private ConvaiNPC FindEnabledConvaiNPC()
|
||||||
|
{
|
||||||
|
// Find all ConvaiNPC components in the scene (including inactive GameObjects)
|
||||||
|
var allNPCs = FindObjectsOfType<ConvaiNPC>(true);
|
||||||
|
|
||||||
|
// Return the first one that's on an active GameObject
|
||||||
|
foreach (var npc in allNPCs)
|
||||||
|
{
|
||||||
|
if (npc.gameObject.activeInHierarchy && npc.enabled)
|
||||||
|
{
|
||||||
|
return npc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void HandlePeerDiscovered(string peerIP)
|
private void HandlePeerDiscovered(string peerIP)
|
||||||
{
|
{
|
||||||
targetIP = peerIP;
|
targetIP = peerIP;
|
||||||
@ -128,7 +185,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
|
|
||||||
private void InitializeConvai()
|
private void InitializeConvai()
|
||||||
{
|
{
|
||||||
// Prefer local ConvaiNPC on the same GameObject, then fall back to active NPC
|
// Prefer local ConvaiNPC on the same GameObject, then fall back to finding enabled NPC
|
||||||
var localNPC = GetComponent<ConvaiNPC>();
|
var localNPC = GetComponent<ConvaiNPC>();
|
||||||
if (localNPC != null)
|
if (localNPC != null)
|
||||||
{
|
{
|
||||||
@ -136,7 +193,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
}
|
}
|
||||||
else if (useActiveNPC)
|
else if (useActiveNPC)
|
||||||
{
|
{
|
||||||
sourceNPC = ConvaiNPCManager.Instance?.GetActiveConvaiNPC();
|
sourceNPC = FindEnabledConvaiNPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
SubscribeToNPCEvents();
|
SubscribeToNPCEvents();
|
||||||
@ -259,6 +316,10 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
// Only increment sequence after the entire clip is sent
|
// Only increment sequence after the entire clip is sent
|
||||||
_speechSequence++;
|
_speechSequence++;
|
||||||
|
|
||||||
|
// Update metrics
|
||||||
|
_totalClipsSent++;
|
||||||
|
_lastClipSentTime = DateTime.UtcNow;
|
||||||
|
|
||||||
OnSpeechSent?.Invoke(transcript);
|
OnSpeechSent?.Invoke(transcript);
|
||||||
|
|
||||||
if (enableDebugLogging)
|
if (enableDebugLogging)
|
||||||
|
|||||||
541
Unity-Master/Assets/Scripts/Multiplayer/NetworkDebugUI.cs
Normal file
541
Unity-Master/Assets/Scripts/Multiplayer/NetworkDebugUI.cs
Normal file
@ -0,0 +1,541 @@
|
|||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
using Convai.Scripts.Runtime.Multiplayer;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace Convai.Scripts.Runtime.Multiplayer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// In-game debug UI for network diagnostics in VR builds
|
||||||
|
/// Shows peer discovery, voice/speech status, and packet counters
|
||||||
|
/// </summary>
|
||||||
|
public class NetworkDebugUI : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("UI Configuration")]
|
||||||
|
[SerializeField] private bool showOnStart = true;
|
||||||
|
[SerializeField] private KeyCode toggleKey = KeyCode.F1;
|
||||||
|
[SerializeField] private bool useVRInput = true;
|
||||||
|
|
||||||
|
[Header("UI Positioning (VR)")]
|
||||||
|
[SerializeField] private float distanceFromCamera = 2.5f;
|
||||||
|
[SerializeField] private float verticalOffset = 0.5f;
|
||||||
|
[SerializeField] private Vector3 rotationOffset = new Vector3(0, 0, 0);
|
||||||
|
|
||||||
|
[Header("Update Settings")]
|
||||||
|
[SerializeField] private float updateInterval = 0.5f; // Update UI twice per second
|
||||||
|
|
||||||
|
// UI Components
|
||||||
|
private Canvas _canvas;
|
||||||
|
private Text _debugText;
|
||||||
|
private GameObject _panel;
|
||||||
|
private Camera _mainCamera;
|
||||||
|
|
||||||
|
// Component references
|
||||||
|
private UDPPeerDiscovery _peerDiscovery;
|
||||||
|
private ConvaiSimpleUDPAudioSender _audioSender;
|
||||||
|
private ConvaiUDPSpeechSender _speechSender;
|
||||||
|
private ConvaiSimpleUDPAudioReceiver _audioReceiver;
|
||||||
|
private ConvaiUDPSpeechReceiver _speechReceiver;
|
||||||
|
|
||||||
|
// State
|
||||||
|
private bool _isVisible = true;
|
||||||
|
private float _lastUpdateTime;
|
||||||
|
private InputAction _toggleAction;
|
||||||
|
|
||||||
|
private void Start()
|
||||||
|
{
|
||||||
|
_mainCamera = Camera.main;
|
||||||
|
|
||||||
|
// Find components
|
||||||
|
FindNetworkComponents();
|
||||||
|
|
||||||
|
// Create UI
|
||||||
|
CreateDebugUI();
|
||||||
|
|
||||||
|
_isVisible = showOnStart;
|
||||||
|
_panel.SetActive(_isVisible);
|
||||||
|
|
||||||
|
// Setup VR input for toggle
|
||||||
|
if (useVRInput)
|
||||||
|
{
|
||||||
|
SetupVRToggleInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDebugInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FindNetworkComponents()
|
||||||
|
{
|
||||||
|
_peerDiscovery = UDPPeerDiscovery.Instance;
|
||||||
|
|
||||||
|
// Always re-scan for the best (most active) component
|
||||||
|
// This is important for components on NPCs that get enabled/disabled
|
||||||
|
_audioSender = FindBestComponent<ConvaiSimpleUDPAudioSender>();
|
||||||
|
_speechSender = FindBestComponent<ConvaiUDPSpeechSender>();
|
||||||
|
_audioReceiver = FindBestComponent<ConvaiSimpleUDPAudioReceiver>();
|
||||||
|
_speechReceiver = FindBestComponent<ConvaiUDPSpeechReceiver>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds a component, prioritizing enabled ones in active hierarchies
|
||||||
|
/// </summary>
|
||||||
|
private T FindBestComponent<T>() where T : MonoBehaviour
|
||||||
|
{
|
||||||
|
var allComponents = FindObjectsOfType<T>(true); // Include inactive
|
||||||
|
|
||||||
|
if (allComponents.Length == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Priority 1: Enabled component in active hierarchy
|
||||||
|
foreach (var comp in allComponents)
|
||||||
|
{
|
||||||
|
if (comp.enabled && comp.gameObject.activeInHierarchy)
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 2: Component on active GameObject (even if component disabled)
|
||||||
|
foreach (var comp in allComponents)
|
||||||
|
{
|
||||||
|
if (comp.gameObject.activeInHierarchy)
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Priority 3: Any component (even if GameObject is inactive)
|
||||||
|
return allComponents[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDebugUI()
|
||||||
|
{
|
||||||
|
// Create canvas
|
||||||
|
GameObject canvasObj = new GameObject("NetworkDebugCanvas");
|
||||||
|
canvasObj.transform.SetParent(transform);
|
||||||
|
|
||||||
|
_canvas = canvasObj.AddComponent<Canvas>();
|
||||||
|
_canvas.renderMode = RenderMode.WorldSpace;
|
||||||
|
|
||||||
|
CanvasScaler scaler = canvasObj.AddComponent<CanvasScaler>();
|
||||||
|
scaler.dynamicPixelsPerUnit = 10;
|
||||||
|
|
||||||
|
// Create panel background
|
||||||
|
_panel = new GameObject("DebugPanel");
|
||||||
|
_panel.transform.SetParent(canvasObj.transform, false);
|
||||||
|
|
||||||
|
Image panelImage = _panel.AddComponent<Image>();
|
||||||
|
panelImage.color = new Color(0, 0, 0, 0.85f);
|
||||||
|
|
||||||
|
RectTransform panelRect = _panel.GetComponent<RectTransform>();
|
||||||
|
panelRect.sizeDelta = new Vector2(800, 900);
|
||||||
|
|
||||||
|
// Create text
|
||||||
|
GameObject textObj = new GameObject("DebugText");
|
||||||
|
textObj.transform.SetParent(_panel.transform, false);
|
||||||
|
|
||||||
|
_debugText = textObj.AddComponent<Text>();
|
||||||
|
_debugText.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
|
||||||
|
_debugText.fontSize = 18;
|
||||||
|
_debugText.color = Color.white;
|
||||||
|
_debugText.alignment = TextAnchor.UpperLeft;
|
||||||
|
_debugText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||||
|
_debugText.verticalOverflow = VerticalWrapMode.Overflow;
|
||||||
|
|
||||||
|
RectTransform textRect = textObj.GetComponent<RectTransform>();
|
||||||
|
textRect.anchorMin = new Vector2(0, 0);
|
||||||
|
textRect.anchorMax = new Vector2(1, 1);
|
||||||
|
textRect.offsetMin = new Vector2(20, 20);
|
||||||
|
textRect.offsetMax = new Vector2(-20, -20);
|
||||||
|
|
||||||
|
// Setup canvas transform for VR
|
||||||
|
RectTransform canvasRect = _canvas.GetComponent<RectTransform>();
|
||||||
|
canvasRect.sizeDelta = new Vector2(800, 900);
|
||||||
|
canvasRect.localScale = Vector3.one * 0.001f; // Scale down for VR viewing
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupVRToggleInput()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create input action for Y/B button (left controller secondary button)
|
||||||
|
_toggleAction = new InputAction("ToggleDebug", InputActionType.Button);
|
||||||
|
|
||||||
|
// Bind to left controller secondary button (Y on Quest)
|
||||||
|
_toggleAction.AddBinding("<XRController>{LeftHand}/secondaryButton");
|
||||||
|
_toggleAction.AddBinding("<OculusTouchController>{LeftHand}/secondaryButton");
|
||||||
|
_toggleAction.AddBinding("<MetaTouchController>{LeftHand}/secondaryButton");
|
||||||
|
_toggleAction.AddBinding("<QuestProTouchController>{LeftHand}/secondaryButton");
|
||||||
|
|
||||||
|
// Also bind keyboard for editor testing
|
||||||
|
_toggleAction.AddBinding("<Keyboard>/f1");
|
||||||
|
|
||||||
|
_toggleAction.started += ctx => ToggleVisibility();
|
||||||
|
_toggleAction.Enable();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Failed to setup VR toggle input: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
// Handle keyboard toggle
|
||||||
|
if (Input.GetKeyDown(toggleKey))
|
||||||
|
{
|
||||||
|
ToggleVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update UI text periodically
|
||||||
|
if (Time.time - _lastUpdateTime >= updateInterval)
|
||||||
|
{
|
||||||
|
UpdateDebugInfo();
|
||||||
|
_lastUpdateTime = Time.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position UI in front of camera
|
||||||
|
if (_isVisible && _mainCamera != null)
|
||||||
|
{
|
||||||
|
PositionUIInFrontOfCamera();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PositionUIInFrontOfCamera()
|
||||||
|
{
|
||||||
|
Vector3 cameraForward = _mainCamera.transform.forward;
|
||||||
|
Vector3 cameraRight = _mainCamera.transform.right;
|
||||||
|
Vector3 cameraUp = _mainCamera.transform.up;
|
||||||
|
|
||||||
|
// Position in front of camera
|
||||||
|
Vector3 targetPosition = _mainCamera.transform.position +
|
||||||
|
cameraForward * distanceFromCamera +
|
||||||
|
cameraUp * verticalOffset;
|
||||||
|
|
||||||
|
_canvas.transform.position = targetPosition;
|
||||||
|
|
||||||
|
// Rotate to face camera
|
||||||
|
_canvas.transform.rotation = Quaternion.LookRotation(cameraForward, cameraUp);
|
||||||
|
_canvas.transform.Rotate(rotationOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleVisibility()
|
||||||
|
{
|
||||||
|
_isVisible = !_isVisible;
|
||||||
|
_panel.SetActive(_isVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDebugInfo()
|
||||||
|
{
|
||||||
|
if (_debugText == null) return;
|
||||||
|
|
||||||
|
// Re-check for components that might have been disabled at startup
|
||||||
|
FindNetworkComponents();
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
// Header
|
||||||
|
sb.AppendLine("═══ NETWORK DEBUG ═══");
|
||||||
|
sb.AppendLine($"Time: {DateTime.Now:HH:mm:ss}");
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Peer Discovery Status
|
||||||
|
AppendPeerDiscoveryStatus(sb);
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Audio Sender (Voice Input)
|
||||||
|
AppendAudioSenderStatus(sb);
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Speech Sender (NPC Response)
|
||||||
|
AppendSpeechSenderStatus(sb);
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Audio Receiver
|
||||||
|
AppendAudioReceiverStatus(sb);
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Speech Receiver
|
||||||
|
AppendSpeechReceiverStatus(sb);
|
||||||
|
sb.AppendLine();
|
||||||
|
|
||||||
|
// Event Log
|
||||||
|
AppendEventLog(sb);
|
||||||
|
|
||||||
|
_debugText.text = sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendPeerDiscoveryStatus(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("🔍 PEER DISCOVERY");
|
||||||
|
|
||||||
|
if (_peerDiscovery != null)
|
||||||
|
{
|
||||||
|
string stateColor = GetConnectionStateColor(_peerDiscovery.CurrentState);
|
||||||
|
string stateIcon = GetConnectionStateIcon(_peerDiscovery.CurrentState);
|
||||||
|
|
||||||
|
sb.AppendLine($"State: {_peerDiscovery.CurrentState} {stateIcon}");
|
||||||
|
sb.AppendLine($"Local Player ID: {_peerDiscovery.LocalPlayerID}");
|
||||||
|
sb.AppendLine($"Peer Player ID: {(_peerDiscovery.PeerPlayerID > 0 ? _peerDiscovery.PeerPlayerID.ToString() : "None")}");
|
||||||
|
sb.AppendLine($"Peer IP: {(_peerDiscovery.IsConnected ? _peerDiscovery.PeerIP : "None")}");
|
||||||
|
|
||||||
|
if (_peerDiscovery.IsConnected)
|
||||||
|
{
|
||||||
|
float timeSince = _peerDiscovery.TimeSinceLastPeerPacket;
|
||||||
|
sb.AppendLine($"Last Packet: {timeSince:F1}s ago");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("❌ NOT FOUND - Add UDPPeerDiscovery component!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendAudioSenderStatus(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("🎤 VOICE INPUT SENDER");
|
||||||
|
|
||||||
|
if (_audioSender != null && _audioSender.gameObject != null)
|
||||||
|
{
|
||||||
|
// Check if component's GameObject is active AND enabled in hierarchy
|
||||||
|
bool isActive = _audioSender.enabled && _audioSender.gameObject.activeInHierarchy;
|
||||||
|
|
||||||
|
if (!isActive)
|
||||||
|
{
|
||||||
|
sb.AppendLine("⏸️ FOUND BUT DISABLED");
|
||||||
|
sb.AppendLine($"(GameObject: {_audioSender.gameObject.name})");
|
||||||
|
sb.AppendLine($"(Active: {_audioSender.gameObject.activeSelf}, InHierarchy: {_audioSender.gameObject.activeInHierarchy})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"GameObject: {_audioSender.gameObject.name} ✅");
|
||||||
|
sb.AppendLine($"Target: {_audioSender.CurrentTargetIP}:{_audioSender.CurrentTargetPort}");
|
||||||
|
sb.AppendLine($"Recording: {(_audioSender.IsRecording ? "YES ✅" : "NO")}");
|
||||||
|
sb.AppendLine($"Packets Sent: {_audioSender.TotalPacketsSent}");
|
||||||
|
|
||||||
|
float timeSince = _audioSender.TimeSinceLastSend;
|
||||||
|
if (timeSince >= 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Send: {timeSince:F1}s ago");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Send: Never");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"Using Discovery: {(_audioSender.UsingDiscovery ? "YES" : "NO")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("❌ NOT FOUND IN SCENE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendSpeechSenderStatus(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("🔊 SPEECH SENDER");
|
||||||
|
|
||||||
|
if (_speechSender != null && _speechSender.gameObject != null)
|
||||||
|
{
|
||||||
|
// Check if component's GameObject is active AND enabled in hierarchy
|
||||||
|
bool isActive = _speechSender.enabled && _speechSender.gameObject.activeInHierarchy;
|
||||||
|
|
||||||
|
if (!isActive)
|
||||||
|
{
|
||||||
|
sb.AppendLine("⏸️ FOUND BUT DISABLED");
|
||||||
|
sb.AppendLine($"(GameObject: {_speechSender.gameObject.name})");
|
||||||
|
sb.AppendLine($"(Active: {_speechSender.gameObject.activeSelf}, InHierarchy: {_speechSender.gameObject.activeInHierarchy})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"GameObject: {_speechSender.gameObject.name} ✅");
|
||||||
|
sb.AppendLine($"Target: {_speechSender.CurrentTargetIP}:{_speechSender.CurrentTargetPort}");
|
||||||
|
|
||||||
|
// Show source NPC
|
||||||
|
var sourceNPC = _speechSender.SourceNPC;
|
||||||
|
if (sourceNPC != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Source NPC: {sourceNPC.characterName} ✅");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Source NPC: None ⚠️");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"Transmitting: {(_speechSender.IsSendingSpeech ? "YES ✅" : "NO")}");
|
||||||
|
sb.AppendLine($"Clips Sent: {_speechSender.TotalClipsSent}");
|
||||||
|
|
||||||
|
float timeSince = _speechSender.TimeSinceLastSend;
|
||||||
|
if (timeSince >= 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Send: {timeSince:F1}s ago");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Send: Never");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"Using Discovery: {(_speechSender.UsingDiscovery ? "YES" : "NO")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("❌ NOT FOUND IN SCENE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendAudioReceiverStatus(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("🎧 VOICE INPUT RECEIVER");
|
||||||
|
|
||||||
|
if (_audioReceiver != null && _audioReceiver.gameObject != null)
|
||||||
|
{
|
||||||
|
// Check if component's GameObject is active AND enabled in hierarchy
|
||||||
|
bool isActive = _audioReceiver.enabled && _audioReceiver.gameObject.activeInHierarchy;
|
||||||
|
|
||||||
|
if (!isActive)
|
||||||
|
{
|
||||||
|
sb.AppendLine("⏸️ FOUND BUT DISABLED");
|
||||||
|
sb.AppendLine($"(GameObject: {_audioReceiver.gameObject.name})");
|
||||||
|
sb.AppendLine($"(Active: {_audioReceiver.gameObject.activeSelf}, InHierarchy: {_audioReceiver.gameObject.activeInHierarchy})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"GameObject: {_audioReceiver.gameObject.name} ✅");
|
||||||
|
sb.AppendLine($"Listen Port: {_audioReceiver.ListenPort}");
|
||||||
|
|
||||||
|
// Show active NPC
|
||||||
|
var targetNPC = _audioReceiver.TargetNPC;
|
||||||
|
if (targetNPC != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Active NPC: {targetNPC.characterName} ✅");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Active NPC: None ⚠️");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine($"Receiving: {(_audioReceiver.IsReceivingAudio ? "YES ✅" : "NO")}");
|
||||||
|
sb.AppendLine($"Packets Received: {_audioReceiver.TotalPacketsReceived}");
|
||||||
|
|
||||||
|
float timeSince = _audioReceiver.TimeSinceLastReceive;
|
||||||
|
if (timeSince >= 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Receive: {timeSince:F1}s ago");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Receive: Never");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("❌ NOT FOUND IN SCENE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendSpeechReceiverStatus(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("🔉 SPEECH RECEIVER");
|
||||||
|
|
||||||
|
if (_speechReceiver != null && _speechReceiver.gameObject != null)
|
||||||
|
{
|
||||||
|
// Check if component's GameObject is active AND enabled in hierarchy
|
||||||
|
bool isActive = _speechReceiver.enabled && _speechReceiver.gameObject.activeInHierarchy;
|
||||||
|
|
||||||
|
if (!isActive)
|
||||||
|
{
|
||||||
|
sb.AppendLine("⏸️ FOUND BUT DISABLED");
|
||||||
|
sb.AppendLine($"(GameObject: {_speechReceiver.gameObject.name})");
|
||||||
|
sb.AppendLine($"(Active: {_speechReceiver.gameObject.activeSelf}, InHierarchy: {_speechReceiver.gameObject.activeInHierarchy})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"GameObject: {_speechReceiver.gameObject.name} ✅");
|
||||||
|
sb.AppendLine($"Listen Port: {_speechReceiver.ListenPort}");
|
||||||
|
sb.AppendLine($"Playing: {(_speechReceiver.IsPlayingSequence ? "YES ✅" : "NO")}");
|
||||||
|
sb.AppendLine($"Clips Received: {_speechReceiver.TotalClipsReceived}");
|
||||||
|
sb.AppendLine($"Queued Clips: {_speechReceiver.QueuedClipCount}");
|
||||||
|
|
||||||
|
float timeSince = _speechReceiver.TimeSinceLastReceive;
|
||||||
|
if (timeSince >= 0)
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Receive: {timeSince:F1}s ago");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine($"Last Receive: Never");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("❌ NOT FOUND IN SCENE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AppendEventLog(StringBuilder sb)
|
||||||
|
{
|
||||||
|
sb.AppendLine("📋 EVENT LOG");
|
||||||
|
|
||||||
|
if (_peerDiscovery != null && _peerDiscovery.EventLog.Count > 0)
|
||||||
|
{
|
||||||
|
int startIndex = Math.Max(0, _peerDiscovery.EventLog.Count - 8);
|
||||||
|
for (int i = startIndex; i < _peerDiscovery.EventLog.Count; i++)
|
||||||
|
{
|
||||||
|
sb.AppendLine(_peerDiscovery.EventLog[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sb.AppendLine("No events yet");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetConnectionStateColor(UDPPeerDiscovery.ConnectionState state)
|
||||||
|
{
|
||||||
|
return state switch
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.ConnectionState.Connected => "green",
|
||||||
|
UDPPeerDiscovery.ConnectionState.Discovering => "yellow",
|
||||||
|
UDPPeerDiscovery.ConnectionState.Lost => "red",
|
||||||
|
_ => "white"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetConnectionStateIcon(UDPPeerDiscovery.ConnectionState state)
|
||||||
|
{
|
||||||
|
return state switch
|
||||||
|
{
|
||||||
|
UDPPeerDiscovery.ConnectionState.Connected => "✅",
|
||||||
|
UDPPeerDiscovery.ConnectionState.Discovering => "⏳",
|
||||||
|
UDPPeerDiscovery.ConnectionState.Lost => "❌",
|
||||||
|
UDPPeerDiscovery.ConnectionState.Disconnected => "⭕",
|
||||||
|
_ => "❓"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
if (_toggleAction != null)
|
||||||
|
{
|
||||||
|
_toggleAction.Disable();
|
||||||
|
_toggleAction.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public methods for external control
|
||||||
|
public void Show()
|
||||||
|
{
|
||||||
|
_isVisible = true;
|
||||||
|
_panel.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Hide()
|
||||||
|
{
|
||||||
|
_isVisible = false;
|
||||||
|
_panel.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
||||||
@ -58,12 +58,19 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
Lost
|
Lost
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Event log for debugging
|
||||||
|
private System.Collections.Generic.List<string> _eventLog = new System.Collections.Generic.List<string>();
|
||||||
|
private const int MAX_LOG_ENTRIES = 20;
|
||||||
|
|
||||||
// Public properties
|
// Public properties
|
||||||
public string PeerIP => _peerIP;
|
public string PeerIP => _peerIP;
|
||||||
public byte LocalPlayerID => localPlayerID;
|
public byte LocalPlayerID => localPlayerID;
|
||||||
public byte PeerPlayerID => _peerPlayerID;
|
public byte PeerPlayerID => _peerPlayerID;
|
||||||
public ConnectionState CurrentState => _connectionState;
|
public ConnectionState CurrentState => _connectionState;
|
||||||
public bool IsConnected => _connectionState == ConnectionState.Connected;
|
public bool IsConnected => _connectionState == ConnectionState.Connected;
|
||||||
|
public float TimeSinceLastPeerPacket => _connectionState == ConnectionState.Connected ?
|
||||||
|
(float)(DateTime.UtcNow - _lastPeerPacketTime).TotalSeconds : -1f;
|
||||||
|
public System.Collections.Generic.List<string> EventLog => _eventLog;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
@ -91,6 +98,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
_cancellationTokenSource = new CancellationTokenSource();
|
||||||
|
LogEvent($"🔍 Discovery started (Player {localPlayerID})");
|
||||||
StartDiscovery();
|
StartDiscovery();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,6 +409,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
SetConnectionState(ConnectionState.Connected);
|
SetConnectionState(ConnectionState.Connected);
|
||||||
|
|
||||||
ConvaiLogger.Info($"✅ Peer discovered! Player {peerPlayerID} at {peerIP}", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info($"✅ Peer discovered! Player {peerPlayerID} at {peerIP}", ConvaiLogger.LogCategory.Character);
|
||||||
|
LogEvent($"✅ Peer discovered! Player {peerPlayerID} at {peerIP}");
|
||||||
|
|
||||||
// Notify listeners
|
// Notify listeners
|
||||||
OnPeerDiscovered?.Invoke(peerIP);
|
OnPeerDiscovered?.Invoke(peerIP);
|
||||||
@ -409,6 +418,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
private void HandlePeerLost()
|
private void HandlePeerLost()
|
||||||
{
|
{
|
||||||
ConvaiLogger.Warn($"⚠️ Peer connection lost (Player {_peerPlayerID} at {_peerIP})", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Warn($"⚠️ Peer connection lost (Player {_peerPlayerID} at {_peerIP})", ConvaiLogger.LogCategory.Character);
|
||||||
|
LogEvent($"⚠️ Peer connection lost (Player {_peerPlayerID})");
|
||||||
|
|
||||||
string lostPeerIP = _peerIP;
|
string lostPeerIP = _peerIP;
|
||||||
_peerIP = "";
|
_peerIP = "";
|
||||||
@ -441,6 +451,7 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
public void RestartDiscovery()
|
public void RestartDiscovery()
|
||||||
{
|
{
|
||||||
ConvaiLogger.Info("Manually restarting peer discovery", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info("Manually restarting peer discovery", ConvaiLogger.LogCategory.Character);
|
||||||
|
LogEvent("🔄 Manually restarting discovery");
|
||||||
_peerIP = "";
|
_peerIP = "";
|
||||||
_peerPlayerID = 0;
|
_peerPlayerID = 0;
|
||||||
SetConnectionState(ConnectionState.Discovering);
|
SetConnectionState(ConnectionState.Discovering);
|
||||||
@ -455,6 +466,19 @@ namespace Convai.Scripts.Runtime.Multiplayer
|
|||||||
ConvaiLogger.Info($"Peer Player ID: {_peerPlayerID}", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info($"Peer Player ID: {_peerPlayerID}", ConvaiLogger.LogCategory.Character);
|
||||||
ConvaiLogger.Info($"Listen Port: {_listenPort}", ConvaiLogger.LogCategory.Character);
|
ConvaiLogger.Info($"Listen Port: {_listenPort}", ConvaiLogger.LogCategory.Character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LogEvent(string message)
|
||||||
|
{
|
||||||
|
string timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||||
|
string logEntry = $"[{timestamp}] {message}";
|
||||||
|
_eventLog.Add(logEntry);
|
||||||
|
|
||||||
|
// Keep only last N entries
|
||||||
|
if (_eventLog.Count > MAX_LOG_ENTRIES)
|
||||||
|
{
|
||||||
|
_eventLog.RemoveAt(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user