Skip to content

Commit

Permalink
Change world loading to only spawn entities in view range, and refact…
Browse files Browse the repository at this point in the history
…ored of WorldEntityManager
  • Loading branch information
tornac1234 committed Jan 11, 2024
1 parent 0caa209 commit 10d1535
Show file tree
Hide file tree
Showing 11 changed files with 370 additions and 515 deletions.
2 changes: 1 addition & 1 deletion NitroxClient/Debuggers/NetworkDebugger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class NetworkDebugger : BaseDebugger, INetworkDebugger
{
nameof(PlayerMovement), nameof(EntityTransformUpdates), nameof(PlayerStats), nameof(SpawnEntities), nameof(VehicleMovement), nameof(PlayerCinematicControllerCall),
nameof(PlayFMODAsset), nameof(PlayFMODCustomEmitter), nameof(PlayFMODStudioEmitter), nameof(PlayFMODCustomLoopingEmitter), nameof(SimulationOwnershipChange),
nameof(CellVisibilityChanged), nameof(BatchVisibilityChanged)
nameof(CellVisibilityChanged)
};
private readonly List<PacketDebugWrapper> packets = new List<PacketDebugWrapper>(PACKET_STORED_COUNT);

Expand Down
165 changes: 65 additions & 100 deletions NitroxClient/GameLogic/Terrain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,131 +2,96 @@
using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
using NitroxClient.Map;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
using WorldStreaming;

namespace NitroxClient.GameLogic
namespace NitroxClient.GameLogic;

public class Terrain
{
public class Terrain
private readonly IMultiplayerSession multiplayerSession;
private readonly IPacketSender packetSender;
private readonly VisibleCells visibleCells;
private readonly VisibleBatches visibleBatches;

private readonly List<AbsoluteEntityCell> addedCells = [];
private readonly List<AbsoluteEntityCell> removedCells = [];

private bool cellsPendingSync;
private bool batchesPendingSync;
private float bufferedTime = 0f;
private const float TIME_BUFFER = 0.05f;

public Terrain(IMultiplayerSession multiplayerSession, IPacketSender packetSender, VisibleCells visibleCells, VisibleBatches visibleBatches)
{
private readonly IMultiplayerSession multiplayerSession;
private readonly IPacketSender packetSender;
private readonly VisibleCells visibleCells;
private readonly VisibleBatches visibleBatches;

private bool cellsPendingSync;
private bool batchesPendingSync;
private float bufferedTime = 0f;
private float timeBuffer = 0.05f;

private List<AbsoluteEntityCell> addedCells = new List<AbsoluteEntityCell>();
private List<AbsoluteEntityCell> removedCells = new List<AbsoluteEntityCell>();
private List<NitroxInt3> addedBatches = new List<NitroxInt3>();
private List<NitroxInt3> removedBatches = new List<NitroxInt3>();
this.multiplayerSession = multiplayerSession;
this.packetSender = packetSender;
this.visibleCells = visibleCells;
this.visibleBatches = visibleBatches;
}

public Terrain(IMultiplayerSession multiplayerSession, IPacketSender packetSender, VisibleCells visibleCells, VisibleBatches visibleBatches)
{
this.multiplayerSession = multiplayerSession;
this.packetSender = packetSender;
this.visibleCells = visibleCells;
this.visibleBatches = visibleBatches;
}
public void CellLoaded(Int3 batchId, Int3 cellId, int level)
{
AbsoluteEntityCell cell = new(batchId.ToDto(), cellId.ToDto(), level);

public void CellLoaded(Int3 batchId, Int3 cellId, int level)
if (!visibleCells.Contains(cell))
{
AbsoluteEntityCell cell = new AbsoluteEntityCell(batchId.ToDto(), cellId.ToDto(), level);

if (!visibleCells.Contains(cell))
{
visibleCells.Add(cell);
addedCells.Add(cell);
cellsPendingSync = true;
}
visibleCells.Add(cell);
addedCells.Add(cell);
cellsPendingSync = true;
}
}

public void CellUnloaded(Int3 batchId, Int3 cellId, int level)
{
AbsoluteEntityCell cell = new AbsoluteEntityCell(batchId.ToDto(), cellId.ToDto(), level);

if (visibleCells.Contains(cell))
{
visibleCells.Remove(cell);
removedCells.Add(cell);
cellsPendingSync = true;
}
}
public void BatchLoaded(Int3 batchId)
{
NitroxInt3 nitroxBatchId = batchId.ToDto();
if (!visibleBatches.Contains(nitroxBatchId))
{
visibleBatches.Add(nitroxBatchId);
addedBatches.Add(nitroxBatchId);
batchesPendingSync = true;
}
}
public void CellUnloaded(Int3 batchId, Int3 cellId, int level)
{
AbsoluteEntityCell cell = new(batchId.ToDto(), cellId.ToDto(), level);

public void BatchUnloaded(Int3 batchId)
if (visibleCells.Contains(cell))
{
NitroxInt3 nitroxBatchId = batchId.ToDto();
if (visibleBatches.Contains(nitroxBatchId))
{
visibleBatches.Remove(nitroxBatchId);
removedBatches.Add(nitroxBatchId);
batchesPendingSync = true;
}
visibleCells.Remove(cell);
removedCells.Add(cell);
cellsPendingSync = true;
}
}

public void UpdateVisibility()
public void UpdateVisibility()
{
bufferedTime += Time.deltaTime;
if (bufferedTime >= TIME_BUFFER)
{
bufferedTime += Time.deltaTime;
if (bufferedTime > timeBuffer)
if (cellsPendingSync)
{
if (cellsPendingSync)
{
CellVisibilityChanged cellsChanged = new CellVisibilityChanged(multiplayerSession.Reservation.PlayerId, addedCells.ToArray(), removedCells.ToArray());
packetSender.Send(cellsChanged);

addedCells.Clear();
removedCells.Clear();

cellsPendingSync = false;
}

if (batchesPendingSync)
{
BatchVisibilityChanged batchesChanged = new BatchVisibilityChanged(multiplayerSession.Reservation.PlayerId, addedBatches.ToArray(), removedBatches.ToArray());
packetSender.Send(batchesChanged);
CellVisibilityChanged cellsChanged = new(multiplayerSession.Reservation.PlayerId, addedCells.ToArray(), removedCells.ToArray());
packetSender.Send(cellsChanged);

addedBatches.Clear();
removedBatches.Clear();
addedCells.Clear();
removedCells.Clear();

batchesPendingSync = false;
}
bufferedTime = 0f;
cellsPendingSync = false;
}

bufferedTime = 0f;
}

/// <summary>
/// Forces world streamer's to load the terrain around the MainCamera and waits until it's done to unfreeze the player.
/// </summary>
public static IEnumerator WaitForWorldLoad()
{
// In WorldStreamer.CreateStreamers() three coroutines are created to constantly call UpdateCenter() on the streamers
// We force these updates so that the world streamer gets busy instantly
WorldStreamer streamerV2 = LargeWorldStreamer.main.streamerV2;
streamerV2.UpdateStreamingCenter(MainCamera.camera.transform.position);
streamerV2.octreesStreamer.UpdateCenter(streamerV2.streamingCenter);
streamerV2.lowDetailOctreesStreamer.UpdateCenter(streamerV2.streamingCenter);
streamerV2.clipmapStreamer.UpdateCenter(streamerV2.streamingCenter);
}

yield return new WaitUntil(() => LargeWorldStreamer.main.IsWorldSettled());
Player.main.cinematicModeActive = false;
}
/// <summary>
/// Forces world streamer's to load the terrain around the MainCamera and waits until it's done to unfreeze the player.
/// </summary>
public static IEnumerator WaitForWorldLoad()
{
// In WorldStreamer.CreateStreamers() three coroutines are created to constantly call UpdateCenter() on the streamers
// We force these updates so that the world streamer gets busy instantly
WorldStreamer streamerV2 = LargeWorldStreamer.main.streamerV2;
streamerV2.UpdateStreamingCenter(MainCamera.camera.transform.position);
streamerV2.octreesStreamer.UpdateCenter(streamerV2.streamingCenter);
streamerV2.lowDetailOctreesStreamer.UpdateCenter(streamerV2.streamingCenter);
streamerV2.clipmapStreamer.UpdateCenter(streamerV2.streamingCenter);

yield return new WaitUntil(() => LargeWorldStreamer.main.IsWorldSettled());
Player.main.cinematicModeActive = false;
}
}
19 changes: 0 additions & 19 deletions NitroxModel/Packets/BatchVisibilityChanged.cs

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,52 @@
using System.Collections.Generic;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.Packets;
using NitroxServer.Communication.Packets.Processors.Abstract;
using NitroxServer.GameLogic.Entities;

namespace NitroxServer.Communication.Packets.Processors
namespace NitroxServer.Communication.Packets.Processors;

public class CellVisibilityChangedProcessor : AuthenticatedPacketProcessor<CellVisibilityChanged>
{
class CellVisibilityChangedProcessor : AuthenticatedPacketProcessor<CellVisibilityChanged>
private readonly EntitySimulation entitySimulation;
private readonly WorldEntityManager worldEntityManager;

public CellVisibilityChangedProcessor(EntitySimulation entitySimulation, WorldEntityManager worldEntityManager)
{
this.entitySimulation = entitySimulation;
this.worldEntityManager = worldEntityManager;
}

public override void Process(CellVisibilityChanged packet, Player player)
{
private readonly EntitySimulation entitySimulation;
player.AddCells(packet.Added);
player.RemoveCells(packet.Removed);

List<Entity> totalEntities = [];
List<SimulatedEntity> totalSimulationChanges = [];

public CellVisibilityChangedProcessor(EntitySimulation entitySimulation)
foreach (AbsoluteEntityCell addedCell in packet.Added)
{
this.entitySimulation = entitySimulation;
worldEntityManager.LoadUnspawnedEntities(addedCell.BatchId, false);

totalSimulationChanges.AddRange(entitySimulation.GetSimulationChangesForCell(player, addedCell));
totalEntities.AddRange(worldEntityManager.GetEntities(addedCell));
}

public override void Process(CellVisibilityChanged packet, Player player)
foreach (AbsoluteEntityCell removedCell in packet.Removed)
{
player.AddCells(packet.Added);
player.RemoveCells(packet.Removed);
entitySimulation.FillWithRemovedCells(player, removedCell, totalSimulationChanges);
}

entitySimulation.BroadcastSimulationChangesForCellUpdates(player, packet.Added, packet.Removed);
if (totalEntities.Count > 0)
{
SpawnEntities batchEntities = new(totalEntities);
player.SendPacket(batchEntities);
}
if (totalSimulationChanges.Count > 0)
{
entitySimulation.BroadcastSimulationChanges(new(totalSimulationChanges));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ public override void Process(EntitySpawnedByClient packet, Player playerWhoSpawn
playerManager.SendPacketToAllPlayers(ownershipChangePacket);
}

SpawnEntities spawnEntities = new(entity, packet.RequireRespawn);
foreach (Player player in playerManager.GetConnectedPlayers())
{
bool isOtherPlayer = player != playerWhoSpawned;
if (isOtherPlayer && player.CanSee(entity))
{
player.SendPacket(new SpawnEntities(entity, packet.RequireRespawn));
player.SendPacket(spawnEntities);
}
}
}
Expand Down
Loading

0 comments on commit 10d1535

Please sign in to comment.