将游戏部署到服务器会导致奇怪的行为
Deploying game to server results in strange behaviour
我使用 HTML5 WebSockets 和 java 作为后端开发了一个类似突破的游戏,并且最近将我的游戏部署在 Glassfish 服务器上,该服务器是 运行 在 20$ Digitalocean droplet 上(3GB 内存,2 个 CPU)。
在开发游戏时,我与 IntelliJ 和一位同事一起使用 Netbeans,在我们 PC 上的 Glassfish 服务器 运行 上部署我们的 WAR 文件时,一切都按预期工作。但是当在 droplet 上部署完全相同的 WAR 文件时,球的移动速度似乎快了 3 倍。
我尝试通过在虚拟机上安装与 Droplet 相同的 Ubuntu 服务器并执行与安装 OpenJDK、Glassfish 等相同的步骤来重现该问题,但在 VM 上它是工作正常。
其他带 1 CPU 的 droplet(已尝试 ubuntu 和 centos)会产生相同的问题。我想知道我遗漏的这个问题的原因可能是什么?
下面是我用于 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;
}
}
游戏class 球的移动方法如下:
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;
}
}
}
也许你可以尝试 System.currentTimeMillis() 因为 System.nanoTime() 不是线程节省。
参考:
Is System.nanoTime() consistent across threads?
在开发多人游戏时,我偶然发现了几个问题,因此决定在 Javascript 中使用 console.log() 检查几个变量。我注意到单击按钮时游戏启动了两次,并通过添加服务器端检查来解决问题,以防止单击 'Play' 按钮时多次启动游戏。
private void gameStart() {
if (!game.isGameRunning()) {
game.start();
sendMessage("Game started");
}
}
现在球速正常。
我使用 HTML5 WebSockets 和 java 作为后端开发了一个类似突破的游戏,并且最近将我的游戏部署在 Glassfish 服务器上,该服务器是 运行 在 20$ Digitalocean droplet 上(3GB 内存,2 个 CPU)。
在开发游戏时,我与 IntelliJ 和一位同事一起使用 Netbeans,在我们 PC 上的 Glassfish 服务器 运行 上部署我们的 WAR 文件时,一切都按预期工作。但是当在 droplet 上部署完全相同的 WAR 文件时,球的移动速度似乎快了 3 倍。
我尝试通过在虚拟机上安装与 Droplet 相同的 Ubuntu 服务器并执行与安装 OpenJDK、Glassfish 等相同的步骤来重现该问题,但在 VM 上它是工作正常。
其他带 1 CPU 的 droplet(已尝试 ubuntu 和 centos)会产生相同的问题。我想知道我遗漏的这个问题的原因可能是什么?
下面是我用于 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;
}
}
游戏class 球的移动方法如下:
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;
}
}
}
也许你可以尝试 System.currentTimeMillis() 因为 System.nanoTime() 不是线程节省。
参考: Is System.nanoTime() consistent across threads?
在开发多人游戏时,我偶然发现了几个问题,因此决定在 Javascript 中使用 console.log() 检查几个变量。我注意到单击按钮时游戏启动了两次,并通过添加服务器端检查来解决问题,以防止单击 'Play' 按钮时多次启动游戏。
private void gameStart() {
if (!game.isGameRunning()) {
game.start();
sendMessage("Game started");
}
}
现在球速正常。