- Home /
Clearing out buffered RPC calls from old Network.Instantiate() calls?
OK, I know that Network.Instantiate() works using buffered RPC calls; does anyone have a recommended system for getting rid of those buffered calls after they're no longer needed?
The issue, as I'm sure many of you have come across, is that those buffered RPCs can stack up after they're useful. The symptom that brought this to our attention is that, because Network.Destroy() doesn't use buffered RPC calls, new clients get the calls to Instantiate already-destroyed objects and don't get corresponding messages to destroy them. This results in ghost copies of whatever it is you instantiate and destroy.
Now, one solution would be to add a buffered RPC call that destroys the objects. Which would work, but the RPC buffer could get really bloated with unnecessary calls, and we'd need to be careful about initialization--we wouldn't really want clients to load models, animations, etc. for things that don't really exist anymore.
So we're thinking we'd like to use Network.RemoveRPCsInGroup() to get rid of the RPCs (reserving a bunch of communication groups to be claimed by each Network.Instantiate() call). Has anyone tried this, who can share their experiences/pitfalls encountered? The other option I can think of would be to make explicit all of the RPC calls included in Network.Instantiate(), which seems less ideal...
Answer by PrimeDerektive · May 03, 2011 at 06:03 PM
Didn't they fix this in 3.2? What's new in Unity 3.2
Networking: It's now possible to remove Network.Instantiate calls from the RPC buffer by calling Network.RemoveRPCs on the first NetworkView in the instantiated object.
So, right before you destroy your Network.Instantiated object, I imagine you do something like:
Network.RemoveRPCs(objectImDestroying.GetComponent(NetworkView));
(I think. If the object has multiple networkViews you might need to use GetComponents and call RemoveRPCs on the first entry in the array it returns).
Hot diggity! That sounds like exactly what I wanted! I will definitely try this...
Not working... at least not for me, i used it like this.
@RPC function DestroyBuffered(ObjectToDestroy : GameObject) {
Network.RemoveRPCs(ObjectToDestroy.GetComponent(NetworkView)); Network.Destroy(ObjectToDestroy);
}
It works for me. $$anonymous$$uch better than our convoluted stuff; thanks a ton! Zaphness: I'm sure it needs to be done on the same machine that initiated the instantiation; when you call DestroyBuffered(), do you RPC it to everyone?.
Glad to help! That issue was one of my longstanding irks with the built-in networking; I was very pleased when I saw they fixed it.
Whoah, actually I have to add a big caveat: this doesn't look like it works as completely as the gross remove-by-group solution, which causes occasional strangeness if (like us, so far) you don't use the levelPrefix. In most cases, yes, it gets rid of ghost players who disconnected; but this solution leaves some things reappearing in later levels for late joiners. I could be mistaken, but I'm afraid I won't have time to investigate much further for a while...
Answer by AKAssassin · Apr 18, 2011 at 09:59 PM
You would use RemoveRPCS and give it the Netowork.player that you want to remove the buffered stuff for. So if you know you used Network.Instantiate() or your own instantiate from a specific client that disconnected all you need to do is call RemoveRPCS(Network.player) for which ever network player disconnected and you shouldn't see their buffered RPCs anymore.
This works for me.
In code it would look like this.
function OnPlayerDisconnected(player: NetworkPlayer) {
//Destroy all the objects currently in the scene created by this player and remove his info from the list
Network.RemoveRPCs(player);
Network.DestroyPlayerObjects(player);
}
This should do the trick.
Thanks! Unfortunately, in our situation this isn't enough for two reasons. First, our server is authoritative for everything (except input, of course); so when players disconnect, we have to recall which server-owned objects were created for that player and net-destroy them as well (switching to a client-authoritative architecture would work for players, but add action-resolution complexity). Second and more importantly, we net-instantiate non-player things; for those, destroying-by-player just wouldn't be possible. Anyway, the destroying-by-group seems to be working for us so far...
Answer by Zhapness · May 02, 2011 at 11:18 PM
Hi I am a bit new to programming, so can you show or explain how to add the Buffered Network.Destroy RPC Call?
Really need this.
Thanks.
Answer by orakga · May 29, 2011 at 08:54 AM
I'd like to add a note to this thread.
I just spent the whole day struggling with this issue (sidetracked by this issue, rather), because the RemoveRPCs call didn't appear to be working correctly.
What I instead discovered was that when I call an RPC on the SERVER and perform Network.Instantiate, it actually ends up creating a separate instance of a network-instantiated object FROM EACH CONNECTED PLAYER. (I confirmed that there were multiple instances of the same object being instantiated right on top of each other, all with their own unique viewIDs belonging to each player; I confirmed it with their Network.Player as well)
I don't know if this is a bug or intended behavior (I don't see why it should behave this way, TBH), but due to this I was not able to remove the RPC(s) correctly from the buffer for the longest time.
In the end, what I did was:
Instead of Network.Instantiating the object from within an RPC on the server,
I called Network.Instantiate directly from the server
That did the trick for me. No more duplicate copies of the object, and RemoveRPCs started behaving "correctly" as well.
I guess the point I'm trying to make is, if you are having ghost objects even after RemoveRPC-ing, check to see how many instances are actually spawned before you try Network.Destroy or RemoveRPC. If you have multiple copies, you could be having similar issues of RPC-induced server-side Network.Instantiations actually instantiating on the Client(s) side.
If I'm not making sense, I apologize. I literally spent 8 hours cutting this issue from every angle and am not sure if I'm even thinking straight anymore. ;D-
sounds like it was expected behavior if I understand what you did. If you did:
void Somewherincode() { . . networkView.RPC("dosomething", RPC$$anonymous$$ode.All); . . )
void dosomething() { Network.Instantiate(....) }
Then yes, you'll end up having every client run a Network.Instantiate and create the same object on top of itself. This is because Network.Instantiate already does an RPC call to all clients so you're double dipping if you will.
Ins$$anonymous$$d you have two options:
Just call Network.Instantiate and that's it. It'll create one copy on every client and also add the object to the server buffer to create the object on any new clients that join
alternatively use your RPC call, but in the remote function just do an Instantiate without the "Network." in front of it. But if you do it this way you first have to create a viewid and pass it in, so all the remote objects share the same network id, like this:
void Somewherincode() { . . NetworkViewID _nvid = Network.AllocateViewID(); networkView.RPC("dosomething", RPC$$anonymous$$ode.All, _nvid); . . )
void dosomething(NetworkViewID _nvid ) { GameObject _go = Instantiate(....); NetworkView _nv = (NetworkView)_go.GetComponent(typeod(NetworkView)); _nv.viewID = _nvid; }
Note: In my final version I'm using RPC$$anonymous$$ode.All, not a buffered mode. This is my preference so I can control when things are instantiated and destroyed, but you might want to use AllBuffered.
Answer by jorgenpt · Oct 07, 2012 at 06:15 AM
Like PrimeDerektive says in the "accepted" answer, you should use Network.RemoveRPCs. One subtlety that isn't clear is that the RPCs are buffered on the server, even if you call Network.Instantiate on one of the clients. This means that you should use a method like this:
[RPC]
void RemoveBufferedInstantiate (NetworkViewID viewID) {
if (Network.isServer) {
Network.RemoveRPCs (viewID);
} else {
networkView.RPC ("RemoveBufferedInstantiate", RPCMode.Server, viewID);
}
}
With this code, you can call `RemoveBufferedInstantiate (networkView.viewID);` without having to consider if you're on the server or the client.
Your code would then look like:
RemoveBufferedInstantiate (someObject.networkView.viewID);
Network.Destroy (someObject);
Your answer
Follow this Question
Related Questions
Deleting client objects if internet connection was lost... 0 Answers
RPC and inheritance 1 Answer
Send GameObject active in network to all clients 2 Answers
Disconnect a player from a network 1 Answer
Check client RPC and Instantiation calls 0 Answers