using System;
using System.Text;
using Convai.Scripts.Runtime.Multiplayer;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
namespace Convai.Scripts.Runtime.Multiplayer
{
///
/// In-game debug UI for network diagnostics in VR builds
/// Shows peer discovery, voice/speech status, and packet counters
///
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();
_speechSender = FindBestComponent();
_audioReceiver = FindBestComponent();
_speechReceiver = FindBestComponent();
}
///
/// Finds a component, prioritizing enabled ones in active hierarchies
///
private T FindBestComponent() where T : MonoBehaviour
{
var allComponents = FindObjectsOfType(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