The Humanoid:SetStateEnabled(Enum.HumanoidStateType.Swimming, false) function doesn’t replicate to clients.
This is problematic when the Humanoid is not part of a player (i.e. an NPC).
The consequence is that if any humanoid model changes network owner from server to a client, the humanoid may enter a state that was explicitly disabled on the server.
For example, if an NPC humanoid’s Swimming state is disabled in the server, when it comes near a player, it will suddenly begin swimming again when the network owner changes.
Expected behavior
Humanoid:GetStateEnabled() should return the same value on the server as on the client (after being changed by the server).
I think this is normal behavior considering that networked assemblies owned by the client are managed by the client, including their humanoid state. It’s the same reason that you can’t change an actual player’s humanoid state on the server.
Have you tried setting the network ownership of your NPCs strictly to the server (nil) every frame? I say every frame because it has some behavior where it will still set back to a player even if you set it back to the server once.
i wouldn’t recommend setting it each frame due to performance but maybe setting it before the humanoid replicates would work? haven’t really tried something like this yet
I haven’t benchmarked the performance myself, but I do personally have this functionality in my own games which don’t really show much of an impact at all. Of course, this can be different for each developer depending on how many NPCs there actually are and other factors.
There is no exposed API at this time that signals when the network ownership changes, so setting it every frame was the way to go in my case.
Setting the network owner to nil does solve this issue, but with the unfortunate consequence that the server must compute all the physics of any assembly attached to the humanoid.
I have quite a few NPCs in this game, and forcing the server to own all of them causes more slow-down than is worth it.
Fixing this ‘bug’ would unfortunately break quite a lot of current day use-cases. I know one of my older experiences mix-matches the enabled states, presumably because I was aware of this behaviour and hence knew to only set the state enabled on the client. However, player death is why that de-sync matters; because on player death, the network owner is set to the server (yes this is the default behaviour btw) where I disable much more of the states as so players don’t magically ‘revive’ to the server because of a race condition with different state activations or similar. I’m not exactly sure if that was the exact reason, but I’d like to think that it’s at least partially why. I haven’t opened the project in months, so I’m really just re-calling the case from memory.
Seems like your best bet here is to just manually replicate your state enabled changes and treat StateEnabled as a not-replicated property of humanoids.
I may be wrong on this, but afaik using SetNetworkOwner disables the automatic network ownership changes for the object. That’s presumably why SetNetworkOwnershipAuto exists. Hence, you probably don’t need to run the function more than once on each part, assuming you don’t want to manually change the network owner again.