Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Unity 2022.2 is now available as the latest Tech release.
    Dismiss Notice
  3. We are making some changes to the DOTS forums.
    Dismiss Notice
  4. Have a look at our Games Focus blog post series which will show what Unity is doing for all game developers – now, next year, and in the future.
    Dismiss Notice

RPCMode.Server

Discussion in 'Multiplayer' started by podperson, Mar 30, 2010.

  1. podperson

    podperson

    Joined:
    Jun 6, 2006
    Posts:
    1,370
    I'm a bit puzzled by RPCMode.Server. What I want to do is implement direct fire weapons on the server, so:

    1. Player fires gun
    2. Player's weapon calls server (using RPCMode.???) to resolve shot from X in direction Y
    3. Server receives message, determines where the shot lands, and then network instantiates explosion and inflicts damage as appropriate

    I'm currently working on a network which blocks Unity's master server so I can only test single player mode (where the player is the server) and in this case shooting doesn't work. If I use RPCMode.All it works* (so the RPC call is made and received, and the shot resolved -- but if I use RPCMode.Server then nothing happens at all).

    * But using RPCMode.All would be wrong if I weren't in single player mode.

    Any ideas? Am I doing something grievously wrong?

    I suppose that as a workaround in single-player mode I could simply make the call directly (i.e. not via RPC), but as a general principle, I'd like to know what is going wrong.

    --- Edit ---

    As a further aside, using @RPC appears to make a function callable via RPC only, so short-circuiting server RPC calls by simply calling the method directly entails creating a new wrapper function and calling that instead. Ugh.

    As I was reading up on RPC calls it also looks like RPC calls simply live in global message namespace... so I take it that there's no guarantee that the RPC call can "see" the same stuff that the caller sees (e.g. if I have a weapon script that is used for more than one kind of weapon, then the RPC handler might be attached to some random weapon). Is this correct?
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    RPCMode.Server is what you would use in step 2. It tells that the RPC is sent to the server only, nobody else.

    If the server is at the same time a player you optimally still use RPCMode.Server for all others aside the one who is the server (Network.isServer helps here), the one that is the server would just call the function directly without the RPC.

    Reason that it does not receive it with RPCMode.Server when you send it is that you are already the server so there is no reason for it to send.
     
  3. podperson

    podperson

    Joined:
    Jun 6, 2006
    Posts:
    1,370
    Of course there's a reason to send. Otherwise how do you explain the behavior of RPCMode.All vs. RPCMode.Other? The point is you want to be able to write code that doesn't care if it's on a client or the server, and write server code that doesn't care if it's talking to a local or a remote object.

    I'd call this a pretty annoying bug (and I'm not the only one -- a search for threads containing RPCMode.Server will reveal many others).

    Meanwhile, I've come up with a workaround:

    If Network.isServer I use RPCMode.All and then in ResolveImpact (my handler) I do nothing if !Network.isServer. This way I don't need to write all my code twice or create a bunch of useless wrapper functions. It's *slightly* inefficient in the case of a multiplayer game with a non-dedicated server, but I can live with that for now. Hopefully this bug will get fixed someday... 3.x?
     
  4. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    All sends it to really all including self, which can be smart but is something I personally never use aside of spawn requests so the network time is in sync.

    Server sends it to the server, if you are not already the server.
    If you are the server and send it to the server you know it already so just call the RPC function cause thats what would happen anyway. You just flood the network message queue / stack without any gain.


    If you detect that you are server, why do you even use an networkview RPC call?
    You can just call the very function because RPC functions are just normal functions but marked to be RPC usable in addition to the normal use.
    In that case you don't need to write code twice either and its much more efficient than bombing all users for no reason.

    I'm not sure if this behavior is a bug, cause I personally don't understand or see it as one.
    Its just the consequence of running as client while you are the server.
     
  5. podperson

    podperson

    Joined:
    Jun 6, 2006
    Posts:
    1,370
    Bullets etc. are a kind of spawn.

    Is this actually documented somewhere? If this is how it's supposed to behave then it's merely annoying. If not, it's a bug.

    I want a single-player mode that works just like a client mode. So the single player is effectively both client and server. I want everything to work the same way, not to have to write special case code for players who happen to be hosting the server OR playing in single-player mode. As far as I'm concerned, the underlying network code should nicely handle the special case when we're sending messages to ourself -- it's presumably handling it in many other cases (e.g. RPCMode.All).

    Can you? I can't. When I try to call an @RPC function as if it were an ordinary function, I get an error.
     
  6. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Then you do something wrong or don't pass in all the desired data, as you need to add one parameter if you send it directly.

    I'm not sure if it is documented somewhere that you can not send network messages to the server if you are the server and not only on clients that connect to the server.



    As for the "spawn" part: if you spawn it, why do you use server if you want all to know about it?
    If you don't want all to know about it there is nothing to worry about.


    To me it seems like you heavily attempt to make something much more complex and troublesome for the user than needed. Especially in the single player case you don't want to initialize the networking at all as he gets a firewall allow request if you do and that for single player ... good luck explaining that to him
     
  7. podperson

    podperson

    Joined:
    Jun 6, 2006
    Posts:
    1,370
    All I want is for code executing on the server to operate as much as possible like code on a client -- this doesn't seem to me to be perverse or complicating.

    I figured I was probably missing a magical parameter for the RPC short circuit, but darned if I could find the documentation explaining what it was. Even if this short circuit works, it makes my code more complex, not less.

    What I want is:

    Code (csharp):
    1. function x(){
    2.    networkView.RPC( "y", RPCMode.Server, a, b );
    3. }
    4.  
    5. @RPC
    6. function y( a, b ){
    7.   // works whether x() was called on client or server
    8. }
    What you're proposing is:

    Code (csharp):
    1. function x(){
    2.   if( Network.isServer ){
    3.     y( magic_parameter, a, b);
    4.   } else {
    5.     networkView.RPC( "y", RPCMode.Server, a, b );
    6.   }
    7. }
    8.  
    9. @RPC
    10. function y( a, b ){
    11.   // works whether x() was called on client or server
    12. }
    How is this simpler?

    And why is this perverse? If y() opens a door, why shouldn't it work the same if a bad guy who lives on the server opens the door as if a good guy on a client (or a good guy on the server) opens the door?
     
  8. dynamicman

    dynamicman

    Joined:
    Apr 3, 2010
    Posts:
    8
    Because the server is authoritative and contains the master state for the game.

    I would recommend reading
    http://www.m2h.nl/files/M2H_Networking_Tutorial.pdf

    Most importantly this bit of text:

    -Mikal
     
  9. clausman

    clausman

    Joined:
    Mar 29, 2012
    Posts:
    1
    This is an old thread, but I thought I would add my 2 cents on how we are getting around this on a project I am working on. We created a NetUtil class which contains Instantiate and RPC calls. When a call is made it checks the state of Network and if there is no networking or if it is an RPC to self, it does a local invoke. The local invoke is done using reflection. It is important to note that reflection is not supported on all platforms (e.g. iOS) so this is not a perfect solution, just our fix since we are only doing computer deployment.

    Here are the import code snippets:

    Code (csharp):
    1.  
    2. public Object Instantiate(GameObject go, Vector3 pos, Quaternion rot, int group=0, bool serverOnly=true)
    3.     {
    4.         Object rtn=null;
    5.         if (isNetworked)
    6.         {
    7.             if (serverOnly  isClient)
    8.             {
    9.                 Debug.LogWarning(string.Format(
    10.                     "NetUtil: Instantiate of {0} failed since you are a client and called in client only mode", go.name));
    11.             }
    12.             else
    13.             {
    14.                 rtn = Network.Instantiate(go, pos, rot, group);
    15.             }
    16.         }
    17.         else
    18.         {
    19.             rtn = Object.Instantiate(go, pos, rot);
    20.         }
    21.         return rtn;
    22.     }
    23.  
    24.     public void RPC(MonoBehaviour caller, string methodName, RPCMode mode, params object[] args)
    25.     {  
    26.         if (!isClient  RPCMode.Server == mode)
    27.         { // Local rpc call
    28.             LocalInvoke(caller, methodName, args);
    29.         }
    30.         else if (isNetworked)
    31.         {
    32.             caller.networkView.RPC(methodName, mode, args);
    33.         }
    34.         else if (mode == RPCMode.All || mode == RPCMode.AllBuffered)
    35.         {  // If there is no network and this is supposed to go to everyone, make sure it goes to me!
    36.             LocalInvoke(caller, methodName, args);
    37.         }
    38.         else
    39.         {
    40.             Debug.LogWarning("NetUtil::RPC - Method '" + methodName + "' on object '" + caller + "' with mode '" + mode + "' failed !" +
    41.                 " Ignoring method call!");
    42.         }
    43.     }
    44.  

    And here is the reflection code for local invocation:

    Code (csharp):
    1.  
    2.     private void LocalInvoke(MonoBehaviour caller, string name, object[] args)
    3.     {
    4.         System.Type myType = caller.GetType();
    5.         MethodInfo method = myType.GetMethod(name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);
    6.  
    7.         if (method != null)
    8.         {
    9.             method.Invoke(caller, args);
    10.         }
    11.         else
    12.         {
    13.             Debug.LogWarning("NetUtil::RPC - Could not find local method " + name + " in class "+myType.Name+". Make sure it is a public, non-static method!");
    14.         }
    15.     }
    16.  

    The only real difference with using these RPC methods is that the object you would like to call the RPC on must be included in the method call in case we need to do reflection.

    Attached is the entire NetUtil class. It using a singleton pattern rather than being a static class so doing an RPC looks like this 'NetUtil.Instance.RPC(...)'. The class has not been thoroughly tested, but has proved useful in our app. If you are deploying to a system which support reflection, it probably could be adapted to your needs.

    View attachment $NetUtil.cs
     
  10. George Foot

    George Foot

    Joined:
    Feb 22, 2012
    Posts:
    399
    That usage of reflection should work fine on iOS too, in general.
     
  11. Poindexter

    Poindexter

    Joined:
    Aug 16, 2011
    Posts:
    19
    Nice snippet, clausman.

    I find this behaviour very annoying. The same thing happens when you call
    networkView.RPC ("SomeFunction", networkView.owner)
    and you happen to be the object's owner. Nothing happens at all. (I know that a RPC can only target a NetworkPlayer that's in the Network.connections list, but hey, Unity could relay these RPCs automatically through the server....)
    This combo of poor documentation and lack of feedback makes development much harder.
    And before people start saying that none of this makes no sense, remember that fully authoritative servers are not the only solution. When your server is not authoritative, the line between server and client can get very blurry.
     
  12. burkedrane

    burkedrane

    Joined:
    Jul 9, 2013
    Posts:
    8
    I complete agree. This is awful functionality. I mostly use uLink which fixes almost all of the shortcomings of Unity.Network, but they don't do p2p, which I need today.

    Forcing the code to do something in 2 ways, rather than have it work in 1 is always going to be less elegant and more bug prone.