Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deploying game to server results in strange behaviour

I developed a breakout alike game with a friend using HTML5 WebSockets and java as backend and recently deployed my game on a Glassfish server that's running on the 20$ Digitalocean droplet (3GB ram, 2cpu's).

When developing the game I worked with IntelliJ and a co-worker with Netbeans, when deploying our WAR file on the Glassfish servers running on our PC everything is working as expected. But when deploying the exact same WAR file on the droplet, the ball seems to be moving 3 times as fast.

I have tried reproducing the issue by installing the same Ubuntu server as the droplet on a virtual machine and executing the same steps I used to install OpenJDK, Glassfish, ... but on the VM it was working fine as well.

Other droplets with 1 CPU (tried ubuntu and centos) produces the same issues. I wonder what might be the cause of this issue that I'm missing?

Below is the code that I'm using for the connection/game:

WebSocket:

@ServerEndpoint("/singleplayer")
public class SingleplayerSocket {

    private static final Set<Session> PLAYERS = Collections.synchronizedSet(new HashSet<Session>());

    private Session session;
    private Gson gson;
    private Game game;

    private void sendMessage(String message) {
        try {
            for (Session player: PLAYERS) {
                if (player == session) {
                    player.getBasicRemote().sendText(message);
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    private void gameStart() {
        game.start();
        sendMessage("Game started");
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        gson = new Gson();
        PLAYERS.add(session);

        sendMessage("Connection established");
    }

    @OnMessage
    public void onMessage(String message) {
        if (session != null && session.isOpen()) {
            String messageType = gson.fromJson(message, MessageType.class).getMessage();

            switch (messageType) {

                case "gameSetup":
                    gameSetup(message);
                    break;

                case "gameStart":
                    gameStart();
                    break;
            }
        }
    }

    @OnClose
    public void onClose(Session session) {
        PLAYERS.remove(session);
        this.session = null;
    }
}

Game class with the ball move method underneath:

        public class Game implements Runnable {

        private final int TARGET_FPS = 60;
        private final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;

        private volatile boolean gameRunning;
        private volatile boolean gamePaused;

        private Session session;
        private Thread thread;
        private Gson gson;

        public Game(Session session, int width, int height, String difficulty) {
            this.session = session;
            this.WIDTH = width;
            this.HEIGHT = height;
            gson = new Gson();
            timer = new Timer();

            setup(difficulty);
        }

        private void setGameRunning(boolean gameRunning) {
            this.gameRunning = gameRunning;
        }

        private void update(double delta) {
            ball.move(delta);
            collisionDetectionWalls();
            collisionDetectionPaddle();
            collisionDetectionBricks();
        }

        public void start() {
            thread = new Thread(this);
            thread.start();
            setGameRunning(true);
        }

        public void stop() {
            setGameRunning(false);
        }

        private void end(boolean won) {
            updateScore();
            sendGameEnd(won);
            stop();
        }

        private void sendMessage(String message) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        private void sendGameUpdate() {
            GameUpdateData data = new GameUpdateData(paddle, ball, bricks);
            GameUpdateResponse response = new GameUpdateResponse("gameUpdate", data);
            sendMessage(gson.toJson(response));
        }

        @Override
        public void run() {
            long lastLoopTime = System.nanoTime();
            long lastFpsTime = 0;

            while (gameRunning) {
                long currentTime = System.nanoTime();
                long updateLength = currentTime - lastLoopTime;
                lastLoopTime = currentTime;
                double delta = updateLength / ((double) OPTIMAL_TIME);

                lastFpsTime += updateLength;
                if (lastFpsTime >= 1000000000) {
                    lastFpsTime = 0;
                }

                if (!gamePaused) {
                    update(delta);
                    sendGameUpdate();
                }

                try {
                    long sleepTime = (System.nanoTime() - lastLoopTime + OPTIMAL_TIME) / 1000000;
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e) {
                }
            }
        }
    }

public class Ball {
    public void move(double delta) {
        if (isLaunched()){
            double trigoX = Math.cos(angle);
            double trigoY = Math.sin(angle);

            x += trigoX * velocity * delta;
            y += trigoY * velocity * delta;
        }
    }
}
like image 427
josephzigler Avatar asked Dec 10 '17 15:12

josephzigler


2 Answers

While working on the multi player game I stumbled on a couple of issues and decided to check a couple of variables with console.log() in Javascript. I noticed the game was being started twice when clicking on the button and solved the issue by adding a server side check to prevent starting the game multiple times when clicking on the 'Play' button.

private void gameStart() {
    if (!game.isGameRunning()) {
        game.start();
        sendMessage("Game started");
    }
}

The ball speed is working fine now.

like image 164
josephzigler Avatar answered Nov 15 '22 02:11

josephzigler


Maybe you can try System.currentTimeMillis() since System.nanoTime() is not threadsave.

Reference: Is System.nanoTime() consistent across threads?

like image 32
Tobi Tiggers Avatar answered Nov 15 '22 04:11

Tobi Tiggers