[Dedicated Server] Net Cull Distance and Network Relevancy for spawned/owned items

I can drop an item from my inventory and it spawns/replicates on the server, every player receives an update that it spawned and can interact with it.

The Net Cull Distance is around 50 meters (25000000uu) and works fine for items dropped by other players, but items dropped (and owned) by the player seem to always stay relevant. Even when moving over 500 meters away and zooming in I can still see the items I dropped.

For example:

Item dropped is a replicated static mesh actor with replicate movement enabled.

  • Only Relevant to Owner = off
  • Always Relevant = off
  • Net Use Owner Relevancy = off

_

  • Player A and B join game
  • Player B drops an item
  • Player A moves outside the net cull distance of Player B’s item
  • Player B’s item gets culled according to the cull distance

_

  • Player C and D join game
  • Player C and D drop an item on the same location
  • Player C moves outside the net cull distance of both items
  • Only the item Player D dropped gets culled, the item Player C (owner) dropped stays visible

I’ve tried spawning the actor without an owner and setting the owner to none after spawning, but the results are the same.

Also setting any possible combination of relevancy switches on the component itself had no effect (all set on the server). I read that owned objects are always considered relevant regardless of distance, but I can’t seem to change it with Blueprints.

I’m already familiar with eXi’s Network Compendium and the Actor Relevancy and Priority flow and haven’t found any other good leads besides AActor::IsNetRelevantFor() in C++.

Network cull distance has zero to do with the visibility of an actor. It only effects the relevancy of the actor in regards to network replication.

Once an actor has been replicated to you (made the client aware of its existence) it will remain in the client world proxy. Until, something network relevant changes with the actor. Like destroyed, moved or modified in a way that the client should be updated with the change. Yet, only when inside the cull distance radius will you be updated with those changes.

Ownership has no bearing as well. Technically anything dropped by the player should be owned by the server. When held or in inventory, the player owns it. On drop the server should then be given ownership. Or simply destroy the players dropped actor and have the server spawn a replicated actor in its place.

Relevancy helps reduce bandwidth usage and network saturation.

What you need to do is setup Render Culling.
Actor → static mesh → Details: Rendering → LOD → min/max Draw Distance
Settings of 0 & 5000 will work.


Suggestions…
Personally I’d increase the network cull distance for lootable items to around 75-100m and have the render culling at 50m. This should/will reduce the case instances of where multiple players in range aren’t seeing the same things.

e.g. Player A (low stable ping) gets the update and sees the items. Player B (higher, jittery connection) does not. yet a few frames later they appear.

Overall net cull distance should be balanced in regards to number of players in game and the density of lootable items. Multiple small updates are better than bulk updates. Less fragmentation and loss.

Is that always true tho? Pretty sure I’ve see glitches with characters disappearing when they’re teleported a distance away, but should still be visible (not at rig right now to check).

If the server spawns an actor and replicates it to you, then your client spawns a proxy. When you walk out of net cull distance the client doesn’t destroy it, then respawn it when you get back in range. It remains in your local proxy world until the server modifies or destroys it. Both would require a replication update (server → client).

Anything disappearing outside net cull distance is tied to either render culling, occlusion or level streaming distance. If you’re using world composition, then assets on the streamed level (tile) are typically bound to the levels streaming distance… as far as I recall. If it’s culled assets bound to it are culled as well.

Also with world comp you have to look at origin rebasing. If the offset gets screwed up then the world position is incorrect. That can lead to disappearing characters/actors.

Understood. It wasn’t a case of the actor being destroyed / re-created. it was like it wasn’t being updated and so effectively hidden iirc. The quick dirty solution was just force ‘always relevant’ on for the Character (level-streaming / culling-occlusion / wc origin-rebasing didn’t apply). Can’t rule out the possibility that always tick anim / bones wasn’t connected to this tho. :wink:

I think once you got in range the server updated the new location and seeing as the distance to the new location was too large to interpolate over a few frames it was teleported in 1 frame. I see this all the time in multiplayer games like Battlefield 4 & 1, Destiny 2 and PubG. Players with high pings (350+) and terrible jitter/loss warp around quite a bit.

In PubG (UE4 w/world comp) we get players being warped 500m+ on occasion. When you drive into a new area of the larger maps (8x8) and get slammed with updates there’s a chance of catching a player doing this. If memory serves their character net cull is just under 1Km…980m or so. Driving into an area at 120Km/h+ you get hammered with updates. 100’s of loot items and potentially 20+ players.

Thanks for the detailed replies! I was already trying to balance net cull distances in regards to items dropped by players, which is why I ran into the issue I mentioned. I’ll try to tweak the item mesh LOD distance based on the net cull distance from now on.

Take into consideration movement speeds and pings as well. Ping wise you need to set a high ping limit for design. A good standard is 120-130ms. Most popular multiplayer games have player with pings well over 250ms. The majority will be 80ms or less, but there are still a large chunk that grossly exceed that.

Use this High ping (HP) as your upper boundary when calculating net cull to render buffers.

Visualize your net and render cull distances as spheres. You want some level of buffer distance between net and render. A significant gap that allows for network resends and client processing to occur before reaching the render distance.

How fast your players can bridge the gap between net and render has an impact when you consider ping.

Using a 125ms HP as the limit average gives you a 63ms update travel time (UTT = ping/2). Which is roughly 40cm of movement distance @ 600cm/s speed ( 4 frames at 60Hz, 63 / 16.667). Then there’s a min of 1 frame processing time (16.667ms, 10cm movement). 3 times that if there’s loss… 205.667ms * 0.6cm/ms = 123.4002cm movement distance.

A good starter buffer algo would be 2.5 * ((HP Limit ms / 2) * (1000ms / max speed cm/s)).
Net cull would be Render cull + Buffer.

If you have vehicles then use the vehicle’s top speed instead of player’s.

@Rev0verDrive can you please clarify what happens in this case:

  1. game mode (server) spawns actors across the map
  2. when the spawn occurs the setver sets a specific static mesh for each actor

now, if I set all actors to be always relevant,
when the clients connect they can see the correct meshes.

however, if I set to just use network relevancy, when clients get into the relevancy area they don’t see the correct mesh (the one specified by the game mode when they first spawned).

now, do I need to create some kind of Multicast on Begin Play of each actor to change to the correct mesh,
OR
do I need to set some kind of rep notify with, for example, the Actor Begin Overlap?

thank you

I know this is old but I’m pretty sure when you walk out of net cull distance the client destroys the actor and it respawns again when you walk back in, this is of course when the actor is spawned by the server.

a repNotify is probably better, set it to initial only in the replication condition and set the variable when the mesh is set by the server.

The actors the server is spawning need to have Replicated Ticked. If the actors can be moved, then rep movement needs to be ticked.

Always relevant Floods replication (aka your network). Almost nothing in the game world should be set to always relevant.

If you have a dynamic component, say a mesh that can change, then you need to tick Component Replicates.

If the mesh changes, then those in net cull distance will be updated asap. For those outside, no update until they re-enter net cull. The change will be updated automatically via default replication.

If you need a demo let me know.
Actually here’s a networked doors demo that covers this topic. Timestamp 15:45 covers replication and net cull w/ a demo.

Thanks and also Kaidoom15 thanks.
I’ll try probably today.

What I’m seeing from my tests is that the actors are spawned when I ‘use network relevancy’
but with no mesh applied (meaning it is spawning with the actors initial mesh, not the one I want). both Actor replicated and Static Mesh Component replication are ticked.

@Rev0verDrive
I managed to test here in my end.
I’m changing the mesh in the class constructor in cpp and spawning that the first time in game mode, dedicated server. Both replicates and component replicates are true in the constructor.

Then clients enter the game, but they only see the updated mesh if I mark the actor as always relevant. If I try to use network relevancy they can collide with the actors, but it has no mesh. Hence, invisible. The mesh is not updated in this case.

I cannot understand why the mesh is not replicated, since it is defined in the actor class constructor , that is called before Begin Play.

The Actor Constructor:

ASimpleActor::ASimpleActor()
{
	bReplicates = true;
	bAlwaysRelevant = true; // if true, the correct meshes are show
	bNetLoadOnClient = false;
	bNetUseOwnerRelevancy = true;
	NetCullDistanceSquared = 1500000;
		
	RootComp = CreateDefaultSubobject<USceneComponent>(TEXT("RootComponent"));
	SetRootComponent(RootComp);

	StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("SMComponent"));

	if(StaticMeshComp)
	{		
		StaticMeshComp->AttachToComponent(GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform );
		InStaticMesh = LoadObject<UStaticMesh>(NULL, TEXT("/Game/Assets/Meshes/SM_SimpleCone.SM_SimpleCone"), NULL, LOAD_None, NULL);
		StaticMeshComp->SetIsReplicated(true);

		if(InStaticMesh)
		{
			StaticMeshComp->SetStaticMesh(InStaticMesh);
		}
	}
}

Issue is you’re “Creating” the static mesh component at runtime vs using class inheritance.

Personally I’d create a base actor with all the needed components and have child actors with the adjusted settings. Spawn the correct child as needed.

Thank you very much, this was being a pain in the ■■■ to fix.

Do you know whether there is an Event that is called on the Server when an actor becomes relevant to another?

Say, Player A and Player B are together.

Player A moves out of NetCullDistanceSquared and disappears

Player A then comes back into NetCullDistanceSquared and becomes relevant again to Player B, is there any server event called at this point? I check PostNetInit() but that seems to be Client-Side only, unless I messed something up.

Off the top of my head I’m not aware of any “EVENT” that’s called.
These two docs should guide you.

1 Like