Skip to content

feat: INetworkMessage refactor#1121

Closed
ShadauxCat wants to merge 42 commits intodevelopfrom
wip/inetworkmessage
Closed

feat: INetworkMessage refactor#1121
ShadauxCat wants to merge 42 commits intodevelopfrom
wip/inetworkmessage

Conversation

@ShadauxCat
Copy link
Copy Markdown
Collaborator

@ShadauxCat ShadauxCat commented Aug 31, 2021

Refactor to implement a zero-alloc flow for sending and receiving messages.

…(aside from INetworkSerializable, which isn't ready to be tested yet due to lack of BufferSerializer)
- Streamlined the implementations of bit-packing uints and ulongs.
…lacing with byte* allocated directly through UnsafeUtility.Malloc() (same function used under the hood by NativeArray). This is required in order to be able to store pointers to FastBufferReader and FastBufferWriter in order to be able to wrap them with BufferSerializer - NativeArray contains a managed variable in it that disallows taking a pointer to it. And since FBW and FBR are structs, pointers are the only way to wrap them "by reference" in another struct - ref fields aren't allowed even inside ref structs.
…g values in other ref structs in a capture-by-reference style, updated BitReader and BitWriter to use that. Also aggressively inlining properties in FBW and FBR.
…. it's a little slower, but apparently some platforms won't support unaligned memory access (e.g., WEBGL, ARM processors) and there's no compile time way to detect ARM processors since the bytecode is not processor-dependent... the cost of the runtime detection would be more expensive than the cost of just doing the memcpy.
…afeUtility.MemCpy for small values, while still supporting unaligned access.
…ork correctly (requesting beyond MaxCapacity and requesting more than double current capacity)

-Added support for FastBufferReader to be used in a mode that doesn't copy the input buffer
- Fixed incorrect text in a warning in FastBufferReader
private Type[] m_ReverseTypeMap = new Type[255];

private Dictionary<Type, byte> m_MessageTypes = new Dictionary<Type, byte>();
private NativeHashMap<ulong, Ref<DynamicUnmanagedArray<SendQueueItem>>> m_SendQueues = new NativeHashMap<ulong, Ref<DynamicUnmanagedArray<SendQueueItem>>>(64, Allocator.Persistent);
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just as a note, I could replace Ref<DynamicUnmanagedArray<SendQueueItem>> with List<SendQueueItem> and change SendQueueItem to a class instead of a struct and change this from NativeHashMap to Dictionary, and things will work the way they need to. But it'll result in GC every time a client connects and every time a new batch is started. So that's why I'm doing it this way even though it's a bit more dangerous and C++-like.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd definitely support using & embracing Unity.Collections usage but I generally have mixed feelings about crafting our own utilities & data structures like Ref<T>, DynamicUnmanagedArray<T> etc.
can we use more Unity stuff if possible and where it makes sense instead of .NET types?

Copy link
Copy Markdown
Collaborator Author

@ShadauxCat ShadauxCat Sep 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unity collections don't work here. I specifically need the ability to GetByRef() when interacting with elements of the list. List<> and NativeArray<> both return copies of the items in the list if they're value types. And if I make it not a value type, NativeArray<> becomes not an option - so it's either DynamicUnmanagedArray of structs, or List of class types + gc every time a client connects.

But... clients don't connect that often, so that might be an acceptable amount of gc. (And if I do that, I won't need Ref here.)

Or I could also do

NativeHashMap<ulong, Ref<NativeArray<Ref<SendQueueItem>>>>

...but that's getting a little ridiculous... if Ref and DynamicUnmanagedArray are out, it'll have to be

Dictionary<ulong, SendQueueItem>

and SendQueueItem as a class instead of a struct.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually I've just learned NativeList wraps UnsafeList, and UnsafeList has ref T ElementAt, which is exactly what I need. I'll replace DynamicUnmanagedArray with NativeList. And since the NativeList is just a wrapper around a pointer to the UnsafeList, I won't need to use Ref<> there either.

NativeHashMap<ulong, NativeList<SendQueueItem>>

supports everything I need. I'll make that change once I get the develop merge finished.


namespace Unity.Netcode
{
public struct DynamicUnmanagedArray<T> : IReadOnlyList<T>, IDisposable where T : unmanaged
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to where it's currently used, I'm also intending to update FastBufferReader/FastBufferWriter to be able to use this. The big benefit is that length and capacity are dissociated here and the length value can be changed, so this would give us the ability to have messages with dynamic-length data that could be written directly into this array. This would mean we could reuse one array for reading a given piece of data and it'd be able to grow if needed (and FastBufferReader could do that automatically) and if it doesn't need to grow, FastBufferReader could read a variable amount of data and update the length value without having any allocations... and when it grows it would allocate through Malloc instead of GC...

Downside, though, is that it has to be manually freed via Dispose() when it's no longer being used. So I'm open for feedback on whether something like this is useful/good, or too dangerous to be willing to accept.

# Conflicts:
#	com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef
#	com.unity.netcode.gameobjects/Tests/Editor/com.unity.netcode.editortests.asmdef
…ConnectionApprovedMessage.

A couple of tests are known to be failing... this is to let the work start being divided up.
@ShadauxCat ShadauxCat changed the title First INetworkMessage WIP - not hooked up to anything yet. INetworkMessage WIP - code complete, tests still failing. Sep 14, 2021
@ShadauxCat ShadauxCat changed the base branch from feature/inetworkmessage to develop September 14, 2021 02:40
@ShadauxCat ShadauxCat changed the base branch from develop to feature/inetworkmessage September 14, 2021 02:40
@ShadauxCat ShadauxCat changed the title INetworkMessage WIP - code complete, tests still failing. INetworkMessage WIP - code complete, tests passing, gonna open a proper PR to develop tomorrow. Sep 15, 2021
/// <summary>
/// ulong array version of target id list - use either this OR TargetClientIdsNativeArray
/// </summary>
public ulong[] TargetClientIds;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you suggested in our discussion we can use IReadOnlyList here to also support List<ulong> here without any downsides.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can it even be more restrictive and become IEnumerable<T> which only provides iteration but not index access?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't that be less restrictive?

# Conflicts:
#	com.unity.netcode.gameobjects/Runtime/Configuration/NetworkConfig.cs
#	com.unity.netcode.gameobjects/Runtime/Core/NetworkBehaviour.cs
#	com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs
#	com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs
#	com.unity.netcode.gameobjects/Runtime/Messaging/IInternalMessageHandler.cs
#	com.unity.netcode.gameobjects/Runtime/Messaging/InternalMessageHandler.cs
#	com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueContainer.cs
#	com.unity.netcode.gameobjects/Runtime/Messaging/MessageQueue/MessageQueueProcessor.cs
#	com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkDictionary.cs
#	com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkList.cs
#	com.unity.netcode.gameobjects/Runtime/NetworkVariable/Collections/NetworkSet.cs
#	com.unity.netcode.gameobjects/Runtime/NetworkVariable/NetworkVariableBase.cs
#	com.unity.netcode.gameobjects/Runtime/Profiling/InternalMessageHandlerProfilingDecorator.cs
#	com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs
#	com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs
#	com.unity.netcode.gameobjects/Runtime/com.unity.netcode.runtime.asmdef
#	com.unity.netcode.gameobjects/Tests/Editor/DummyMessageHandler.cs
#	com.unity.netcode.gameobjects/Tests/Runtime/NetworkVariableTests.cs
… memory leak if the server forcibly disconnects a client
@ShadauxCat ShadauxCat changed the base branch from feature/inetworkmessage to develop September 15, 2021 19:45
@ShadauxCat ShadauxCat changed the title INetworkMessage WIP - code complete, tests passing, gonna open a proper PR to develop tomorrow. feat: INetworkMessage refactor Sep 15, 2021
@ShadauxCat ShadauxCat closed this Sep 15, 2021
@ShadauxCat ShadauxCat deleted the wip/inetworkmessage branch September 15, 2021 19:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants