It can be summarized as follows: the less information a client has, the less opportunity there is of exploiting said information for cheating. Also, the less information a client needs, the less bandwidth is required.
Previously, we've generously sent information about every player, every update cycle. In this recipe, we'll change that so that the server checks what players can be seen by others, and only send that information.
We'll build this on top of the Implementing a network code for FPS recipe.
We need to add some complexity to the simpleUpdate
method of the server application. So, instead of sending information about all players to everybody, we need to check who should receive what.
Perform the following steps to optimize a bandwidth:
PlayerUpdateMessage
. This is so that a client knows when a player has disappeared from the view.ServerPlayerControl
needs to maintain a list of player IDs it currently sees.Collection<NetworkedPlayerControl> players = game.getPlayers().values(); for(NetworkedPlayerControl p: players){ p.update(tpf); }
playerMap
object. Here, we add a simple range check to see whether a player is visible or not, and lastly broadcast the information to the relevant players, as follows:Iterator<HostedConnection> it = playerMap.keySet().iterator(); while(it.hasNext()){ HostedConnection conn = it.next(); ServerPlayerControl player = playerMap.get(conn); for(NetworkedPlayerControl otherPlayer: players){ float distance = player.getSpatial().getWorldTranslation().distance(otherPlayer.getSpatial().getWorldTranslation()); PlayerUpdateMessage updateMessage = null; if(distance < 50){ updateMessage = createUpdateMessage(otherPlayer); player.addVisiblePlayer(otherPlayer.getId()); } else if (player.removeVisiblePlayer(otherPlayer.getId())){ updateMessage = createUpdateMessage(otherPlayer); updateMessage.setVisible(false); } if(updateMessage != null){ server.broadcast(Filters.in(conn), updateMessage); } }
ClientPlayerControl
.ClientMessageHandler
. We check whether the player is supposed to be visible, and whether it's attached to the scene graph or not:if(p.isVisible() && p.getSpatial().getParent() == null){ gameClient.getRootNode().attachChild(p.getSpatial()); } else if (!p.isVisible() && p.getSpatial().getParent() != null){ gameClient.getRootNode().detachChild(p.getSpatial()); }
By using this principle, each client will only receive updates on other relevant players. We can't, however, just stop sending updates about certain players without also letting the client know why, or they would just freeze in their last known position. That's why the last message the server sends about a player is with visible
set to false
. However, to do so, the server must keep track of when a player has disappeared, and not just when it's not visible. That's why each ServerPlayerControl
class needs to keep track of which players it saw the last update in its visibleList
.
This recipe focused on the networking aspects of visibility and how and when to send updates. A proper game (at least an FPS) will need to keep track of obscured players as well, not only how far away they are.
Optimization can be done in different ways, and it all comes down to the application. An MMO may for example not be as dependent on frequent updates. In a game like that, network updates can be done with less frequency, if a player is further away, and instead rely on good interpolation to avoid jerkiness.
If we're using interpolation, and not absolute updates, we should also turn off interpolation when visible switches from false to true, to avoid players possibly gliding to the new position. We can also turn off updates when visible is false.
3.148.144.228