opened 09:59PM - 04 Jun 21 UTC
closed 12:35PM - 03 May 22 UTC
research
## Overview
I did some investigation into multiplayer support with Cesium for U…nreal - specifically, whether multiplayer would be compatible with World Origin Rebasing.
As mentioned in the [reference frames documentation](https://github.com/CesiumGS/cesium-unreal/blob/54c0e4f0afb37a6e1c2866f36939e28a4a747667/Documentation/reference-frames.md), Unreal Engine uses both an FIntVector (int32) to represent the position of a "floating" world origin relative to the absolute world origin (0,0,0), and an FVector (float) to represent the location of actors relative to that floating origin.
World Origin Rebasing is the process of moving the floating origin closer to the player character's location in order to avoid floating precision errors, which can be noticeable after traveling a few kilometers away from the floating origin. Cesium for Unreal will call Unreal Engine's world origin rebasing function once the world origin is a user-specified distance away from the player camera, if the `Keep World Origin Near Camera` feature is enabled in the `CesiumGeoreference`.
The normal World Origin Rebasing functionality is not compatible with multiplayer, as it cannot handle a client + server having different world origins. But a few engine versions ago, a [multiplayer version was added](https://github.com/EpicGames/UnrealEngine/pull/2359) and can be enabled via the Project Settings -> `Enable Multiplayer World Origin Rebasing`.
## How it works
The way the multiplayer origin rebasing resolves the "multiple world origin" issue is by interpreting location vectors in the absolute world frame. That is, when a server sends (replicates) an updated movement location to a client, that location vector is rebased to the absolute origin frame before being sent, and when the client receives it, they rebase it to their own floating origin before applying the location update.
This all happens under the hood when the `Enable Multiplayer World Origin Rebasing` feature is enabled. But you will also need to account for this when writing gameplay code, so here's a blueprint example, which demonstrates a teleportation mechanic that can be called from a client. My goal is to teleport the player pawn to the location of `BP Teleport Actor`. On the client side, I rebase that vector to the absolute origin before sending it to `S_Teleport` (server-side function), which rebases it back onto its own floating origin before actually teleporting the actor. The consequence of the server-side teleport is then propagated back to the client.
Cesium for Unreal's world origin rebasing logic is compatible out of the box with multiplayer origin rebasing.
![image](https://user-images.githubusercontent.com/10490807/120864779-c4896800-c55a-11eb-93c9-767b7458a42f.png)
_Blueprint example showing a custom teleportation mechanic, accounting for the multiplayer origin rebasing changes._
## Caveats
The glaring compromise to this approach, however, is that there is still a maximum distance we can reasonably play in before the floating point accuracy breaks down (approx 20x20 km play area). The philosophy behind doing this at all is that the jitter from client-side systems (in animation, client-side physics, particles) is noticeable sooner than in server-side systems like movement correction, which are more macroscopic. So, we can freely rebase client-side systems to benefit from smooth animations, until we reach the server-side accuracy limit.
In order to have multiplayer gameplay on a truly global scale, Unreal Engine will most likely need double floating precision support.
There was also a Cesium for Unreal specific issue I encountered as well, which is that when Frustum Culling is turned on, the ground that the client is standing on can be unloaded at any time in the server's game instance. Because physics is mostly handled server-side, and the server is the authority on what's actually happening in-game, the client is now falling into oblivion. Meanwhile, the client does not immediately notice - after all, in their game instance, they're standing on solid ground - until the discrepancy manifests itself via some catastrophic glitch.
Disabling Frustum Culling resolved the issue, but also creates a ton of overhead, as all loaded tiles must be held in memory. A possible solution is for the server to avoid unloading tiles in the vicinity of clients. What would be even better is to only load the collision geometry rather than the entire gltf mesh + texture. This would also be a helpful step towards supporting dedicated servers, which do not actually render anything, and only handle gameplay logic, physics simulation, and network replication. As Cesium for Unreal currently decides which tiles should be rendered via calculating a view frustum, it probably wouldn't work at all in a dedicated server in its current state.
## Links
Link to a sample project with gameplay examples at this [Unreal Engine forum thread](https://forums.unrealengine.com/t/pull-request-world-origin-shifting-in-mp/62649/77) and a [direct Google Drive link](https://drive.google.com/file/u/1/d/1yv1LZljx5JeyQLhkzVMr1BbNsRVFXecp/view?usp=drive_open).
![image](https://user-images.githubusercontent.com/10490807/120861749-cc92d900-c555-11eb-8772-db88b4f1a076.png)
_Screenshot of a server + client with different floating world origins, as indicated by the location of the red spheres._
Note: When enabling the multiplayer world origin rebasing in your own project, I found that a restart was necessary, even though it wouldn't prompt you to restart. Otherwise the behavior is very buggy.