Search Unity

Does OnApplicationQuit happen after network is shut down?

Discussion in 'Multiplayer' started by burnumd, Jul 23, 2009.

  1. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Hi all,

    We've got a project where a number of standalone clients connect to a webplayer running on a LAN, which manages logging research data (the user clicked on x tab, went to y spot in the game world, et&c.) to a database. The specs call for a confirmation dialog when a client tries to quit (either from a quit button within the game or cmd+q, Windows' red "X", et&c.), which is handled by the following bit of code when the client is closed by any means other than the in-game button:
    Code (csharp):
    1. function OnApplicationQuit() {
    2.     if (!okToQuit) {
    3.         Application.CancelQuit();
    4.         displayQuitConfirmation = true;
    5.     }
    6. }
    The problem here is that the Unity player seems to shut down the networking before OnApplicationQuit has a chance to cancel the operation. Can someone confirm for me whether networking gets closed before MonoBehaviours get their OnApplicationQuit() called, or if it's a race condition (it doesn't happen 100% of the time), or the entirely plausible solution that I'm doing something screwy somewhere?
     
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Its your job to quit the network connection actually.
    If it is closed before then potentially the game object with the network view is beeing removed by some other behaviour of the application, removing the corresponding data connection
     
  3. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    I created a small test project (attached) to demonstrate the behavior. If you build the only scene in the project to a standalone and click "Host," then try to quit using your favorite manner (cmd-q, alt+f4, red x, File->Quit), you'll notice you get disconnected and popped back to the Host/Join menu. Looking at the console logs, it appears as though OnApplicationQuit() is getting called first, but that the Unity player continues shutting down the network, disregarding the Application.CancelQuit() call in OnApplicationQuit(). What's odd is that if you connect as a client and not a server, the network disconnect issue isn't happening; this behavior is the opposite of what I'm seeing in my main project (the client is shutting down the network when I try to cancel quitting).

    I don't believe I'm doing anything to manually shut down the network, but below is the entirety of the code in the scene if you don't want to run the project:
    Code (csharp):
    1. var connected : boolean = false;
    2. var hosting : boolean = false;
    3.  
    4. var displayJoinConfig : boolean = false;
    5. var ipToConnectTo: String = "";
    6.  
    7. private var okToQuit : boolean;
    8.  
    9. function OnGUI () {
    10.     if (!connected) {
    11.         if (displayJoinConfig) {
    12.             JoinConfig();
    13.         }
    14.         else {
    15.             if (GUILayout.Button("Host")) {
    16.                 Host();
    17.             }
    18.             if (GUILayout.Button("Join")) {
    19.                 displayJoinConfig = true;
    20.             }
    21.         }
    22.     }
    23.     else if (hosting) {
    24.         GUILayout.Label(networkView.owner.ipAddress);
    25.     }
    26. }
    27.  
    28. function Host() {
    29.     if (Network.InitializeServer(1, 25000) == NetworkConnectionError.NoError) {
    30.         connected = true;
    31.         hosting = true;
    32.     }
    33. }
    34.  
    35. function JoinConfig() {
    36.     GUILayout.BeginHorizontal();
    37.     GUILayout.Label("IP Address:");
    38.     ipToConnectTo = GUILayout.TextArea(ipToConnectTo, GUILayout.Width(100));
    39.     if (GUILayout.Button("Connect")  Network.Connect(ipToConnectTo, 25000) == NetworkConnectionError.NoError) {
    40.         connected = true;
    41.     }
    42.     GUILayout.EndHorizontal();
    43. }
    44.  
    45. function OnDisconnectedFromServer(mode : NetworkDisconnection) {
    46.     Debug.Log("Disconnected from server by: " + mode);
    47.     connected = false;
    48. }
    49.  
    50. function OnApplicationQuit() {
    51.     Debug.Log("OnApplicationQuit() called.");
    52.     if (!okToQuit) {
    53.         Application.CancelQuit();
    54.         okToQuit = true;
    55.     }
    56. }
    . . . and my logs:
    Code (csharp):
    1.          OnApplicationQuit() called.
    2. UnityEngine.Debug:Log(Object)
    3. NetworkTest:OnApplicationQuit()
    4.  
    5. (Filename: /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1658)
    6.  
    7. Disconnected from server by: Disconnected
    8. UnityEngine.Debug:Log(Object)
    9. NetworkTest:OnDisconnectedFromServer(NetworkDisconnection)
    10.  
    11. (Filename: /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1658)
    12.  
    13. OnApplicationQuit() called.
    14. UnityEngine.Debug:Log(Object)
    15. NetworkTest:OnApplicationQuit()
    16.  
    17. (Filename: /Users/build/builds/unity-branches-2.5.x/unity-2.5.x/Projects/../Runtime/Export/Generated/BaseClass.cpp Line: 1658)
    18.  
    19. Cleanup player
    20.  
     

    Attached Files:

  4. zumwalt

    zumwalt

    Joined:
    Apr 18, 2007
    Posts:
    2,287
    Looks to be a little bit more interesting than that.
    (I removed the code block, see CS file attached to response below)

    What appears to be happening, is that the network is still "live" but the NetworkView is no longer active. I am still playing with this, will post my findings as I dig through it. I am sure there is a hack to fix this one, just haven't figured it out yet.

    This is an interesting find, don't know if it is considered a bug, but definately something to be aware of. For some reason, the network view components loose the port they are attached to when the application is quitting. Nothing we can fix, that is internal Unity code in the engine itself. I've tried everything I can think of. See my hack in the next response for a work around.
     
  5. zumwalt

    zumwalt

    Joined:
    Apr 18, 2007
    Posts:
    2,287
    Ok, here is a hack that works, I think this is the desired result you are looking for.
     

    Attached Files:

  6. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Thanks for the detective work, zumwalt. Unfortunately, I still get disconnects with the code you posted, and I'd ideally like to send information on a quit request, so just handling the disconnect gracefully is better, but not quite what I need. I doubt I can get what I want without hooks into the network, so I'm going to file a bug report since I haven't heard from any UT folks and even if what's happening is expected behavior, it's still a little odd. Again, thanks for looking into this problem, it's nice to have another pair of eyes tell me if it looks like I'm doing something I didn't intend.
     
  7. zumwalt

    zumwalt

    Joined:
    Apr 18, 2007
    Posts:
    2,287
    Yea, the disconnects happen because the client port closes. As you can see in my code, I simply detect that the port was reset to "0" on application quit call, even though you tell it to cancel quit, its to late, the port is closed. Which of course means the client is no longer talking to the server, now, you can change the code to automatically do a reconnect, I had that in there but I took that out, if you want I can put it back in and post that script.

    This script just resets the client to show the server/client menu again if the port is set to 0.

    It is easy enough for me to put it back in and give more options on when the client has asked to quit, although as a reminder, the port number will in fact change.

    Let me know.

    {edit} what the hay, here it is, didn't take but a moment to put it back in, try this one instead.
     

    Attached Files:

  8. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Yeah, I thought about doing a reconnect when the quit gets canceled just to send the log info, but it's still an annoyance because if the client clicks quit and then decides they don't really want to quit, they've already lost their place, and the server has registered them as a timeout/forceful disconnect.
     
  9. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    if the clients clicks quit he actually asks to "kill it".

    to handle "client decides to quit, ask him about it" you would use a gui to ask him that appears on request, not as a result of ApplicationQuit.
    At the point of application quit, you would store the required data that are needed on the next session and then close it, not do some complex stuff or even completely cancel the quit.
     
  10. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    @dreamora, in the real project, I do have a "Quit" button and a dialog that confirms the user's intentions. As I mentioned in my OP, the GUI quit method works fine, but if the user is ever tempted to touch that shiny red x in the corner of the window or cmd-Q (my preferred method of quitting) or anything that kills the program by any means other than my insignificant GUI button, my program doesn't get a say in the network shutdown. I just did the "if you really want to quit, you have to quit twice" because it was easy to code for the test to isolate the behavior.
     
  11. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Fully understand that.
    Should have been a bit more specific I think.

    I hope you will find an easy solution that works in your case independent of the situation.


    Question though is if the network handling is of any impact at all for the logging itself.
    If a user clicked the "kill" triggers, he was not able to do anything inbetween that required logging, so what actually would be needed in such a case is the server realizing that the user quit and logging that.
    Should the user on the client decide to not quit, he would send a corresponding message after reconnecting that tells the server to remove the quit log entry for example.
     
  12. zumwalt

    zumwalt

    Joined:
    Apr 18, 2007
    Posts:
    2,287
    I updated my response post above to include a new peer reset that checks to make sure the player really wants to quit which is where I think he is going with this, in the fact that Unity internally when the application quit is called, actually seems to close the port that the client was using to talk to the server, in essence, closing the client network connection, first code I gave just notices that this happens and boots them back to the host/join menu, which is still a disconnection state.

    Second script I posted resets the connection, reconnects, and asks if they really want to quit. You will notice in the client results, the port changes since we had to do another connection. Don't know if this really matters in his game or not.

    edit: I forgot to mention, since your server side code is dependent on client continuous connection state, you might want to change that. It is highly likely that during any point and time, the peer will be disconnected, like a storm outside, a power outage, etc. You will want to keep an eye on users connected, and when they disconnect, just store there last save state of information somewhere, then when they reconnect, just reload it and pass it back to them as if they never quit to begin with.

    Have a "resume" tickler sent to the client and have it act on that tickler.
     
  13. burnumd

    burnumd

    Joined:
    May 27, 2008
    Posts:
    367
    Thanks to both of you for looking into this problem. The client wants to log disconnects in all sorts of different ways depending on what triggered them, so I think we'll hold off on a workaround until there's some guidance on what they'd like given the circumstances. Thanks again!