PUN item pickups re-appear for new clients

conraddu

Member
Hello,

I noticed a funny behavior with item pickups in the PUN integration. What is happening is when items are dropped onto the ground, and then they are picked up, the client who picks them up deletes them from their game scene. But the other clients in the lobby only set the pickup to disabled, and do not delete them.

Is this a purposeful behavior that when one client deletes a picked up item, the other clients only disable it? The disabling is occuring through the DestroyInternal of PunObjectPool, which does ObjectPool.Destroy(). This disables the GameObject, which triggers the PunLocationMonitors to send a SetActiveRPC call to the other clients. If this is purposeful, why wouldn't we want the other clients to delete the GameObject, instead of just disabling it?

The draw back of this behavior is demonstrated by this workflow:
A non-master client picks up a dropped item, the master client is only told to disable the picked up item (not delete it). This causes the master client to not perform DestroyInternal of PunObjectPool, and the m_ActiveGameObjects dictionary does not remove the pun pickup item from its entries. When a new client connects, the master client then performs PunObjectPool.OnPlayerEnteredRoom which checks the m_ActiveGameObjects to raise the object instantiation event. Since the pun pickup item still exists in that dictionary, it gets recreated and placed back into the world for the other clients.

What would be the recommended solution here?
 
Possibly intended behavior for demo's. So that anyone can pickup, you could simply use an rpc to disable it when required.
 
In regards to that for v3 I am helping Opsive to develop a more fulfilled multiplayer addon, more like UFPSv1 so you have deathmatch, free for all, team death match and team vs team examples also, along with a scoreboard. Stay tuned.
 
Possibly intended behavior for demo's. So that anyone can pickup, you could simply use an rpc to disable it when required.
Disabling already occurs. The problem is that disabling is not enough. The master client does not remove the gameobject from m_ActiveGameObjects when a non-master client picks up an item. Resulting in glitchy behaviors where objects are instantiated that should not be instantiated when new players enter the room.
 
When the object is destroyed it should also be removed from the active objects. I have been distracted by the v3 release so I haven't been able to take a look at the other PUN issues that you reported but plan on it in November. I'll take a look at this one as well.
 
Disabling already occurs. The problem is that disabling is not enough. The master client does not remove the gameobject from m_ActiveGameObjects when a non-master client picks up an item. Resulting in glitchy behaviors where objects are instantiated that should not be instantiated when new players enter the room.
I have not ever experienced this (never looked for it before but just tested again since I never noticed), yet I have put "pickups" to the most extreme of tests and "other" use cases. Do you have your pickups assigned in the runtime pickups section for pun? Is all of your items coming from the same item collection?.
 
I have not ever experienced this (never looked for it before but just tested again since I never noticed), yet I have put "pickups" to the most extreme of tests and "other" use cases. Do you have your pickups assigned in the runtime pickups section for pun? Is all of your items coming from the same item collection?.
Just confirmed on a clean install of Opsive w/ Pun integration in the demo room scene.

Steps to reproduce:
- Login a master client
- Login a non-master client
- Kill one of the clients so that they drop their assault rifle
- Use the non-master client to pickup the assault rifle
- Login an additional non-master client
- See that the assault rifle that was picked up is on the ground and can be picked up again by the new client

Like I stated above, the problem is that sending the SetActiveRPC to disable items is not enough. The DestroyInternal function must ALWAYS trigger for the master client so that the m_ActiveGameObjects gets cleaned up.

Furthermore, if the objective with disabling is to pool things for performance, then it is incorrect for the client that picks up the item to destroy the gameobject, while the others disable it.
 
It is also worth noting that the disabled items are never reactivated. I would have expected that the point of disabling was so that they could be re-used in the future. But this workflow doesn't do that:
- Drop an assault rifle
- Pickup the assault rifle with another client
- See that the pun item pickup got disabled in the hierarchy for the client who did not pick it up
- Drop another assault rifle, see that a new GameObject gets instantiated instead of just reusing the disabled pun item pickup that was dropped earlier.

So this makes me think that the whole disabling workflow is just a bug and the gameobjects are intended to be removed from the scene when picked up.
 
Last edited:
I have not ever experienced this (never looked for it before but just tested again since I never noticed), yet I have put "pickups" to the most extreme of tests and "other" use cases. Do you have your pickups assigned in the runtime pickups section for pun? Is all of your items coming from the same item collection?.
The PUN integration works quite flawlessly when you have a lobby where the master client and all the other clients join the game at the same time in a very clean state. But as soon as clients start joining the game after it has started, where the scene is in a "dirty" state, a lot of things begin to go wrong across the opsive workflows. I have detailed a few across some forum posts that @Justin mentioned above, but some I have just been working through myself.

So I would make sure that these tests and use cases that you mentioned consider these different client joining workflows. Master client switching is a whole other beast that I actually gave up on because of how buggy it was ?
 
I currently have a solution, but I think it throws object pooling out the window for item pickups, so there is probably a better solution than this. What I do is as follows:

DestroyInternal now takes an optional parameter of forceSend, which is defaulted to false. We send a value of true for forceSend in ObjectPickup during ObjectPickedUp(). When we check for master client in destroy internal (to send the object destruction event to other clients) I also check for force send with an OR. This way, both master clients and non-master clients can raise the object destruction event.

I also removed the line that set the PhotonView.viewId of the destroyed object to 0 in DestroyInternal (which is a head scratcher for me as to why that existed, as setting photon view id's like that seems quite dangerous for synchronicity). This way, OnEvent for object destruction calls DestroyInternal for the correct photon view. So now all the clients are going through the DestroyInternal workflow.

This fixed the issues I presented above, but I found another issue for clients joining while items existed on the ground. When a client joins, the master client's pun object pool hits OnPlayerEnteredRoom, which raises an Object Instantiation event for every game object in m_ActiveGameObjects. This is fine, but the Object Instantiation event targets all OTHER clients (resulting in other clients duplicating items on the ground). To solve this I built a new RaiseEventOptions that sets the target actors to only the player entering the room, so that only the player entering the room is told to instantiate objects.

Let me know what you think of this solution, and any glaring errors that you might see as part of it.
 
Top