Skip to content
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

SyncVar hooks and Sync[Collection] handlers firing during initial spawn #3097

Closed
MrGadget1024 opened this issue Feb 11, 2022 · 3 comments
Closed
Assignees
Labels
bug Something isn't working enhancement New feature or request

Comments

@MrGadget1024
Copy link
Collaborator

MrGadget1024 commented Feb 11, 2022

Currrently for initial spawn payload this happens:

  1. OnObjectSpawnStarted
  2. SyncVar hooks fire as objects are spawned
  3. SyncList / SyncDictionary / etc. callbacks are fired as objects are spawned
  4. OnObjectSpawnFinished

Would be better to defer the calling of hooks and callbacks until OnObjectSpawnFinished instead of firing them randomly during the spawn phase.

Additionally, in OnObjectSpawnFinished, we have this, but I think CheckForLocalPlayer should be first for any given object, not last.

foreach (NetworkIdentity identity in spawned.Values.OrderBy(uv => uv.netId))
{
    identity.NotifyAuthority();
    identity.OnStartClient();
    CheckForLocalPlayer(identity);
}

And finally, it would be ideal if we waited until end-of-frame before running OnObjectSpawnFinished so that Awake on everything we just spawned has had a chance to fire, where callback handlers can get wired up before we invoke them.

public readonly SyncList<SomeStruct> structData = new SyncList<SomeStruct>();

void Awake()
{
    // this needs to fire before OnObjectSpawnFinished runs
    structData.Callback += OnDataUpdated;
}

One more thing...

Now that we have Client To Server, SyncVars don't fire the hook on the owner. It may be that the owner gets the update, but since it already matches the value owner has, the hook would be suppressed as "not changed". I think if Client To Server is set and hasAuthority is true, we should fire the hook on the owner. Otherwise we need to document that it won't fire so users know what to expect.

@MrGadget1024 MrGadget1024 added the enhancement New feature or request label Feb 11, 2022
@miwarnec miwarnec added the bug Something isn't working label Feb 12, 2022
@miwarnec
Copy link
Collaborator

there are a few options later.
mostly after we moved OnDeserialize to C#.

  • we could have a list of Actions for the hook. invoke them all after spawn. only issue here is the parameters. casting to object would allocate
  • we could have list of actions and byte[] writers. then deserialize for the hook.
  • OR: call all OnDeserialize only after all were spawned. but then they wouldn't have the correct data by the time the hook gets called. i.e. A.hook() might try to access B, which didn't have deserialize called yet

@David548219
Copy link

This issue introduces all kinds of undesirable side effects. For example, when hooks are fired for initial spawn isClient & isServer variables are not set and will always be false, which is not very intuitive.

Current workaround I use to fix execution order and all side effects (perhaps it will help while this is not fixed):

[SyncVar(hook = nameof(TestHook))] private int hookedSyncVar = 0;

private bool muteHooks = true;

private void TestHook(int oldVal, int newVal) {
    if (muteHooks) return;
    
    // Hook logic goes here
}

public override void OnStartClient() {
    base.OnStartClient();
    
    muteHooks = false;
    if (hookedSyncVar != 0)
        TestHook(0, hookedSyncVar);
}

All of the observations and suggested workaround are from version 66.0.9.

MrGadget1024 added a commit that referenced this issue Dec 29, 2024
Virtually eliminates null refs with GO/NI/NB SyncVars by getting all objects into spawned dictionary before applying payloads and invoking hooks.
- No change to normal spawning, only initial spawn
- Uses and clears a static dictionary, so no garbage allocation

See ticket #3097
miwarnec added a commit that referenced this issue Dec 30, 2024
#3097 (#3961)

* fix(NetworkClient): Defer ApplySpawnPayload until OnObjectSpawnFinished
Virtually eliminates null refs with GO/NI/NB SyncVars by getting all objects into spawned dictionary before applying payloads and invoking hooks.
- No change to normal spawning, only initial spawn
- Uses and clears a static dictionary, so no garbage allocation

See ticket #3097

* Update NetworkClient.cs

* Update Assets/Mirror/Core/NetworkClient.cs

---------

Co-authored-by: mischa <16416509+miwarnec@users.noreply.github.com>
@miwarnec
Copy link
Collaborator

#3961

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants