Skip to content

fix: Parenting does not preserve WorldPositionStays option and other parenting related issues #2146

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

NoelStephensUnity
Copy link
Collaborator

@NoelStephensUnity NoelStephensUnity commented Aug 24, 2022

When parenting and setting the WorldPositionStays to false on the server, clients would not apply the same WorldPositionStays setting because it was not being synchronized. This PR also provides parent child serialization ordering during the initial client synchronization that happens when the connection is approved.
MTT-4616

For Visual Validation via Manual Testing:

Use the companion test project PR #2207

Changelog

  • Added: Position, rotation, and scale to the ParentSyncMessage which provides users the ability to specify the final values on the server-side when OnNetworkObjectParentChanged is invoked just before the message is created (when the Transform values are applied to the message).

  • Added: NetworkObject.TryRemoveParent method for convenience purposes opposed to having to cast null to either GameObject or NetworkObject.

  • Fixed: Issue where the WorldPositionStays parenting parameter was not being synchronized with clients.

  • Fixed: Issue where parented in-scene placed NetworkObjects would fail for late joining clients.

  • Fixed: Issue where scale was not being synchronized which caused issues with nested parenting and scale when WorldPositionStays was true.

  • Fixed: Issue with NetworkTransform.ApplyTransformToNetworkStateWithInfo where it was not honoring axis sync settings when NetworkTransformState.IsTeleportingNextFrame was true.

  • Fixed: issue with NetworkTransform.TryCommitTransformToServer where it was not honoring the InLocalSpace setting.

Testing and Documentation

  • Includes integration test that validates with and without WorldPositionStays.
  • Includes integration test for in-scene placed multi-generation parented NetworkObjects.
  • Includes integration test for in-scene placed NetworkObject nested under a GameObject with no NetworkObject
  • Documentation updates are necessary. (PR-778)

NoelStephensUnity and others added 6 commits August 17, 2022 13:48
This fixes the issue with the client-side keeping the world position when parenting during initial spawning and when parenting post-spawn.
We should set the position to zero and the quaternion to identity when parenting with the world position stays disabled.
updated comment
minor addition to the same comment.
NoelStephensUnity and others added 5 commits August 24, 2022 17:50
Code clarity.

Co-authored-by: Jesse Olmer <[email protected]>
Code clarity.

Co-authored-by: Jesse Olmer <[email protected]>
Changing the property name to m_WorldPositionStays.
Re-organized the order in which we write and read this value.
Only writing the value in the ParentSyncMessage if we are reparented (not sure why we wouldn't be).
@ShadauxCat
Copy link
Collaborator

Code looks good... will approve once tests are committed.

NoelStephensUnity and others added 4 commits September 8, 2022 14:53
Setting WorldPositionStays when sending  ParentSyncMessage in OnTransformParentChanged.
To better handle late joining players with synchronizing NetworkObjects, added parent child sorting to the SceneEventData's synchronization serialization process.

Added world position stays parameter to CreateLocalNetworkObject to preserve worldPostionStays.
Adding the integration test for this fix.
@NoelStephensUnity NoelStephensUnity marked this pull request as ready for review September 9, 2022 01:44
@NoelStephensUnity NoelStephensUnity requested a review from a team as a code owner September 9, 2022 01:44
Setting position before parenting.
Added the same logic for in-scene placed NetworkObjects.
Testing with and without WorldPositionStays.
@@ -1007,7 +983,6 @@ public override void OnNetworkSpawn()
{
m_CachedIsServer = IsServer;
m_CachedNetworkManager = NetworkManager;
m_TickFrequency = 1.0 / NetworkManager.NetworkConfig.TickRate;
Copy link
Contributor

Choose a reason for hiding this comment

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

💯 nice unused variable

@JesseOlmer JesseOlmer dismissed their stale review September 28, 2022 22:56

No time to review - don't block on me.

adjusted lossyScale comment that was missing "if".
return true;
var parentNetworkObject = transform.parent.GetComponent<NetworkObject>();

// If the parent is a GameObject then preserve that hierarchy
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit... Comment says "if the parent is a GameObject", but the check is actually checking if it's a NetworkObject

Copy link
Collaborator Author

@NoelStephensUnity NoelStephensUnity Oct 3, 2022

Choose a reason for hiding this comment

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

Ahh... yeah I need to improve the clarity here...
If the parent is just a GameObject (without a NetworkObject...which would mean parentNetworkObject is null)... but it could be rephrased to be less confusing indeed.

if (!reader.TryBeginRead(readSize))
{
throw new OverflowException("Could not deserialize SceneObject: Out of buffer space.");
}

if (Header.HasParent)
if (IsLatestParentSet)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not entirely related to this change, but it's just occurred to me that we could make these ulongs into longs and effectively use the sign bit to encode whether they're set or not... read one byte, if it's negative it's unset, if it's positive then seek back one byte and read it as ulong. That is, assuming the actual value of this doesn't ever need that bit...

Could be a useful function in BytePacker... WriteOptionalValue() that writes 8 bytes with a 0 leading bit if set, or 1 byte with a 1 leading bit if unset.

Curious what you'd think of that (not an action item for this PR, just a thought)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually it also occurs to me that there are several boolean values here...

What about writing them as a bitmask using writer.EnterBitwiseContext()?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think we should have a separate PR where we go through all INetworkMessage implementations and see if we can reduce the total size for each message type. Perhaps not in this PR?

if (IsLatestParentSet)
{
writer.WriteValueSafe((ulong)LatestParent);
BytePacker.WriteValuePacked(writer, (ulong)LatestParent);
Copy link
Collaborator

Choose a reason for hiding this comment

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

ooh, fancy.

...but might I suggest looking at BytePacker.WriteValueBitPacked() and see if it's viable? It may not be. It has better compression than WriteValuePacked() but for a ulong it does limit the allowed value to only 61 bits, as it uses the first 3 bits to encode a byte count. (As opposed to WriteValuePacked(), which uses a whole byte to encode the byte count on top of the actual value itself, and if all bytes of the value are needed, it actually makes the encoded value larger than the original instead of smaller)

Copy link
Collaborator

Choose a reason for hiding this comment

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

(In contrast, WriteValueBitPacked() guarantees it will never increase the size of the value, but does have a limitation of only 61 bits... also there's a typo in the comment that says it's limited to 31 bits, but it's not, it's 61.)

Updating comment for clarity as per Kitty's suggestion.
Replacing WriteValuePacked and ReadValuePacked with WriteValueBitPacked and ReadValueBitPacked.
@NoelStephensUnity NoelStephensUnity enabled auto-merge (squash) October 4, 2022 16:16
@NoelStephensUnity NoelStephensUnity enabled auto-merge (squash) October 4, 2022 16:48
@NoelStephensUnity NoelStephensUnity merged commit 64c4331 into develop Oct 5, 2022
@NoelStephensUnity NoelStephensUnity deleted the fix/parenting-does-not-preserve-worldpositionstays-option branch October 5, 2022 00:25
jakobbbb pushed a commit to GooseGirlGames/com.unity.netcode.gameobjects that referenced this pull request Feb 22, 2023
…parenting related issues (Unity-Technologies#2146)

* fix
Parenting:
Fixed the issue where WorldPositionStays was not being synchronized with clients.
Removed m_IsReparented and associated properties.
Added world position stays parameter to CreateLocalNetworkObject to preserve worldPostionStays.
This fixes some edge case scenarios and issues with parenting in-scene placed NetworkObjects.
One in particular is to provide the local space position and rotation in ParentSyncMessage if the WorldPositionStays value is false.  The server might maintain the original local space values and upon being reparented it re-applies the local space values.  This is only pertinent if the user decides they don't want to use a NetworkTransform for the NetworkObject in question.
Now, a users can control what the final position, rotation, and scale of a NetworkObject is when being added/removed to/from a parent's sibling list ("parented or de-parented").
Parenting and Scale:
Fixed issue where GetMessageSceneObject wasn't using the NetworkObject's lossy scale value.  This would result in improper scaling of parented (WorldPositionStays = true) child NetworkObjects for late joining clients.  Added comments with details on this.
Fixed edge case scenario where nested in-scene placed NetworkObject children are de-parented and then a client late joins. Under this scenario (only for in-scene placed NetworkObjects) we want the late joining client to remove its parent, then set the transform values, and then get spawned.

NetworkTransform:
Fixed issue where TryCommitTransformToServer was not honoring the NetworkTransform.InLocalSpace property.
Fixed issue where teleporting did not honor the NetworkTransforms's current synchronize axis settings.
Removed the private m_TickFrequency property as it is no longer being used.

* refactor
CreateLocalNetworkObject now just accepts the NetworkObject.SceneObject structure as opposed to the 10 properties of the NetworkObject.SceneObject structure passed in as parameters.
Provided the ability to invoke NetworkObject.TrySetParent with a null value in order to be able to remove a parent while also selecting whether they want WorldPositionStay to be true or false (when removing a parent WorldPositionStays impacts the final transform values and can lead to scale issue if you parent with WorldPositionStays set to false and then it remove the parent with WorldPositionStays set to true).
Consolidated the CreateLocalNetworkObject's assignment of the transform values, parenting, and DDOL migration.
Removed parenting from CreateLocalNetworkObject
Removed setting the cached parent directly within SpawnNetworkObjectLocallyCommon as this is done within NetworkObject.ApplyNetworkParenting.

* add
Added RemoveParent to the ParentSync message.
Added applying scale to CreateLocalNetworkObject.
Added parent child sorting to the SceneEventData's synchronization serialization process.
Added the TryRemoveParent helper method to make removing a parent easier.  Without this method users will have to use TrySetParent and cast the null value to either GameObject or NetworkObject.

* test
Added ParentingWorldPositionStaysTest:
Testing with and without WorldPositionStays while also varying child position, rotation, and scale values.
Validates scale with multi-generation nested children where most of the parents have a scale other than Vector3.one. 
Created abstract IntegrationTestWithApproximation to avoid replicating the exact same set of Approximately methods.

Added ParentingInSceneObjectsTests:
This includes all of the scripts and assets that the ParentingInSceneObjectsTests needs.
It includes adding the ParentingInSceneObjects scene to the scenes in build list as well.
This also verifies an in-scene placed NetworkObject parented under a GameObject will preserve that hierarchy.

NetworkTransformStateTest:
Updated the bool parameters to be more meaningful named enums so you know what exactly is being tested.
Added additional NetworkTansformState.IsTeleportingNextFrame logic to the existing test.
Added an additional "step" which verifies the NetworkTransformState is additively collapsing for each detected axial delta over the relative threshold value.

Co-authored-by: Jesse Olmer <[email protected]>
Co-authored-by: Unity Netcode CI <[email protected]>
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.

5 participants