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]