SFML community forums
Help => Network => Topic started by: ingwik on March 18, 2012, 10:02:57 pm
-
Environment: Win 7 Enterprise 64, VC++ 2010 express, SFML 1.6
I'm trying to make a simple game. A boy is running from a cow. The boy is running on the server PC and the cow is running on the client. It's a simple test to see if it's possible to run a two player game over a LAN. I've followed the tutorials but somehow, I'm stuck.
The class both inherit from (could as well has been a struct, I think):
class spelare
{
public:
sf::Uint8 ID; //0=boy, 1=cow
sf::Int32 hastighetX; //velocity, x
sf::Int32 hastighetY; //velocity y
sf::Sprite sprite; //Sprite with pic
};
The packet, more or less a copy from the lesson:
sf::Packet &operator <<(sf::Packet &Packet, const spelare &C)
{return Packet << C.ID << C.hastighetX << C.hastighetY;}
sf::Packet &operator >>(sf::Packet &Packet, spelare &C)
{ return Packet >> C.ID >> C.hastighetX >> C.hastighetY;}
Function for a server, this function seems to work just fine.
void RunServer(unsigned short Port)
{
sf::SocketTCP Server;
if (!Server.Listen(Port))
return;
std::cout << "Listen on port " << Port << ", wait for connect... " << std::endl;
sf::IPAddress ClientAddress;
sf::SocketTCP Client;
Server.Accept(Client, &ClientAddress);
std::cout << "Client connected : " << ClientAddress << std::endl;
}
And the client, seems to work too.
void RunClient(unsigned short Port)
{
sf::IPAddress ServerAddress;
do
{
std::cout << "IP to connect to? : ";
std::cin >> ServerAddress;
}
while (!ServerAddress.IsValid());
sf::SocketTCP Client;
if (Client.Connect(Port, ServerAddress) != sf::Socket::Done)
return;
std::cout << "Connected to server " << ServerAddress << std::endl;
}
These two functions are run once, at the startup before any windows are shown. I suppose they will be valid for the rest of the session.
Then, I have a function to send the packet with information. Since I never recieve anything, I'm not really sure if it works, but I think something is sent...
void PlayerSend(class spelare &s)
{
spelare C = {s.ID, s.hastighetX, s.hastighetY};
sf::Packet RegularPacket; //Packet created
sf::SocketTCP Client; //Socket created
RegularPacket << C; //Packet recieve it's three values
if (C.ID == 0)
{std::cout << "Boy sent to client: " << std::endl;}
else
{std::cout << "Cow sent to client: " << std::endl;}
if (Client.Send(RegularPacket) != sf::Socket::Done)
return;
}
And finally, the recieving function that doesn't seem to work at all.
void PlayerRecieve(class spelare &s)
{
sf::Packet RegularPacket; //Packet created
sf::SocketTCP Client;
//Client.SetBlocking(false);
spelare C; //Create new C
if (Client.Receive(RegularPacket) != sf::Socket::Done)
return;
//Nothing below this point ever activates
if (RegularPacket >> C)
{
if (C.ID == 0)
{std::cout << "Boy recieved from server: " << std::endl;}
else
{std::cout << "Cow recieved from server: " << std::endl;}
}
else //No regular packet C recieved
{std::cout << "Nothing recieved from server: " << std::endl;}
}
The "PlayerSend" function fires every time an arrow key is pressed so it should be possible for the other PC to have the player moved.
The "PlayerRecieve" function is fired just before the cow and boy are moved. As it is now, it's possible for the client to move the cow and the server to move the boy and it seems as if the two computers are connected somehow.
I can't find what I'm doing wrong. I have followed the tutorial but obviously I have made something wrong. If someone else who is better than I can show me what might be wrong, I'll apprciate it a lot.
Yours sincerely
Ingemar
(The complete code, even though the comments are in swedish, can be found here (http://sv.wikibooks.org/wiki/Programmera_spel_i_C%2B%2B_f%C3%B6r_nyb%C3%B6rjare/N%C3%A4tverksprogrammering_1#Kodexempel.2C_UDP_och_TCP_test_av_kontakt_mellan_datorer) at the bottom of the page)
-
You're creating a new socket in your PlayerSend (and PlayerRecieve) function, instead of using the one that you previously connected. It can't work ;)
-
So, what I have to do is to create a socket outside the functions in main or as a global and then use the same socket within all functions?
Or, am I supposed to make two different sockets? One to send and one to recieve? i.e.:
sf::SocketTCP Server;
and
sf::SocketTCP Client;
Thanks Laurent, for your quick answer.
//Ingemar
-
So, what I have to do is to create a socket outside the functions in main or as a global and then use the same socket within all functions?
Yes.
Or, am I supposed to make two different sockets? One to send and one to recieve?
No.
-
I changed the code to use just one TCP socket. But I am not sure if I’m supposed to use one packet that move back and forth, or use one packet to send and one other packet to receive.
Now, I have these as globals:
//Create one global packet
sf::Packet RegularPacket; //Id, x and y velocity
// Create one global TCP socket
sf::SocketTCP spelsocket;
The send function looks like this now:
void PlayerSend(class spelare &s)
{
spelare C = {s.ID, s.hastighetX, s.hastighetY};
RegularPacket << C;
if (C.ID == 0)
{std::cout << "Boy sent to client: " << std::endl;}
else if (C.ID == 1)
{std::cout << "Cow sent to client: " << std::endl;}
else
{std::cout << "Garbage sent to client: " << std::endl;}
if (spelsocket.Send(RegularPacket) != sf::Socket::Done)
return;
}
And the receive function looks like this:
void PlayerRecieve(class spelare &s)
{
spelsocket.SetBlocking(false); //Prevents connection otherwise
spelare C;
if (RegularPacket >> C)
{
if (C.ID == 0)
{std::cout << "Boy received from server: " << std::endl;}
else if(C.ID == 1)
{std::cout << " Cow received from server: " << std::endl;}
else
{std::cout << "Garbage received from server: " << std::endl;}
//Let the player sprite claim the values from the sent class/struct
s.hastighetX = C.hastighetX;
s.hastighetY = C.hastighetY;
// RegularPacket.Clear();
}
else
{std::cout << " Nothing received from server: " << std::endl;}
if (spelsocket.Receive(RegularPacket) != sf::Socket::Done)
return;
}
My problem now is that the boy is sending and also receiving himself (and so is the cow). It means that the cow and the boy sprites move in the same pattern for each player but the computers have no contact with each other, or at least so it seems.
I’ve tried to clear the packet with “// RegularPacket.Clear(); “ after the values have been given to the sprite/class, assuming that the reason for the unchanged ID value is that the same package is sent back and forth. But I’m really not sure about why it doesn’t seem to change ID when it obviously change X and Y values.’
Any input to the problem would be highly appreciated.
//Ingemar
-
Don't reuse packets, especially between send and receive, and especially with non-blocking sockets! Just create one whenever you need to (right before sending / receiving).
-
Don't reuse packets, especially between send and receive, and especially with non-blocking sockets! Just create one whenever you need to (right before sending / receiving).
New try, I removed the global packet and creates a packet inside the function instead.
void PlayerSend(class spelare &s)
{
sf::Packet ToSend;
ToSend << s.ID << s.hastighetX << s.hastighetY;
if (spelsocket.Send(ToSend) != sf::Socket::Done)
return;
}
And the recieving end:
void PlayerRecieve(class spelare &s)
{
spelsocket.SetBlocking(false);
spelare R;
sf::Packet Received;
if (Received >> R)
{
if (R.ID == 0)
{std::cout << "Boy recieved from server: " << std::endl;}
else if(R.ID == 1)
{std::cout << "Cow recieved from server: " << std::endl;}
else
{std::cout << "Garbage recieved from server: " << std::endl;}
}
else
{std::cout << "Nothing recieved from server: " << std::endl;}
s.hastighetX = R.hastighetX;
s.hastighetY = R.hastighetY;
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
}
I'm constantly getting "Nothing recieved from server: " even when I try to run both a server and a client on my own, single machine. Any idea?
//Ingemar
-
I'm constantly getting "Nothing recieved from server: "
Yeah, since you extract data from the packet before receiving it ;)
-
Obviously, you see something that I don't Laurent:
*I create a player dummy
*I create a packet
*I check if I recieve something
*If I recieve something I get a message
*If I don't recieve anything, I get another message
*I Give the player the values from the dummy struct
*I keep doing it as long as the information keep coming
Where do I extract before recieving?
if I put this at the very beginning in the recieve function. The program ends before any messages and the screen freeze after connect and window creation but before any graphic is shown:
sf::Packet Received;
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
//Ingemar
-
sf::Packet Received; // <----- you create the packet
if (Received >> R) <----- you extract data
...
if (spelsocket.Receive(Received) != sf::Socket::Done) <----- you receive
-
Something is odd, even if I shrink the code for recieving to:
void PlayerRecieve(class spelare &s)
{
sf::Packet Received;
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
spelsocket.SetBlocking(false);
}
The program freeze, turns white and nothing else happens. It doesn't matter if I set blocking to true or false. Odd, really odd, and I have no clue about how to fix it.
-
You set the socket as non-blocking after trying to receive data...
Seriously, do you understand at least a little bit what you're writting?
-
I am seriously trying to understand. I really do appreciate the help you are giving me Laurent. My biggest problem is that all other sides of C++ programming and game programming are covered one way or the other in hundreds of e-books out on the Internet, but network programming is hardly mentioned anywhere. That puts me in an awkward situation where I try to learn from the few sources that are around, and try to implement that slim knowledge to SFML that has a standard of its own. I do my best, have patience with me please.
And yes, after sitting in front of a PC and being programming a long time with few breaks, you do silly mistakes. Putting blocking to false before recieving the packet made the game run as it should again.
One thing that I don't understand though is why no code at all is executed after
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
The code breaks and not a single line of code or any couts of any kind is sent to the screen. If you, or any one else can give a hint, I think I can have a chance to make it run as it should .
I'm a "doer" when coming to programming. If I get a piece of code that works, I can build on that and continue to make more and more advanced routines. The problem is that I need to see just one working example, that's what I'm working with, and asking for, here.
//Ingemar
-
I'm glad you made it work.
This is exactly what I meant: sometimes you need to take a break, think quietly about what you've done, what could be wrong, the logic of the program, etc. Instead of rushing to the forum after every error :)
I know it's hard to begin, especially with network. It's hard to find "generic" tutorials that explain the basic concepts.
-
One thing that I don't understand though is why no code at all is executed after
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
If the socket is in blocking mode, this function will wait for data before returning. That's what "blocking" means, it blocks the execution.
This is explained in the doc/tutorials, isn't it? ;)
-
Sorry Laurent, I think I've read about blocking and non-blocking calls in the forum and not in the lessons for 1.6 network.
My code isn't working. Why? I just don't know and it is so frustrating since I think I have followed your examples to create working code for a simple two player game. But as I said, the code just stop at the check if it's done. No cout at all is sent to the player after that and the other sprite doesn't move. Just as if it never actually gets any packets.
void PlayerRecieve(class spelare &s)
{
spelsocket.SetBlocking(false); //Don't halt
sf::Packet Received; //Create local packet
spelare R; //Create local player
if (spelsocket.Receive(Received) != sf::Socket::Done)
return;
if (Received >> R) //If packet isn't empty?
{
s.hastighetX = R.hastighetX; //give player new speed
s.hastighetY = R.hastighetY;
std::cout << "Recieved: " << std::endl; //Just for checking
}
else
{std::cout << "Nothing recieved: " << std::endl;} //Just for checking
}
//Ingemar
-
Do you keep on calling PlayerRecieve continuously until something happens?
By the way, you don't have to set the non-blocking mode everytime, once at init time is enough.
-
I have it in the game loop just before the movement. Speed is set to 100 * adjustment (ElapsedTime) to make me be able send integers for the velocity over the network. The function sets the speed on the other players sprite in X and Y, like this:
if (server == true)
PlayerRecieve(cow);
else
PlayerRecieve(boy);
boy.sprite.Move(boy.hastighetX * ElapsedTime,boy.hastighetY * ElapsedTime);
cow.sprite.Move(cow.hastighetX * ElapsedTime,cow.hastighetY* ElapsedTime);
If no change has been, PlayerRecieve wont do a thing and the speed is kept. The values for speed in X and Y are saved inside the class.
By the way, you don't have to set the non-blocking mode everytime, once at init time is enough.
Good to know, I'll change the code.
-
Ok, looks good.
Now that the pieces of code that you show seem to be correct, it's hard to help you more. The error is probably in the higher-level logic of your program.
Instead of directly trying to make your game work, you should start with something minimal, and build on top of it incrementally, only adding something after the current step works as expected.
-
hmm. I have boiled it down to the " != sf::Socket::Done" routine. I need help to understand the diference between handling structs and simple numbers.
According to the examples on the homepage, this is the correct way to handle a recieving packet:
char Buffer[128];
std::size_t Received;
if ([Socketname].Receive(Buffer, sizeof(Buffer), [Packetname]) != sf::Socket::Done)
{
// Error...
}
So, if I follow the code from the code example for sending structs between computers, I should use:
if ([Socketname].Receive([Packetname]) != sf::Socket::Done)
return;
If I want to just check, I use this mixed code:
if ([Socketname].Receive([Packetname]) != sf::Socket::Done)
{
// Error...
}
And here is the culprit that has annoyed me for hours. I'm getting the Error this way, but since the "return" statement is missing next code executes and I end up having the same movement for both sprites. I've checked what's going into the sending function and all values are fine. It's the recieving that doesn't work. If I use the routine that waits for a "return" value, I won't get anything to sprite no 2 and no other sprite is moved other than the one the player control on the local PC.
The odd thing is that if I use a very simple handle of structs in the game:
Sending:
sf::Int32 entete;
sf::Int32 chaine2;
sf::Int32 chaine3;
if (Who == 's')
entete = 100;
else
entete = 500;
if (Who == 's')
chaine2 = 100;
else
chaine2 = 500;
if (Who == 's')
chaine3 = 100;
else
chaine3 = 500;
sf::Packet paquet1;
spelare Bob;
Bob.ID = entete;
Bob.hastighetX = chaine2;
Bob.hastighetY = chaine3;
paquet1<< Bob;
Recieving
sf::Packet paquet2;
paquet2 >> Bob;
Everything actually works, even though it's handled outside the function and inside the Main code. The server (s) sends correct values and recieves the correct values from the client, without any kind of check if the socket is done.
So the question is; why is the game stopping at:
if ([Socketname].Receive([Packetname]) != sf::Socket::Done)
return;
When it never halts in the send routine:
if ([Socketname].Send([Packetname]) != sf::Socket::Done)
return;
It seems as if the recieving socket is never Done even when it's put in non-blocking mode.
If I remove this row from the function, nothing works (obviously), but as I said, it do work outside the function in the example above without check if the socket is ready or not.
I can obviously circumvent this issue by handling all calls inside the main loop and not using the socket check in a function at all, but I really want to understand what's going wrong here.
//Ingemar
-
I'm giving up any tries to continue this kind of projekt. I just can't find a way to make it work. Here, I have narrowed down the code to a bare bone. If you hit space, you send the info to the other PC. It's not more complicated than that, but I've tried both with two copies on the same PC and with two different PC:s om the same network and it is nigh impossible to recieve anything and I can't send if I'm a server. As a client sending is just fine.
If someone smarter can find the bug, I'll be happy.
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/Network.hpp>
//#include <vector>
#define SFML_STATIC //No extra DLL-filer
class spelare
{
public:
sf::Int32 ID; //0=pojke, 1=ko (sf::Uint8) ger ascii tecken
sf::Int32 hastighetX; //Hastighet i X-led, kan vara negativ
sf::Int32 hastighetY; //Hastighet i Y-led, kan vara negativ
sf::Sprite sprite; //Bilden som representerar pojken eller kon
};
// Server = handles boy
// Client = handles cow
//--------------------------------------------------------------
// Create packet structure
//--------------------------------------------------------------
sf::Packet &operator <<(sf::Packet &Packet, const spelare &C)
{
return Packet << C.ID << C.hastighetX << C.hastighetY;
}
sf::Packet &operator >>(sf::Packet &Packet, spelare &C)
{
return Packet >> C.ID >> C.hastighetX >> C.hastighetY;
}
// Create a global TCP socket
sf::SocketTCP spelsocket;
//Functions
void RunClient(unsigned short Port);
void RunServer(unsigned short Port);
void PlayerSend(class spelare &s);
void PlayerRecieve( class spelare &s);
//---------------------------------------------------------
// Program start
//---------------------------------------------------------
int main (int argc, char **argv)
{
char Who = 'Z'; //Are you a client or a server?
//Create a boy
spelare pojke;
pojke.hastighetX = 100;
pojke.hastighetY = 100;
pojke.ID=0;
//Create a cow
spelare ko;
ko.hastighetX = 500;
ko.hastighetY = 500;
ko.ID=1;
//--------------------------------------------------------
// Show local adress, probably 192.168.x.x or 10.100.x.x or 169.254.x.x
//-------------------------------------------
sf::IPAddress Address1 = sf::IPAddress::GetLocalAddress();
std::string IP1 = Address1.ToString();
std::cout<<"Your local IP = " << IP1 << std::endl << std::endl;
//Create a port
const unsigned short Port = 2435;
// Client or server ?------------------------------------------------
std::cout << "Do you want to be a server ('s') or a client ('c') ? ";
std::cin >> Who;
if (Who == 's')
RunServer(Port);
else
RunClient(Port);
spelsocket.SetBlocking(false); //Recieve without stop
std::string caption;
if (Who == 's')
caption= "SERVER ";
else
caption= "CLIENT ";
sf::RenderWindow App(sf::VideoMode(800, 600, 32), caption);
while(App.IsOpened())
{ //Gameloop
sf::Event Event;
while (App.GetEvent(Event))
{ //while 2
if (Event.Type == sf::Event::Closed) //hit [x] symbol?
App.Close();
if (Event.Type == sf::Event::KeyPressed)
{ //if 1
if (Event.Key.Code == sf::Key::Escape) // ESC tangenten = close
App.Close();
if (Event.Key.Code == sf::Key::Space)
{
if (Who == 's')//server
{
PlayerSend(pojke); //Send info about boy
PlayerRecieve(ko); //Recieve info about cow
}
else //client
{
PlayerSend(ko); //Send info about cow
PlayerRecieve(pojke); //Recieve info about boy
}
}
} //end if 1
} //end, while 2
} // end Gameloop
return 0;
} //end program
void RunClient(unsigned short Port)
{
// Ask for server IP
sf::IPAddress ServerAddress;
do
{
std::cout << "Write down IP to connect to: ";
std::cin >> ServerAddress;
}
while (!ServerAddress.IsValid());
if (spelsocket.Connect(Port, ServerAddress) != sf::Socket::Done)
return;
std::cout << "Connecting to server " << ServerAddress << std::endl;
}
//Serverfunktionerna
void RunServer(unsigned short Port)
{
if (!spelsocket.Listen(Port))
return;
std::cout << "Server is listening to port " << Port << ", wait for connect... " << std::endl;
//Wait for connect
sf::IPAddress ClientAddress;
sf::SocketTCP Client;
spelsocket.Accept(Client, &ClientAddress);
std::cout << "Client connected : " << ClientAddress << std::endl;
}
void PlayerSend(class spelare &s)
{
sf::Packet paquetout;
spelare P; //Dummy player to save info in
P.ID = s.ID;
P.hastighetX = s.hastighetX;
P.hastighetY = s.hastighetY;
paquetout << P;
if (spelsocket.Send(paquetout) != sf::Socket::Done)
{
std::cout<<"Packet can't be sent"<<std::endl;
return;
}
else
{
std::cout<<"Packet is sent"<<std::endl;
std::cout<<"########################## " << std::endl;
std::cout<<"ID = " << P.ID << std::endl;
std::cout<<" X = " << P.hastighetX << std::endl;
std::cout<<" Y = " << P.hastighetY << std::endl;
std::cout<<"########################## " << std::endl;
}
}
void PlayerRecieve(class spelare &s)
{
spelare R; //Dummy player to extract info to
sf::Packet paquetin;
if (spelsocket.Receive(paquetin) != sf::Socket::Done)
{
std::cout << "Packet is lost" << std::endl;
return;
}
else
{
std::cout<<"Packet is recieved"<<std::endl;
paquetin >> R;
std::cout<<"00000000000000000000000000000000 " << std::endl;
std::cout<<"ID = " << R.ID << std::endl;
std::cout<<" X = " << R.hastighetX << std::endl;
std::cout<<" Y = " << R.hastighetY << std::endl;
std::cout<<"00000000000000000000000000000000000 " << std::endl
}
}
//Ingemar
-
On server side, there must be two sockets:
- one for listening to incoming connections
- one that represents the connection to the client
Your mistake is that you're using your global socket for both things. Once you've received a connection from a client:
spelsocket.Accept(Client, &ClientAddress);
... the listener socket becomes useless (because you only have one client) and you must use the new one (Client) to communicate with the client; here you were just ignoring the client socket after receiving it.
You would have seen this if you had read more carefully the tutorial and sample application ;)
You should definitely try to find some good article about the basic concepts of IP networking. You won't succeed in anything if you don't first understand how things are supposed to work. Unlike the graphics or audio module of SFML, which abstract things with user-friendly concepts (image, sound, ...), the network module remains close to the low-level stuff, with sockets, listeners, selectors, etc. that can't be understood just by looking at classes and functions names.
-
You're right Laurent, network programming was a bridge to far for me. I thank you for the patience you've showed me and the time you have spent on trying to solve my problems.
//Ingemar