Java游戏网络编程使用Kryonet:基础数据包传输

3

我正在使用Opengl和Jbox2d编写Java实时2D游戏。

我想开始编写网络组件。

尽管它使用了box2d,但我的游戏非常小,我想使用Kryonet库创建一个简单的架构。

程序本身是一个类似于国际象棋的“对弈游戏”。我能想到的最合理的系统是有一个专用服务器来存储所有玩家数据。

PlayerA和PlayerB将连接专用服务器,该服务器会在他们之间建立一个TCP链接。

比赛结束后,两名玩家将把结果数据传回专用服务器,服务器将验证并保存他们各自的玩家数据。

对于熟悉的人来说,Diablo2实现了类似的设置。

我希望这个TCP连接只向客户端(Player B)发送主机(假设为PlayerA)的形状坐标矢量数据,然后客户端会自行呈现。

然后我希望客户端将鼠标/键盘数据发送回主机。所有处理都将在主机计算机上运行。

我的第一个问题:这种网络逻辑中是否存在任何缺陷?

我的第二个问题:如何使用Kryonet实现裸奔服务器/客户端数据包传输(如上所述)?

注意:我已经使用不同的库在C++中完成了这种精确类型的数据包传输。我找到的Kryonet文档/教程都很糟糕。建议另一个具有良好支持的库是可以接受的答案。

1个回答

2
我知道这是一个老问题,我相信原帖的提问者已经得到了他们的答案,但为了好玩,我还是想回答一下。自从最近我一直在使用Kryonet进行游戏开发以来,这个确切的问题一直在我的脑海中。
一些早期的网络游戏,比如Bungie的马拉松(1994)似乎正是这样做的:每个玩家的事件都会使用UDP发送给其他玩家。因此,如果一个玩家移动或开火,该玩家的移动或射击方向、速度等信息将被发送给其他玩家。这种方法存在一些问题。如果其中一个玩家的操作在网络上暂时丢失,那么一个或多个玩家看起来就会与其他所有人不同步。在这种情况下,没有游戏状态的“真相”或“协调”。
另一种方法是让玩家在客户端计算他们的移动和行为,并将更新后的位置发送到专用服务器。由于服务器接收到所有玩家状态更新,因此有机会对它们进行协调。如果网络上丢失了一些数据,它们也不会永久地失去同步。
与前面的例子相比,这相当于每个玩家将他们的位置发送到服务器,然后由服务器将每个玩家的位置发送给所有其他玩家。如果其中一个更新由于某种原因丢失,后续更新将进行更正。但是,如果只发送按键,则单个丢失的按键会使游戏不同步,因为所有客户端都在单独计算其他客户端的位置。
对于动作游戏,您可以使用混合方法来最小化明显的延迟。我已经成功地在一个动作游戏中使用了Kryonet这种方式。每个玩家在每个渲染时刻向服务器发送他们的状态(尽管这可能过度,并应进行优化)。状态包括位置、剩余子弹数、健康状况等。玩家还会发送他们所发射的子弹(起始速度和位置)。
服务器只是将这些东西回传给客户端。每当客户端接收到一枪时,它就会在客户端内部进行计算,包括子弹是否击中了接收方玩家。由于接收方玩家仅计算自己的状态,因此从他们自己的角度来看,一切似乎都保持同步。当他们被击中时,他们感受到了打击。当他们击中另一个玩家时,他们认为自己击中了另一个玩家。将“接收”一枪的玩家更新其健康状况并将该信息发送回服务器。
这意味着一枪理论上可能会延迟或“丢失”,而玩家可能会认为他们的射击已经击中了另一个玩家,但在另一个玩家的屏幕上没有击中。但在实践中,我发现这种方法效果很好。
以下是一个示例(伪代码,请不要期望它能编译):
class Client {
    final Array<Shot> shots;
    final HashMap<String, PlayerState> players; // map of player name to state
    final String playerName;
    void render() {
        // handle player input

        // compute shot movement
        // for shot in shot, shot.position = shot.position + shot.velociy * delta_t

        // if one of these shots hits another player, make it appear as though they've been hit, but wait for an update in their state before we know what really happened

        // if an update from another player says they died, then render their death

        // if one of these shots overlaps _me_, and only if it overlaps me, deduct health from my state (other players are doing their own hit detection)

        // only send _my own_ game state to server
        server.sendTCP(players.get(playerName));
    }

    void listener(Object receivedObject) {
 
        if(o instanceOf PlayerState) {
            // update everyone else's state for me
            // but ignore my own state update (since I computed it.)
            PlayerState p = (PlayerState)o;
            if(!p.name.equals(playerName) {
                players.add(p.name, p);
            }
        } else if (o instanceof Shot) {
            // update everyone else's shots for me
            // but ignore my own shot updates (since I computed them.)
            Shot s = (Shot)o;
            if(!s.firedBy.equals(playerName) {
                shots.add(s);
            }
        }
    }
}

class Server {
    final HashMap<String, PlayerState> players; // map of player name to 

    void listener(Object receivedObject) {

        // compute whether anybody won based on most recent player state

        // send any updates to all players
        for(Connection otherPlayerCon : server.getConnections()) {
            otherPlayerCon.sendTCP(o);
        }
    }
}


我相信这种方法也存在缺陷,并且可以通过各种方式进行改进。(例如,它很容易允许“黑客”客户端占主导地位,因为他们可以始终发送未考虑任何损伤等因素的更新。但我认为这个问题超出了问题的范围。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接