In this repro file the yellow parts dropped by the server have parts welded to them on the client.
This causes network ownership to change.
Expected Behavior:
Parts that are created on the client and only ever seen by the client should not be able to change the network ownership of parts created by the server that also have their network ownership set manually using SetNetworkOwner(nil).
Actual Behavior:
Parts that are owned by the server have their network ownership changed by the local parts. This causes issues in tycoon-style games with typical Drop part > travel on conveyor > collect (delete) part setups such as in the repro file above. Sometimes parts can tunnel through the collector, impacting gameplay.
Workaround:
Setting RootPriority to be very high on the parts that should always retain network ownership by the server fixes the issue.
Something to keep in mind. Local-only parts as they are possible right now are not a 100% supported behavior. At this point, while its been used widely by the community I don’t think we will ever break using Client-local parts only, but we cannot always fix curious problems like this.
I’ll explain.
Roblox networking relies on a deterministic computation that tries to optimally pick a Root Part in all assemblies consistently. If you spawn three parts on the server, weld them together, each client and server will run their own “What is the root” calculation that will arrive to the same answer.
The tricky part is that when you introduce local-only objects into the engine, you pollute this computation and risk deviating it between the server and other clients. Because of this, you risk to break physics replication, network ownership, and other synchronization systems.
The RootPriority work around can save you in some cases as you have discovered, but be very careful when creating local-only objects, ESPECIALLY when welding them to objects the Server and other Clients know about.
TLDR:
Local-parts are not a fully support behavior (We won’t break them, but do be careful with how you use them)
Welding local parts to non-local parts runs the risk for breaking replication for those objects.
Makes sense to me. It feels like that should all be documented however, considering how widely used local parts are. The article on network ownership does not mention these things at all, and it feels like at least root priority should be mentioned somewhere in there.
However, one thing I noticed was that, using part:GetNetworkOwner() on the parts that have their network ownership changed by local parts will always return nil, even though ownership has been handed off to the client. Could this be fixed so that it is always clear to the server when network ownership changes, even if the source of the ownership transfer is not known to the server?
Does this mean you recommend avoiding local parts everywhere possible? ie static or physically simulated parts? (I think bullets must still be local?).
One case I’ve always wondered about (and which has come up again now for me) is turrets that are placed by players and need to be animated identically by all players. I could either make the turrets local (perhaps with just hitboxes on the server) and animate locally, or create the turret models on the server and animate locally, relying on streaming enabled for efficiency. I made a post about this a while ago and previously went with the all-local approach, but it sounds like you might recommend non-local turrets instead?