Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Sync player movement causes duplications  (Read 2998 times)

0 Members and 1 Guest are viewing this topic.

Snikur

  • Newbie
  • *
  • Posts: 2
    • View Profile
Sync player movement causes duplications
« on: March 05, 2014, 11:27:06 pm »
I tried to make a simple Pong with 1 vs 1 over the local area network and it works just fine but it get slightly desynced after a while. It looks like 2 balls and 4 "flippers" or whatever I should call em.

Any ideas on how to sync the games better?

I've synchronized the two players and the ball.
I can't provide a screenshot as the game alter between frames, making it impossible for a camera or a monitor to actually capture it.

I'm fairly new to programming so any advice is appreciated.

Code:
                ballP.x = inputSend3.x + ballV.x; //ball position + velocity
                ballP.y = inputSend3.y + ballV.y; //ball position + velocity
                playerP.y = inputSend1; //player position
                opponentP.y = inputSend2; //player two position
//Changes values of player and opponent using arrowkeys and W/S.
        globalMutex.lock(); //sends values to sync
                inputSend1 = playerP.y;
                inputSend2 = opponentP.y;
                inputSend3.x = ballP.x;
                inputSend3.y = ballP.y;
                globalMutex.unlock();

        sync();

void sync(void)
{ //sync values between server and client
        sf::Packet packetSend;
        globalMutex.lock();
        packetSend << inputSend3.x << inputSend3.y << inputSend2 << inputSend1;
        globalMutex.unlock();
        socket.send(packetSend);

        sf::Packet packetReceive;
        socket.receive(packetReceive);
        packetReceive >> inputSend3.x >> inputSend3.y >> inputSend2 >> inputSend1;
}


 
« Last Edit: March 05, 2014, 11:42:05 pm by Snikur »

zsbzsb

  • Hero Member
  • *****
  • Posts: 1409
  • Active Maintainer of CSFML/SFML.NET
    • View Profile
    • My little corner...
    • Email
Re: Sync player movement causes duplications
« Reply #1 on: March 06, 2014, 02:44:52 am »
I can't provide a screenshot as the game alter between frames, making it impossible for a camera or a monitor to actually capture it.

I would like to know why you can't provide a screenshot, I have never seen any issues that would prevent a screenshot from working (now video recording is another story  ;)).

To your issue, with the code provided it is impossible to directly tell what is causing the issue. But something I noticed is that you are using a mutex, is there a reason for a mutex? And if you are doing multithreading are you sure you really need it? Because I fairly certain there is not any really reason you should need it for such a small simple game as 1v1 pong.

Another thing is how often are you sending data? If you send data every frame then it is possible TCP is unable to keep up with the amount of data, which in turn causes your game to lag more and more causing what you have described.  :)
Motion / MotionNET - Complete video / audio playback for SFML / SFML.NET

NetEXT - An SFML.NET Extension Library based on Thor

The Terminator

  • Full Member
  • ***
  • Posts: 224
  • Windows and Mac C++ Developer
    • View Profile
Sync player movement causes duplications
« Reply #2 on: March 06, 2014, 02:45:06 am »
That isn't enough code to go on. Do you have a simple and minimal example that depicts your issue?
Current Projects:
Technoport

Snikur

  • Newbie
  • *
  • Posts: 2
    • View Profile
Re: Sync player movement causes duplications
« Reply #3 on: March 06, 2014, 12:43:58 pm »
I found some example code online so I personally don't know what the difference between mutex is and not.
The problem with the screenshot is that it alter between frames so a screenshot will not be able to show the multiple position (same as when you circle the mouse the eye can see 8-10 pointers at the same time because of refresh rate).

I got it working though and the error was simply because both the client and server were sending the same data causing multiple inputs. I fixed it by simply telling the server to send ball position and right flipper while the client only sends information about the left one.

Heres my code for future reference, feel free to give me advice on how to make it better other than implement better ball logic and actually using headers, classes and functions :P

Oh, and another thing, I can't make a release static on the way the project is set up so I can't share my game with people that don't have visual studio installed on their pc. It do however work if I exclude network and audio for some reason. Any ideas?

#ifdef SFML_STATIC
#pragma comment(lib, "glew.lib")
#pragma comment(lib, "freetype.lib")
#pragma comment(lib, "jpeg.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "gdi32.lib")  
#endif // SFML_STATIC

#include <SFML/Graphics.hpp>
#include <cmath>
#include <iostream>
#include <ostream>
#include <fstream>
#include <sstream>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>


const int FPS = 60, WINDOWWIDTH = 320, WINDOWHEIGHT = 180;
const unsigned short PORT = 5000;
const std::string IPADDRESS("123.456.789.1");
const float PI = 3.14159265359, shapeRadius = 5;
int score = 0;
int inputSend1 = 0, inputSend2 = 0;
bool serverPick = false;

sf::RenderWindow window(sf::VideoMode(WINDOWWIDTH, WINDOWHEIGHT), "Pong");
sf::Vector2f ballV, ballP, playerV, playerP, opponentP, inputSend3;
sf::CircleShape shape(shapeRadius);
sf::Texture brickTxt;
sf::Sprite brickSpr, opponentSpr, middleSpr;
sf::Event event;
sf::Font font1;
sf::Text scoreText;
sf::TcpSocket socket;
sf::Mutex globalMutex;
sf::SoundBuffer buffer;
sf::Sound sound;
void windowCloser(), sync(void), server(void), client(void);

//main
int main(int argc, char* argv[])
{
        if (!brickTxt.loadFromFile("resources/brick.png"));
        if (!font1.loadFromFile("resources/arial.ttf"));
        if (!buffer.loadFromFile("resources/jump.wav"));
        sound.setBuffer(buffer);
        sf::Thread* thread = 0;
        char who = 'a';

        while (who != 'S' && who != 'C')
        {
                std::cout << "Server or Client? S/C ";
                std::cin >> who;
                toupper(who);

                if (who == 'S')
                        server();
                if (who == 'C')
                        client();
        }

        brickSpr.setTexture(brickTxt);
        opponentSpr.setTexture(brickTxt);      
        middleSpr.setTexture(brickTxt);

        shape.setFillColor(sf::Color::White);
        ballP.x = WINDOWWIDTH/2;
        ballP.y = WINDOWHEIGHT/2;
        playerP.x = 0;
        playerP.y = WINDOWHEIGHT / 2;
        opponentP.x = WINDOWWIDTH - opponentSpr.getGlobalBounds().width;
        opponentP.y = WINDOWHEIGHT / 2;
        ballV.x = 3;
        ballV.y = 3;
        middleSpr.setScale(0.5, 6.7);
        middleSpr.setPosition(WINDOWWIDTH / 2, 0);

        thread = new sf::Thread(&sync);
        thread->launch();

        if (thread)
        {
                thread->wait();
                delete thread;
        }

        //main loop
        while (window.isOpen())
        {
                event;
                windowCloser();

                ballP.x = inputSend3.x + ballV.x;
                ballP.y = inputSend3.y + ballV.y;
                shape.setPosition(ballP.x, ballP.y);
                playerP.y = inputSend1;
                opponentP.y = inputSend2;

                playerP.y = playerP.y + playerV.y;
                opponentP.x = WINDOWWIDTH - opponentSpr.getGlobalBounds().width;
                brickSpr.setPosition(playerP.x, playerP.y);
                opponentSpr.setPosition(opponentP.x, opponentP.y);

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
                {
                        playerP.y -= 5;
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
                {
                        playerP.y += 5;
                }


                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up))
                {
                        opponentP.y -= 5;
                }

                if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down))
                {
                        opponentP.y += 5;
                }

                if (shape.getPosition().x < -10)
                {
                        score = 0;
                        ballV.x = -ballV.x;
                        ballP.x = (WINDOWWIDTH / 2);
                        ballP.y = (WINDOWHEIGHT / 2);
                }
                if (shape.getPosition().x > WINDOWWIDTH + 10)
                {
                        score = 0;
                        ballV.x = -ballV.x;
                        ballP.x = (WINDOWWIDTH / 2);
                        ballP.y = (WINDOWHEIGHT / 2);
                }

                if (shape.getPosition().y > (WINDOWHEIGHT-WINDOWHEIGHT))
                {
                        ballV.y = -ballV.y;
                }
                if (shape.getPosition().y < (WINDOWHEIGHT-shapeRadius*2))
                {
                        ballV.y = -ballV.y;
                }


                //collision

                if (brickSpr.getGlobalBounds().intersects(shape.getGlobalBounds()))
                {
                        sound.play();
                        ballP.x = 6;
                        ballV.x = -ballV.x;
                        score++;
                }

                if (opponentSpr.getGlobalBounds().intersects(shape.getGlobalBounds()))
                {
                        sound.play();
                        ballP.x = WINDOWWIDTH - 12;
                        ballV.x = -ballV.x;
                        score++;
                }

                std::ostringstream scoreString;
                scoreString << score;
                scoreText.setString(scoreString.str());
                scoreText.setFont(font1);
                scoreText.setColor(sf::Color::White);
                scoreText.setPosition((WINDOWWIDTH / 2)+10, 0);

                if (!serverPick)
                {
                        globalMutex.lock();
                        inputSend1 = playerP.y;
                        globalMutex.unlock();
                }

                if (serverPick)
                {
                        globalMutex.lock();
                        inputSend2 = opponentP.y;
                        inputSend3.x = ballP.x;
                        inputSend3.y = ballP.y;
                        globalMutex.unlock();
                }



                sync();
                window.clear();
                window.draw(shape);
                window.draw(brickSpr);
                window.draw(opponentSpr);
                window.draw(middleSpr);
                window.setFramerateLimit(FPS);
                window.draw(scoreText);
                window.display();
        }

        return 0;
}

void windowCloser()
{
        while (window.pollEvent(event))
        {
                if (event.type == sf::Event::Closed)
                        window.close();
        }
}

void sync(void)
{
        if (serverPick)
        {
                sf::Packet packetSend;
                globalMutex.lock();
                packetSend << inputSend3.x << inputSend3.y << inputSend2;
                globalMutex.unlock();
                socket.send(packetSend);

                sf::Packet packetReceive;
                socket.receive(packetReceive);
                packetReceive >> inputSend1;
        }


        if (!serverPick)
        {
                sf::Packet packetSend;
                globalMutex.lock();
                packetSend << inputSend1;
                globalMutex.unlock();
                socket.send(packetSend);

                sf::Packet packetReceive;
                socket.receive(packetReceive);
                packetReceive >> inputSend3.x >> inputSend3.y >> inputSend2;
        }
}

void server(void)
{
        sf::TcpListener listener;
        listener.listen(PORT);
        listener.accept(socket);
        std::cout << "New client connected: " << socket.getRemoteAddress() << std::endl;
        serverPick = true;
}

void client(void)
{
        if (socket.connect(IPADDRESS, PORT) == sf::Socket::Done)
        {
                std::cout << "\nConnected\n" << std::endl;
                serverPick = false;
        }
        else
        {
                std::cout << "Can't connect.";
        }
}

Nexus

  • SFML Team
  • Hero Member
  • *****
  • Posts: 6287
  • Thor Developer
    • View Profile
    • Bromeon
Re: Sync player movement causes duplications
« Reply #4 on: March 07, 2014, 10:46:27 am »
Quote
Oh, and another thing, I can't make a release static on the way the project is set up so I can't share my game with people that don't have visual studio installed on their pc. It do however work if I exclude network and audio for some reason. Any ideas?
When using sfml-audio, you must include the dynamic link libraries OpenAL32.dll and libsndfile1.dll. Their license doesn't allow static linking.

Concerning static standard libraries, I would avoid it where possible. As soon as you use different libraries, it can create all kinds of problems. You don't require the client to have Visual Studio installed; the redistribution package or even the shipped release DLLs are enough.
Zloxx II: action platformer
Thor Library: particle systems, animations, dot products, ...
SFML Game Development:

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 11034
    • View Profile
    • development blog
    • Email
Re: Sync player movement causes duplications
« Reply #5 on: March 07, 2014, 12:00:16 pm »
...libraries OpenAL32.dll and libsndfile1.dll. Their license doesn't allow static linking.
Unless you put your code under GPL or LGPL. ;)
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/