-
Notifications
You must be signed in to change notification settings - Fork 450
feat: snapshot. Fully integrated despawn, mtt-1092, mtt-1056 #1062
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
…hot-system-spawn3
…hot-system-spawn3
…Technologies/com.unity.multiplayer.mlapi into experimental/snapshot-prep2-spawn
…apshot-system-spawn3
…apshot-system-spawn3
…hot-system-spawn3
…hot-system-spawn3
…hot-system-spawn3
…hot-system-spawn3
…hot-system-spawn3
…function. This will allow reusing it from NetworkShow. But as-is, no impact
…<NetworkObject>) instead of calling a deeper-nested function. Make the code more uniform and prepares for incoming snapshot changes
…n' into experimental/snapshot-system-spawn3
…ide(List<NetworkObject>). Same reasons as for NetworkShow
…n' into experimental/snapshot-system-spawn3
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.
Looks good to me.
:)
{ | ||
SnapshotDespawnCommand command; | ||
command.NetworkObjectId = NetworkObjectId; | ||
command.TickWritten = default; // will be reset in Despawn |
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.
Not sure if I understand this comment. Will this value later be set correctly by the SnapshotSystem or?
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.
Yes, tickWritten is meant to be set by SnapshotSystem, not by the calling code. But for some obscure C# design reason, apparently that struct can't have either default constructor nor static initializer.
I'll change the comment to // will be set internally by SnapshotSystem
as the comment is from before it was moved.
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.
ok, so when we call this function we know we want a Despawn command but we don't know yet what tick it is associated with because we haven't written it yet and are not sure which tick it will be written on?
I guess I always thought the associated tick would be the server tick on which it occurred not when it was written, which I think we would know at this point.
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.
The way it is handled, "on which it occurred" is "when it was written". I just didn't want NetworkObject
to do it because NetworkObject
doesn't need to care about time.
internal void Spawn(SnapshotSpawnCommand command)
{
command.TickWritten = (ushort)m_CurrentTick;
this is called when the spawn/despawn arrives in Snapshot. Not when the command is sent. So, yeah, the original comment was very misleading.
// will be set internally by SnapshotSystem
is the new comment. Please let me know if it is still unclear
@@ -9,7 +9,7 @@ public class SnapshotRttTests | |||
[Test] | |||
public void TestBasicRtt() | |||
{ | |||
var snapshot = new SnapshotSystem(); | |||
var snapshot = new SnapshotSystem(NetworkManager.Singleton); |
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.
So this is an editor test. And I don't see us creating a NetworkManager in this test or in SetUp/Teardown. So where does this NetworkManger.Singleton come from. Pretty sure it is null. But then why is this test not failing?
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.
nice catch. As it turns out snapshot
is only used to snapshot.GetConnectionRtt
. It doesn't run or anything. The rest is really unit testing the ConnectionRtt
. As such, it doesn't care to have a null
NetworkManager.
I think it would be clearer if we did:
var snapshot = new SnapshotSystem(default);
And then, it would still compile when the Singleton gets removed in the future.
if (count > 20) | ||
{ | ||
// timeout waiting for object to reach the expect visibility | ||
Debug.Assert(false); |
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.
Debug.Assert(false); | |
Assert.Fail("timeout waiting for object to reach the expect visibility"); |
I think we should use NUnit asserts here and not unity logger.
@@ -10,21 +10,31 @@ namespace Unity.Netcode | |||
// Might include tick in a future milestone, to address past variable value |
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.
I can understand that spawn/despawn have to be part of the SnapshotSystem. (even though I'd prefer them to be separate 😃). But could we consider maybe moving them into a partial class for better code separation and visibility. (What I mean is make SnapshotSystem partial then create SnapshotSystem.Spawning.cs and add a partial SnapshotSystem class to it and move all spawning functions over.
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.
MTT-1099
:-) that's the best I can offer at the moment. Acknowledging it, and remembering to come back to it.
@@ -437,7 +555,7 @@ private INetworkVariable FindNetworkVar(VariableKey key) | |||
|
|||
internal class ClientData | |||
{ | |||
internal struct SentSpawn | |||
internal struct SentSpawn // also despawns |
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.
I don't understand what this code comment means.
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.
SentSpawnOrDespawn
would be the proper class name. The comment is a bit terse. Maybe I could comment // this struct also stores Despawns, not just Spawns
{ | ||
if (NumSpawns >= m_MaxSpawns) | ||
{ | ||
Array.Resize(ref Spawns, 2 * m_MaxSpawns); |
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.
Why are we not just using a list for this? Looks like we keep track of size and resize which is exactly what a list is for?
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.
That might be me coming from C++. Are C# list contiguous in memory ? If so, do they grow in an amortized linear fashion ? If both are yes, then sure, List would work then, too.
What I wanted to avoid was triggering memory alloc at every AddSpawn
. I'll learn on C# Lists first, then we can decide.
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.
Might not be too early to use NativeList
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.
I'm willing to add a bug/task to review it and then prioritize, but I'd rather have this go in as a first step, to keep things moving.
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.
Lists in c# are contiguous in memory. They grow by doubling the size of their underlying buffer (and memory copy the old content). Same goes for NativeList.
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.
👍 thanks, I really got confused by their naming.
MTT-1101 will address this.
s.SequenceNumber = clientData.SequenceNumber; | ||
clientData.SentSpawns.Add(s); | ||
|
||
writer.WriteUInt64(despawn.NetworkObjectId); |
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.
writer.WriteUInt64(despawn.NetworkObjectId); | |
writer.WriteUInt64Packed(despawn.NetworkObjectId); |
{ | ||
SnapshotDespawnCommand command; | ||
|
||
command.NetworkObjectId = reader.ReadUInt64(); |
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.
command.NetworkObjectId = reader.ReadUInt64(); | |
command.NetworkObjectId = reader.ReadUInt64Packed(); |
clientData.SentSpawns.Add(s); | ||
|
||
writer.WriteUInt64(despawn.NetworkObjectId); | ||
writer.WriteUInt16(despawn.TickWritten); |
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.
Ticks are now ulongs
in the new tick system. I think we might need to adjust this here?
{ | ||
if (command.TargetClientIds == default) | ||
{ | ||
command.TargetClientIds = GetClientList(); |
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.
This allocates each spawn/despawn. Don't think we can fix this here quickly. Just pointing out that we are again adding a significant amount of allocations.
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.
Yes. This is unfortunate, but hopefully bearable for "small scale coop". The plan is to replace this with a bit mask, but I'm not seeing this making it in time for the next release.
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.
The overall logic itself looks functional to me and should allow us to do spawns/despawns over snapshots. Added a few suggestions to improve code quality and clarity.
@@ -234,6 +234,11 @@ public void NetworkShow(ulong clientId) | |||
throw new VisibilityChangeException("The object is already visible"); | |||
} | |||
|
|||
if (NetworkManager.NetworkConfig.UseSnapshotSpawn) |
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.
Interesting, when I look at this code I'm expecting to see an 'else' where the old way of spawning happens. So SnapshotSpawn
doesn't include a call to SendSpawnCallForObject
? Could 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.
I've put in Slack a link to the lucidchart diagram about this "[Thursday PR]". NetworkObject.Show
calls directly into Snapshot. It then calls into SendSpawnCallForObject
in SpawnManager
. The old way, in SpawnManager
is then disabled, there. That's where the missing else
is
That design came around because the other flow, SpawnInternal
, can be put in Snapshot at once (one command for all connections). If I had put the Snapshot hook in SendSpawnCallForObject
then SnapshotSystem
would get the same spawn multiple times. Similarly if I had refactored the old ways to be done for all connections at once, I would have touched code I didn't need to and risk breaking things.
command.ObjectPosition = transform.position; | ||
command.ObjectRotation = transform.rotation; | ||
command.ObjectScale = transform.localScale; | ||
command.TickWritten = default; // will be reset in Spawn |
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.
Same as above, I thought we record tick it happened not tick it was written out
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.
we do record the tick it was written, just deeper in the stack:
SnapshotSystem.Spawn() at com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs:line 849
NetworkObject.SnapshotSpawn() at com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:line 451
NetworkObject.SpawnInternal() at com.unity.netcode.gameobjects/Runtime/Core/NetworkObject.cs:line 493
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.
internal void Spawn(SnapshotSpawnCommand command)
{
command.TickWritten = m_CurrentTick;
{ | ||
Array.Resize(ref Spawns, 2 * m_MaxSpawns); | ||
m_MaxSpawns = m_MaxSpawns * 2; | ||
Debug.Log($"[JEFF] spawn size is now {m_MaxSpawns}"); |
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.
[MATT] Did you mean to leave this in? :)
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.
Actually, yes. But if it is an issue, I'm willing to remove it. The release will not be cut just yet, and even if it were, this is not used, by default. Even it it were, this log would only occur every time the buffer doubles, so, at most 4-5 times ?
Otherwise, with the number of merges we do, I found I was wasting tons of time adding/removing these. Your call.
{ | ||
Array.Resize(ref Despawns, 2 * m_MaxDespawns); | ||
m_MaxDespawns = m_MaxDespawns * 2; | ||
Debug.Log($"[JEFF] despawn size is now {m_MaxDespawns}"); |
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.
Comment leftover?
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.
Same as above.
…nsform * develop: (21 commits) test: adding more details to multiprocess readme (#1050) refactor!: convert NetworkTransform.NetworkState to `struct` (#1061) fix: networkmanager destroy on app quit (#1011) feat: snapshot, using unreliable packets, now that the underlying foundation supports it. Only merge after #1062 (#1064) feat: snapshot. Fully integrated despawn, mtt-1092, mtt-1056 (#1062) fix: eliminate bad use-after-free(destroy) pattern (#1068) chore: cleanup meta files for empty dirs (#1067) chore: minor MLAPI to Netcode rename (#1065) feat: report network behaviour name to the profiler (#1058) fix: player movement (#1063) test: Add unit tests for NetworkTime properties (#1053) chore: remove authority & netvar perms from NetworkTransform (#1059) feat: networktransform pos/rot/sca thresholds on state sync (#1055) feat: expose network behaviour type name internally (#1057) chore: remove all the old profiling code (#1048) fix: if-guard `NetworkManager.__rpc_name_table` access (#1056) fix: Disabling fixedupdate portion of SpawnRpcDespawn test because it's failing for known reasons that will be fixed in the IMessage refactor. (#1049) feat: Implement metrics for the new network profiler (#960) chore!: change package name & asmdefs (#1026) feat: per axis networktransform state sync (+bitwise state comp) (#1042) ... # Conflicts: # com.unity.netcode.gameobjects/Prototyping/NetworkTransform.cs
…echnologies#1062) * feat: snapshot. Attempt at applying snapshot spawn branch over new message queue * feat: snapshot. Reusing spawn entries. * feat: snapshot fix, prefab handling * feat: snapshot. reduce log spam * snapshot: merge preparation. Removing old acks, removing unused variables * feat: snapshot. merge preparation. Removing old acks, removing unused variables * feat: snapshot. more reasonable sentinel checks around snapshot send/receive * feat: snapshot. small clenaup * feat: snapshot. Moving configuration items to NetworkConfig * feat: snapshot. Adjusting to code standard * refactor: moved code from SpawnInternal into separate SendToSnapshot function. This will allow reusing it from NetworkShow. But as-is, no impact * refactor: calling networkShow(NetworkObject) code in networkshow(List<NetworkObject>) instead of calling a deeper-nested function. Make the code more uniform and prepares for incoming snapshot changes * style: whitespace * refactor: also using NetworkHide(NetworkObject) to implement NetworkHide(List<NetworkObject>). Same reasons as for NetworkShow * feat: snapshot. Safer access to Connection RTT structures * feat: snapshot. placeholder comment for code to come later * feat: snapshot. Snapshot fully working for spawn test. Keeps reference to proper network manager. Allows sending spawn to individual instances * feat: snapshot. despawn going via snapshot. snapshot message size limitation. snapshot message packing in a LRU fashion * feat: snapshot. Incrementing the local span and despawn store (amortized linear) when game code demands too much spawn/despawns * feat: snapshot. Using different list to keep track of spawn despawn times. Probably not needed, might be revert in the future, but eases debugging. Also debug prints, obviously temporary * feat: snapshot. extra logging for debugging * fix: snapshot. MTT-1056 despawn loss * feat: snapshot. Proper test for length used in snapshot message. Removed redundant condition for target client list * fix: snapshot. NetworkHide in snapshot mode was incorrectly hiding the object on all machines * feat: snapshot. Keeping snapshot disabled for this release * style: coding standards fix * test: making the networkshow/hide test more resistant to timing difference on the test machine, waiting half a second instead of a tenth. * test: removing Sleep() code in NetworkShowHide that attempted to deal with timing. Used explicit timeout instead. Less flaky * feat: snapshot. First pass at PR code review * feat: snapshot. Adjusting to int ticks * feat: snapshot. Reducing the amount of logging, as this is getting merged to develop Co-authored-by: M. Fatih MAR <mfatihmar@gmail.com>
…ndation supports it. Only merge after Unity-Technologies#1062 (Unity-Technologies#1064)
This completes the despawn with snapshot feature, leaving it disable by default.
Also makes all the API internal as opposed to public.
Fixes issues with despawning.
The SnapshotSystem.cs file is still prototypical, but the other files are fully up for review.