Search Unity

Buffered Instantiation RPCs Occur While In Lobby, uLink

Discussion in 'Multiplayer' started by alexander_q, Sep 15, 2014.

  1. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    My clients begin in a lobby scene, where they choose to connect or host. Upon connecting, the clients waits for a startup packet from the server and then loads the first game level. However, also upon connecting, all buffered Network.Instantiate calls occur immediately, while the player is still in the lobby. These objects are then destroyed when the new level loads.

    As such, I've had to give all possible network instantiated objects DontDestroyOnLoad(gameObject) so that they carry over to the first game level. This is problematic, as I'll want them to be destroyed in the normal way later.

    How can I have the objects instantiated only on their intended levels?

    What I've tried: Network.SetLevelPrefix(1) before loading the level. This makes no difference. I've also tried to use Network.SetSendingEnabled(0, false) and Network.isMessageQueueRunning = false straight after connecting, but these prevent the client from receiving the startup packet which tells them the random seed, their player number, and instructs them to load the first level.
     
  2. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    You need to be able to actually stop buffered rpcs from executing automatically. In unity's built in networking it's not possible. You have to rewrite instantiate using the guidance in manual about manual instantiations and in the sending side send the scene name with object id and prefab name, in the receiving side you should delay actual creation until you are in the mentioned scene and after OnLevelWasLoaded callback.

    in uLink of unityPark suite we have a callback uLink_OnPreBufferedRPCs() which allows full control over the RPCs, take a look at www.muchdifferent.com and developer.muchdifferent.com for more info.

    The page about buffered RPCs is here
    http://developer.muchdifferent.com/unitypark/uLink/RPCDetails
    See the part on buffered RPCs near end of it.
     
  3. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    Thankyou Ashkan_gc, you've pointed me in the right direction. Normal Unity networking features can't do what I need them to do, though they come close. I will explore the alternative you've suggested.

    EDIT: The relevant code looks like this (adapted from the uLink API reference):

    Code (CSharp):
    1.  
    2. void uLink_OnPreBufferedRPCs(uLink.NetworkBufferedRPC[] bufferedArray) {
    3.     Debug.Log("Message uLink_OnPreBufferedRPCs was detected!");
    4.     foreach (uLink.NetworkBufferedRPC rpc in bufferedArray){
    5.         if (rpc.isInstantiate){
    6.             Debug.Log("Got bufferd instantiate");
    7.             DontExecuteOnConnected();
    8.             listOfInstantiates.Add(rpc);
    9.        
    10.         }
    11.         else{
    12.             Debug.Log("Found RPC with name = " + rpc.rpcName);
    13.             //Do normally
    14.         }
    15.     }
    16. }
    17.  
    This is where I get stuck. Using a foreach loop I could then go through the RPCs and execute them with ExecuteNow...

    Code (CSharp):
    1.  
    2. foreach (uLink.NetworkBufferedRPC rpc in listOfInstantiates){
    3.     //check associated levelID
    4.     rpc.ExecuteNow();
    5. }
    6. listOfInstantiates.Clear();
    7.  
    ... However, the RPCs don't inherently contain their level ID. How can I add this information to the network.instantiates? Or can I somehow check the viewID level prefix of the RPC?

    EDIT: NetworkBufferedRPC has a property of ViewID, so it's definitely possible to check it. However, uLink "SetLevelPrefix" is marked as "not implemented/obsolete" in the API reference

    EDIT: It occurs to me that you could replicate this if you could, say, add 1000 to all viewIDs for level 1, 2000 to all viewIDs for level 2. Is this possible, or am I way off track? The advanced form of ulink.network.instantiate found here allows for manually assigning the viewID upon instantiation.

    EDIT: Another possibility is to differentiate using the "rpcName" flag, assuming you use your own manually created RPC calls for instantiating, and programmatically affix the level prefix to the name of the call (and set up a function for every possible level prefix)
     
    Last edited: Sep 17, 2014
  4. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Glad it helped. Actually if you choose to use UnityPark suite, you can go even further if you need. You can host the lobby using another unity exe and by using uLobby. Then connect to the game server and change your scene to the game scene, you can automatically start and stop game servers and add a lot of features easily.
    There is a suite demo which you can look at
    http://developer.muchdifferent.com/unitypark/Tutorials/UnityParkSuiteDemo

    The code is a little more complex than what you might love because we tried to make it robust. Additional documentation and videos for that are coming soon but at the time of writing are not finished yet. That's a good starting point for complex setups.

    However if you don't need that or at least don't need that for now, I think it's a good idea to just get it working with normal uLink and revise your gameplay and gameplay networking code which is the harder part and then move to these infrastructure related stuff: lobbies, servers, databases, account systems and etc.
     
  5. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    I looked at a video just before about using multiple server instances for different levels. I will likely do this in the future. For the time being - how can I network.instantiate something with ulink in such a way that the client can detect which room the RPC belongs in?
     
  6. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    What do you mean by which room? Server is only in one level at a fixed point in time, You either need to send infor about the level to all clients or not. If you need to send the level name then use uLink_OnPlayerApproval to send something back to the client which can be read inside uLink_OnConnectedToServer for loading the level. See the manual page for connections for more info about sequence of callbacks.

    If you mean network groups used for different games at the same scene/level then you only need to set group number as the last argument of Network.Instantiate and then players will automatically create and destroy the object when they join the group or leave it.
    Players automatically join a group when they own an object which is in the group but there are methods to add NetworkPlayers to a group and remove them as well.

    There is a game session manager using groups feature, example here
    http://developer.muchdifferent.com/unitypark/Tutorials/uLinkNetworkGroups

    Let me know if I misunderstood you.
     
  7. alexander_q

    alexander_q

    Joined:
    Feb 7, 2014
    Posts:
    32
    Sorry "room" is a holdover from my Game Maker days. I mean "scene". I'm using uLink_OnPreBufferedRPCs client side to stop RPCs executing automatically, but I need to check each one to see which scene it belongs to. Say for example the server is handling multiple scenes, instantiating many prefabs, and sending these messages to all clients. Each client checks the RPC to see if it is relevant to them before executing them. Using built-in network instantiates, I can't add some sort of room identifier to make this happen. Can a manual method do this somehow?

    My question is what is the best way to accomplish this? (ignoring the multiple server-instance method for now)

    Re: Game Session Manager. Using that structure, would I have each scene be home to a group matching its level number? Then as a player enters a scene, have them unsubscribe from the previous scene, and subscribe to the new one. And whenever the server network.instantiates, make sure to attach the relevant network group argument?
     
  8. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Game Maker is pretty nostalgic to me.
    Well you can connect to one server at a time and each server can load one scene at a time so it can not manage multiple ones.
    In case of groups and gameSessionManager you are right. You only get messages about the groups that you are registered in (i.e. you either have objects in them or you registered explicitly).
    You can read RPC names in the OnPreBufferedRPCs callback so you can have a SetLevel RPC and in OnPreBufferedRPCs and you can execute it or put it for execution first and then run all of your instantiates if the server is always in one level but directs clients to different scenes.
    If the server sends RPCs of all levels to all groups then you'll be in trouble and should use a manual instantiate method which is a little harder to implement unless uLink provides a way to read the bitstream of data sent by instantiate , you can send additional data in instantiate, If you think you can not solve your problem in another way I can talk to the development team and we might add it for a future version because it seems interesting to me to be able to read more imfo from RPCs before deciding to execute them or not.
     
  9. Ashkan_gc

    Ashkan_gc

    Joined:
    Aug 12, 2009
    Posts:
    1,124
    Actually there is another way of achieving this, you should send messages for different groups from gameObjects with different names/viewIDs. Then you can read the RPC sender's viewID in the OnPreBufferedRPC and using uLink.NetworkView.Find() find the gameObject with that id and using its name you can decide if it's in your level or not (names of course should contain level info then).