-
Notifications
You must be signed in to change notification settings - Fork 450
fix: Fix NetworkVariable<INetworkSerializable> so that it will properly call NetworkSerialize instead of using the default memcpy operation. #1383
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
Conversation
…ly call NetworkSerialize instead of using the default memcpy operation.
Co-authored-by: Andrew Spiering <andrews@unity3d.com>
@@ -24,6 +24,7 @@ Additional documentation and release notes are available at [Multiplayer Documen | |||
- Added `try`/`catch` around RPC calls, preventing exception from causing further RPC calls to fail (#1329) | |||
- Fixed an issue where ServerClientId and LocalClientId could have the same value, causing potential confusion, and also fixed an issue with the UNet where the server could be identified with two different values, one of which might be the same as LocalClientId, and the other of which would not.(#1368) | |||
- IL2CPP would not properly compile (#1359) | |||
- Fixed NetworkVariable not calling NetworkSerialize on INetworkSerializable types (#1383) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we move this up into the unreleased section as pre.3 is going to go out today and this will likely land in pre.4 which will include this and a UTP update to pre.8 for the UTP Adapter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
do you mean also I'm not entirely sure if all this complexity would be justified by just "avoiding boxing allocation". |
@MFatihMAR
No - I mean that one way to avoid boxing is to look up the correct method using reflection and basically do exactly what this solution does. The difference being in how it finds the delegate. Boxing would actually be done twice - apparently |
alright, I got you now. well, you can have only 1 object boxing with something like this: if (myNetVar is INetworkSerializable serializableNetVar) // <-- check type and define the variable with type
{
// do something with `serializableNetVar` which has `INetworkSerializable` type
} so, personally, I'm still not entirely convinced that the code complexity would worth it. |
So the is check will always cause a box in our case but the compiler is smart enough using the syntax above to only do one box.
|
well, what I will say is this implementation was the result of a couple of hours of conversation with @andrews-unity and @mattwalsh-unity . They're entitled to changing their minds, of course, but this was the solution we landed on after discussing several options, one of which was just accepting the box. Personally I think this would be one of the worst possible places to have a box because this would cause One box If user code has 100 objects, each of which is writing to 10 network variables every frame, we'd be saddling our users with 1,000 boxing allocations per frame.... and the number of allocations scales with user usage patterns... I think avoiding that is worth the complexity, but that's just my opinion. |
if we're really trying to avoid it, we could synthesize serialize/deserialize method delegates in the constructor with reflection (as your other reflection-based suggestion). we could use that'd avoid ILPP at a reasonable (one-off) runtime cost and get us a non-boxed solution. |
@MFatihMAR That was actually one of my initial suggestions, but I was told Unity style forbids using reflection at all. Plus, based on what we're seeing with tests, xbox one, xbox scarlet, and switch may not even work with that approach.... they seem to be aggressively removing functions that we only access via reflection. |
To be clear I do not think we should be using reflection within Runtime code and we generally avoid doing so at all costs with most Unity code. (not saying we don't do it at times but you won't find many cases within the core engine or many packages where we are doing it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the constraints and problems we have, I guess I'll bite the bullet here
…ly call NetworkSerialize instead of using the default memcpy operation. (#1383) * fix: Fix NetworkVariable<INetworkSerializable> so that it will properly call NetworkSerialize instead of using the default memcpy operation. * Changelog entry * standards fix * Whoops, syntax error. * More standards fixes * Fixed typo * Moved changelog entry to Unreleased
…ly call NetworkSerialize instead of using the default memcpy operation. (#1383) (#1463) * fix: Fix NetworkVariable<INetworkSerializable> so that it will properly call NetworkSerialize instead of using the default memcpy operation. * Changelog entry * standards fix * Whoops, syntax error. * More standards fixes * Fixed typo * Moved changelog entry to Unreleased Co-authored-by: Jaedyn Draper <284434+ShadauxCat@users.noreply.github.com> Co-authored-by: Unity Netcode CI <74025435+netcode-ci-service@users.noreply.github.com>
…ly call NetworkSerialize instead of using the default memcpy operation. (Unity-Technologies#1383) * fix: Fix NetworkVariable<INetworkSerializable> so that it will properly call NetworkSerialize instead of using the default memcpy operation. * Changelog entry * standards fix * Whoops, syntax error. * More standards fixes * Fixed typo * Moved changelog entry to Unreleased
…ly call NetworkSerialize instead of using the default memcpy operation. (Unity-Technologies#1383) (Unity-Technologies#1463) * fix: Fix NetworkVariable<INetworkSerializable> so that it will properly call NetworkSerialize instead of using the default memcpy operation. * Changelog entry * standards fix * Whoops, syntax error. * More standards fixes * Fixed typo * Moved changelog entry to Unreleased Co-authored-by: Jaedyn Draper <284434+ShadauxCat@users.noreply.github.com> Co-authored-by: Unity Netcode CI <74025435+netcode-ci-service@users.noreply.github.com>
This is implemented by putting NetworkVariable's read and write functionality inside a delegate and using ILPP to override that delegate at startup for INetworkMessage types.
The reason this is done is to avoid runtime reflection and boxing in NetworkVariable - without this, NetworkVariable would need to do a
var is INetworkSerializable
check, and then cast to INetworkSerializable, both of which would cause a boxing allocation. Alternatively, NetworkVariable could have been split into NetworkVariable and NetworkSerializableVariable or something like that, which would have caused a poor user experience and an API that's easier to get wrong than right. This is a bit ugly on the implementation side, but it gets the best achievable user experience and performance.MTT-1444
Changelog
com.unity.netcode.gameobjects
Testing and Documentation