RTSConnection::onDisconnect()--question
by Stephen Zepp · in RTS Starter Kit · 12/02/2004 (2:03 pm) · 18 replies
I'm troubleshooting why we are crashing the server whenever a client disconnects, as well as some troublesome calls to playDeathAnimation (on objects that no longer exist) when the client disconnects as well, and I've discovered that onDisconnect never seems to be called for RTSConnections.
Now, my understanding of the disconnect process is admittedly a little ragged right now, but my main question is:
shouldn't onDisconnect be called regardless of the reason for disconnection? In other words, it looks like the code (as well as my assumptions) intends RTSConnection::onDisconnect() to be called whenever the client leaves the game for whatever reason. Primarily due to the fact that this is where all the controlling connections for the units left behind are cleared in the visManager (VisManager::handleConnectionDrop() which is called by RTSConnection::onDisconnect() ).
Now, my understanding of the disconnect process is admittedly a little ragged right now, but my main question is:
shouldn't onDisconnect be called regardless of the reason for disconnection? In other words, it looks like the code (as well as my assumptions) intends RTSConnection::onDisconnect() to be called whenever the client leaves the game for whatever reason. Primarily due to the fact that this is where all the controlling connections for the units left behind are cleared in the visManager (VisManager::handleConnectionDrop() which is called by RTSConnection::onDisconnect() ).
#2
--client escapes from game, sends notification
--server catches the disconnect packet with NetInterface::processPacketReceiveEvent --where packetType == disconnect, passes along to:
--NetInterface::handleDisconnect, which does two primary things:
define NetConnection *conn = NetConnection::lookup(address);
execute conn->onDisconnect(reason); (which is typed to a NetConnection directly, NOT an RTSConnection), which is fine.
--based on debug spew output, the handling function that gets called with that conn->onDisconnect() is the one in GameConnection, NOT the one in RTSConnection:
--GameConnection::onDisconnect, which is where you see Con::printf("Client %d disconnected.", getId());
I think that's what's going on is that somewhere the netInterface code is interpreting the connection as a GameConnection (just like it does when you first log in), and the RTSConnection::onDisconnect never actually gets called ever--and since this is where we do cleanup on our units in the game (specifically VisManager::handleConnectionDrop(), we have a lot of loose pointers floating around...
12/02/2004 (2:22 pm)
I'm chasing down the execution sequence for a client requested disconnect, and I'm reasonably confident (well, no, I'm 100% confident) that RTSConnection::onDisconnect is never being called. I think the sequence is this:--client escapes from game, sends notification
--server catches the disconnect packet with NetInterface::processPacketReceiveEvent --where packetType == disconnect, passes along to:
--NetInterface::handleDisconnect, which does two primary things:
define NetConnection *conn = NetConnection::lookup(address);
execute conn->onDisconnect(reason); (which is typed to a NetConnection directly, NOT an RTSConnection), which is fine.
--based on debug spew output, the handling function that gets called with that conn->onDisconnect() is the one in GameConnection, NOT the one in RTSConnection:
--GameConnection::onDisconnect, which is where you see Con::printf("Client %d disconnected.", getId());
I think that's what's going on is that somewhere the netInterface code is interpreting the connection as a GameConnection (just like it does when you first log in), and the RTSConnection::onDisconnect never actually gets called ever--and since this is where we do cleanup on our units in the game (specifically VisManager::handleConnectionDrop(), we have a lot of loose pointers floating around...
#3
I am at work with no code around so this is just a guess
12/02/2004 (2:32 pm)
Could you do something like GameConnection::ondisconnect(%this){
RTSConnection.onDisconnect(%this)
}I am at work with no code around so this is just a guess
#4
There is something wrong with the inheritence of the Connection--we know it's an RTSConnection, and in most cases the server does as well, but on the disconnect sequence it's not treating it like one.
12/02/2004 (2:43 pm)
Yeah, was what I was thinking as a hack fix as well, but it's not particularly clean ;)There is something wrong with the inheritence of the Connection--we know it's an RTSConnection, and in most cases the server does as well, but on the disconnect sequence it's not treating it like one.
#5
Thing is, we can't forcefully cast it as an RTSConnection in netInterface.cc, because it may very well be a normal GameConnection after all (for those folks that decide to allow FPS players for example).
My server crashes in RTSUnit::processTick where I am reaching in to the visManager to find any spotted enemy units. Thing is, it is after GameConnection::onDisconnect is called, yet the units still have a controlling connection, which means that for sure, RTSConnection::onDisconnect isn't called (and of course I put in a debug line there as well to confirm). I'm relying on RTSUnit::onDisconnect to call visManager::handleConnectionDrop(), but it is never being called.
Anyone from GG have any idea why RTSConnection::onDisconnect() isn't being called, but GameConnection::onDisconnect() is?
12/02/2004 (3:03 pm)
Well, I'm stuck on this one. I'm positive that GameConnection::onDisconnect is being called before RTSConnection::onDisconnect (both in code), which implies that something is wrong with the server not recognizing the connection as an RTSConnection.Thing is, we can't forcefully cast it as an RTSConnection in netInterface.cc, because it may very well be a normal GameConnection after all (for those folks that decide to allow FPS players for example).
My server crashes in RTSUnit::processTick where I am reaching in to the visManager to find any spotted enemy units. Thing is, it is after GameConnection::onDisconnect is called, yet the units still have a controlling connection, which means that for sure, RTSConnection::onDisconnect isn't called (and of course I put in a debug line there as well to confirm). I'm relying on RTSUnit::onDisconnect to call visManager::handleConnectionDrop(), but it is never being called.
Anyone from GG have any idea why RTSConnection::onDisconnect() isn't being called, but GameConnection::onDisconnect() is?
#6
12/02/2004 (3:23 pm)
Again I don't have my code, but are the units/buildings and the like being deleted in the gameconnection::onDisconnect if not try something like gameconenction::ondisconnect(%this){
if(isObject(%this.units))
%this.units.delete();
//ect and so forth
}
#7
12/02/2004 (3:29 pm)
Keep in mind everything I'm talking about here is code, not script.
#8
I'm very hesitant to post the hack because it is a perfect example of what a "hack fix" is--it takes care of the crash, but in no way addresses the actual problem.
I'm hoping strongly that GG can provide some guidance on this issue if at all possible, since we have no real previous examples to work from (regarding inheriting from GameConnection like we do with RTSConnection), but if anyone is completely stuck and wants to know how I fixed at least my crash, email me and we can discuss the method.
12/03/2004 (4:46 am)
I've put together a hack to solve my immediate issue (clearing the connections of all units left behind when a player disconnects), but it does not in any way address the root cause of the problem.I'm very hesitant to post the hack because it is a perfect example of what a "hack fix" is--it takes care of the crash, but in no way addresses the actual problem.
I'm hoping strongly that GG can provide some guidance on this issue if at all possible, since we have no real previous examples to work from (regarding inheriting from GameConnection like we do with RTSConnection), but if anyone is completely stuck and wants to know how I fixed at least my crash, email me and we can discuss the method.
#9
NetConnection defines the onDisconnect() as a virtual function.
GameConnection implements this. (not virtual)
RTSConnection inherits this, and implements it also.
It would appear that we need to virtualize the functions in GameConnection to get the correct pointer to the correct class?
I'm basing this on this:
---
A virtual function is a function member of a class, declared using the "virtual" keyword. A pointer to a derived class object may be assigned to a base class pointer, and a virtual function called through the pointer. If the function is virtual and occurs both in the base class and in derived classes, then the right function will be picked up based on what the base class pointer "really" points at.
---
12/03/2004 (5:16 am)
Bear with me as my c++ is a bit shaky on this point...NetConnection defines the onDisconnect() as a virtual function.
GameConnection implements this. (not virtual)
RTSConnection inherits this, and implements it also.
It would appear that we need to virtualize the functions in GameConnection to get the correct pointer to the correct class?
I'm basing this on this:
---
A virtual function is a function member of a class, declared using the "virtual" keyword. A pointer to a derived class object may be assigned to a base class pointer, and a virtual function called through the pointer. If the function is virtual and occurs both in the base class and in derived classes, then the right function will be picked up based on what the base class pointer "really" points at.
---
#10
Thats returning a GameConnection pointer.
which in turn calls
Both those functions resolve out to the GameConnection versions by passing the RTSConnection versions.
Still digging through this so any tips on where to look would be great.
12/03/2004 (9:56 am)
Ok poking around a bit more, it looks like somewhere in there we are getting a GameConnection pointer, so when it goes to resolve out which onDisconnect to call it calls the GameConnection one as that what the NetInterface::handleDisconnect is getting with:NetConnection *conn = NetConnection::lookup(address);
Thats returning a GameConnection pointer.
which in turn calls
conn->onDisconnect(reason); conn->deleteObject();
Both those functions resolve out to the GameConnection versions by passing the RTSConnection versions.
Still digging through this so any tips on where to look would be great.
#11
12/03/2004 (10:09 am)
Might want to put this in the bug forum
#12
12/06/2004 (3:50 pm)
Stephen, if this is still an issue in your set-up, let me know via email and I'll try to take a look during my vacation. Sorry for the delay... wish I could clone myself or Ben or Pat.
#13
12/06/2004 (3:58 pm)
@Josh: We hacked in a fix for ourselves (expose handleConnectionDrop as a console function, and then call it directly from onClientLeaveGame), so we're good to go until you guys can get a more correct solution in place!
#14
My inital idea on this is to simply change the onDisconnect (and some other functions) in GameConnection to be virtual functions. I'm going to delay on declaring that to be the solution until I am sure that it is.
12/06/2004 (11:27 pm)
Stephen, I'm working on a fix for this but I'm running into some wierd issues.My inital idea on this is to simply change the onDisconnect (and some other functions) in GameConnection to be virtual functions. I'm going to delay on declaring that to be the solution until I am sure that it is.
#15
It was at this point that we brought it to the boards as a bug, hehe.
12/07/2004 (3:03 am)
@Pat: yes, we tried that as well, did not work (although it should have we feel as well).It was at this point that we brought it to the boards as a bug, hehe.
#16
12/07/2004 (9:00 am)
Doh. Well right now I'm getting some kind of datablock mismatch error, so something is broken in our SVN right now aparantly :b I'll try and fix this today.
#17
Here's the story. You need to change, in gameConnection.h this:
And in RTSConnection.h
Here's the kicker...
RTSConnection.cc
12/07/2004 (10:51 am)
This is fixed now, and it created a fix for TGE too!Here's the story. You need to change, in gameConnection.h this:
/// @name Event Handling
/// @{
virtual void onTimedOut();
virtual void onConnectTimedOut();
virtual void onDisconnect(const char *reason);
virtual void onConnectionRejected(const char *reason);
virtual void onConnectionEstablished(bool isInitiator);
virtual void handleStartupError(const char *errorString);
/// @}
/// @name Packet I/O
/// @{
virtual void writeConnectRequest(BitStream *stream);
virtual bool readConnectRequest(BitStream *stream, const char **errorString);
virtual void writeConnectAccept(BitStream *stream);
virtual bool readConnectAccept(BitStream *stream, const char **errorString);
/// @}And in RTSConnection.h
virtual void onDisconnect(const char *reason);
Here's the kicker...
RTSConnection.cc
void RTSConnection::onDisconnect(const char *reason)
{
[b]if( !isServerConnection() )[/b]
{
gServerVisManager->handleConnectionDrop(this);
}
Parent::onDisconnect(reason);
}Someone forgot the '!' in there. That will fix the problem.
#18
12/07/2004 (11:11 am)
@Pat: thanks a ton for the time spent on this--would have never figured it out--it seems counter-intuitive to be calling the gserverVisManager->handleConnectionDrop when you're not dealing with a server connection, but in hindsight it makes perfect sense--you're working with the client that got disconnected!
Associate Anthony Rosenbaum