Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 463
feat: Unified Netcode POC migration#3963
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
2973bb93190354d0b7f1c2eeb2c7fb7d11aaa6ea10c15498c83e3c62dd4a95cd759121ba19e82c6e2fd9a97064b9deb6ecb9446a4187d6eacc5c3a6bae60bb5cfc6c2ec01428d1f28b51dd4bd26cf04131f3c7a790559b321143ece83eb239546ff2211a50fc6315ce9038909526f30bff84ee2ea80eb03d4a803a6b852f0bd4efdcce1185adf29e191cf5385802407da1beaff2f6580b5f232bfd1263f8bc8800ac625b2c31e7c2038b1571e75ba1be7b59c6f575ae13cbe7166e64832c98801ebed5f6b14c13b003ae424512b3986e0249d543b034bbda354fc66b55c9ad501dc6b39205ca97b1daa585ea9d4a5d9f7418ed866613ef0131c8b43dfe75aa378874ff164a7130cc639143083452916b6759f8208ab1addf453c8e2a4e7bab7c87a87b80b0c38c7650425576e361688b9481ff8e707c439779afef00b140c05138a4e66c22ebf4f27cabcb4a6ad90421985836271a5af5570dcb77f1b592b599a1fa5b5e8917f0a175fb2d140d15bf70333853cdb89eb5a6d19c712161c077f18378e36cb560bf37c95a3f09f70c406067a3129d41242c7d039f0046b49bd899cd07687d2578615e1e79dcdf2226b467e26c34acb92003e5773be0719fecd845cea8ac5b8c41c53fc9a09206d52e86b5ade2409559cf8fbe6c1ea3baeae82d8c45de18e2943256494ff49eac4dd6361bb31d4e514868a424774e103bdf68c57b528d49ee8fad8571cd1b3bf6b365c25a1b0a520829e08c6fe4cff9d282be3File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| #if UNIFIED_NETCODE | ||
| using Unity.NetCode; | ||
| #if UNITY_EDITOR | ||
| using UnityEditor; | ||
| #endif | ||
| using UnityEngine; | ||
| namespace Unity.Netcode | ||
| { | ||
| /// <summary> | ||
| /// TODO-UNIFIED: Needs further peer review and exploring alternate ways of handling this. | ||
| /// This is a component that is added to the root of all N4E-spawned hybrid prefab instances. It is used to link | ||
| /// <see cref="NetworkObject.SerializedObject"/> the N4E-spawned hybrid prefab instances to the incoming <see cref="CreateObjectMessage"/> | ||
| /// specific to the N4E-spawned hybrid prefab instance that has the matching <see cref="NetworkObjectId"/>. | ||
| /// </summary> | ||
| public partial class NetworkObjectBridge : GhostBehaviour | ||
| { | ||
| #if UNITY_EDITOR | ||
| [HideInInspector] | ||
| [SerializeField] | ||
| private bool m_Sorted = false; | ||
| private void OnValidate() | ||
| { | ||
| // TODO-UNIFIED: GhostAdapter must be above all GhostBehaviours in order to assure the GhostAdapter is initialized before any GhostBehaviour. | ||
| // This auto-sorting is required because the GhostBehaviours rely on the GhostAdapter.Awake being invoked before any GhostBehaviour.Awake. | ||
| if (!m_Sorted && !EditorApplication.isPlaying) | ||
| { | ||
| while (UnityEditorInternal.ComponentUtility.MoveComponentUp(this)) | ||
| { | ||
| // Keep moving until it can't go higher | ||
| } | ||
| var ghostAdapter = gameObject.GetComponent<GhostAdapter>(); | ||
| // Now move the GhostAdapter to the top so it is above NetworkObjectBridge | ||
| while (ghostAdapter != null && UnityEditorInternal.ComponentUtility.MoveComponentUp(ghostAdapter)) | ||
| { | ||
| // Keep moving until it can't go higher | ||
| } | ||
| m_Sorted = true; | ||
| } | ||
| } | ||
| #endif | ||
| /// <summary> | ||
| /// This is used to link <see cref="NetworkObject.SerializedObject"/> data to | ||
| /// N4E-spawned hybrid prefab instances. | ||
| /// </summary> | ||
| internal GhostField<ulong> NetworkObjectId = new GhostField<ulong>(); | ||
| public void SetNetworkObjectId(ulong networkObjectId) | ||
| { | ||
| NetworkObjectId.PresetValue(networkObjectId); | ||
| NetworkObjectId.Value = networkObjectId; | ||
| } | ||
| /// <summary> | ||
| /// Currently, NGO provides the parenting event handling via <see cref="ParentSyncMessage"/>. | ||
| /// Once <see cref="GhostField{InternalTypeT}"/> can provide a form of event notification that | ||
| /// the value has changed, we can then invert this flow such that the change in the parent value | ||
| /// drives the event. | ||
| /// </summary> | ||
| /// <param name="scale">We use NGO scale, delivered via ParentSyncMessage, that is applied to this | ||
| /// instance's entity's PostTransformMatrix.</param> | ||
| internal void HybridParentUpdate(Vector3 scale) | ||
| { | ||
| var current = Ghost.GetPositionAndRotation(); | ||
| //Debug.Log($"---- Current LT: {current.Position} | {current.Rotation.eulerAngles}"); | ||
| //Debug.Log($"---- New LT: {transform.localPosition} | {transform.localRotation}"); | ||
| Ghost.ApplyPostTransformMatrixScale(scale); | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| #if UNIFIED_NETCODE | ||
| using System; | ||
| using Unity.Entities; | ||
| using Unity.NetCode; | ||
| using UnityEngine; | ||
| namespace Unity.Netcode | ||
| { | ||
| /// <summary> | ||
| /// TODO-UNIFIED: Needs furthery review and proper error messaging using the new logging context. | ||
| /// Handles the bootstrap process for both client and server in unified mode. This is used to create the world and set it on the | ||
| /// NetworkManager during initialization. | ||
| /// </summary> | ||
| internal class UnifiedBootstrap : ClientServerBootstrap | ||
| { | ||
| public static UnifiedBootstrap Instance { get; private set; } | ||
| public static Action OnInitialized; | ||
| public static ushort Port = 7979; | ||
| public static NetworkManager CurrentNetworkManagerForInitialization; | ||
| public static World LastCreatedWorld { get; private set; } | ||
| private static int s_WorldCounter = 0; | ||
| public override bool Initialize(string defaultWorldName) | ||
| { | ||
| var networkManager = CurrentNetworkManagerForInitialization; | ||
| if (networkManager == NetworkManager.Singleton) | ||
| { | ||
| Instance = this; | ||
| } | ||
| AutoConnectPort = Port; | ||
| if (base.Initialize(defaultWorldName)) | ||
| { | ||
| Debug.LogError($"[{nameof(UnifiedBootstrap)}] Auto-bootstrap is enabled!!! This will break the POC!"); | ||
| return true; | ||
| } | ||
| if (networkManager != null) | ||
| { | ||
| Debug.Log($"Starting a world for {(networkManager.IsServer ? "Host" : "Client")}"); | ||
| s_WorldCounter++; | ||
| LastCreatedWorld = networkManager.IsServer ? CreateSingleWorldHost($"HostSingleWorld-{s_WorldCounter}") | ||
| : CreateClientWorld($"ClientWorld-{s_WorldCounter}"); | ||
| if (LastCreatedWorld == null) | ||
| { | ||
| s_WorldCounter--; | ||
| Debug.LogError($"[{nameof(UnifiedBootstrap)}] World is null!"); | ||
| return false; | ||
| } | ||
| if (!LastCreatedWorld.IsCreated) | ||
| { | ||
| s_WorldCounter--; | ||
| Debug.LogError($"[{nameof(UnifiedBootstrap)}] World was not created!"); | ||
| return false; | ||
| } | ||
| if (networkManager.LogLevel <= LogLevel.Developer) | ||
| { | ||
| NetworkLog.LogInfo($"[{nameof(UnifiedBootstrap)}] Created world: {LastCreatedWorld.Name} / {LastCreatedWorld.SequenceNumber}"); | ||
| } | ||
| networkManager.NetcodeWorld = (NetcodeWorld)LastCreatedWorld; | ||
| #if UNIFIED_NGO_REGISTERS_PREFABS | ||
| if (networkManager.NetworkConfig.Prefabs.HasPendingGhostPrefabs) | ||
| { | ||
| if (networkManager.LogLevel <= LogLevel.Developer) | ||
| { | ||
| NetworkLog.LogInfo($"[{nameof(UnifiedBootstrap)}] Registering hybrid prefabs..."); | ||
| } | ||
| networkManager.NetworkConfig.Prefabs.RegisterGhostPrefabs(networkManager); | ||
| } | ||
| #endif | ||
| } | ||
| else | ||
| { | ||
| LastCreatedWorld = CreateLocalWorld("LocalWorld"); | ||
| } | ||
| OnInitialized?.Invoke(); | ||
| return true; | ||
| } | ||
| ~UnifiedBootstrap() | ||
| { | ||
| LastCreatedWorld = null; | ||
| Instance = null; | ||
| } | ||
| } | ||
| } | ||
| #endif |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| #if UNIFIED_NETCODE | ||
| using System.Collections.Generic; | ||
| using Unity.Collections; | ||
| using Unity.Entities; | ||
| using Unity.NetCode; | ||
| using UnityEngine; | ||
| namespace Unity.Netcode.Components | ||
| { | ||
| public struct NetcodeConnection | ||
| { | ||
| internal World World; | ||
| internal Entity Entity; | ||
| public int NetworkId; | ||
| public bool IsServer => World.IsServer(); | ||
| public void GoInGame() | ||
| { | ||
| World.EntityManager.AddComponentData(Entity, default(NetworkStreamInGame)); | ||
| } | ||
| public void SendMessage<T>(T message) where T : unmanaged, IRpcCommand | ||
| { | ||
| var req = World.EntityManager.CreateEntity(); | ||
| World.EntityManager.AddComponentData(req, new SendRpcCommandRequest { TargetConnection = Entity }); | ||
| World.EntityManager.AddComponentData(req, message); | ||
| } | ||
| } | ||
| internal partial class UnifiedUpdateConnections : SystemBase | ||
| { | ||
| private List<NetcodeConnection> m_TempConnections = new List<NetcodeConnection>(); | ||
| private Dictionary<int, NetcodeConnection> m_NewConnections = new Dictionary<int, NetcodeConnection>(); | ||
| protected override void OnUpdate() | ||
| { | ||
| var isServer = World.IsServer(); | ||
| var commandBuffer = new EntityCommandBuffer(Allocator.Temp); | ||
| foreach (var networkManager in Object.FindObjectsByType<NetworkManager>()) | ||
| { | ||
| foreach (var (networkId, connectionState, entity) in SystemAPI.Query<NetworkId, ConnectionState>().WithNone<NetworkStreamConnection>().WithEntityAccess()) | ||
| { | ||
| commandBuffer.RemoveComponent<ConnectionState>(entity); | ||
| m_TempConnections.Add(new NetcodeConnection | ||
| { | ||
| World = World, | ||
| Entity = entity, | ||
| NetworkId = networkId.Value | ||
| }); | ||
| } | ||
| foreach (var con in m_TempConnections) | ||
| { | ||
| NetworkManager.OnNetCodeDisconnect?.Invoke(con); | ||
| } | ||
| m_TempConnections.Clear(); | ||
| // TODO: We should figure out how to associate the N4E NetworkId with the NGO ClientId | ||
| foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithAll<NetworkStreamConnection>() | ||
| .WithNone<NetworkStreamInGame>().WithEntityAccess()) | ||
| { | ||
| if (!m_NewConnections.ContainsKey(networkId.Value)) | ||
| { | ||
| var newConnection = new NetcodeConnection | ||
| { World = World, Entity = entity, NetworkId = networkId.Value }; | ||
| m_NewConnections.Add(networkId.Value, newConnection); | ||
| } | ||
| } | ||
| // If we have any pending connections | ||
| if (m_NewConnections.Count > 0) | ||
| { | ||
| foreach (var entry in m_NewConnections) | ||
| { | ||
| // Server: always connect | ||
| // Client: wait until we have synchronized before announcing we are ready to receive snapshots | ||
| if (networkManager.IsServer || (!networkManager.IsServer && networkManager.IsConnectedClient)) | ||
| { | ||
| // Set the connection in-game | ||
| commandBuffer.AddComponent<NetworkStreamInGame>(entry.Value.Entity); | ||
| commandBuffer.AddComponent(entry.Value.Entity, default(ConnectionState)); | ||
| NetworkManager.OnNetCodeConnect?.Invoke(entry.Value); | ||
| m_TempConnections.Add(entry.Value); | ||
| } | ||
| } | ||
| // Remove any connections that have "gone in-game". | ||
| foreach (var connection in m_TempConnections) | ||
| { | ||
| m_NewConnections.Remove(connection.NetworkId); | ||
| } | ||
| } | ||
| m_TempConnections.Clear(); | ||
| // If the local NetworkManager is shutting down or no longer connected, then | ||
| // make sure we have disconnected all known connections. | ||
| if (networkManager.ShutdownInProgress || !networkManager.IsListening) | ||
| { | ||
| foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithEntityAccess()) | ||
| { | ||
| commandBuffer.RemoveComponent<ConnectionState>(entity); | ||
| NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection | ||
| { World = World, Entity = entity, NetworkId = networkId.Value }); | ||
| } | ||
| } | ||
| } | ||
| commandBuffer.Playback(EntityManager); | ||
| } | ||
| /// <summary> | ||
| /// Always disconnect all known connections when being destroyed. | ||
| /// </summary> | ||
| protected override void OnDestroy() | ||
| { | ||
| var commandBuffer = new EntityCommandBuffer(Allocator.Temp); | ||
| foreach (var (networkId, entity) in SystemAPI.Query<NetworkId>().WithEntityAccess()) | ||
| { | ||
| commandBuffer.RemoveComponent<ConnectionState>(entity); | ||
| NetworkManager.OnNetCodeDisconnect?.Invoke(new NetcodeConnection { World = World, Entity = entity, NetworkId = networkId.Value }); | ||
| } | ||
| commandBuffer.Playback(EntityManager); | ||
| base.OnDestroy(); | ||
| } | ||
| } | ||
| } | ||
| #endif |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.