|
5 | 5 | using System.Text; |
6 | 6 | using Unity.Netcode.Components; |
7 | 7 | using Unity.Netcode.Runtime; |
| 8 | +#if UNIFIED_NETCODE |
| 9 | +using Unity.NetCode; |
| 10 | +#endif |
| 11 | + |
8 | 12 | #if UNITY_EDITOR |
9 | 13 | using UnityEditor; |
10 | 14 | #if UNITY_2021_2_OR_NEWER |
@@ -243,6 +247,10 @@ private static void CheckPrefabStage(PrefabStage prefabStage) |
243 | 247 | /// </summary> |
244 | 248 | internal void OnValidate() |
245 | 249 | { |
| 250 | +#if UNIFIED_NETCODE |
| 251 | + UnifiedValidation(); |
| 252 | +#endif |
| 253 | + |
246 | 254 | // Always exit early if we are in prefab edit mode and this instance is the |
247 | 255 | // prefab instance within the InContext or InIsolation edit scene. |
248 | 256 | if (s_PrefabInstance == this) |
@@ -338,6 +346,27 @@ private void CheckForInScenePlaced() |
338 | 346 | } |
339 | 347 | #endif // UNITY_EDITOR |
340 | 348 |
|
| 349 | +#if UNIFIED_NETCODE |
| 350 | + [HideInInspector] |
| 351 | + [SerializeField] |
| 352 | + internal GhostAdapter GhostAdapter; |
| 353 | + |
| 354 | + [HideInInspector] |
| 355 | + [SerializeField] |
| 356 | + internal bool HasGhost; |
| 357 | + |
| 358 | + private void UnifiedValidation() |
| 359 | + { |
| 360 | + NetworkObjectBridge = GetComponent<NetworkObjectBridge>(); |
| 361 | + GhostAdapter = GetComponent<GhostAdapter>(); |
| 362 | + HasGhost = GhostAdapter != null; |
| 363 | + if (HasGhost && NetworkObjectBridge == null) |
| 364 | + { |
| 365 | + NetworkObjectBridge = gameObject.AddComponent<NetworkObjectBridge>(); |
| 366 | + } |
| 367 | + } |
| 368 | +#endif |
| 369 | + |
341 | 370 | internal bool HasParentNetworkObject(Transform transform) |
342 | 371 | { |
343 | 372 | if (transform.parent != null) |
@@ -2836,6 +2865,10 @@ internal struct SceneObject |
2836 | 2865 | public ulong OwnerClientId; |
2837 | 2866 | public ushort OwnershipFlags; |
2838 | 2867 |
|
| 2868 | +#if UNIFIED_NETCODE |
| 2869 | + public int GhostId; |
| 2870 | +#endif |
| 2871 | + |
2839 | 2872 | public bool IsPlayerObject |
2840 | 2873 | { |
2841 | 2874 | get => ByteUtility.GetBit(m_BitField, 0); |
@@ -2911,6 +2944,14 @@ public bool HasInstantiationData |
2911 | 2944 | set => ByteUtility.SetBit(ref m_BitField, 11, value); |
2912 | 2945 | } |
2913 | 2946 |
|
| 2947 | +#if UNIFIED_NETCODE |
| 2948 | + public bool HasGhost |
| 2949 | + { |
| 2950 | + get => ByteUtility.GetBit(m_BitField, 12); |
| 2951 | + set => ByteUtility.SetBit(ref m_BitField, 12, value); |
| 2952 | + } |
| 2953 | +#endif |
| 2954 | + |
2914 | 2955 | // When handling the initial synchronization of NetworkObjects, |
2915 | 2956 | // this will be populated with the known observers. |
2916 | 2957 | public ulong[] Observers; |
@@ -3000,6 +3041,13 @@ public void Serialize(FastBufferWriter writer) |
3000 | 3041 | writer.WriteValue(OwnerObject.GetSceneOriginHandle()); |
3001 | 3042 | } |
3002 | 3043 |
|
| 3044 | +#if UNIFIED_NETCODE |
| 3045 | + if (HasGhost) |
| 3046 | + { |
| 3047 | + writer.WriteValueSafe(GhostId); |
| 3048 | + } |
| 3049 | +#endif |
| 3050 | + |
3003 | 3051 | // write placeholder for serialized data size. |
3004 | 3052 | // Can't be bitpacked because we don't know the value until we calculate it later |
3005 | 3053 | var positionBeforeSynchronizing = writer.Position; |
@@ -3079,6 +3127,13 @@ public void Deserialize(FastBufferReader reader) |
3079 | 3127 | // scene handle that the NetworkObject resides in. |
3080 | 3128 | reader.ReadValue(out NetworkSceneHandle); |
3081 | 3129 |
|
| 3130 | +#if UNIFIED_NETCODE |
| 3131 | + if (HasGhost) |
| 3132 | + { |
| 3133 | + reader.ReadValueSafe(out GhostId); |
| 3134 | + } |
| 3135 | +#endif |
| 3136 | + |
3082 | 3137 | // Read the size of the remaining synchronization data |
3083 | 3138 | // This data will be read in AddSceneObject() |
3084 | 3139 | reader.ReadValueSafe(out SynchronizationDataSize); |
@@ -3175,7 +3230,11 @@ internal SceneObject GetMessageSceneObject(ulong targetClientId = NetworkManager |
3175 | 3230 | Hash = CheckForGlobalObjectIdHashOverride(), |
3176 | 3231 | OwnerObject = this, |
3177 | 3232 | TargetClientId = targetClientId, |
3178 | | - HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0 |
| 3233 | + HasInstantiationData = InstantiationData != null && InstantiationData.Length > 0, |
| 3234 | +#if UNIFIED_NETCODE |
| 3235 | + HasGhost = HasGhost, |
| 3236 | + GhostId = HasGhost ? GhostInstance.ghostId : 0, |
| 3237 | +#endif |
3179 | 3238 | }; |
3180 | 3239 |
|
3181 | 3240 | // Handle Parenting |
@@ -3248,11 +3307,9 @@ internal static NetworkObject AddSceneObject(in SceneObject sceneObject, FastBuf |
3248 | 3307 | reader.ReadValueSafe(out instantiationData); |
3249 | 3308 | } |
3250 | 3309 |
|
3251 | | - |
3252 | 3310 | // Attempt to create a local NetworkObject |
3253 | 3311 | var networkObject = networkManager.SpawnManager.CreateLocalNetworkObject(sceneObject, instantiationData); |
3254 | 3312 |
|
3255 | | - |
3256 | 3313 | if (networkObject == null) |
3257 | 3314 | { |
3258 | 3315 | // Log the error that the NetworkObject failed to construct |
@@ -3490,7 +3547,121 @@ private void Awake() |
3490 | 3547 | #endif |
3491 | 3548 | SetCachedParent(transform.parent); |
3492 | 3549 | SceneOrigin = gameObject.scene; |
| 3550 | +#if UNIFIED_NETCODE |
| 3551 | + InitGhost(); |
| 3552 | +#endif |
| 3553 | + |
| 3554 | + } |
| 3555 | + |
| 3556 | + private void OnEnable() |
| 3557 | + { |
| 3558 | + Debug.Log("Enabled!"); |
| 3559 | + } |
| 3560 | + |
| 3561 | + private void OnDisable() |
| 3562 | + { |
| 3563 | + Debug.Log("Disabled!"); |
| 3564 | + } |
| 3565 | + |
| 3566 | +#if UNIFIED_NETCODE |
| 3567 | + |
| 3568 | + private void Start() |
| 3569 | + { |
| 3570 | + enabled = true; |
3493 | 3571 | } |
| 3572 | + internal GhostInstance GhostInstance; |
| 3573 | + [SerializeField] |
| 3574 | + [HideInInspector] |
| 3575 | + internal NetworkObjectBridge NetworkObjectBridge; |
| 3576 | + |
| 3577 | + private void InitGhost() |
| 3578 | + { |
| 3579 | + enabled = true; |
| 3580 | + // All instances with Ghosts are automatically registered |
| 3581 | + if (HasGhost && NetworkObjectBridge) |
| 3582 | + { |
| 3583 | + Debug.Log($"[{nameof(NetworkObject)}] GhostBridge {name} detected and instantiated."); |
| 3584 | + NetworkObjectBridge.NetworkObjectIdChanged += OnNetworkObjectIdChanged; |
| 3585 | + if (NetworkObjectBridge.NetworkObjectId.Value != 0) |
| 3586 | + { |
| 3587 | + RegisterGhostBridge(); |
| 3588 | + } |
| 3589 | + //var networkObjectRegistration = (false, (ulong)0); |
| 3590 | + //NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, NetworkObjectBridge.NetworkObjectId); |
| 3591 | + //try |
| 3592 | + //{ |
| 3593 | + // networkObjectRegistration = GhostAdapter.GetNetworkObjectId(); |
| 3594 | + //} |
| 3595 | + //catch (Exception ex) |
| 3596 | + //{ |
| 3597 | + // Debug.LogException(ex); |
| 3598 | + //} |
| 3599 | + |
| 3600 | + //if (networkObjectRegistration.Item1) |
| 3601 | + //{ |
| 3602 | + // // Authority and Non-Authority: |
| 3603 | + // // Upon instantiation it will always register itself as a Ghost that is pending NGO spawn. |
| 3604 | + |
| 3605 | + // // Non-Authority: |
| 3606 | + // // - If registered prior to the CreateObjectMessage, then upon receiving the CreateObjectMessag it will be processed immediately using this instance. |
| 3607 | + // // - If registered after receiving the CreateObjectMessage, then upon registering it will also process any deferred CreateObjectMessages |
| 3608 | + // // If this happens prior to receiving the is received, |
| 3609 | + // // Authority: |
| 3610 | + // // Upon spawning locally, this entry is removed from the ghost pending spawn table. |
| 3611 | + |
| 3612 | + |
| 3613 | + |
| 3614 | + //} |
| 3615 | + //else if (!NetworkManager.IsServer) |
| 3616 | + //{ |
| 3617 | + // StartCoroutine(WaitForGhostData()); |
| 3618 | + //} |
| 3619 | + //else |
| 3620 | + //{ |
| 3621 | + // Debug.LogError($"[{name}] Failed to get ghost instance or GhostId is zero!"); |
| 3622 | + //} |
| 3623 | + } |
| 3624 | + } |
| 3625 | + |
| 3626 | + private void RegisterGhostBridge() |
| 3627 | + { |
| 3628 | + Debug.Log($"[{nameof(NetworkObject)}][{nameof(NetworkObjectId)}] NetworkObjectBridge notified instance exists with assigned ID of: {NetworkObjectBridge.NetworkObjectId.Value}"); |
| 3629 | + NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, NetworkObjectBridge.NetworkObjectId.Value); |
| 3630 | + } |
| 3631 | + |
| 3632 | + private void OnNetworkObjectIdChanged(ulong networkObjectId) |
| 3633 | + { |
| 3634 | + RegisterGhostBridge(); |
| 3635 | + } |
| 3636 | + |
| 3637 | + //private System.Collections.IEnumerator WaitForGhostData() |
| 3638 | + //{ |
| 3639 | + // var waitPeriod = new WaitForSeconds(0.1f); |
| 3640 | + // var timeout = Time.realtimeSinceStartup + 5.0f; |
| 3641 | + // while (timeout > Time.realtimeSinceStartup) |
| 3642 | + // { |
| 3643 | + // enabled = true; |
| 3644 | + // var networkObjectRegistration = (false, (ulong)0); |
| 3645 | + // try |
| 3646 | + // { |
| 3647 | + // networkObjectRegistration = GhostAdapter.GetNetworkObjectId(); |
| 3648 | + // } |
| 3649 | + // catch (Exception ex) |
| 3650 | + // { |
| 3651 | + // Debug.LogException(ex); |
| 3652 | + // } |
| 3653 | + // if (networkObjectRegistration.Item1) |
| 3654 | + // { |
| 3655 | + // NetworkManager.SpawnManager.RegisterGhostPendingSpawn(this, networkObjectRegistration.Item2); |
| 3656 | + // yield break; |
| 3657 | + // } |
| 3658 | + // yield return waitPeriod; |
| 3659 | + // } |
| 3660 | + |
| 3661 | + // Debug.Log("Timed out waiting for Ghost to be registered!"); |
| 3662 | + |
| 3663 | + //} |
| 3664 | +#endif |
3494 | 3665 |
|
3495 | 3666 | /// <summary> |
3496 | 3667 | /// Update |
|
0 commit comments