RTTI crash

Started by
14 comments, last by Driv3MeFar 17 years, 9 months ago
I'm having a problem with my program crashing when it tries to get a typeid. The problem occurs when trying to use my network object. All my net objects inhereit from a base class, but what I am doing is creating either a Client or Server object based on user input. In my game loop, I'm trying to use typeid to determine which object is in use, but the cast crashes consitantly with the error message 'bad typeid'. NObject.h:
//NObject.h
//@TODO: winsock shutdown & sockets closing needs to be added

#ifndef NOBJECT_H
#define NOBJECT_H

// Includes_______________________________________________
#include <windows.h>
#include <winsock.h>
#include <string>
#include <queue>
#include <vector>
#include <time.h>
#include <typeinfo>
#include "Vector.h" //Math vector, not to be confused with <vector>
#include "defines.h"
// Libs___________________________________________________
#pragma comment(lib, "ws2_32.lib")

// Defines________________________________________________
#define UID		0xC5
#define TIMEOUT	10 //timeout length, in seconds
//these are to be OR'd into the flages section of a message
#define CONN	0x01	//flag for joining (if not already connected) or leaving (if already connected)
#define	SHOT	0x02
#define	HIT		0x04
#define	SCORED	0x08
//The last for bytes are used if a player drops
//If a player should drop, their player number will be
//sent in the last four bytes
//so: flag & (ONE | TWO | FOUR | EIGTH) >> 4 = player that dropped
#define ONE		0x10
#define TWO		0x20
#define FOUR	0x40
#define EIGHT	0x80

// Structs_______________________________________________

//All messages are assumed to be position & velocity updates.
//If an event occured, it will be recorded in the flags section.
struct stNetHeader
{
	stNetHeader(BYTE f=0, BYTE s=0, BYTE l=0, int r=0) : uId((const BYTE)UID), flags(f), seqNum(s), lastSeqRecieved(l), randSeed(r) {}

	const BYTE uId; //unique ID for all messages from this game
	BYTE flags; //this will store any additional information for the message
	BYTE seqNum;
	BYTE lastSeqRecieved;
	int randSeed;
}; //sizeof header: 8	<--1/2 of this is rand seed...must it be an int?
//message contains position and velocity at time
struct stNetGameMessage : public stNetHeader
{
	stNetGameMessage(BYTE f, BYTE s, BYTE l, int r, clock_t t, float p[3], float v[3]) : stNetHeader(f, s, l, r), time(t)
	{
		for (int i = 0; i < 3; ++i)
		{
			pos = p;
			vel = v;
		}
	}

	clock_t time;
	float pos[3];
	float vel[3];
};	//sizeof message: 48 bytes including header, discluding c-tors
	//this is way too big, especially if these will be queued...
	//@TODO: message packing

struct stNetChatMessage : public stNetHeader
{
	char text[NETBUFFLEN];
};

// Classes____________________________________________
//Net base class
class CNetObject
{
public:
	CNetObject() : s(0), name(""), seqNumSending(0), seqNumRecieved(0), randSeed(0), time(0), players(0) {}
	virtual ~CNetObject() = 0; //virtual for the sake of inheritance
	bool Init(bool isServer);
	bool DeInit(); //close socket, shutdown winsock
	virtual bool Shutdown() = 0;

	//Send() not provided in base class becasue templatized functions cannot be virtual
	virtual bool RelSend(stNetGameMessage msg) = 0; //no need to templatize because chat messages
													//will not be sent reliably

	virtual int Recv() = 0; //return num messages recieved

	virtual int Poll() = 0;
	virtual bool Connect(int Id = 0) = 0;
	virtual bool Disconnect(int Id = 0) = 0;
	//getters
	SOCKET getSocket()
	{
		return s;
	}
	SOCKADDR_IN getAddr()
	{
		return saBind;
	}
	//setters
	void setSocket(SOCKET sock)
	{
		s = sock;
	}
	void setAddr(SOCKADDR_IN addr)
	{
		saBind = addr;
	}

protected:
	SOCKET s;
	SOCKADDR_IN saBind;

	std::string name;
	//Queue to hold reliable messages
	//How reliability will work:
	//all reliable messages get queued.
	//That queue is sent along with all position updates.
	//The queue will be emptied upon recieving a message from the server
	//up to the last reliable message recieved, as indicated in the
	//server message header
	std::queue< stNetGameMessage > relMsgQueue;
	BYTE seqNumSending;
	BYTE seqNumRecieved;
	int randSeed;
	float time;
	short players; // 1 | 2 | 3 | ... | 15 | 16 
};

class CNetServer; //forward declaired for CNetCleint

class CNetClient : public CNetObject
{
public:
	CNetClient();
	~CNetClient();
	//Init code does not change
	virtual bool Shutdown();

	template <typename T>
	bool Send(T msg);

	virtual bool RelSend(stNetGameMessage msg);

	virtual int Recv(); //return num messages recieved

	virtual int Poll(); //poll for servers
	virtual bool Connect(int Id = 0); //connect to servers[Id]
	virtual bool Disconnect(int Id = 0); //disconnect from server, Id ignored

	CNetServer *GetServer(int i)
	{
		if (i < 0 || i >= numServers)
		{
			return NULL;
		}
		return servers;
	}
private:
	std::vector< CNetServer* > servers;
	int numServers;
};

class CNetServer : public CNetObject
{
public:
	CNetServer();
	~CNetServer();
	//Init code does not change
	virtual bool Shutdown();

	template <typename T>
	bool Send(T msg, int Id);  //send msg to clients[Id]

	virtual bool RelSend(stNetGameMessage msg);

	virtual int Recv(); //return num messages recieved

	virtual int Poll(); //looks for clients polling, should be called every game loop
	virtual bool Connect(int Id = 0); //accept or deny connect request, Id ignored
	virtual bool Disconnect(int Id = 0); //disconnect clients[Id] from server

	CNetClient *GetCleint(int i)
	{
		if (i < 0 || i >= numClients)
		{
			return NULL;
		}
		return clients;
	}

private:
	std::vector< CNetClient* > clients;
	int numClients;
};

bool CreateNetworkedObject(bool isServer, CNetObject **pObj);

#endif


NObject.cpp (not fully implement:

#include "NObject.h"
#include <assert.h>

// Functions__________________________________________________________
bool CreateNetworkedObject(bool isServer, CNetObject **pObj)
{
	if ( !(*pObj) )
	{
		if (isServer)
		{
			(*pObj) = new CNetServer;
		}
		else
		{
			(*pObj) = new CNetClient;
		}
	}
	else
	{
		return false;
	}

	return (*pObj)->Init(isServer); //init winsock

	
}

bool timeout(clock_t start, clock_t t)
{
	if ( (t - start) >= TIMEOUT * CLK_TCK )
	{
		return true;
	}
	return false;
}

// Object_____________________________________________________________
CNetObject::~CNetObject()
{
}

bool CNetObject::Init(bool isServer)
{
	int err;

	WSADATA wsadata;
	if ( (err = WSAStartup( MAKEWORD( 2, 2 ), &wsadata )) != NULL )
	{
		return false;
	}
	

	if ( ( s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == INVALID_SOCKET )
	{
		return false;
	}

	
	saBind.sin_family      = AF_INET;
	saBind.sin_addr.s_addr = INADDR_ANY;
	if (isServer)
	{
		saBind.sin_port        = htons( SPORT );
	}
	else
	{
		saBind.sin_port        = htons( CPORT );
	}
	
	if ( ( err = bind( s, (const sockaddr*) &saBind, sizeof( saBind ) ) ) != NULL )
	{
		return false;
	}

	u_long nNonblocking = 1; 
	if ( ( err = ioctlsocket( s, FIONBIO, &nNonblocking ) ) != NULL )
	{
		return false;
	}

	BOOL nBroadcasts = 1;
	if ( ( err = setsockopt( s, SOL_SOCKET, SO_BROADCAST, (const char*) &nBroadcasts, sizeof( nBroadcasts ) ) ) != NULL )
	{
		return false;
	}

	return true;
}

bool CNetObject::DeInit()
{
	closesocket(s);
	WSACleanup();

	return true;
}


// Client_____________________________________________________________
CNetClient::CNetClient() : numServers(0)
{
	servers.reserve(16); //hold at most 16 servers
	for (std::vector< CNetServer* >::iterator it = servers.begin(); it != servers.end(); ++it)
	{
		(*it) = NULL; //zero out vector
	}
}

CNetClient::~CNetClient()
{
	Shutdown();
}

bool CNetClient::Shutdown()
{
    for (std::vector< CNetServer* >::iterator it = servers.begin(); it != servers.end(); ++it)
	{
		SAFEDEL(*it);
	}
	numServers = 0;
	return true;
}

template <typename T>
bool CNetClient::Send(T msg)
{
	return false;
}

bool CNetClient::RelSend(stNetGameMessage msg)
{
	return false;
}

int CNetClient::Recv() //return num messages recieved
{
	return 0;
}

int CNetClient::Poll() //poll for servers
{
	//remove any existing server data
	if ( !Shutdown() )
	{
		return -1;
	}
    
	int err;
	SOCKADDR_IN saBroadcast;
	
	saBroadcast.sin_addr.s_addr = INADDR_BROADCAST;
	saBroadcast.sin_family      = AF_INET;
	saBroadcast.sin_port        = htons( SPORT );

	stNetHeader poll((BYTE)0xFF, 0, 0, 0); //every flag should be set for a poll message

	char buf[sizeof(stNetHeader)];
	memcpy(buf, &poll, sizeof(stNetHeader));

	err = sendto( s, buf, sizeof(buf), 0, (const sockaddr *)&saBroadcast, sizeof(saBroadcast) ); //send poll
	if (err == SOCKET_ERROR)
	{
		int nErr = WSAGetLastError();
		//sendTo shouldnt block, so all errors are bad
		return -1;
	}

	clock_t start = clock();

	while( !timeout( start, clock() ) ) //wait TIMEOUT seconds to get a response to the poll
	{
		if (numServers == 16) //server list full
		{
			break;
		}

		SOCKADDR_IN saFrom;

		char recvBuf[sizeof(stNetHeader)];
		ZeroMemory( recvBuf, sizeof(stNetHeader) );
		int fromLen = (int)sizeof(saFrom);

		err = recvfrom( s, recvBuf, sizeof(stNetHeader), 0, (sockaddr *)&saFrom,  &fromLen);
		if (err == SOCKET_ERROR)
		{
			int nErr = WSAGetLastError();

			if( nErr == WSAEWOULDBLOCK )
			{
				continue;
			}
			else
			{
				return -1;
			}
		}

		stNetHeader recvHeader(0, 0, 0, 0);
		memcpy( &recvHeader, recvBuf, sizeof(stNetHeader) );
		if ( recvHeader.uId != (BYTE)UID ) //message is not from this game, disregard it
		{
			continue;
		}

		assert( !(recvHeader.flags ^ 0xFF) ); //make sure that this is a poll response

		//Create new server with address saFrom, and add to list
		CNetServer* server = new CNetServer();
		server->setAddr(saFrom);
		servers[numServers] = server;
		++numServers;
	}

	return numServers;
}

bool CNetClient::Connect(int Id) //connect to servers[Id]
{
	return false;
}

bool CNetClient::Disconnect(int Id) //disconnect from server, Id ignored
{
	return false;
}

// Server___________________________________________________________

CNetServer::CNetServer() : numClients(0)
{
	clients.reserve(16); //hold at most 16 clients
	for (std::vector< CNetClient* >::iterator it = clients.begin(); it != clients.end(); ++it)
	{
		(*it) = NULL; //zero out vector
	}
	numClients = 0;
}

CNetServer::~CNetServer()
{
	Shutdown();
}

bool CNetServer::Shutdown()
{
	for (std::vector< CNetClient* >::iterator it = clients.begin(); it != clients.end(); ++it)
	{
		SAFEDEL(*it);
	}
	return true;
}

template <typename T>
bool CNetServer::Send(T msg, int Id)  //send msg to clients[Id]
{
	return false;
}

bool CNetServer::RelSend(stNetGameMessage msg)
{
	return false;
}

int CNetServer::Recv() //return num messages recieved
{
	return 0;
}

int CNetServer::Poll() //responds to client polls, should be called every game loop
{
	int err;
	SOCKADDR_IN saFrom;

	char recvBuf[ sizeof(stNetHeader) ];
	int fromLen = sizeof(saFrom);
    
	err = recvfrom( s, recvBuf, sizeof(stNetHeader), 0, (sockaddr *)&saFrom, &fromLen );
	
	if ( err = SOCKET_ERROR )
	{
		int nErr = WSAGetLastError();

		if ( nErr == WSAEWOULDBLOCK ) //recvfrom would block, no polls waiting
		{
			return 0;
		}
		else
		{
			return  -1;
		}
	}

	stNetHeader recvHeader;
	memcpy( &recvHeader, recvBuf, sizeof(stNetHeader) );

	if ( recvHeader.uId != (BYTE)UID )
	{
		return 0;
	}

	if ( !(recvHeader.flags ^ 0xFF) ) //message was a poll
	{
		stNetHeader poll((BYTE)0xFF, 0, 0, 0); //set every flag in poll message

		char buf[ sizeof(stNetHeader) ];
		memcpy( buf, &poll, sizeof(stNetHeader) );
		//send poll reply
		err = sendto( s, buf, sizeof(stNetHeader), 0, (const sockaddr *)&saFrom, sizeof(saFrom) );
		if (err == SOCKET_ERROR)
		{
			//sendto shouldnt block, so all errors are bad
			return -1;
		}
	}
	else
	{
		return 0;
	}


	return 1;
}

bool CNetServer::Connect(int Id) //accept or deny connect request, Id ignored
{
	return false;
}

bool CNetServer::Disconnect(int Id) //disconnect clients[Id] from server
{
	return false;
}




relevant parts of main.cpp:

CNetObject *g_NetObj;
...
void MainMenuCallback(int id, int state)
{
	switch(id)
	{
	case BUTTON_HOST_ID:
		if (state == BUTTON_DOWN)
		{
			g_currentGUI = GUI_CHAT_SCREEN;
			bServing = true;
			CreateNetworkedObject(bServing, &g_NetObj);
		}
		break;
	case BUTTON_JOIN_ID:
		if (state == BUTTON_DOWN)
		{
			g_currentGUI = GUI_JOIN_SCREEN;
			bServing = false;
			CreateNetworkedObject(bServing, &g_NetObj);
		}
		break;
	case BUTTON_REFRESH_ID:
		if ( state == BUTTON_DOWN )
		{
			assert( g_currentGUI == GUI_JOIN_SCREEN );
			if ( typeid(*g_NetObj) == typeid(CNetClient) ) //crash here
			{
				int n = g_NetObj->Poll();

				for (int i = 0; i < n; ++i)
				{
					CNetServer *server = ((CNetClient *)g_NetObj)->GetServer(i);
					std::stringstream stream;
					stream << i << ") " << inet_ntoa(server->getAddr().sin_addr) << std::endl;
					if( !g_Renderer->AddGUIStaticText(g_mainGui, STATIC_TEXT_ID, const_cast<char *>(stream.str().c_str()),
										PERCENT_OF(WIN_WIDTH, 0.3), PERCENT_OF(WIN_HEIGHT, nextOpenLine),
										COLOR_ARGB(255, 255, 0, 0), g_arialID) )
					{
						return;
					}
					nextOpenLine += 0.03f;
				}
			}
			
		}
		break;
	case BUTTON_CREDITS_ID:
		if (state == BUTTON_DOWN)
		{
			g_currentGUI = GUI_CREDITS_SCREEN;
		}
		break;
	case BUTTON_BACK_ID:
		if (state == BUTTON_DOWN)
		{
			g_currentGUI = GUI_MAIN_SCREEN;
			bServing = false;
			SAFEDEL(g_NetObj);
		}
		break;
	case BUTTON_QUIT_ID:
		if (state == BUTTON_DOWN)
		{
			PostQuitMessage(0);
		}
		break;
	case BUTTON_LEVEL_1_ID:
		if (state == BUTTON_DOWN)
		{
			//start level here
		}
		break;
	}
}
...
void GameLoop()
{
   MainMenuRender();
   //@CRASH here, steps into dbgheap.c
   if ( typeid(*g_NetObj) == typeid(CNetServer) ) //if we are serving
   {
	   g_NetObj->Poll(); //look for polling clients
   }
}


I dont know why the cast would crash as apposed to just returning NULL. Thanks. Edit: No more warning thanks to SiCrane and Fruny, but still a crash. [Edited by - Driv3MeFar on July 3, 2006 1:58:24 AM]
Advertisement
Using dynamic_cast when RTTI is not enabled is bad mojo. Try turning it on.
Quote:Original post by SiCrane
Using dynamic_cast when RTTI is not enabled is bad mojo. Try turning it on.


< stupid question >
How? I assume its a command line switch or project setting, but I can't find it in VS2005
< /stupid question >
/GR or C/C++, Language, Enable Run-Time Type Info.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny
/GR or C/C++, Language, Enable Run-Time Type Info.


Ok, thanks.

My program still crashes at the tyepid, however.

[Edited by - Driv3MeFar on July 2, 2006 6:40:51 PM]
Quote:Original post by Driv3MeFar
My program still crashes at the tyepid, however.


Then you probably have a memory corruption problem.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Quote:Original post by Fruny

Then you probably have a memory corruption problem.


That promises to be fun to track down.
Not that I really expect anyone to be able or want to read the code I posted, but does anyone see any glairing obvious mistakes on my part?

Thanks.
Well, a C cast like CNetServer *server = ((CNetClient *) g_NetObj)->GetServer(i); never looks auspicious, but outside of that, nothing jumps at me.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
I doubt anyone can give you a conclusive answer based on the code you posted, but I would hazard a guess and say the g_NetObj is not a valid pointer, and that it is not the cast that is failing, but the dereferencing.


jfl.
Can you post the exact error message of the crash? A screenshot would be ideal if you can arrange it.

Is the crash a Windows "illegal operation," or an unhandled exception error, or maybe an assertion failure? Does the error message give you an option to debug? If so, where does your debugger indicate the crash is occurring?

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

This topic is closed to new replies.

Advertisement