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;
}
}
}