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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Processing external commands

Discussion in 'Scripting' started by qwertyuiop11110, Apr 8, 2020.

  1. qwertyuiop11110

    qwertyuiop11110

    Joined:
    Mar 16, 2020
    Posts:
    24
    I have a general problem. Lets say I have a game of chess that is Peer-2-Peer. I wish that every choice that is being made is being transfered. Lets say I make a move, and my opponent is hosting the game. After unpacking and authenticating my request, host may process my request as such:
    Code (CSharp):
    1. public class myChessDemo {
    2.  
    3.     enum action {
    4.         chat,
    5.         meta,
    6.         move
    7.     }
    8.  
    9.     public void processIt(action thisAct, params object[] data)
    10.     {
    11.      
    12.         switch (thisAct) {
    13.  
    14.             case action.chat:
    15.                 if (data[0] != null) string theSayer = (string)data[0];
    16.                 else return;
    17.  
    18.                 if (data[0] != null) string theMessage = (string)data[0];
    19.                 else return;
    20.  
    21.                 myChatClass.Says(theSayer, theMessage);
    22.             break;
    23.  
    24.  
    25.             case action.meta:
    26.                 switch ((int)data[0])
    27.                 {
    28.                     case 0:
    29.                         myUI.RequestDraw();
    30.                     break;
    31.  
    32.                     case 1:
    33.                         myUI.Resign();
    34.                     break;
    35.                 }
    36.             break;
    37.  
    38.  
    39.             case action.move:
    40.                 switch ((int)data[0])
    41.                 {
    42.                     case 0:
    43.                         myChessClass.Movement("castles-long");
    44.                     break;
    45.  
    46.                     case 1:
    47.                         myChessClass.Movement("castles_short");
    48.                     break;
    49.  
    50.                     case 2:
    51.                         myChessClass.Movement((ChessPos)data[1], (ChessPos)data[2]);
    52.                     break;
    53.                 }
    54.             break;
    55.         }
    56.     }
    57.  
    58. }
    It is fast, straightforward and simple, but also horrifically ugly. But now imagine following. It's a different game, there's about 500 different requests to be made, now imagine all the casts I would need to make, imagine the exponentially increasing maintenance. I could quickly redirect requests to separate functions and handle it off there, but it would still require casting. No matter how you paint it, switch is not a solution for such a game. So the question is, how do you handle it? How to allow other users to send requests to my script so I can write code for processing in a clean way?
     
  2. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    try to flip your perspective the other way.

    how does the post office work? does it have a clerk that is responsible for filtering all mail to every single person in all the cities in the country? he goes around with a book, looks at every letter, and checks every single destination city, address, name and surname...

    no, you react to every message in one place by calling all interested "cities" and broadcasting them the message that seems to be addressed to a particular "city". listeners can then call an action on that message, redirect it, or ignore it altogether (and/or return to sender).

    at the start, all interested parties can subscribe to this central repository by also giving some additional information, the type of the messages they're into etc. the central system's responsibility is to connect the lower messaging input/output system with the "cities" and broadcast back and forth.

    this way, your scripts react only to what they're interested into, and the central repo can be pretty generalized in how the messages are handled. what you need to learn is how to systematically decouple such a logic, and how to make an approach that is flexible enough to stay readable and manageable in the long run.

    for starters, learn more about C# events and event subscription.
     
    Joe-Censored likes this.
  3. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I try to avoid a monolithic message processing method, and only use something like that for rather general messages not directly associated with anything in the game world. Otherwise I send messages directed at specific scripts on specific objects. The main networking system just reads some message headers and handles delivering the message to the correct destination, and that destination script has the code which knows what to do with that message.
     
  4. qwertyuiop11110

    qwertyuiop11110

    Joined:
    Mar 16, 2020
    Posts:
    24
    How? Don't you need a primary function that will sort through the commands and send them to appropriate functions? How do you manage InvalidCastException and NullReferenceException in params?

    Do I look for .NET events or Unity events?
     
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I use a pretty complex and layered approach. I'd love to explain every detail, but most is probably not relevant. But essentially there is a "primary function that will sort through the commands" which reads what type of message it is. If the message is one of two types (RPC or SyncVar types, probably 99%+ of my network traffic) it uses the included ID of the object to send that object the message. That object should be expecting such a message, and will have its own deserialize method for handling it.
    I test all the methods for serializing/deserializing network messages for these kinds of issues, but I also wrap the processing of an individual message a try/catch which should result in a problem message getting dropped when it hits an exception instead of gumming up the works.
     
  6. orionsyndrome

    orionsyndrome

    Joined:
    May 4, 2014
    Posts:
    3,043
    Well, Unity events, as far as I can tell, are a feature intended to be used with Unity UI. maybe you can use them for more than that, but in any case, C# events are a language feature, much more fundamental and robust. Learn what system offers you first, it'll only let you understand other derivatives better.

    In fact, if you ask me, start with the problem you have, and consider the following: You want to have separate pieces of code that can 'lend' their pieces to some central repository, so that central repo can call executives according to some message headers, without bothering with the contents. To do this, you need to basically 'lend' pointers to some methods, and group them in some fashion. After you learn about delegates and such things that basically offer you this functionality, then go learn about C# events, see what they solve, and what are the common mistakes that they try to avoid, and boilerplates that come bundled with them. Events are mostly just a syntactic sugar with a few really grand conveniences that save you time, are more readable, and prevent some classic design issues.
     
  7. qwertyuiop11110

    qwertyuiop11110

    Joined:
    Mar 16, 2020
    Posts:
    24
    Fantastic that I found out that for last 3 months I was working on ever increasing networking library, whilst you name "SyncVar" and it appears to be in library which handles everything far better than I ever could... smh