So I implemented networking in my game about 2 weeks ago. I knew the way I set it up probably wasn't that good, but all I wanted to see is if it would work from computer to computer. And it did! So then I wanted to test it over the internet from my house to my brother's house to see if would work, and it did! It was really laggy though, and hardly playable. So now I'm at the point where I want to clean it up and make it faster/more efficient.
I'll start out by posting the
entire client and server code. It's really not that complicated.
Both the client and server code is in a thread called runNet
online setup
void runNet(void* UserData)
{
SoccerLevelsClass* obj = static_cast<SoccerLevelsClass*>(UserData);
obj->netQuit=false;
obj->netType = 0;
bool good = false;
while(!good)
{
cout<<"What are you? 0 for server, 1 for client : ";
cin>>obj->netType; //store network type( 0 for sever, 1 for client)
if(obj->netType!=0 && obj->netType!=1)
{
cout<<"\n.Invalid input. Try again.\n";
}else{good=true;}
}
good = false;
string myName="";
while(!good)
{
cout<<"\n";
cout<<"Enter Online ID (must be atleast 1 character): ";
cin>>myName; //store network type( 0 for sever, 1 for client)
if(myName=="")
{
cout<<"\n.Please enter atleast one character.\n";
}
else
{
good=true;
}
}
cout<<"\n";
obj->connected=false;
obj->numMultiplayers=0;
from there it runs the server or client code based on the 'netType' value (0 = server, 1 = client)
server// start of server code
if(obj->netType==0)
{
obj->numMultiplayers++;
obj->onlinePlayers[0].host=true;
obj->onlinePlayers[0].name=myName;
obj->onlinePlayers[0].playerNumber=0;
obj->onlinePlayers[0].rank=0;
obj->myOnlineNumber=0;
obj->connected=true;
// Create a socket to listen to new connections
sf::TcpListener listener;
listener.listen(2000);
// Create a list to store the future clients
std::vector<sf::TcpSocket*> clients;
// Create a selector
sf::SocketSelector selector;
// Add the listener to the selector
selector.add(listener);
//listener.setBlocking(false);
// Endless loop that waits for new connections
int ticks = 0; //amount of loops the thread makes every second
int bytesSent=0; //amount of bytes sent every second
int bytesRecieved=0; //amount of bytes recieved second
int totalBytes=0; //amount of totalBytes sent and recieved every second
sf::Clock timeTest; //keeps track of time
while (obj->netQuit==false)
{
//if the clock is greater than 1 second
if(timeTest.getElapsedTime().asSeconds()>1)
{
//output and reset everything
totalBytes = bytesSent + bytesRecieved;
cout<<"ticks = "<<ticks<<". bytesSent = "<<bytesSent<<". bytesReceived = "<<bytesRecieved<<". total = "<<totalBytes<<".\n";
bytesSent=0;
bytesRecieved=0;
ticks=0;
timeTest.restart();
}
// Make the selector wait for data on any socket
if (selector.wait(sf::milliseconds(1)))
{
//cout<<"done waiting. listening.\n";
// Test the listener
if (selector.isReady(listener))
{
//cout<<"trying to add client.\n";
// The listener is ready: there is a pending connection
sf::TcpSocket* client = new sf::TcpSocket;
if (listener.accept(*client) == sf::Socket::Done)
{
//client->setBlocking(false);
// Add the new client to the clients list
clients.push_back(client);
// Add the new client to the selector so that we will
// be notified when he sends something
selector.add(*client);
sf::Packet packet;
while(client->receive(packet)!= sf::Socket::Done)
{
}
sf::Uint8 header;
packet >> header;
if(header==1)
{
//make a new player
string newName;
packet >> newName;
obj->onlinePlayers[obj->numMultiplayers].name = newName;
obj->onlinePlayers[obj->numMultiplayers].playerNumber = obj->numMultiplayers;
obj->onlinePlayers[obj->numMultiplayers].host = false;
obj->onlinePlayers[obj->numMultiplayers].rank = 0;
}
for (int i = 0; i<clients.size(); i++)
{
sf::Packet sendPacket;
sf::Uint8 newHeader=0;
//send new player to all players
for(int x = 0; x < obj->numMultiplayers+1; x++)
{
sendPacket.clear();
sendPacket << newHeader;
sendPacket << obj->onlinePlayers[x];
while(clients[i]->send(sendPacket) != sf::Socket::Done)
{
}
}
}
obj->numMultiplayers++; //increment number of players
}
else
{
// Error, we won't get a new connection, delete the socket
delete client;
}
}
else
{
// The listener socket is not ready, test all other sockets (the clients)
for (int i = 0; i<clients.size(); i++)
{
if (selector.isReady(*clients[i]))
{
sf::Packet packet, sendPacket;
if(clients[i]->receive(packet) == sf::Socket::Done)
{
sf::Uint8 recHeader;
packet >> recHeader;
switch(recHeader){
// recieved controller input
case 3:
{
SoccerLevelsClass::controllerStruct tempController;
packet >> tempController;
obj->contStructs[tempController.playerNumber]=tempController;
bytesRecieved += sizeof(tempController); //increment bytes recieved
sendPacket << recHeader;
sendPacket << tempController;
//send input to rest of clients
for(int j = 0; j<clients.size(); j++)
{
clients[j]->send(sendPacket);
bytesSent += sizeof(sendPacket); //increment bytesSent
}
break;
}
}
}
}
}
}
}
else
{
//tells clients to start their game (this only runs once)
if(obj->startGame)
{
sf::Uint8 gameHeader=2;
sf::Packet gamePacket;
gamePacket << gameHeader;
//iterate through clients
for (int i = 0; i<clients.size(); i++)
{
while(clients[i]->send(gamePacket) !=sf::Socket::Done)
{
bytesSent += sizeof(gamePacket);
}
}
obj->startGame=false;
}
//this if statement runs when every client loads the level and is ready to play
if(obj->inOnlineMatch==true)
{
//if the server's controller input changed, send it to all clients
if(obj->mgControllerChanged==true)
{
//iterate through clients
for (int i = 0; i<clients.size(); i++)
{
sf::Packet packet;
sf::Uint8 heads = 3;
packet << heads;
obj->myContStruct.playerNumber=0;
packet << obj->myContStruct;
//send to all clients
if(clients[i]->send(packet) == sf::Socket::Done)
{
obj->mgPlayer1->hasChanged=false;
obj->mgControllerChanged=false;
bytesSent += sizeof(packet); //increment bytes sent
}
}
}
//the onlineUpdate value turns true everytime the game loop finishes
if(obj->onlineUpdate==true)
{
//iterate through every client socket
for (int i = 0; i<clients.size(); i++)
{
//iterate through every player
for(int x = 0; x<obj->numMultiplayers; x++)
{
sf::Packet packet;
sf::Uint8 heads = 6;
packet << heads;
obj->onlinePositions[x].playerNumber=x;
packet << obj->onlinePositions[x];
clients[i]->setBlocking(false);
clients[i]->send(packet);
bytesSent += sizeof(packet); //increment bytes sent
//this statement is true when one of the player's points change
if(obj->updateStats[x]==true)
{
packet.clear();
heads = 7;
packet << heads;
obj->playerStats[x].playerNumber=x;
packet << obj->playerStats[x];
clients[i]->send(packet);
bytesSent += sizeof(packet); //increment bytes sent
}
}
}
obj->onlineUpdate=false;
}
//iterate through players to see if they died
for(int x = 0;x<8;x++)
{
//if a player died
if(obj->onlineDeaths[x].activateDeath)
{
sf::Packet packet;
sf::Uint8 header = 5;
packet << header;
obj->onlineDeaths[x].playerNumber=x;
packet << obj->onlineDeaths[x];
for (int i = 0; i<clients.size(); i++)
{
while(clients[i]->send(packet) !=sf::Socket::Done)
{
bytesSent += sizeof(packet); //increment bytes sent
}
}
obj->onlineDeaths[x].activateDeath=false;
}
}
}
}
ticks++;
sf::sleep(sf::milliseconds(5));
}
}
// end of server code
client// start of client code
if(obj->netType==1)
{
sf::IpAddress server;
do
{
std::cout << "Type the address or name of the server to connect to: ";
std::cin >> server;
}
while (server == sf::IpAddress::None);
//cout<<"here1\n";
sf::TcpSocket socket;
while(socket.connect(server,2000) != sf::Socket::Done)
{
}
cout<<"connected.\n";
obj->connected=true;
{
sf::Packet packet;
sf::Uint8 header = 1;
packet << header;
packet << myName;
while(socket.send(packet) != sf::Socket::Done)
{
}
}
socket.setBlocking(false);
int ticks = 0; //amount of loops the thread makes every second
int bytesSent=0; //amount of bytes sent every second
int bytesRecieved=0; //amount of bytes recieved second
int totalBytes=0; //amount of totalBytes sent and recieved every second
sf::Clock timeTest; //keeps track of time
while (obj->netQuit==false)
{
//if the clock is greater than 1 second
if(timeTest.getElapsedTime().asSeconds()>1)
{
//output and reset everything
totalBytes = bytesSent + bytesRecieved;
cout<<"ticks = "<<ticks<<". bytesSent = "<<bytesSent<<". bytesReceived = "<<bytesRecieved<<". total = "<<totalBytes<<".\n";
bytesSent=0;
bytesRecieved=0;
ticks=0;
timeTest.restart();
}
sf::Packet packet;
sf::Socket::Status status = socket.receive(packet);
if (status == sf::Socket::Done)
{
sf::Uint8 header;
packet >> header;
//packet control
switch(header){
//recieve a new player
case 0:
{
SoccerLevelsClass::onlinePlayerStruct tempStruct;
packet >> tempStruct;
obj->onlinePlayers[tempStruct.playerNumber]=tempStruct;
if(obj->onlinePlayers[tempStruct.playerNumber].name == myName){obj->myOnlineNumber=tempStruct.playerNumber;}
bytesRecieved += sizeof(packet); //increment bytes recieved
break;
}
//told to start the game
case 2:
{
obj->startGame=true;
//only recieve once so don't increment
break;
}
//recieve and update contoller input
case 3:
{
SoccerLevelsClass::controllerStruct tempController;
packet >> tempController;
obj->contStructs[tempController.playerNumber]=tempController;
bytesRecieved += sizeof(packet); //increment bytes recieved
break;
}
//recieved a enw player death
case 5:
{
SoccerLevelsClass::onlineDeathBody deathStruct;
packet >> deathStruct;
obj->onlineDeaths[deathStruct.playerNumber]=deathStruct;
bytesRecieved += sizeof(packet); //increment bytes recieved
break;
}
//recieved an updated player position
case 6:
{
SoccerLevelsClass::playerPosition plPosish;
packet >> plPosish;
obj->onlinePositions[plPosish.playerNumber]=plPosish;
obj->onlineUpdate=true;
obj->updatePlayerPos[plPosish.playerNumber]=true;
bytesRecieved += sizeof(packet); //increment bytes recieved
break;
}
//recieved update player points
case 7:
{
SoccerLevelsClass::playerStat stat;
packet >> stat;
obj->playerStats[stat.playerNumber]=stat;
obj->onlineUpdate=true;
bytesRecieved += sizeof(packet); //increment bytes recieved
break;
}
}
//cout<<"herh\n";
}
else if(status == sf::Socket::NotReady)
{
//cout<<"not ready.\n";
}
else if(status == sf::Socket::Disconnected)
{
//cout<<"disconnected.\n";
}
else if(status == sf::Socket::Error)
{
//cout<<"error.\n";
}
//if this client's controller input has changed
if(obj->mgControllerChanged==true)
{
sf::Packet packet, sendPacket;
sf::Uint8 heads = 3;
packet << heads;
obj->myContStruct.playerNumber=obj->myOnlineNumber;
packet << obj->myContStruct;
if(socket.send(packet) == sf::Socket::Done)
{
obj->mgPlayer1->hasChanged=false;
obj->mgControllerChanged=false;
bytesSent += sizeof(packet); //increment bytesSent
}
}
ticks++; //increment ticks
sf::sleep(sf::milliseconds(5));
}
}// end of client code
That's the entire networking code.
So I'm testing all of this on two computers in my room, my laptop, and my desktop.
But there is some weird behavior.
Scenario 1: Host on DesktopEverything runs fine on both computers. At first, the laptop shows no signs of latency. After a while though, the latency begins and slowly gets worse.
But even then, the desktop receives the input from the laptop with no noticeable latency.
Scenario 2: Host on LaptopRight out of the gate, the latency is horrible on the desktop, and slowly gets worse.
But even then, the laptop receives the input from the desktop with no noticeable latency.
So basically, the server gets the input from the client immediately no matter who's hosting and how much time has been spent in the game. But the lag continues to get worse and worse at a slow rate no matter what on the client side.
Below I've posted screenshots of the output of the ticks, bytesSent, bytesRecieved, and totalBytes values after each second, for each computer.
The laptop runs through the thread about 2-3 times as much as the Desktop. Is this the problem?