Hi!
I've been making a Breakout/Arkanoid clone game with a "simple" client-server arquitecture using C++, SDL, SDL_net and Visual Studio 2013 in Windows 8.1. Working through various articles and webs on network programming (e.g. beej's & Glenn Fiedler) I've created two classes: UDPSocket and Connection (virtual connection over UDP using a protocol ID) and they seemed to work "fine" in other simple projects. Now I want to send some struct between the server and the client with input or game state (like ball x position), but when the packet arrives at the other end the data inside it has changed (e.g 0x0072F868 vs 0x0072F6a4). I think I've pinned down the problem in the UDPSocket class, but I can't seem to solve it. The problem is in the functions in UDPSocket that copy an array with the data to the packet's data (Uint8*) and viceversa.
In order to send the struct I copy the struct to an unsigned char* and pass it to Connection, where the protocol ID is added. After that Connection passes the unsigned char* to the Send in UDPSocket, where the unsigned char* is copied to the packet. I feel the solution would be pretty obvious, but my actual knowledge don't let me see it.
As you can see in the code below, I get the game information with GetGameInfo(unsigned char* data) and pass it verbatim to Connection::SendPacket (const unsigned char data[]). In the client I try to do the opposite.
int UDPSocket::Receive(IPaddress& sender, void * data, int size)
{
if (socket == nullptr)
{
return 0;
}
packet->data[0] = 0;
if (SDLNet_UDP_Recv(socket, packet))
{
sender.host = packet->address.host;
sender.port = packet->address.port;
memcpy(data, (char*)packet->data, sizeof(data));
return packet->len;
}
return 0;
}
bool UDPSocket::Send(const IPaddress &destination, const void * data, int size)
{
packet->data[0] = 0;
//Fill packet
memcpy((char*)packet->data, data, sizeof(data));
packet->address.host = destination.host;
packet->address.port = destination.port;
packet->len = size + 1;
//Send packet. Return false if the packet cannot be sent;
if (!SDLNet_UDP_Send(socket, -1, packet))
{
return false;
}
return true;
}
bool Connection::SendPacket(const unsigned char data[])
{
if (!running)
{
std::cerr << "connection is not running" << std::endl;
return false;
}
if (address.host == 0)
{
std::cerr << "address host is empty";
return false;
}
//Insert the protocolId + data into the packet data
unsigned char packet[PACKET_SIZE];
int protocolIdAux = SDL_SwapBE32(protocolId);
memcpy(packet, &protocolIdAux, sizeof(protocolIdAux));
memcpy(packet + sizeof(protocolIdAux) + 1, data, sizeof(data));
return socket.Send(address, packet, PACKET_SIZE);
}
int Connection::ReceivePacket(unsigned char data[])
{
if (!running)
{
std::cerr << "connection is not running" << std::endl;
return 0;
}
IPaddress sender;
unsigned char packet[PACKET_SIZE];
int bytes = socket.Receive(sender, packet, PACKET_SIZE);
//If packet has no data or only the protocolId return 0
if (bytes == 0 || bytes <= 4)
{
return 0;
}
int protocolIdAux = 0;
memcpy(&protocolIdAux, packet, sizeof(protocolId));
//If protocolID is not the connection's protocolId return 0
if (protocolId != SDL_SwapBE32(protocolIdAux))
{
return 0;
}
//If packet comes from a client the server does not know anything about then server connects with the client (just one client at the moment)
if (mode == Server && !IsConnected())
{
state = Connected;
address = sender;
}
if (sender.host == address.host && sender.port == address.port)
{
if (mode == Client && state == Connecting)
{
std::cout << "client completes connection with server" << std::endl;
state = Connected;
}
timeoutAccumulator = 0.0f;
memcpy(data, packet + sizeof(protocolId) + 1, sizeof(data));
return PACKET_SIZE - 4;
}
return 0;
}
struct NetworkGameState
{
int ballX;
int ballY;
int paddleX;
int idQuantity;
char bricksID[40];
int order;
};
void ServerGame::GetGameInfo(unsigned char* data)
{
NetworkGameState state;
state.ballX = SDL_SwapBE32((int)ball->GetPosX());
state.ballY = SDL_SwapBE32((int)ball->GetPosY());
state.paddleX = SDL_SwapBE32((int)paddle->GetPosX());
state.idQuantity = SDL_SwapBE32((int)bricksDestroyed.size());
state.order = SDL_SwapBE32((int)order);
int brickID;
int displacement;
int i = 0;
if (state.idQuantity)
{
displacement = 0;
std::vector<int>::iterator it;
for (it = bricksDestroyed.begin(); it != bricksDestroyed.end(); ++it)
{
brickID = *it;
memcpy(state.bricksID + displacement, &brickID, sizeof(int));
++i;
displacement = i*sizeof(int)+1;
}
}
else
{
state.bricksID[0] = 0;
}
memcpy(data, &state, sizeof(NetworkGameState));
}
Thanks in advance.