Author Topic: Reconnect after TNManager.Disconnect Protocol Version Error  (Read 6515 times)

Monkey Man

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Hello Aren,

First off thank you so much for the plugins you've made.  I have NGUI, TNet, HUDText and Starlink.  All very good stuff!

I watched you're tutorials on how to create custom packets.  I have made a custom Login System and Registration System which passes strings to and from a  a remote dedicated server.  It's working really well! Fast, stable, packets are at a reasonable size and the Mysql Database query's just fine.  I did notice though that in order to send custom packets to the player I had to connect using TNManager.Connect() first and then send packets.  That way the server could have the packet sent back to the player using EndSend(true, player).  The client reads the response packet and continues successfully if the credentials are correct and disconnects the user if the credentials are incorrect.  However, When i try to reconnect while in Menu Scene (where all this takes place) i get an exception thrown in the console.

I am using you're Starlink Disconnect methods "LoadMenu" and "EndNow"

  1.  
  2.         static void EndNow ()
  3.         {
  4.                         //gameType = GameType.None;
  5.                         Time.timeScale = 0f;
  6.                         mTargetTimeScale = 0f;
  7.                         mPause = 0;
  8.                        
  9.                         // This would be a good place to show a match summary screen... but since I don't have one, just load the menu instead.
  10.                         LoadMenu();    
  11.         }
  12.  
  13. static void LoadMenu ()
  14.         {
  15.                 //gameType = GameType.None;
  16.                 Time.timeScale = 1f;
  17.                 mTargetTimeScale = 1f;
  18.                 mPause = 0;
  19.  
  20.                 if(Application.loadedLevelName != "Main Menu")
  21.                 {
  22.                         if (instance != null)
  23.                         {
  24.                                 Destroy(instance);
  25.                                 instance = null;
  26.                                 Debug.Log(TNManager.playerID);
  27.                         }
  28.                         Application.LoadLevel("Main Menu");
  29.                 }
  30.                
  31.         }
  32.  
  33. void OnNetworkDisconnect ()
  34.         {              
  35.                         EndNow();
  36.                
  37.                 _sessionID = "";
  38.         }
  39.  
  40.  

  1. VerifyResponseID expected ResponseID, got Error
  2. UnityEngine.Debug:LogWarning(Object)
  3. TNet.TcpProtocol:VerifyResponseID(Packet, BinaryReader) (at Assets/TNet/Common/TNTcpProtocol.cs:696)
  4. TNet.GameClient:ProcessPacket(Buffer, IPEndPoint) (at Assets/TNet/Client/TNGameClient.cs:630)
  5. TNet.GameClient:ProcessPackets() (at Assets/TNet/Client/TNGameClient.cs:603)
  6. TNManager:Update() (at Assets/TNet/Client/TNManager.cs:769)

  1. Protocol version mismatch!
  2. UnityEngine.Debug:LogError(Object)
  3. GameManager2:OnNetworkConnect(Boolean, String) (at Assets/Scenes/Scripts/GameManager2.cs:214)
  4. System.Reflection.MethodBase:Invoke(Object, Object[])
  5. TNet.UnityTools:Broadcast(String, Object[]) (at Assets/TNet/Client/TNUnityTools.cs:41)
  6. TNManager:OnConnect(Boolean, String) (at Assets/TNet/Client/TNManager.cs:784)
  7. TNet.GameClient:ProcessPacket(Buffer, IPEndPoint) (at Assets/TNet/Client/TNGameClient.cs:645)
  8. TNet.GameClient:ProcessPackets() (at Assets/TNet/Client/TNGameClient.cs:603)
  9. TNManager:Update() (at Assets/TNet/Client/TNManager.cs:769)

Strangely when I unpause the editor to continue the login process it succeeds.  But i'm still curious as to why the exception is being thrown.  I searched through the forums to see if anyone else is getting a "Protocol Version Mismatch" but it seems no one is having a similar issue.

Furthermore, I also think it's important to note that i change the player's ID to their mysql database ID during the login process.  If the credentials are correct the player's ID and name is changed on both the client and server side. 

This error is only thrown if I stay in the Menu Scene, disconnect the TNManager when the credentials do not match, and then attempt to re-login.  If i disconnect from another scene, everything works normally.

Thanks again for these great tools!  Hope all is going well.

Best Regards,
Monkey Man

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #1 on: July 26, 2013, 03:30:37 AM »
Interesting case. Protocol mismatch is thrown when you're using a different server version than the client version -- but in your case that doesn't seem to be what's happening. It seems what's happening instead is you get an error packet instead. What's the cause of it and where does it come from? Can you put some Debug.Logs inside the functions that send Packet.Error on both the client and the server?

Monkey Man

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #2 on: July 26, 2013, 02:18:56 PM »
I tried putting in some debug statements but strangely nothing new popped up in my Console.

Here Is the packet response client side that should post a Debug Statement



Here is the Error Function on the GameServer



Here's the first error that's shown in the console when i type in invalid credentials. As you can see no error is posted in the unity console.



After I then type in my correct credentials the second time and got an Object Disposed error.  This error has come an gone randomly.



The the third attempt to login shows the Protocol Mismatch Error with the Failure to VerifyID warning



Here's my custom Login Packet.  The username and password is sent.  If the username and password match it sets Login result to true, false if they do not.  If the credentials do match, grab the userID and attempt to grab a sessionID which returns null if the user is already logged in by someone else.  errorcode returns the client the nature of the error to display to the user ("Credentials did not match, "User already logged in").  If there was an error then the server sends errorcode and disconnects the player, otherwise it sends the userID, playername, and sessionID.  I have an authentication packet that sends the session ID corresponding to the player ID every minute to test if there is any funny business going on.  The code seems valid and shouldn't cause an error which is why i'm stumped as to why i'm getting a version check error.
  1. case Packet.LoginRequest:
  2.             {
  3.                 string user = reader.ReadString();
  4.                 string pass = reader.ReadString();
  5.                 bool loginResult = MysqlFunctions.ProcessLogin(user, pass);
  6.                 int userID = 0;
  7.                 ushort errorCode = 0;
  8.                 string sessionID = "";
  9.                 if (loginResult)
  10.                 {
  11.                     userID = MysqlFunctions.GrabUserID(user);
  12.                     sessionID = MysqlFunctions.CheckIfPlayerAlreadyInSession(userID);
  13.                 }
  14.                 else
  15.                 {
  16.                     errorCode = 1;
  17.                 }
  18.                 if (!string.IsNullOrEmpty(sessionID))
  19.                 {
  20.                     player.id = userID;
  21.                     player.name = user;
  22.                 }
  23.                 if (errorCode != 1 && string.IsNullOrEmpty(sessionID))
  24.                 {
  25.                     errorCode = 2;
  26.                 }
  27.                 BinaryWriter writer = BeginSend(Packet.LoginResponse);
  28.                 writer.Write(errorCode);
  29.                 if (errorCode == 0)
  30.                 {
  31.                     writer.Write(loginResult);
  32.                     writer.Write(player.id);
  33.                     writer.Write(sessionID);
  34.                     EndSend(true, player);
  35.                 }
  36.                 else
  37.                 {
  38.                     EndSend(true, player);
  39.                     RemovePlayer(player);
  40.                 }
  41.                 break;

I also looked at the VerfiyResponseID function under the TNGameClient class and i see that its receiving an Error packet.  However i think it's a blank packet because there isn't any console messages popping up.  I think the problem might lie under the TcpProtocol.Stage where it's not set to the correct stage when the process packet function is executed.  I think the client still thinks it's in the verified stage and it's not being reset.  Just a thought i could be wrong.

Thanks for you're help!  Let me know if you want me to try anything else that comes to mind.

-Monkey Man

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #3 on: July 27, 2013, 12:09:33 PM »
Verifying stage is something that happens internally in TNet. It's basically an internal handshake, and is always the first packet sent and received. It's used to ensure that you are communicating with a TNet server, and not some other type of a server. You need to wait until you are past the verified stage before sending anything. You are past the verified stage when you get the "OnNetworkConnect" notification. When do you begin sending your packets?

Monkey Man

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #4 on: July 29, 2013, 07:07:14 PM »
Sorry for the late response I've been busy and I wanted to figure this out on my own as best I could.  But it am truly stumped with this one.  :P

Here is my OnNetworkConnect function

  1. void OnNetworkConnect (bool result, string message)
  2.         {
  3.                 if (result)
  4.                 {      
  5.                         if(loggingIn)
  6.                         {
  7.                                 UnityTools.Broadcast("Login");
  8.                         }
  9.                         else
  10.                         if(Onregister)
  11.                         {
  12.                                 UnityTools.Broadcast("Register");
  13.                         }
  14.                        
  15.                         /*
  16.                         if (!string.IsNullOrEmpty(connectSuccessFunctionName))
  17.                         {
  18.                                 UnityTools.Broadcast(connectSuccessFunctionName);
  19.                         }
  20.                         */
  21.                 }
  22.                 else {
  23.                        
  24.                         Debug.LogError(message);
  25.                         //_notifierText.GetComponent<UILabel>().text = "Failed to Connect";
  26. //                      if (!string.IsNullOrEmpty(failureFunctionName))
  27. //              {
  28. //                      UnityTools.Broadcast(failureFunctionName, message);
  29. //              }
  30.                         }
  31.                
  32.         }



Here is my Login function "username" and "password" are both UIInput's

  1. void Login()
  2.         {
  3.                 //_notifierText.GetComponent<UILabel>().text = "Logging in...";
  4.                 GameManager2.instance.Username = username.text;
  5.                 BinaryWriter writer = TNManager.BeginSend(Packet.LoginRequest);
  6.                 writer.Write(username.text);
  7.                 writer.Write(password.text);
  8.                 TNManager.EndSend();
  9.                
  10.         }
  11. }

I have a button that switches the boolean value along with triggering my GameManager's connect function

  1. void OnClick() {
  2.        
  3.                 //UIWindow.Show(loggingWindow);
  4.                 GameManager2.instance.loggingIn = true;
  5.                
  6.                 if(GameSettings.CallRememberCredentials() == 1)
  7.                 {
  8.                         GameSettings.SetPlayerUsername(username.text);
  9.                         GameSettings.SetPlayerPassword(password.text);
  10.                 }
  11.                
  12.                 GameManager2.instance.Connect();
  13.                
  14.         }

 and my packet handling functions are as follows.

Server Packet Handler

  1. case Packet.LoginRequest:
  2.             {
  3.                 string user = reader.ReadString();
  4.                 string pass = reader.ReadString();
  5.                 bool loginResult = MysqlFunctions.ProcessLogin(user, pass);
  6.                 int userID = 0;
  7.                 ushort errorCode = 0;
  8.                 string sessionID = "";
  9.                 if (loginResult)
  10.                 {
  11.                     userID = MysqlFunctions.GrabUserID(user);
  12.                     sessionID = MysqlFunctions.CheckIfPlayerAlreadyInSession(userID);
  13.                 }
  14.                 else
  15.                 {
  16.                     errorCode = 1;
  17.                 }
  18.                 if (!string.IsNullOrEmpty(sessionID))
  19.                 {
  20.                     player.id = userID;
  21.                     player.name = user;
  22.                 }
  23.                 if (errorCode != 1 && string.IsNullOrEmpty(sessionID))
  24.                 {
  25.                     errorCode = 2;
  26.                 }
  27.                 BinaryWriter writer = BeginSend(Packet.LoginResponse);
  28.                 writer.Write(errorCode);
  29.                 if (errorCode == 0)
  30.                 {
  31.                     writer.Write(loginResult);
  32.                     writer.Write(player.id);
  33.                     writer.Write(sessionID);
  34.                     EndSend(true, player);
  35.                 }
  36.                 else
  37.                 {
  38.                     EndSend(true, player);
  39.                     RemovePlayer(player);
  40.                 }
  41.                 break;
  42.             }

GameClient Function that handles the response

  1. void LoginHandler (Packet response, BinaryReader reader, IPEndPoint source)
  2.         {
  3.                 if (response == Packet.LoginResponse)
  4.                 {
  5.                         ushort errorCode = reader.ReadUInt16();
  6.                         string sessionID = "";
  7.                         if(errorCode == 0)
  8.                         {
  9.                                 bool myVal = reader.ReadBoolean();
  10.                                 int uid = reader.ReadInt32();
  11.                                 sessionID = reader.ReadString();
  12.                                 GameManager2.instance.UserID = uid;
  13.                                 TNManager.player.id = uid;
  14.                                 TNManager.player.name = GameManager2.instance.Username;
  15.                                 Debug.Log("Registered Player ID: " + TNManager.playerID);
  16.                                 Debug.Log("Registered Player Name: " + TNManager.playerName);
  17.                                 GameManager2.instance.loggingIn = false;
  18.                                 //TNManager.JoinChannel(channelID, firstLevel);
  19.                                 Application.LoadLevel(GameSettings.levelNames[1]);     
  20.                         }
  21.                        
  22.                         _notiferText.text = GameSettings.loginErrorMessages[errorCode];
  23.                         Debug.Log ("Session ID: " + sessionID);
  24.                         GameManager2.instance.sessionID = sessionID;   
  25.                 }
  26.         }

« Last Edit: July 30, 2013, 05:22:21 PM by Monkey Man »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #5 on: July 30, 2013, 04:23:14 PM »
Why do you assign TNManager.player.id and name in the login function? You should never be setting the ID yourself. And the name? TNManager.playerName should be used for that. You basically should not be modifying values inside TNManager.player at all. Doing so will result in unpredictable behaviour as your client values won't match the server.

Furthermore, you call Application.LoadLevel (without going through TNManager.LoadLevel at that), and then afterwards set the error message? Why do you do this? Your code is confusing me...

Monkey Man

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #6 on: July 30, 2013, 05:05:49 PM »
I assign the player.id both Client and Server Side so they match up.

Stated in Case: Packet.LoginRequest in GameServer.CS

  1. if (!string.IsNullOrEmpty(sessionID))
  2.                 {
  3.                     player.id = userID;
  4.                     player.name = user;
  5.                 }

MysqlFunctions.CheckIfPlayerAlreadyInSession(userID); Returns a GUID string to and creates a session ID for authentication during gameplay ONLY if there isn't already one for the user, which is why it takes in an (int) as a parameter (userID).  If there is already a session created then the user is already logged in and returns an empty string so two accounts can't be logged in at the same time.  I change the player.id to the user id and player name to the player's username here so i don't have to request another packet request for a name change, which ultimately does the exact same thing.  The packet is returned successfully because the player does get logged in successfully as shown in my screenshots. 

But obviously I have to do the same thing client side but only if there wasn't an error during the login proccess, which my "LoginRequest" Packet Case Statement checks for (invalid credentials or a sesionID already given to a player, which are errorcode 1 and 2 respectfully).  My LoginHandler checks for ushort errorcode and if errorcode != 0 then there was an error during the login proccess.  On the client this displays text for an NGUI Label "Incorrect Login Credentials" or "User is already Logged in by a another player" an executes GameManager2.EndNow().  But if it was successful it sets the player ID and name as it did on the server side.

  1. TNManager.player.id = uid;
  2. TNManager.player.name = GameManager2.instance.Username;
  3.  

If the player.id is truly more depended on by other methods and functions, I can reference the player's user account by adding a userID as a parameter of all my packet sending functions although it is definitely not ideal.  It's much easier and more optimal to link the player.id directly to their username id on login because i can just pass the player.id into my MySQL functions as a parameter when a request packet is made instead of passing the username id as an extra parameter in every request packet.

But if i have to do it that way than it is what it is.

As for TManager.LoadLevel, This is only supposed to be used when it affects every player in the room and only works when a player is in a channel.  I'm simply just changing scenes locally in the client to the "character selection screen" where the player select they're character, but they're still connected to the server and can send/receive packets.  When they've selected they're character then they join channel 1 which I've deemed the "General" Channel in my custom chat.  TNet is going to be primarily used for sending packets which execute mysql queries and request data based on players to individual user clients more than syncing with other players.  It will sync with other players for chat purposes and an updated player character stat list so all players see the same data.  I don't think i'll ever be using TManager.LoadLevel for the purposes of changing a scene for an entire group of players in the same channel.  Unless i'm misinterpreting TManager.LoadLevel's purpose.

I really do appreciate you're help.  I'm going to make some changes to my packets and not switch the player Id when the user logs in.  Come to think of it, it would actually be smarter and more secure to use the sessionID to reference the player in my sessions table and grab the userID from there for mysql queries, so i'll give that a try and see if that changes anything.  Let me know if you see anything else I should take a look at that might be causing the problem.  I'll update my post if anything changes before you get back to me.

Cheers! and Best Regards,
-Monkey Man
« Last Edit: July 30, 2013, 05:23:47 PM by Monkey Man »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #7 on: July 30, 2013, 06:53:17 PM »
Again, you should not be modifying player ID or name yourself directly like that. ID is internal to the TNet server, and name must be set using TNManager.playerName. Have you looked inside TNet.GameServer? If you do, you will notice that it keeps a list of players in a dictionary in player ID-player pairs for faster lookup: mDictionaryID. You changing the ID like that breaks the consistency.

Monkey Man

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 7
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #8 on: July 30, 2013, 07:04:44 PM »
I feel foolish >.< Thanks for pointing that out. haha

Well I've been re-writing it to take in a byte array that's been converted from a concatenated string.  I assume this is more efficient than sending strings and other types of data through packets?  If so then I'm attaching the unique session ID to every packet a player sends to identify the user.  This seams like a better method anyway.

By the way, Is there a difference between TNManager.client.BeginSend and TNManager.BeginSend are handled? 

Thanks again!

Best Regards,
-Monkey Man

EDIT: I also just realized you I could add custom data to the player class in the section you specified.  I've been working too long and hard this summer i'm beginning to miss the simplest of things.

UPDATE: I made the changes and no longer change the player ID.  I added a public uint uid property to the player class that is assigned when the player logs in. Now I'm only getting one error. "The object was used after being disposed"

  1. The object was used after being disposed.
  2. UnityEngine.Debug:LogError(Object)
  3. GameManager2:OnNetworkConnect(Boolean, String) (at Assets/Scripts/GameManager2.cs:214)
  4. System.Reflection.MethodBase:Invoke(Object, Object[])
  5. TNet.UnityTools:Broadcast(String, Object[]) (at Assets/TNet/Client/TNUnityTools.cs:41)
  6. TNManager:OnConnect(Boolean, String) (at Assets/TNet/Client/TNManager.cs:784)
  7. TNet.GameClient:ProcessPacket(Buffer, IPEndPoint) (at Assets/TNet/Client/TNGameClient.cs:802)
  8. TNet.GameClient:ProcessPackets() (at Assets/TNet/Client/TNGameClient.cs:603)
  9. TNManager:Update() (at Assets/TNet/Client/TNManager.cs:769)


« Last Edit: July 30, 2013, 09:39:42 PM by Monkey Man »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Re: Reconnect after TNManager.Disconnect Protocol Version Error
« Reply #9 on: July 31, 2013, 12:26:34 PM »
GameManager2 is your class, so I don't know what it's trying to use -- but whatever it is, it was already destroyed.