123 lines
6.4 KiB
C#
123 lines
6.4 KiB
C#
using UnityEngine;
|
|
using Fusion;
|
|
using Opsive.UltimateCharacterController.Networking;
|
|
using Opsive.UltimateCharacterController.Networking.Character;
|
|
using Opsive.UltimateCharacterController.Character;
|
|
using Opsive.UltimateCharacterController.Camera; // Corrected namespace
|
|
using Opsive.UltimateCharacterController.Input; // Corrected namespace
|
|
|
|
namespace Hallucinate.Network
|
|
{
|
|
// Ensure Opsive components load before this
|
|
[DefaultExecutionOrder(100)]
|
|
public class FusionClientMovementBridge : NetworkBehaviour, INetworkInfo, INetworkCharacter
|
|
{
|
|
public override void Spawned()
|
|
{
|
|
// We use InputAuthority to identify the local player because this is Host mode.
|
|
bool isLocal = Object.HasInputAuthority;
|
|
|
|
// 1. Isolate Input: Only the local player should read keyboard/mouse inputs.
|
|
var locoHandler = GetComponent<UltimateCharacterLocomotionHandler>();
|
|
if (locoHandler != null) locoHandler.enabled = isLocal;
|
|
|
|
var activeInput = GetComponent<UnityInput>(); // Corrected class reference
|
|
if (activeInput != null) activeInput.enabled = isLocal;
|
|
|
|
// 2. Isolate Camera: Only attach the camera if this is the local player.
|
|
if (isLocal)
|
|
{
|
|
var cameraController = UnityEngine.Object.FindFirstObjectByType<CameraController>();
|
|
if (cameraController != null)
|
|
{
|
|
cameraController.Character = gameObject;
|
|
}
|
|
|
|
// CRITICAL: Disable NetworkTransform on the local client so Fusion doesn't pull the character back!
|
|
var nt = GetComponent<NetworkTransform>();
|
|
if (nt != null) nt.enabled = false;
|
|
}
|
|
}
|
|
|
|
// --- INetworkInfo Implementation ---
|
|
|
|
// We use InputAuthority to identify the local player in Host/Client mode
|
|
public bool IsLocalPlayer() => Object.HasInputAuthority;
|
|
public bool IsServer() => Runner.IsServer;
|
|
public bool IsServerAuthoritative() => false;
|
|
|
|
// --- Client-Auth Movement Sync via RPC ---
|
|
// Since the Server holds State Authority, the client must send its transform to the server.
|
|
// The server then broadcasts it to everyone else via NetworkTransform.
|
|
|
|
public override void FixedUpdateNetwork()
|
|
{
|
|
if (Object.HasInputAuthority)
|
|
{
|
|
RPC_UpdateTransform(transform.position, transform.rotation);
|
|
}
|
|
}
|
|
|
|
public override void Render()
|
|
{
|
|
// If this is a remote proxy, Fusion's NetworkTransform will update the Unity transform.
|
|
// But Opsive maintains its own internal position and will snap it back unless we tell it to update!
|
|
if (!Object.HasInputAuthority)
|
|
{
|
|
var loco = GetComponent<UltimateCharacterLocomotion>();
|
|
if (loco != null)
|
|
{
|
|
// Feed the NetworkTransform's current position to Opsive so it knows we moved,
|
|
// which also allows the Animator to play the correct movement animations!
|
|
loco.SetPositionAndRotation(transform.position, transform.rotation, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority)]
|
|
private void RPC_UpdateTransform(Vector3 pos, Quaternion rot)
|
|
{
|
|
// The Server receives this and applies it. NetworkTransform then syncs it to all other clients.
|
|
transform.position = pos;
|
|
transform.rotation = rot;
|
|
}
|
|
|
|
public void SetPosition(Vector3 position, bool snapAnimator)
|
|
{
|
|
if (Object.HasInputAuthority) transform.position = position;
|
|
}
|
|
public void SetRotation(Quaternion rotation, bool snapAnimator)
|
|
{
|
|
if (Object.HasInputAuthority) transform.rotation = rotation;
|
|
}
|
|
public void SetPositionAndRotation(Vector3 position, Quaternion rotation, bool snapAnimator)
|
|
{
|
|
if (Object.HasInputAuthority) transform.SetPositionAndRotation(position, rotation);
|
|
}
|
|
public void ResetRotationPosition() { }
|
|
public void SetActive(bool active, bool uiEvent) { }
|
|
|
|
// Optional interface stubs for weapons/items (leave empty for now to focus on movement)
|
|
public void LoadDefaultLoadout() { }
|
|
public void EquipUnequipItem(uint itemID, int slotID, bool equip) { }
|
|
public void ItemIdentifierPickup(uint id, int amount, int slot, bool immediate, bool force) { }
|
|
public void RemoveAllItems() { }
|
|
|
|
// New missing interface stubs for Shooter/Melee compiled code
|
|
public void Fire(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, float strength) { }
|
|
public void StartItemReload(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction) { }
|
|
public void ReloadItem(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, bool fullClip) { }
|
|
public void ItemReloadComplete(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, bool success, bool immediateReload) { }
|
|
public void MeleeHitCollider(Opsive.UltimateCharacterController.Items.Actions.ItemAction itemAction, int hitboxIndex, RaycastHit raycastHit, GameObject hitGameObject, UltimateCharacterLocomotion hitCharacterLocomotion) { }
|
|
|
|
public void ThrowItem(Opsive.UltimateCharacterController.Items.Actions.ItemAction action) { }
|
|
public void EnableThrowableObjectMeshRenderers(Opsive.UltimateCharacterController.Items.Actions.ItemAction action) { }
|
|
public void StartStopBeginEndMagicActions(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, bool begin, bool start) { }
|
|
public void MagicCast(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, int index, uint castID, Vector3 dir, Vector3 target) { }
|
|
public void MagicImpact(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, uint castID, GameObject source, GameObject target, Vector3 pos, Vector3 norm) { }
|
|
public void StopMagicCast(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, int index, uint castID) { }
|
|
public void ToggleFlashlight(Opsive.UltimateCharacterController.Items.Actions.ItemAction action, bool active) { }
|
|
public void PushRigidbody(Rigidbody rb, Vector3 force, Vector3 point) { }
|
|
}
|
|
}
|