将游戏部署到服务器会导致奇怪的行为

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将游戏部署到服务器会导致奇怪的行为相关的知识,希望对你有一定的参考价值。

我和朋友一起使用html5 WebSockets和java作为后端开发了类似于游戏的游戏,并且最近将我的游戏部署在运行在20 $ Digitalocean Droplet(3GB ram,2cpu)上的Glassfish服务器上。

在开发游戏时,我使用IntelliJ和Netbeans的同事,在我们的PC上运行的Glassfish服务器上部署WAR文件时,一切都按预期工作。但是当在液滴上部署完全相同的WAR文件时,球的移动速度似乎快了3倍。

我尝试通过在虚拟机上安装与Droplet相同的Ubuntu服务器并执行我用于安装OpenJDK,Glassfish的相同步骤来重现该问题,但是在VM上它也运行良好。

其他具有1个CPU(试过ubuntu和centos)的液滴会产生同样的问题。我想知道这个问题的原因可能是我错过了什么?

下面是我用于连接/游戏的代码:

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

下面用球移动方法的游戏类:

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

在处理多人游戏时,我偶然发现了几个问题,并决定用javascript中的console.log()检查几个变量。我注意到点击按钮时游戏开始了两次,并通过添加服务器端检查解决了问题,以防止在点击“播放”按钮时多次启动游戏。

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

现在球的速度很好。

另一答案

也许您可以尝试System.currentTimeMillis(),因为System.nanoTime()不是线程安全的。

参考:Is System.nanoTime() consistent across threads?

以上是关于将游戏部署到服务器会导致奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章

导致资产预编译在heroku部署上失败的代码片段

角度 4 将包含其他对象的对象传递给 FormBuilder.group 函数会导致奇怪的表单行为

带有 reloadData 的 AKPickerView 会导致奇怪的行为

调试优化的构建会导致程序行为不同吗?

ExtJS 4 奇怪的树加载行为

通过 '*this' 传递副本时的奇怪行为