I discovered & reported this (https://github.com/MirrorNetworking/Mirror/issues/4066) while debugging my object pooling. I took a closer look at the client behaviour when calling NetworkServer.Unspawn(object) and discovered that it actually destroys the object on the client. Curious why the client destroys while the server sets object.active = false (allowing it to effectively only be pooled on the server)?
#Unspawn behaviour
60 messages · Page 1 of 1 (latest)
Also noticed that the ObjectHideMessage also results in the client calling DestroyObject()
You may not have your pooling set up correctly someplace. See the Spawner script in the Room example that works
oh I see, so there's an expectation to link in custom spawn, unspawn handlers on the client. Namely to use: NetworkClient.RegisterPrefab(prefab, SpawnHandler, UnspawnHandler);
Would it make sense to also have generic spawn/unspawn handlers instead of specific ones for each assetId? And then let the custom code worry about the type of asset and how to spean each?
Also by default: the server uses .setActive(false) while the client uses Destroy(). I was expecting the behaviour to be identical on both sides and was wondering why it was decided to have this difference between them (I'm mainly curious)?
Server:
Sends ObjectDestroy message to client
https://github.com/MirrorNetworking/Mirror/blob/master/Assets/Mirror/Core/NetworkServer.cs#L1863
Then disables the object (.setActive(false))
https://github.com/MirrorNetworking/Mirror/blob/master/Assets/Mirror/Core/NetworkServer.cs#L1897
Client:
ObjectDestory is bound to Destroy
https://github.com/MirrorNetworking/Mirror/blob/master/Assets/Mirror/Core/NetworkClient.cs#L525
Destroy(object) is called
https://github.com/MirrorNetworking/Mirror/blob/master/Assets/Mirror/Core/NetworkClient.cs#L1930
Unspawn removes from client and keeps it on server. If it was a scene object created at design time, it would be hidden / disabled on cliens. Runtime-spawned objects are destroyed on clients. Interest management works the same way.
Yep, was curious why that was chosen as the client behaviour? would it not be more consistent for the client to also keep the object (regardless of if its runtime spawned or scene)?
IMO it would be a bit easier to reason about what happens under the hood if both sides did the same thing when Uspawn is called.
We can't leave tens of thousands of objects on the client like that, as could happen in interest mgmt. Clients are treated as hostile, so you'd want the client to have as little information as possible. Having it know about disabled objects would be a cheat vector...think spawn or resource camping as an example.
That's true! But then wouldn't that be the deciding factor for whether you'd want to call Destroy() or Unspawn()? If the information about inactive objects must not leak, go for Destroy() otherwise use Unspawn with a pooling solution integrated
The server doesn't "leak". Used correctly, pooling can make the server more efficient. It's not magical, however. Using it wrong can make things worse. "Pool everything" would generally be terrible.
Agreed. What I'm getting at is: to discard a non pooled object wouldn't one always call NetServer.Destroy(). While to discard a pooled object, never call NetServer.Destroy() always call NetServer.Unspawn()?
(and hence expect the server and client to do the exact same discard action on both client and server, either both destroy the object or both return it to pool and set it as inactive)
pooling isn't the only use case for Unspawn
Oh I think that's what's missing from my mental model of it
What other usecases is it for?
moving NPC's on the server after they've been killed to be respawned elsewhere. Hiding doors or other resources pending reveal per player action, proximity, or other event. Hiding interactive things so they're only spawned for brief time at intervals. Hiding imposter trees in favor or real trees when being harvested. And that's just what comes to mind in the moment.
Ooh, I see what you mean. Form the server's perspective it may be perfectly fine to have stuff inactive (hidden) ready to be re-revealed later while the client should not know about them at all.
I have been definitely biased by my own need of Unspawn 🥲
I pool single use objects (mines, very low fire rate bullets, etc.) stuff that isn't hidden but rather reused later for a different shot, so both client and server can keep track of the hidden instances for reuse in my case
Cool, good to hear the reasoning behind it
Generally shouldn't network projectiles if there's going to be a lot of them. Use ParticleSystem.Emit instead. Built in pooling, almost no network cost, collision events, and secondary effects are all part of PS, and much lighter.
That's a good shout, high fire rate projectiles I only spawn locally (not networked) but i'm using a whole rigidbody and collider on a game object for that. Will look at the ParticleSystem.Emit for an alternative. The projectile i pool is a slow moving homing bullet that i want to be server authoritative, the fire rate is around every 4 seconds
So for the latter I think that having it networked is a necessity si that the following the target part looks the same on all mirrors
Particles can move toward a target too.
Indeed, but what about the server authoritative part?
Hmm, if I could load a single particle with an ID, then this would work pretty well 
id for what...the shooter? emitter is the shooter, collision gives the victim.
.Emit is for a single particle (mesh)
yep got that, id for each bullet - i use those to allow both client and server to report where the bullet hit and to try to validate if i believe the client or not
Believe the client about what? Client is just VFX. Only the server records hits
True if I use lag compensation, but I do not use that yet (plan to). So at the moment my prototype just blindly believes the hit reports coming from the client
lag compression is irrelevant. Server decides hits, never clients.
For full fairness, yes. but for the client experience, that's very frustrating to fire at a "ghost" position, to believe that you should be getting hits and then in reality there being no hits
This happend to Sea of Thieves as well AFAIK, they released with server authoritative hit registration, and people noticed it
This is temporary though, i don't plan to believe client reported hits but am doing that at the moment just to prevent that frustration of missed hits on the server
I'm looking into enabling lag compensation to get rid of this consideration
You do understand that there's no way to speed up msg packets between client and server, right? There's no magic there. RTT is what it is. Make the colliders a bit fatter on the server so very near misses count as hits. Delay the client VFX by 1/2 RTT so they'll be really close to what server is doing. Both use the same pos and direction for the shot, and therefore the same result.
Yes, I'm aware. I've just decided to temporarily trust the client to ensure that the lack of lag compensation doesn't affect the experience.
So as a consequence I do collect too much information for sure: i.e. each bullet has a guid, origin, direction
When eventually I'll just need origin, direction and time of shot
Server actually knows all three 🙂
Well give me a bit more of your mental model of how that happens 😄
the server is the authority, but my client controls their turret with authority (for smooth results) so when it fires, it sends a message to the server
at this point, the orientation of the turret on the server may be slightly older than the client's, no?
also, the position from which the client shot is in the past from the servers persoective when the fire message arrives
you have NetworkTransform (any flavor) on the turret, so other clients see it moving. Server sees that too. CmdShoot is processed alongside the NT update arriving, so server has the turret pos and aiming direction. Server tells all clients (except shooter because shooter already does their VFX "predictively" with 1/2 RTT delay) to show VFX of the shot with direction...they'll start it from the end of the turret barrel but run it in the correct direction. Server then runs it's own shot and determines hits. Clients (including shooter) can show explosion secondary particle effect on contact with whatever.
Problem: The turret is mounted on a server authoritative NetworkTrasform
the client only controls the orientation locally
Client should move their own tank, not server, as well as aim their turret. See PlayerTest example with the Tank player prefab.
the server is the one sending NT updates to everyone else. The client is not in control of that in my setup. So the CmdShoot has happened on an older position by the time it arrives
i agree for that type of game but not here. we are talking about a plane where one player is the pilot and the other is the gunner
so the pilot has move authority, while the gunner does not
Then it doesn't matter
other client is moving the plane...so what? all that matters for the shot is where the turret is in the world.
Where it is in the world at the time of the shot
Which server knows
Shot originates at T0
plane travels at 30m/s in a straight line, movement is server authoritative.
Shot command arrives at the server at T60(ms)
the plane's position on the server at T60 would be further ahead (1.8M at this speed) than when the player has clicked fire if the plane movement is server authoritative, would't it?
without the ability for the server to look in the recent past that is
so effectively like this the shot is spawned 1.8 meters ahead along the plane's forward axis. That would be 1.8M away from the true shot origin
In any case, once i get to polish the approach to this bit I can report back findings.