using Unity.Netcode; using UnityEngine; namespace AfterHours.Interaction { /// /// Raycast auf dem Owner-Client für den Prompt, Ausführung auf dem Server. /// Der Server vertraut dem Client die Zielauswahl NICHT blind – er prüft /// Distanz und Sichtbarkeit erneut (siehe CLAUDE.md §3). /// [RequireComponent(typeof(NetworkObject))] public sealed class PlayerInteractor : NetworkBehaviour { [SerializeField] private Transform _eyes; [SerializeField] private float _reach = 2.5f; [SerializeField] private LayerMask _interactableMask = ~0; /// Toleranz, weil Client und Server nie exakt denselben Frame sehen. private const float ServerReachTolerance = 1.5f; private IInteractable _current; public string CurrentPrompt { get; private set; } private void Update() { if (!IsOwner) return; RefreshTarget(); if (_current != null && Input.GetKeyDown(KeyCode.E)) { var target = ((MonoBehaviour)_current).GetComponent(); if (target != null) RequestInteractRpc(target); } } private void RefreshTarget() { var previous = _current; _current = null; CurrentPrompt = null; if (Physics.Raycast(_eyes.position, _eyes.forward, out var hit, _reach, _interactableMask) && hit.collider.TryGetComponent(out var interactable)) { _current = interactable; CurrentPrompt = interactable.GetPrompt(OwnerClientId); } // TODO: entfernen, sobald ein echter Prompt im UI angezeigt wird. if (_current != previous) { Debug.Log(_current != null ? $"[PlayerInteractor] Ziel im Blick: {CurrentPrompt}" : "[PlayerInteractor] Kein Ziel mehr im Blick."); } } [Rpc(SendTo.Server)] private void RequestInteractRpc(NetworkObjectReference targetRef) { if (!targetRef.TryGet(out var target)) return; // Server-Validierung: ist der Spieler überhaupt in Reichweite? float distance = Vector3.Distance(transform.position, target.transform.position); if (distance > _reach + ServerReachTolerance) { Debug.LogWarning($"[PlayerInteractor] Client {OwnerClientId} zu weit entfernt ({distance:F1} m)."); return; } if (target.TryGetComponent(out var interactable)) interactable.Interact(NetworkObject); } } }