Retain spawn replication through admission and nearby crate use
This commit is contained in:
@@ -14,8 +14,9 @@ modded class SCR_SpawnRequestComponent
|
||||
protected bool m_ERRScenePreloadRequired;
|
||||
protected bool m_ERRSceneObserver;
|
||||
protected vector m_ERRScenePosition;
|
||||
protected bool m_ERRSceneReleased;
|
||||
protected bool m_ERRSceneFinalRequested;
|
||||
protected bool m_ERRSceneVerified;
|
||||
protected ref ERR_SpawnSceneLease m_ERRSceneLease;
|
||||
protected ref array<IEntity> m_ERRSceneRoots = {};
|
||||
protected ref array<IEntity> m_ERRSceneStockOwners = {};
|
||||
protected ref array<IEntity> m_ERRSceneStockItems = {};
|
||||
@@ -31,17 +32,14 @@ modded class SCR_SpawnRequestComponent
|
||||
|
||||
protected void ERR_StreamScene(IEntity entity, bool normalStreaming)
|
||||
{
|
||||
if (!entity || !GetPlayerController()) return;
|
||||
RplIdentity identity = GetPlayerController().GetRplIdentity();
|
||||
if (!identity.IsValid() || identity == RplIdentity.Local()) return;
|
||||
RplComponent component = RplComponent.Cast(entity.FindComponent(RplComponent));
|
||||
if (component) component.EnableStreamingConNode(identity, normalStreaming);
|
||||
ERR_SpawnSceneLease.Stream(GetPlayerController(), entity, normalStreaming);
|
||||
}
|
||||
|
||||
void ERR_BeginSpawnScene(array<IEntity> entities, bool valid, vector position)
|
||||
{
|
||||
if (!Replication.IsServer()) return;
|
||||
ERR_EndSpawnScene();
|
||||
if (m_ERRSceneLease) m_ERRSceneLease.Stop();
|
||||
m_ERRScenePosition = position;
|
||||
// Native StartSpawnPreload normally installs this observer. Keep streaming
|
||||
// the requested position while we defer asset preload until inventory arrives.
|
||||
@@ -87,7 +85,7 @@ modded class SCR_SpawnRequestComponent
|
||||
m_ERRSceneEntities.Clear();
|
||||
m_ERRSceneToken++;
|
||||
m_ERRSceneAcknowledged = false;
|
||||
m_ERRSceneReleased = false;
|
||||
m_ERRSceneFinalRequested = false;
|
||||
m_ERRSceneVerified = false;
|
||||
m_ERRScenePreloadRequired = true;
|
||||
m_ERRSceneInventoryPoll = GetGame().GetWorld().GetWorldTime() + 500;
|
||||
@@ -141,24 +139,32 @@ modded class SCR_SpawnRequestComponent
|
||||
{
|
||||
return ERR_SpawnSceneReady() && m_ERRSceneVerified;
|
||||
}
|
||||
// Called only after native preload completes. Recheck under normal streaming,
|
||||
// with the observer still at the spawn point, before handing over control.
|
||||
bool ERR_VerifySceneRelease()
|
||||
// Called only after native preload completes. Retain the scene through the
|
||||
// final check and possession; normal streaming cannot yet keep every node.
|
||||
bool ERR_VerifyLoadedScene()
|
||||
{
|
||||
if (m_ERRSceneTimedOut) return true;
|
||||
if (!ERR_SpawnSceneReady() || m_ERRScenePreloadRequired || IsPreloading()) return false;
|
||||
if (!m_ERRSceneReleased)
|
||||
if (!m_ERRSceneFinalRequested)
|
||||
{
|
||||
ObserversSystem observers = GetObserversSystem();
|
||||
if (observers && GetPlayerController())
|
||||
observers.InsertObserverMP(GetPlayerController().GetRplIdentity(), m_ERRScenePosition[0], m_ERRScenePosition[2], null);
|
||||
foreach (IEntity entity : m_ERRSceneEntities) ERR_StreamScene(entity, true);
|
||||
m_ERRSceneReleased = true;
|
||||
PrintFormat("Reintroduced [spawn scene]: player=%1 token=%2 preload finished; verifying storage after stream release", GetPlayerId(), m_ERRSceneToken);
|
||||
m_ERRSceneFinalRequested = true;
|
||||
PrintFormat("Reintroduced [spawn scene]: player=%1 token=%2 preload finished; verifying held storage", GetPlayerId(), m_ERRSceneToken);
|
||||
Rpc(ERR_ConfirmClientSpawnScene, m_ERRSceneToken);
|
||||
}
|
||||
return m_ERRSceneVerified;
|
||||
}
|
||||
void ERR_RetainSpawnScene(IEntity actor)
|
||||
{
|
||||
if (!Replication.IsServer() || !ERR_SpawnSceneVerified() || !actor || !GetPlayerController()) return;
|
||||
if (!m_ERRSceneLease) m_ERRSceneLease = new ERR_SpawnSceneLease();
|
||||
m_ERRSceneLease.Start(GetPlayerController(), actor, m_ERRScenePosition, m_ERRSceneEntities);
|
||||
PrintFormat("Reintroduced [spawn scene]: player=%1 retaining %2 objects until leaving the spawn area", GetPlayerId(), m_ERRSceneEntities.Count());
|
||||
// Transfer ownership of the streaming hold; request cleanup must not undo it.
|
||||
m_ERRSceneEntities.Clear();
|
||||
}
|
||||
bool ERR_SpawnSceneMayFinish()
|
||||
{
|
||||
if (!m_ERRScenePending) return true;
|
||||
@@ -187,7 +193,7 @@ modded class SCR_SpawnRequestComponent
|
||||
m_ERRSceneAcknowledged = false;
|
||||
m_ERRSceneTimedOut = false;
|
||||
m_ERRScenePreloadRequired = false;
|
||||
m_ERRSceneReleased = false;
|
||||
m_ERRSceneFinalRequested = false;
|
||||
m_ERRSceneVerified = false;
|
||||
}
|
||||
|
||||
@@ -256,7 +262,12 @@ modded class SCR_SpawnRequestComponent
|
||||
foreach (RplId id : m_ERRClientSceneNodes)
|
||||
{
|
||||
RplComponent component = RplComponent.Cast(Replication.FindItem(id));
|
||||
if (!component || !component.GetEntity()) { m_ERRClientSceneStableSince = -1; return; }
|
||||
if (!component || !component.GetEntity())
|
||||
{
|
||||
ERR_ReportStockWait(id, RplId.Invalid(), -1);
|
||||
m_ERRClientSceneStableSince = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!ERR_ClientStockReady()) { m_ERRClientSceneStableSince = -1; return; }
|
||||
if (m_ERRClientSceneFinal)
|
||||
@@ -277,7 +288,7 @@ modded class SCR_SpawnRequestComponent
|
||||
if (!Replication.IsServer() || !m_ERRScenePending || m_ERRSceneTimedOut || token != m_ERRSceneToken) return;
|
||||
if (finalCheck)
|
||||
{
|
||||
if (!m_ERRSceneReleased || !m_ERRSceneAcknowledged) return;
|
||||
if (!m_ERRSceneFinalRequested || !m_ERRSceneAcknowledged) return;
|
||||
m_ERRSceneVerified = true;
|
||||
}
|
||||
else m_ERRSceneAcknowledged = true;
|
||||
@@ -314,5 +325,6 @@ modded class SCR_SpawnRequestComponent
|
||||
if (!GetGame()) return;
|
||||
ERR_ClearClientSpawnScene();
|
||||
ERR_EndSpawnScene();
|
||||
if (m_ERRSceneLease) m_ERRSceneLease.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ modded class SCR_FreeSpawnHandlerComponent
|
||||
if (!requestComponent.ERR_SpawnSceneMayFinish()) return false;
|
||||
if (requestComponent.ERR_TakeScenePreload()) super.OnFinalizeBegin_S(requestComponent, data, entity);
|
||||
if (!super.CanFinalize_S(requestComponent, data, entity)) return false;
|
||||
return requestComponent.ERR_VerifySceneRelease();
|
||||
return requestComponent.ERR_VerifyLoadedScene();
|
||||
}
|
||||
return super.CanFinalize_S(requestComponent, data, entity);
|
||||
}
|
||||
@@ -104,6 +104,7 @@ modded class SCR_FreeSpawnHandlerComponent
|
||||
return SCR_ESpawnResult.SPAWN_NOT_ALLOWED;
|
||||
}
|
||||
SCR_ESpawnResult result = super.FinalizeRequest_S(requestComponent, data, entity);
|
||||
if (result == SCR_ESpawnResult.OK) requestComponent.ERR_RetainSpawnScene(entity);
|
||||
requestComponent.ERR_EndSpawnScene();
|
||||
if (result != SCR_ESpawnResult.OK) ERR_RejectOwnedActor(requestComponent, entity, data);
|
||||
return result;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Keep the bounded spawn snapshot relevant through possession and nearby crate
|
||||
// use. Releasing it before possession can stream out runtime inventory nodes.
|
||||
class ERR_SpawnSceneLease
|
||||
{
|
||||
protected SCR_PlayerController m_Controller;
|
||||
protected IEntity m_Actor;
|
||||
protected vector m_Position;
|
||||
protected ref array<IEntity> m_Entities = {};
|
||||
|
||||
static void Stream(SCR_PlayerController controller, IEntity entity, bool normalStreaming)
|
||||
{
|
||||
if (!controller || !entity) return;
|
||||
RplIdentity identity = controller.GetRplIdentity();
|
||||
if (!identity.IsValid() || identity == RplIdentity.Local()) return;
|
||||
RplComponent component = RplComponent.Cast(entity.FindComponent(RplComponent));
|
||||
if (component) component.EnableStreamingConNode(identity, normalStreaming);
|
||||
}
|
||||
|
||||
void Start(SCR_PlayerController controller, IEntity actor, vector position, array<IEntity> entities)
|
||||
{
|
||||
Stop();
|
||||
m_Controller = controller;
|
||||
m_Actor = actor;
|
||||
m_Position = position;
|
||||
m_Entities.Copy(entities);
|
||||
GetGame().GetCallqueue().CallLater(Check, 500, true);
|
||||
}
|
||||
|
||||
protected void Check()
|
||||
{
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (!mode || !mode.Active() || !m_Controller || !m_Actor || m_Controller.GetControlledEntity() != m_Actor || vector.DistanceXZ(m_Actor.GetOrigin(), m_Position) > 120)
|
||||
Stop();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (GetGame()) GetGame().GetCallqueue().Remove(Check);
|
||||
foreach (IEntity entity : m_Entities) Stream(m_Controller, entity, true);
|
||||
m_Entities.Clear();
|
||||
m_Controller = null;
|
||||
m_Actor = null;
|
||||
}
|
||||
|
||||
void ~ERR_SpawnSceneLease() { Stop(); }
|
||||
}
|
||||
Reference in New Issue
Block a user