I am trying to understand why the spaceship moves really jittery on screen on client on same machine using localhost as IP.
This is the code I have, followed by the order of execution -
void createUdpServer(unsigned short port) { // send data to client
// Create a socket to receive a message from anyone
sf::UdpSocket socket;
sf::IpAddress local = sf::IpAddress::getLocalAddress(); // Currently 127.0.0.1 localhost
sf::IpAddress receiver = sf::IpAddress::getLocalAddress();
socket.setBlocking(false);
// I was figuring out and arranging according to example.. set socket non blocking
// Listen to messages on the specified port
if (socket.bind(port) != sf::Socket::Done && !portBound) // You bind once
return; // error
else {
portBound = true;
}
std::cout << "Server is listening to port " << port << ", waiting for a connection... " << std::endl;
// Wait for a message
char in[128];
std::size_t received;
sf::IpAddress sender;
//unsigned short senderPort; we will be receiving and sending data on sa,e port
sf::SocketSelector selector;
selector.add(socket);
if (selector.wait(sf::milliseconds(10.f))) { // times out after 10ms
if (selector.isReady(socket)) {
if (socket.receive(in, sizeof(in), received, sender, port) != sf::Socket::Done) // blocking
return;
std::cout << "Message received from client " << sender << ": \"" << in << "\"" << std::endl;
}
}
// Sends connection established to client
/*
const char out[] = "Connection with server established!";
if (socket.send(out, sizeof(out), sender, port) != sf::Socket::Done)
return;
*/
// acknowledgement of packets
float playerXPosition = player->getPosition().x;
float playerYPosition = player->getPosition().y;
sf::Packet playerData;
sf::Packet bulletData;
playerData << playerXPosition << playerYPosition << bulletPtr->isAlive() << bulletPtr->getLocation().x << bulletPtr->getLocation().y;
//bulletData << bulletPtr->isAlive() << bulletPtr->getLocation().x << bulletPtr->getLocation().y;
socket.send(playerData, sender, port);
//socket.send(bulletData, sender, port);
//const char out[] = "";
//if (socket.send(out, sizeof(out), sender, port) != sf::Socket::Done)
// return;
}
void runUdpClient(unsigned short port) { // receive data from server
// Ask for the server address
server = "127.0.0.1";
/*
if (server == sf::IpAddress::None)
do
{
std::cout << "Type the address or name of the server to connect to: ";
std::cin >> server;
} while (server != sf::IpAddress::None);
*/
// Create a socket for communicating with the server
sf::UdpSocket socket;
sf::IpAddress recipient = sf::IpAddress::getLocalAddress();
char data[100] = "Connection with client established!";
if (socket.send(data, 100, server, port) != sf::Socket::Done && !sendInitialToServer) // if send once dont't send again
{
// added up top send this only once
// error...
}
else{
sendInitialToServer = true;
}
// Think about putting this globally
sf::SocketSelector selector;
selector.add(socket);
if (selector.wait(sf::milliseconds(10.f))) { // not enough time for server to be created with 0.1f
// received something
if (selector.isReady(socket)) {
// Wait for a message
char in[128];
std::size_t received; // am I using this?
sf::IpAddress sender;
sf::Packet playerData;
sf::Packet bulletData;
float playerXPosition;
float playerYPosition;
float clientXPosition;
float clientYPosition;
float clientBulletX;
float clientBulletY;
bool bulletShot;
socket.receive(playerData, sender, port);
//socket.receive(bulletData, sender, port);
if (playerData >> playerXPosition >> playerYPosition >> bulletShot >> clientBulletX >> clientBulletY) { // if you are able to read
clientXPosition = playerXPosition;
clientYPosition = playerYPosition;
player->setPosition(clientXPosition, clientYPosition);
if (bulletShot) {
// - might not need this
bulletPtr->spawn(true); // once bullet spawned, should be taken care of both sides
//
bulletPtr->setLocation(clientXPosition - 13.f, clientYPosition - 24.f);
if (!bulletPtr->isAlive())
{
bulletPtr->kill();
}
//else
// clientbulletx and clientbullety
//bulletPtr->draw(window); // client window handled from both client and server what to do if bullet is alive
}
}
/*
if (bulletData >> bulletShot >> clientBulletX >> clientBulletY) {
if (bulletShot) {
bulletPtr->spawn(true); // once bullet spawned, should be taken care of both sides
bulletPtr->setLocation(player->getPosition().x - 13.f, player->getPosition().y - 24.f);
//bulletPtr->draw(window); // client window handled from both client and server what to do if bullet is alive
}
}*/
else {
// Handle error / packet loss // else simulate?
std::cout << "Error - failed to read from player data packet!" << std::endl; // this is normal when packet is lost
// I think I am getting several packet drops as I am receiving and sending from the same PC same port, localhost
}
//unsigned short senderPort;
//if (socket.receive(in, sizeof(in), received, sender, port) != sf::Socket::Done)
// return;
//std::cout << "Message received from server " << sender << ": \"" << in << "\"" << std::endl;
}
// this shouldn't be here, receive stuff should go on client, server just sends
}
else {
// timeout reached, nothing was received
}
// prediction
// bullet
}
Order of Execution from main -
while (window.isOpen())
{
if (userChoice == NULL){
do {
std::cout << "User Choice - " << userChoice << std::endl;
std::cout << "Server or client? (s/c)" << std::endl;
std::cin >> userChoice;
} while (userChoice != 's' && userChoice != 'c');
// The game clock will keep on restarting until the user makes a decision
clock.restart(); // This solved issue to start movements and updates at same time once user chooses an option
}
if (userChoice == 's' || userChoice == 'c') { // start of main if // else start the whole program
// game time over here once game starts // frame independent
// Binding of ports for both client or server
/*
// bind the socket to a port
if (socket.bind(54000) != sf::Socket::Done)
{
// Using UDP starts listening on this port
// whatever happens, bind for both -> will work with two executables running on same network with two different local IPs
}
*/
// Puts the time counter back to zero and also returns the time elapsed since the clock was started
sf::Time deltaTime = clock.restart();
timeSinceLastUpdate += deltaTime;
while (timeSinceLastUpdate > TimePerFrame) // fixed time steps // userchoice != NULL otherwise it will start immeadiately
{
timeSinceLastUpdate -= TimePerFrame; // solves problem with variable delta time, each frame is unique
processEvents(window);
//update(TimePerFrame);
// Start of if server
if (userChoice == 's'){
//if(!serverCreated){ // if problems of continous connection, this needs to place outside of bind
// create connection
createUdpServer(SERVER_PORT); // over here its happening every frame
//serverCreated = true; // is server actually created though?
//}
// Player 1 movement
sf::Vector2f movement(0.f, 0.f);
if (mIsMovingUp)
movement.y -= playerSpeed;
if (mIsMovingDown)
movement.y += playerSpeed;
if (mIsMovingLeft)
movement.x -= playerSpeed;
if (mIsMovingRight)
movement.x += playerSpeed;
// shooting logic
if (mIsShooting) {
if (!bullet.isAlive()) // && !gameOver
{
bullet.spawn(true);
bullet.setLocation(mPlayer.getPosition().x - 13.f, mPlayer.getPosition().y - 24.f); // mPlayer.getPosition().x + 24 (offset) if origin is not set to center
}
}
// ** These two parts need to be put in an if statement, if server, direct movement, if client, move needs to happen with data received
// from server (Server - main player streamer; Client - viewer)
// Possibly this will be the data that needs to be sent to server
mPlayer.move(movement * TimePerFrame.asSeconds()); // Time.deltaTime frame independent movement
// for each player eventually
screenBound(mPlayer);
// screenbound enemies or else check that they do not go off bounds and change direction
// **
} // end of if server
else {
// Handle control through incoming network data
// vector x = datacoming in
// Hypothetically mPlayer.move(data coming in);
// bullet spawn
// setlocation from data packet
runUdpClient(SERVER_PORT);
}
// Enemy Behaviour // Ideally move to separate method
sf::Vector2f enemyMovement(1.f, 0.f);
float yOffset = 30.f;
float xOffset = 60.f;
// Be careful with iterators as when deleting it will automatically jump to the next item in vector
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) // CAUTION & solved everything again - lesson in C++, if we don't get address, we always create a copy and modify it, which is not what we want
{
//std::cout << "Direction before working move " << direction << std::endl;
i->getSprite().move(enemyMovement * direction); // Issue detected direction only changing sign if greater or less than screen bounds once
if (i->getSprite().getPosition().x + i->getSprite().getLocalBounds().width / 2 > game->getWindowWidth() ||
i->getSprite().getPosition().x - i->getSprite().getLocalBounds().width / 2 < game->getWindowWidth() - game->getWindowWidth()) { // this will be 0
direction = -(direction);
// Time stamp over here for network calculations
//std::cout << "Direction inside if statement " << direction << std::endl;
// another for loop move all down by yOffset
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
i->getSprite().setPosition(i->getSprite().getPosition().x, i->getSprite().getPosition().y + yOffset);
//enemy.move(0.f, enemy.getPosition().y + yOffset);
}
//enemy.setPosition(enemy.getPosition().x, enemy.getPosition().y + yOffset); // y axis is inverted in SFML
//return 0; // move to separate method
}
// This takes care of everything on both sides
if ((i->getSprite().getPosition().y > game->getWindowHeight() - mPlayer.getLocalBounds().height)) {
//removeEnemy(enemy);
// ALIVE is false, then draw based on whether alien is alive or not
std::cout << "Everyone should be dead game over!" << std::endl;
return 0; // change state to paused
}
}
}
updateStatistics(deltaTime, mStatisticsText);
// ** Start of if server
if (userChoice == 's'){
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
// Test player and alien collision
if (CollisionManager::collidesWith(mPlayer, *i) && i->isAlive()) { // *i it points to the enemy, and will give me an enemy
std::cout << "You're dead!" << std::endl;
mPlayerIsAlive = false;
return 0; //-> I'm dead gameOver = true;
}
// Test collision between bullets and aliens
if (CollisionManager::collidesWith(bullet, *i) && i->isAlive())
{
i->kill();
//enemyList->erase(i); // issue here, we are trying to do this in order to get rid of rendering problems .. shit just move it out of way
//i->setLocation(700.f, 10.f);
bullet.kill();
bullet.setLocation(700.f, 0.f); // temporary solution for dealing with not actually killing but stop drawing bullet
// without above line, the bullet will not be drawn but still exist on screen
}
/*
v.erase(std::remove_if(v.begin(), v.end(), //erase returns it, so it = erase
[](int i) { return i < 10; }), v.end());
*/
}
}
// ** end of if server
int deadEnemies = 0;
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
if (!(i->isAlive())) {
deadEnemies++;
}
if (deadEnemies >= NUMBER_OF_ALIENS) {
std::cout << "You win!" << std::endl;
return 0; // Set state win
}
}
//std::cout << "Total Dead Enemies - " << deadEnemies << std::endl;
//for (Enemy& enemy : enemyList) {
// if (enemy.isAlive()) {
// enemy.draw(window);
// }
//}
//enemy.render(alienArray, mWindow);
/*
for (int i = 0; i < mEnemies.size(); i++)
{
sf::Sprite temp = mEnemies.at(i);
mWindow.draw(temp);
}
*/
// Render All
// Remember draw order is important
window.clear();
window.draw(mBackground);
window.draw(mPlayer);
window.draw(mStatisticsText);
for (std::vector<Enemy>::iterator i = enemyList->begin(); i != enemyList->end(); ++i) {
if (i->isAlive()) {
i->draw(window);
//std::cout << "Enemy " << i->getID() << " position - " << i->getLocation().x << " " << i->getLocation().y << std::endl;
}
}
if (bullet.isAlive()) // && !gameOver // for game state and win / lose screen representation
{
//draw bullet
bullet.draw(window);
//move bullet
bullet.getSprite().move(0.f, -20);
}
//test collision with bullet and boundary
if (bullet.getSprite().getPosition().y < 0)
bullet.kill();
window.display();
} // end of main second if
}
return 0;
}