모든 네트워크 오브젝트의 소유권을 서버가 갖도록 함

Distributable -> None
관련 사이드 이펙트로 인한 버그 수정
This commit is contained in:
2026-02-18 02:18:42 +09:00
parent da8c87d082
commit 4ffbbb0aff
10 changed files with 388 additions and 129 deletions

View File

@@ -1,4 +1,5 @@
using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEngine;
using UnityEngine.InputSystem;
@@ -37,6 +38,7 @@ namespace Northbound
private IInteractable _unavailableInteractable;
private Camera _mainCamera;
private Animator _animator;
private NetworkAnimator _networkAnimator;
private EquipmentSocket _equipmentSocket;
private EquipmentData _pendingEquipmentData;
@@ -44,21 +46,51 @@ namespace Northbound
private bool _isInteracting = false;
private Coroutine _interactionTimeoutCoroutine;
private NetworkPlayerController _networkPlayerController;
public bool IsInteracting => _isInteracting;
public float WorkPower => workPower;
public IInteractable CurrentUnavailableInteractable => _unavailableInteractable;
// 로컬 플레이어인지 확인
private bool IsLocalPlayer => _networkPlayerController != null && _networkPlayerController.IsLocalPlayer;
private ulong LocalPlayerId => _networkPlayerController != null ? _networkPlayerController.OwnerPlayerId : OwnerClientId;
private void Awake()
{
_networkPlayerController = GetComponent<NetworkPlayerController>();
_networkAnimator = GetComponent<NetworkAnimator>();
}
public override void OnNetworkSpawn()
{
if (!IsOwner) return;
_mainCamera = Camera.main;
_animator = GetComponent<Animator>();
_equipmentSocket = GetComponent<EquipmentSocket>();
if (rayOrigin == null)
rayOrigin = transform;
// _ownerPlayerId 변경 이벤트 구독
if (_networkPlayerController != null)
{
_networkPlayerController.OnOwnerChanged += OnOwnerPlayerIdChanged;
}
// 이미 로컬 플레이어면 입력 초기화
TryInitializeInput();
}
private void OnOwnerPlayerIdChanged(ulong newOwnerId)
{
TryInitializeInput();
}
private void TryInitializeInput()
{
if (!IsLocalPlayer) return;
if (_inputActions != null) return;
_mainCamera = Camera.main;
_inputActions = new PlayerInputActions();
_inputActions.Player.Interact.performed += OnInteract;
_inputActions.Enable();
@@ -66,7 +98,12 @@ namespace Northbound
public override void OnNetworkDespawn()
{
if (IsOwner && _inputActions != null)
if (_networkPlayerController != null)
{
_networkPlayerController.OnOwnerChanged -= OnOwnerPlayerIdChanged;
}
if (_inputActions != null)
{
_inputActions.Player.Interact.performed -= OnInteract;
_inputActions.Disable();
@@ -76,12 +113,11 @@ namespace Northbound
private void Update()
{
if (!IsOwner) return;
if (!IsLocalPlayer) return;
// Check if current interactable is no longer valid (e.g., building completed)
if (_isInteracting && _currentInteractable != null)
{
if (!_currentInteractable.CanInteract(OwnerClientId))
if (!_currentInteractable.CanInteract(LocalPlayerId))
{
EndInteraction();
}
@@ -92,6 +128,13 @@ namespace Northbound
private void DetectInteractable()
{
// 카메라가 없으면 다시 시도
if (_mainCamera == null)
{
_mainCamera = Camera.main;
if (_mainCamera == null) return;
}
Vector3 origin = rayOrigin.position;
Vector3 direction = useForwardDirection ? transform.forward : _mainCamera.transform.forward;
@@ -112,7 +155,7 @@ namespace Northbound
if (interactable != null)
{
if (interactable.CanInteract(OwnerClientId))
if (interactable.CanInteract(LocalPlayerId))
{
_currentInteractable = interactable;
_unavailableInteractable = null;
@@ -120,7 +163,6 @@ namespace Northbound
}
else
{
// CanInteract가 false인 경우 추적
_currentInteractable = null;
_unavailableInteractable = interactable;
return;
@@ -139,8 +181,14 @@ namespace Northbound
if (_currentInteractable != null)
{
// 상호작용 가능 여부 다시 확인 (NetworkVariable 동기화 지연 대응)
if (!_currentInteractable.CanInteract(LocalPlayerId))
{
return;
}
bool isResource = _currentInteractable is Resource;
bool hasWorker = assignedWorker != null && assignedWorker.OwnerPlayerId == OwnerClientId;
bool hasWorker = assignedWorker != null && assignedWorker.OwnerPlayerId == LocalPlayerId;
bool isWorkerFollowing = hasWorker && (int)assignedWorker.CurrentState == 1;
bool shouldPlayAnimation = !isResource || !hasWorker || !isWorkerFollowing;
@@ -150,9 +198,10 @@ namespace Northbound
string animTrigger = _currentInteractable.GetInteractionAnimation();
bool hasAnimation = !string.IsNullOrEmpty(animTrigger);
if (playAnimations && _animator != null && hasAnimation && shouldPlayAnimation)
if (playAnimations && hasAnimation && shouldPlayAnimation)
{
_animator.SetTrigger(animTrigger);
// 서버에서 애니메이션 실행 (동기화를 위해)
PlayAnimationServerRpc(animTrigger);
_interactionTimeoutCoroutine = StartCoroutine(InteractionTimeout(3f));
}
else
@@ -173,13 +222,18 @@ namespace Northbound
}
}
_currentInteractable.Interact(OwnerClientId);
_currentInteractable.Interact(LocalPlayerId);
}
}
// ========================================
// Animation Event 함수들
// ========================================
[Rpc(SendTo.Server)]
private void PlayAnimationServerRpc(string animTrigger)
{
if (_networkAnimator != null && !string.IsNullOrEmpty(animTrigger))
{
_networkAnimator.SetTrigger(animTrigger);
}
}
public void OnEquipTool()
{
@@ -207,7 +261,7 @@ namespace Northbound
public void OnInteractionComplete()
{
if (!IsOwner) return;
if (!IsLocalPlayer) return;
if (_interactionTimeoutCoroutine != null)
{
@@ -217,10 +271,6 @@ namespace Northbound
_isInteracting = false;
}
// ========================================
// 내부 헬퍼 함수들
// ========================================
private void AttachEquipment(string socketName = null)
{
if (_equipmentSocket == null || _pendingEquipmentData == null)
@@ -283,7 +333,13 @@ namespace Northbound
private void OnGUI()
{
if (!IsOwner) return;
if (!IsLocalPlayer) return;
// 디버그: _mainCamera 상태 확인
if (_mainCamera == null)
{
_mainCamera = Camera.main;
}
GUIStyle style = new GUIStyle(GUI.skin.label)
{
@@ -299,7 +355,7 @@ namespace Northbound
prompt = _currentInteractable.GetInteractionPrompt();
bool isResource = _currentInteractable is Resource;
if (isResource && assignedWorker != null && assignedWorker.OwnerPlayerId == OwnerClientId)
if (isResource && assignedWorker != null && assignedWorker.OwnerPlayerId == LocalPlayerId)
{
prompt += " (워커 할당 가능)";
}
@@ -311,7 +367,7 @@ namespace Northbound
}
}
if (assignedWorker != null && assignedWorker.OwnerPlayerId == OwnerClientId)
if (assignedWorker != null && assignedWorker.OwnerPlayerId == LocalPlayerId)
{
string workerStatus = assignedWorker.CurrentState switch
{