Setting NetworkOwner(nil) on every part in my level causes sync issues on mobile

I am making a 1 player physics based game.

Currently this is broken because half of my level is simulated locally and half is server-side. Impulses from collisions across boundaries do not reliably propagate (if they do at all - it’s like the parts on the other side of the boundary are anchored)

My work around is to set NetworkOwner(nil) (server) on every part in my level recursively.

On mobile this causes parts to get out of sync between gfx and physics. I have floating parts in space that are not where they think they are (you can walk through them).

I haven’t seen the same behavior on desktop but I also haven’t really stress tested it.

2 Likes

cc @Khanovich

Do you have a link? Interpolation is not affected by NetworkOwnership code, besides the fact that something not owned by your is interpolated.

Steps to repro:

  1. Upload to ROBLOX
  2. Recommend set max players in your game to 1
  3. Join game
  4. Shoot ball at some of the stacks of blocks (the different tools at the bottom shoot differented sized balls)
  5. You will maybe see phantom blocks as pictured

I tried it on my phone while sitting next to my wifi router and I didn’t notice the issue. When I played downstairs on my tablet I noticed it. Maybe if the packets are lost we never resend? It’s fairly reproducible.

This same level can also be used to repro impulses not traveling across client/server boundaries.

  1. Disable the script in ServerScriptService
  2. Now when you shoot the ball at towers of blocks sometimes they don’t fall over

Physics ownership.rbxl (17.4 KB)

I will also leave it up here - https://www.roblox.com/games/410071311/Blocks

Sorry I had to upload over it because otherwise Studio won’t load the most recent assets for my game.

Protip: you can press home + lock button simultaneously to take a screenshot on any iOS device.

iOS devices suck.

Sure you can cap the image, but getting it to your main machine is a pain in the ass.

I don’t save my email credentials on my tablet.

imgur?

I turned on Diagnostics ShowOwners and SetNetworkOwner(player) is completely broken.

Even after calling it recursively on my level for a player that just joined, I am still seeing parts owned by the server.

I found a bug in my recursive call. It’s not recursive. :relaxed:

However, I fixed it and I am still seeing parts with no owner (how is this possible, they aren’t anchored?) when I set Owner to nil.

cc @zeuxcg

Ok. I figured it out. SetNetworkOwner is ignoring an edge case.

Here’es the deal:

If I set the network owner of a part that has no owner because it is currently welded (or snapped) to an anchored part (in this case the baseplate), and that part later becomes unwelded/unsnapped from that part, it does not remember that I have set the network owner. Instead it defaults to Auto. It is hard for me to trap this case in Lua land. Therefore I believe the engine needs to track this state for me.

Given this behavior I would further speculate that NetworkOwnership does not survive being welded/unwelded to a part with no owner.

2 Likes

NetworkOwnership is stable under the following conditions:

  • Welding two objects with the SAME Owner and MANUAL ownership.
  • Breaking up a single object that is set to a MANUAL network owner.
  • Anchoring a specific part on an assembly and then unanchoring it (making sure it’s not grounded)

NetworkOwnership reverts to Auto under the following conditions:

  • Welding two objects with different owners, or one is NOT set to Manual.
  • Anchoring the entire assembly.

Network Ownership does not survive being welded/unwelded to an anchored part, because it itself becomes “Grounded”.

Your post brought up a good point that these internal behaviors are not documented. I’m talking to Urist about getting that post fleshed out to include the internal behaviors for when you weld/anchor parts.

Quick off-topic to OP but related to network ownership question. If you create a part locally, does it automatically have its network ownership forced to the creating client? Or does it have some sort of other behavior?

Local Scripts create a part that is immediately assigned to the creator (but Automatic, meaning the server can override).

Server Scripts create a part that is Unassigned, and lets the server’s Automatic process assign an owner.

1 Like

In a FE game, does that ever happen though since as far as the server is aware, the part doesn’t exist (creation doesn’t replicate to server)?

FE Game the server doesn’t know about the part. So it should be owned by only the creator.

There was a weird clause that used to require a player character’s head to exist to assign the ownership to that player and start simulating. I saw that and went “Lolwut” and made it so that ALL parts created locally give ownership to the creator. Unfortunately I only enabled this like a month ago.

1 Like

So for my particular use case, I wanted to load a level server-side and then pass ownership of the entire level to the client.

Here is my algo:

  1. Load level using InsertService into ServerStorage
  2. Clone level
  3. Anchor every part in level (that isn’t already anchored, making a list of them)
  4. Parent level to workspace
  5. Set ownership to Client
  6. Unanchor parts in list

I have found that without steps 3 and 6 my level will replicate to the client before I have the opportunity to make sure that the entire thing is set to be owned by the client. This causes my level to fall apart at the boundaries of what the client is simulating and what the server is simulating.

What I needed was an “atomic” or “transactional” way to complete the entire operation of “clone this thing and set the owner to X”. I think this would probably be required for any large model/vehicle that you want to set the owner on. One possible nice way to do this would be to extend SetNetworkOwner to models so there is only one message that goes over the wire instead of the 100s I have to send.

In my case, I hacked around SetNetworkOwner by making my entire game local and turning on filtering to ensure that the server never simulates anything. The downside of this is that all my game code is in local scripts so it will be easy to steal. Still it might be the easier way to go - not having to wait for things to replicate has simplified my code in many places.

1 Like

Could you go into more detail as far as what you’re trying to do goes?
If the level isn’t going to fall apart immediately, shouldn’t you only change the network ownership to the player when you know that something is going to be physically simulated?

For instance, lets say you have an Explosion, and you know what player caused the explosion to happen. When Explosion.Hit is fired, you could check what part the explosion hit, and then set the network ownership to the player who is responsible for the explosion occurring.

I am trying to load a stack of blocks server-side and set the network owner to client without it falling over.

I am trying to do this in a reliable way for a large number of blocks.

Have you tried calling SetNetworkOwner after you Unanchor (Make sure there are no yields between the calls)? You cannot set ownership to Anchored parts.

Here is a script I have a BS model of parts.

local children = script.Parent:GetChildren()


while #game.Players:GetChildren()<1 do
	wait(.1)
end

local player = game.Players:GetChildren()[1]


for i,v in pairs(children) do
	if v:IsA("BasePart") then
		v.Anchored = false
		v:SetNetworkOwner(player)
	end
end