-
Notifications
You must be signed in to change notification settings - Fork 447
Linking Deterministically client-pre-instanced NetworkObjects #3421
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
Comments
Thanks for being so responsive on this! This looks like something that is a real gap in NGO at this time. To summarize what I understand of the problem: The most important thing from the NGO side is that the solution chosen needs to be:
In order for the approach to be intuitive, we prefer symmetrical solutions and to follow patterns that are already existing in the NGO package. NGO currently has two patterns main patterns for serializing custom user data around network objects: I'm thinking a flow where somehow a custom data handler of some type is registered against the PrefabHandler instance. This custom handler has methods that are invoked as part of the local spawn flow (where the custom data is serialized) and the remote spawn flow (where the custom data is deserialized), then the custom data can be used inside the What are your thoughts on this? |
Thanks! You understood it perfectly, and yes, that's exactly what I’ve been proposing. This isn't about the initial PR description, but one of the follow-up comments I made later in the thread:
Something like this: // On Server:
// Sets custom spawn data for the specified prefab handler.
// The handler must be registered for the given prefab and must implement INetworkCustomSpawnDataReceiver.
// Logs an error if the handler is not registered or does not implement the required interface.
NetworkManager.SpawnManager.SetCustomSpawnData(myBasePrefabToPointTheHandler, customData);
// Internally, the spawn system passes the custom spawn data to the handler.
// This allows the registered INetworkCustomSpawnDataReceiver to process it prior instantiation.
NetworkManager.SpawnManager.InstantiateAndSpawn(myBasePrefabToPointTheHandler); // On the handler:
class MyHandler : INetworkPrefabInstanceHandler, INetworkCustomSpawnDataReceiver
{
// Called just before Instantiate() , providing the spawn data.
// (Note: Could use FastBufferReader instead of byte[] for better alignment with NGO)
public void OnCustomSpawnDataReceived(byte[] customData)
{
// Parse and store the data for use in Instantiate().
}
// Called immediately after OnCustomSpawnDataReceived().
public void NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
{
// Use the cached data to apply advanced logic during the spawn flow,
// for example: returning a pre-instantiated object based on the received data.
}
} To support late joins and scene objects, I suggest placing customSpawnData directly inside SceneObject, toggled via a bitmask. This avoids the need for:
🔶Happy to move forward with implementation if this direction works for you.
|
This approach you've outlined is definitely an improvement on changing the The concern at the moment is that the approach is not symmetrical. It's important for NGO from a usability perspective that if you set custom data, it's hard to forget to get that custom data. From our side we'd prefer a single place with a single hook for both serializing and deserializing. The two main approaches that NGO currently has for this symmetrical synchronization of data are:
We could put something similar to The other option you have is to have a root NetworkObject with a NetworkBehaviour, and then do the instantiation as a child object based on the data that is passed through |
Ok i get why you prefer having symmetry. That said, about the alternative you proposed, these are details on why wont work on my case:
Using a root NetworkObject plus child instantiation based on OnSynchronize:
Note Other alternatives i thought for mi case would be more datadriven or using Netcode for Entities, but would mean a massive rework just to handle something that right now is basically working cleanly in that PR. A Cleaner, Symmetrical AlternativeA way to align perfectly with NGO’s symmetry style would be to tweak the Instead of just introducing public class MyCustomHandler : INetworkPrefabInstanceHandler, INetworkCustomSpawnDataSynchronizer
{
public int deterministicId;
public float otherDataExample;
protected void OnSynchronize<T>(ref BufferSerializer<T> serializer)
{
serializer.SerializeValue(ref deterministicId);
serializer.SerializeValue(ref otherDataExample);
}
public NetworkObject Instantiate(ulong ownerClientId, Vector3 position, Quaternion rotation)
{
// Use the custom spawn data here
}
} Setup would remain the same as always: MyCustomHandler myCustomHandler;
public override void OnNetworkSpawn()
{
NetworkManager.AddNetworkPrefab(basePrefab);
NetworkManager.PrefabHandler.AddHandler(basePrefab, myCustomHandler);
// If the handler implements INetworkCustomSpawnDataReceiver, no extra work needed
}
public override void OnNetworkDespawn()
{
NetworkManager.PrefabHandler.RemoveHandler(basePrefab);
NetworkManager.RemoveNetworkPrefab(basePrefab);
}
void ExecuteSpawnMethod()
{
// Pass the custom spawn data before calling Spawn()
myCustomHandler.deterministicId = oneOfMyAlreadyInstantiatedNetworkObjects.deterministicId;
oneOfMyAlreadyInstantiatedNetworkObjects.GetComponent<NetworkObject>().Spawn();
} Internally, NGO would just call Does this sound like a better fit for NGO design? |
Copying the Unfortunately, there's a few further constraints on the code design. It's very important to ensure that adding this feature is not going to change or slow down the object instantiation process for any game that does not need custom data to be sent before instantiation. Any new system that is being added into NGO needs to be added in a way that does not require changes in any of the existing test suite. Any tests that need to be changed as a result of this change means that we are requiring changes in user's projects when they want to upgrade to the new version of NGO with containing new design. Generics are hard to maintain and expensive to run, we tend to prefer to avoid generics in the NGO codebase for optimization reasons. It would be preferable if this approach to synchronizing the custom data is synchronized during the The pattern of usage in this approach is really nice. We just need to keep iterating to ensure we're future-proofing the code, and ensuring that we're keeping the NGO package open and flexible to all different types of games. |
It's ok! I will try the "moving as much of possible into the I will keep working on it and get back to this post once I'm ready. It would be great if we could stay in touch through the PR draft instead, linking yourself as a reviewer, so feedback would make more sense in context. And again thanks for the feedback! ❤️ |
I got something working with your feedback already, you might wanna check it when you have time 😄 |
This approach is much cleaner. Now that we've iterated much closer to a solution that works, I'm happy to move the conversation into the pull request. Closing this issue and moving the conversation into #3430 |
Problem
Right now, Netcode for GameObjects (NGO) doesn’t provide a way to pass custom metadata during a
NetworkObject.Spawn
that can be read inside theINetworkPrefabInstanceHandler
.This makes it difficult to set up custom spawn logic, for example, using an ID or config sent from the server to decide which object to instantiate on the client side.
Use Case
In my case, I manually instantiate objects on both the server and the clients before they’re spawned over the network.
I have a level editor, and both clients need to create the level following the creation cycle I defined.
so my intention is BOTH clients creates the objects for the level and they store them where needed
then sending a signal to LINK currently existing items by an ID
For example i create, Apple, Orange, Banana, in the client
and Apple, Orange, Banana, in the other client
Then one of the clients (the host)
sends Link data, to spawn their objects in network, but i dont want the other client to re-create these objects, i want the client to use the exact same instaces that where deterministically created
When the server spawns the object, I want each client to match that spawn with an already existing local instance, using something like a shared ID.
Right now, the only way to do this is by sending an RPC first (to set the metadata into the PrefabHandler instance), and then calling
Spawn()
.But this has a problem: the spawn message and the RPC might not arrive in order, especially if several spawns happen in the same frame.
That makes the system fragile and hard to scale.
Proposal
Allow developers to send a small data buffer (like metadata or an ID) alongside the spawn call.
This buffer would then be passed into the
Instantiate()
method in theINetworkPrefabInstanceHandler
, so that the developer can choose or configure the right object at the time of spawn.For example:
The key points on this chanfe is:
INetworkPrefabInstanceHandler
flowhere is the pull request of the change already made in order to understand it better
#3419
The text was updated successfully, but these errors were encountered: