Light synchronization between client and server

Light
It feels like there are two Lights inside the Core on stage, one Predicted and the other not. Does anyone know how to disable the rolling Light?

There is also another problem, and it seems to me they have a similar cause. Orefields are spawned on the server side (i.e. they are not in SubScene at the time of launch and the client should not know about those that are located further than 100 from the ship). And as you can see in the gif below, this is what happens, OreFields appear only at a distance of 100 from the ship, but the light, which is also contained in the OreField prefab, continues to synchronize with the client at all times.

OreDistanceSystem.cs
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
using Unity.Transforms;
using UnityEngine;

public struct GizmosLine : IComponentData
{
	public float3 Start;
	public float3 End;
	public Color Color;
}

[BurstCompile]
[WorldSystemFilter(WorldSystemFilterFlags.ServerSimulation)]
public partial struct OreDistanceSystem : ISystem
{
	[BurstCompile]
	public void OnCreate(ref SystemState state)
	{
		//state.RequireForUpdate<Ship>();
		state.RequireForUpdate<OreField>();
	}

	[BurstCompile]
	public void OnUpdate(ref SystemState state)
	{
		var rev = SystemAPI.GetSingletonRW<GhostRelevancy>();
		if (rev.ValueRO.GhostRelevancyMode != GhostRelevancyMode.SetIsRelevant)
		{
			rev.ValueRW.GhostRelevancyMode = GhostRelevancyMode.SetIsRelevant;
		}
		rev.ValueRW.GhostRelevancySet.Clear();


		foreach (var (_, shipTran, shipCon, shipGhost) in SystemAPI
			.Query<RefRO<Ship>, RefRO<LocalTransform>, RefRO<GhostOwner>, RefRO<GhostInstance>>())
		{
			rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, shipGhost.ValueRO.ghostId), 1);

			foreach (var (_, oreFieldTran, oreFieldGhost) in SystemAPI.Query<RefRO<OreField>, RefRO<LocalTransform>, RefRO<GhostInstance>>())
			{
				if (math.distance(shipTran.ValueRO.Position, oreFieldTran.ValueRO.Position) < 100)
				{
					rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, oreFieldGhost.ValueRO.ghostId), 1);
				}
			}

			foreach (var (_, floorTran, floorGhost) in SystemAPI
				.Query<RefRO<Floor>, RefRO<LocalTransform>, RefRO<GhostInstance>>())
			{
				if (math.distance(shipTran.ValueRO.Position, floorTran.ValueRO.Position) < 2000)
				{
					rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, floorGhost.ValueRO.ghostId), 1);
				}
			}

			foreach (var (_, bulletTran, bullerGhost) in SystemAPI
				.Query<RefRO<Bullet>, RefRO<LocalTransform>, RefRO<GhostInstance>>())
			{
				if (math.distance(shipTran.ValueRO.Position, bulletTran.ValueRO.Position) < 500)
				{
					rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, bullerGhost.ValueRO.ghostId), 1);
				}
			}

			foreach (var (_, oreTran, oreGhost) in SystemAPI
				.Query<RefRO<Ore>, RefRO<LocalTransform>, RefRO<GhostInstance>>())
			{
				if (math.distance(shipTran.ValueRO.Position, oreTran.ValueRO.Position) < 100)
				{
					rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, oreGhost.ValueRO.ghostId), 1);
				}
			}

			foreach (var (_, gateTran, gateGhost) in SystemAPI
				.Query<RefRO<Gate>, RefRO<LocalTransform>, RefRO<GhostInstance>>())
			{
				if (math.distance(shipTran.ValueRO.Position, gateTran.ValueRO.Position) < 1000)
				{
					rev.ValueRW.GhostRelevancySet.TryAdd(
						new RelevantGhostForConnection(shipCon.ValueRO.NetworkId, gateGhost.ValueRO.ghostId), 1);
				}
			}
		}
	}
}

Ores

Thank you in advance for any help!

There’s a setting that’s a bit hidden that allows you to strip whole assemblies from server world baking.

Adding Unity.RenderPipeline.Universal.Runtime worked on my side to strip lights.

For sake of completeness when dealing with stripping, if you want to strip only certain component types on specific prefabs, and not whole assemblies, you can look into using GhostAuthoringInspection, there’s a non-ghost component drop down that should allow you to strip other components. Do note this method can get tricky, as certain systems will assume the presence of certain component types.

In your case I’d use the first option.

Thank you for your prompt response!

Do you mean that the problem is that the light is being rendered on the server side?

I.e. if I make Only Server and Only Client builds, when running the server as a console application and the client without server code, the duplicate lighting will disappear?

Correct me if I misunderstood you

I’m assuming you’re running in the editor both client and server worlds correct? So yes, the server world has the light and is rendering it as well.

In a pure client, that wouldn’t be an issue.
In a dedicated server build, I assume you’re building with the dedicated server build target, not the desktop build target correct? In that case, that build target should strip basic things (like lights and sounds) for you already. You’d still want to use those project settings to make sure entities systems don’t run useless stuff even in your server builds though.

Okay, thanks for the clarifications! Let’s say I want to run a Client And Server build, i.e. there will be both client and server worlds in one instance. In this case, how can I disable the visual and, say, sound effects for ServerWorld? It’s just that I thought they were disabled automatically if you create a server world through

ClientServerBootstrap.CreateServerWorld("ServerWorld");

Could you tell me exactly how to disable the rendering of visual objects for the server world, if it is created together with the client world, as for example here:

public class Bootstrap : ClientServerBootstrap
using System;
using Unity.Collections;
using Unity.Entities;
using Unity.NetCode;
using Unity.Scenes;
using UnityEngine;
using UnityEngine.Scripting;

[Preserve]
public class Bootstrap : ClientServerBootstrap
{
	public const string ip_listen = "0.0.0.0";
	public const ushort port = 7979;
	public override bool Initialize(string defaultWorldName)
	{
		AutoConnectPort = 0;

		foreach (var defaultWorld in World.All)
		{
			Debug.Log($"{defaultWorld.Name} {defaultWorld.Flags}");
			if (defaultWorld.Flags == WorldFlags.Game)
			{
				// Удаляем мир по умолчанию создаваемый даже в обход AutoConnectBootstrap
				defaultWorld.Dispose();
				//break;
			}
		}

#if UNITY_EDITOR
		World.DefaultGameObjectInjectionWorld = CreateClientWorld("ClientWorld");
		CreateServerWorld("ServerWorld");
#else
	#if UNITY_SERVER
		World.DefaultGameObjectInjectionWorld = CreateServerWorld("ServerWorld");
	#else
		World.DefaultGameObjectInjectionWorld = CreateClientWorld("ClientWorld");
		CreateServerWorld("ServerWorld");
	#endif
#endif

		return true;
	}
}

Ah yeah in that case, it’s not a great flow at the moment.
Right now, we strip components according to user specified rules. So could be variants or could be done through ghost authoring inspection. For your specific case, using the GhostAuthoringInspectionComponent you can specify in the Meta-data for non-replicated Components section which component should go where, so for lights, you’ll need to remove the 3 components that start with “Light” plus the 3 companion components used to generate the companion gameObject for your light.
Be aware though if you add other authoring that would also require that companion gameobject, this could become messy. You’ll need to be careful around that.

Had multiple discussions on this in the past, we really need to have a proper way to strip things we know should never be on the server. Hopefully we’ll have a nice way to integrate this with Content Selection from the dedicated server package.

1 Like