Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Teleport-aware client-side terrain loading #14367

Open
Warr1024 opened this issue Feb 12, 2024 · 4 comments
Open

Teleport-aware client-side terrain loading #14367

Warr1024 opened this issue Feb 12, 2024 · 4 comments
Labels
@ Client rendering Feature request Issues that request the addition or enhancement of a feature @ Script API

Comments

@Warr1024
Copy link
Contributor

Warr1024 commented Feb 12, 2024

Problem

There are situations where the player may be about to suddenly move somewhere very far away, such as a teleportation, or an attached spectator moving to a different camera position.

In many cases, the game/mods may have ways of knowing that this is going to happen (a planned/timed/scripted camera move, or a player is very close to a teleportation pad with a known destination).

Normally, we load terrain that is near the player, and then load terrain surrounding that, so that as the player moves around, their experience of the world is as seamless as possible: assuming the performance of the system can keep up as intended, they should never see unloaded terrain other than at the very fringes of their fog/horizon after the initial connection. Teleports break this, and may put a player in a situation where the terrain arbitrarily close to them is not loaded.

One of the problems this creates is breaking the immersion of a seamless teleport, e.g. where you exactly duplicate the terrain at the destination to hide the transition (recently added player:add_pos(v) has made these otherwise possible). Another problem is that it can disrupt movement, because players may briefly collide with ignores.

Solutions

The solution proposed by c55 was to have multiple "loading positions".

Right now, there is only one "loading position", which is the location of the player, and that position is used to determine how mapblocks are prepared (loaded, activated, send to clients, mesh-generated). Instead, we should allow multiple such loading positions, and mapblocks will be prepared based on their distance to the closest such position (possibly with some prioritization).

Alternatives

The engine offers a number of components of the solution, but they have a number of limitations:

  • emerge_area can load terrain, but not cause it to activate, and not prevent it from unloading at arbitrary times.
  • force-loading can load terrain and prevent it from unloading, but it can't NOT activate the blocks, making it potentially expensive.
  • player:send_mapblock(bpos) can send a mapblock to the client, but cannot force the client to generate a mesh for it, and cannot cause the client not to unload it at arbitrary times.

If a well-timed emerge/forceload and then send_mapblock is executed, and then the player enters the teleport shortly after, then the mapblock can be ready client-side for physics (so players don't collide with ignores) but it's still not ready to display until mesh generation is done (even having a potentially stale mesh would be preferable to no mesh at all). If the timing is NOT good (the player approaches the teleporter, causing the preload, but then pauses for a long time before entering) then the terrain may have unloaded already (defeating the measure) or the terrain has to keep being sent (which can cause significant problems in other areas).

Also, apparently functions like emerge_area and send_mapblock do not reset the TTL if the block was already loaded, so cannot prevent blocks from unloading, so affected blocks keep getting evicted and then reloaded (expensive).

If a set of primitives can be provided that allows me to inform a client to mesh-gen a mapblock it's already received, and I can instruct the various engine stages (server-side loading, client-side loading, client-side mesh) to refresh its TTL on a loaded block so it doesn't get evicted until my mod stops refreshing the TTL (or explicitly advises it to discard) then I wouldn't mind managing this myself (though that wouldn't necessarily be the most optimal interface for all modders).

Additional context

This is a continuation of the underlying objective of #14023, i.e. making "seamless teleports" possible. See that issue for additional context: there are a handful of games that are held back from major improvements or new features by the fact that teleportation jank disrupts gameplay and immersion.

@Warr1024 Warr1024 added the Feature request Issues that request the addition or enhancement of a feature label Feb 12, 2024
@Wuzzy2
Copy link
Contributor

Wuzzy2 commented Feb 14, 2024

Good, well written issue. I have thought about this problem as well and thought the alternatives you've listed should be enough from the mod side but I never cared enough to actually try it out. But now you've convinced me without engine support such a feature is only ever going to be half-complete due to rendering.

Portals, teleportation pads, and the like are common in Minetest so having the destination(s) be pre-loaded is a good thing.

@Desour
Copy link
Member

Desour commented Feb 14, 2024

Related: #11093 (Portals would need something like this.)
And: #13052 (IIRC, it disables occlusion and frustum culling. But for far away cameras, you'd also need to load and meshgen the blocks there.)

@lhofhansl
Copy link
Contributor

Continued loading from multiple positions is actually quite expensive. (Just look at RemoteClient::getNextBlocks(...))
So if we add something we need to do so sparingly and have good way to disable if no longer needed.

@Warr1024
Copy link
Contributor Author

I don't imagine this is going to change the amount of work that Minetest needs to do, it is just going to spread out the existing workload between multiple different locations. It is of course always a modder's responsibility to ensure they're using the tools they're given appropriately, and there are a lot of responsible people out there who push the boundaries of what our engine can do and we should encourage them.

The idea that if we have an "add loading position" API, we'd also have a "delete loading position" and "get all loading positions" API seems obvious to me, but maybe it's worth being explicit about that. If loading positions have properties other than their identity itself, we'd probably also want an "update" API, unless removal/re-adding can be done without generating extra network noise that an update API wouldn't have to.

If we allow multiple loading spots, we should allow having a "metric" for them that gets added to the block distance. For example, if your draw distance is 10 blocks, and you are 3 blocks away from a teleporter, you should be able to add the loading spot for the teleporter destination with a metric of 3, which tells the engine to prioritize loading a block that's 5 blocks away from that new loading spot as if it were 8 blocks away. That way we can still keep the blocks that are already close to the player loading with higher priority.

The metric doesn't just have to be physical distance to the teleportation source, but modders can use heuristics for the metric computation, e.g. distance squared instead of distance, to account for the probability that the player may not enter the teleporter at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@ Client rendering Feature request Issues that request the addition or enhancement of a feature @ Script API
Projects
None yet
Development

No branches or pull requests

4 participants