Skip to content

Commit 17a76c3

Browse files
committed
Add global door state manager prototype #5
1 parent 6a9fc37 commit 17a76c3

20 files changed

+433
-113
lines changed

src/Scripts/Game/Components/EPF_BaseInventoryStorageComponentSaveData.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ class EPF_BaseInventoryStorageComponentSaveData : EPF_ComponentSaveData
180180

181181
// Unable to add it to the storage parent, so put it on the ground at the parent origin
182182
if (!storageManager.TryInsertItemInStorage(slotEntity, storageComponent, slot.m_iSlotIndex))
183-
EPF_Utils.Teleport(slotEntity, storageComponent.GetOwner().GetOrigin(), storageComponent.GetOwner().GetYawPitchRoll()[0]);
183+
EPF_WorldUtils.Teleport(slotEntity, storageComponent.GetOwner().GetOrigin(), storageComponent.GetOwner().GetYawPitchRoll()[0]);
184184
}
185185

186186
// Delte any items not found in the storage data for non bakes that always save all slots

src/Scripts/Game/Components/EPF_CompartmentAccessComponentSaveData.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class EPF_CompartmentAccessComponentSaveData : EPF_ComponentSaveData
8282

8383
// No longer able to enter compartment so find free position on the ground to spawn instead
8484
if (SCR_WorldTools.FindEmptyTerrainPosition(currentPos, currentPos, 50, cylinderHeight: 1000))
85-
EPF_Utils.Teleport(owner, currentPos);
85+
EPF_WorldUtils.Teleport(owner, currentPos);
8686

8787
return EPF_EApplyResult.OK;
8888
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[BaseContainerProps()]
2+
class EPF_PersistentDoorStateFilter
3+
{
4+
//------------------------------------------------------------------------------------------------
5+
bool IsMatch(IEntity doorEntity)
6+
{
7+
return true;
8+
}
9+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
class EPF_PersistentDoorStateManager : EPF_PersistentScriptedState
2+
{
3+
/*protected*/ ref map<string, float> m_mStates = new map<string, float>(); //TODO: Make protected again after 0.9.9.5X update
4+
5+
protected EPF_PersistentDoorStateFilter m_pFilter;
6+
7+
//------------------------------------------------------------------------------------------------
8+
static void UpdateDoorState(IEntity doorEntity)
9+
{
10+
EPF_PersistentDoorStateManagerComponent managerComponent = EPF_PersistentDoorStateManagerComponent.GetInstance();
11+
if (!managerComponent)
12+
return;
13+
14+
EPF_PersistentDoorStateManager manager = managerComponent.GetPersistentDoorManager();
15+
if (!manager)
16+
return;
17+
18+
DoorComponent door = EPF_Component<DoorComponent>.Find(doorEntity);
19+
if (!door)
20+
return;
21+
22+
if (manager.m_pFilter && !manager.m_pFilter.IsMatch(doorEntity))
23+
return;
24+
25+
string mapKey = EPF_Utils.GetPrefabGUID(EPF_Utils.GetPrefabName(doorEntity)) + "@" + doorEntity.GetOrigin().ToString(false);
26+
27+
float controlValue = door.GetControlValue();
28+
if (float.AlmostEqual(controlValue, 0))
29+
{
30+
manager.m_mStates.Remove(mapKey);
31+
}
32+
else
33+
{
34+
manager.m_mStates.Set(mapKey, controlValue);
35+
}
36+
}
37+
38+
//------------------------------------------------------------------------------------------------
39+
void SetFilterLogic(EPF_PersistentDoorStateFilter filter)
40+
{
41+
m_pFilter = filter;
42+
}
43+
44+
//------------------------------------------------------------------------------------------------
45+
event void OnPersistenceLoaded()
46+
{
47+
foreach (string mapKey, float controlValue : m_mStates)
48+
{
49+
ResourceName prefab = mapKey.Substring(0, 16);
50+
vector origin = mapKey.Substring(17, mapKey.Length() - 17).ToVector();
51+
52+
IEntity doorEntity = EPF_WorldUtils.FindNearestPrefab(prefab, origin, 0.5);
53+
DoorComponent door = EPF_Component<DoorComponent>.Find(doorEntity);
54+
if (!door)
55+
{
56+
Print(string.Format("Failed to restore door %1@%2 to controlValue: %3. Entity not found.", prefab, origin, controlValue), LogLevel.WARNING);
57+
continue;
58+
}
59+
60+
door.SetControlValue(controlValue);
61+
Print(string.Format("Restored door %1@%2 to controlValue: %3.", prefab, origin, controlValue), LogLevel.VERBOSE);
62+
}
63+
64+
// TODO: Replace hardcoded delay when we can use the max of all doors opening times: https://feedback.bistudio.com/T174535
65+
GetGame().GetCallqueue().CallLater(EPF_PersistentDoorStateManagerComponent.GetInstance().SetLoadingComplete, 1000);
66+
}
67+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
[ComponentEditorProps(category: "Persistence", description: "Optional persistance handling of doors.")]
2+
class EPF_PersistentDoorStateManagerComponentClass : EPF_PersistenceManagerExtensionBaseComponentClass
3+
{
4+
[Attribute(desc: "Optional filter which door entities will be tracked by the manager.")]
5+
ref EPF_PersistentDoorStateFilter m_pFilter;
6+
}
7+
8+
class EPF_PersistentDoorStateManagerComponent : EPF_PersistenceManagerExtensionBaseComponent
9+
{
10+
protected static EPF_PersistentDoorStateManagerComponent m_pInstance;
11+
protected ref EPF_PersistentDoorStateManager m_pDoorStateManager;
12+
protected bool m_bLoaded;
13+
14+
//------------------------------------------------------------------------------------------------
15+
override void OnSetup(EPF_PersistenceManager persistenceManager)
16+
{
17+
m_pInstance = this;
18+
EDF_DataCallbackSingle<EPF_PersistentDoorStateManager> callback(this, "OnDoorStateManagerLoaded");
19+
EPF_PersistentScriptedStateLoader<EPF_PersistentDoorStateManager>.LoadAsync(GetDoorStateManagerPersistentId(), callback);
20+
}
21+
22+
//------------------------------------------------------------------------------------------------
23+
override bool IsSetupComplete(EPF_PersistenceManager persistenceManager)
24+
{
25+
return m_bLoaded;
26+
}
27+
28+
//------------------------------------------------------------------------------------------------
29+
protected static string GetDoorStateManagerPersistentId()
30+
{
31+
return string.Format("00dc%1-0000-0000-0000-000000000000", EPF_PersistenceIdGenerator.GetHiveId().ToString(4));
32+
}
33+
34+
//------------------------------------------------------------------------------------------------
35+
protected void OnDoorStateManagerLoaded(EPF_PersistentDoorStateManager doorStateManager)
36+
{
37+
m_pDoorStateManager = doorStateManager;
38+
if (!m_pDoorStateManager)
39+
{
40+
m_pDoorStateManager = new EPF_PersistentDoorStateManager();
41+
m_pDoorStateManager.SetPersistentId(GetDoorStateManagerPersistentId());
42+
m_bLoaded = true;
43+
}
44+
45+
auto settings = EPF_PersistentDoorStateManagerComponentClass.Cast(GetComponentData(GetOwner()));
46+
m_pDoorStateManager.SetFilterLogic(settings.m_pFilter);
47+
}
48+
49+
//------------------------------------------------------------------------------------------------
50+
static EPF_PersistentDoorStateManagerComponent GetInstance()
51+
{
52+
return m_pInstance;
53+
}
54+
55+
//------------------------------------------------------------------------------------------------
56+
void SetLoadingComplete()
57+
{
58+
m_bLoaded = true;
59+
}
60+
61+
//------------------------------------------------------------------------------------------------
62+
EPF_PersistentDoorStateManager GetPersistentDoorManager()
63+
{
64+
return m_pDoorStateManager;
65+
}
66+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
EPF_PersistentScriptedStateSettings(EPF_PersistentDoorStateManager),
3+
EDF_DbName.Automatic()
4+
]
5+
class EPF_PersistentDoorStateManagerSaveData : EPF_ScriptedStateSaveData
6+
{
7+
ref map<string, float> m_mStates;
8+
9+
//------------------------------------------------------------------------------------------------
10+
override EPF_EReadResult ReadFrom(notnull Managed scriptedState)
11+
{
12+
auto doorStateManager = EPF_PersistentDoorStateManager.Cast(scriptedState);
13+
ReadMetaData(doorStateManager);
14+
m_mStates = doorStateManager.m_mStates;
15+
16+
if (m_mStates.IsEmpty())
17+
return EPF_EReadResult.DEFAULT;
18+
19+
return EPF_EReadResult.OK;
20+
}
21+
22+
//------------------------------------------------------------------------------------------------
23+
override EPF_EApplyResult ApplyTo(notnull Managed scriptedState)
24+
{
25+
auto doorStateManager = EPF_PersistentDoorStateManager.Cast(scriptedState);
26+
doorStateManager.m_mStates = m_mStates;
27+
doorStateManager.OnPersistenceLoaded();
28+
return EPF_EApplyResult.AWAIT_COMPLETION;
29+
}
30+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
modded class SCR_DoorUserAction
2+
{
3+
//------------------------------------------------------------------------------------------------
4+
override void PerformAction(IEntity pOwnerEntity, IEntity pUserEntity)
5+
{
6+
super.PerformAction(pOwnerEntity, pUserEntity);
7+
EPF_PersistentDoorStateManager.UpdateDoorState(pOwnerEntity);
8+
}
9+
}

src/Scripts/Game/EPF_DbFind.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ modded class EDF_DbFindFieldMainConditionBuilder
3636
{
3737
string prefabString = prefab;
3838
if (prefabString.StartsWith("{"))
39-
prefabString = prefabString.Substring(1, 16);
39+
prefabString = EPF_Utils.GetPrefabGUID(prefab);
4040

4141
// Db might contain full paths so we need to do only contains check to cover both cases
4242
#ifdef PERSISTENCE_DEBUG

src/Scripts/Game/EPF_EntitySaveData.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class EPF_EntitySaveData : EPF_MetaDataDbEntity
141141

142142
// Transform
143143
if (m_pTransformation && !m_pTransformation.m_bApplied)
144-
EPF_Utils.ForceTransform(entity, m_pTransformation.m_vOrigin, m_pTransformation.m_vAngles, m_pTransformation.m_fScale);
144+
EPF_WorldUtils.ForceTransform(entity, m_pTransformation.m_vOrigin, m_pTransformation.m_vAngles, m_pTransformation.m_fScale);
145145

146146
// Lifetime
147147
if (attributes.m_bSaveRemainingLifetime)
@@ -325,7 +325,7 @@ class EPF_EntitySaveData : EPF_MetaDataDbEntity
325325
string prefabString = m_rPrefab;
326326
#ifndef PERSISTENCE_DEBUG
327327
if (prefabString.StartsWith("{"))
328-
prefabString = m_rPrefab.Substring(1, 16);
328+
prefabString = EPF_Utils.GetPrefabGUID(m_rPrefab);
329329
#endif
330330
saveContext.WriteValue("m_rPrefab", prefabString);
331331

src/Scripts/Game/EPF_MetaDataDbEntity.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ class EPF_MetaDataDbEntity : EDF_DbEntity
1111
m_iLastSaved = persistenceComponent.GetLastSaved();
1212
}
1313

14+
//------------------------------------------------------------------------------------------------
15+
//! Utility function to read meta-data
16+
void ReadMetaData(notnull EPF_PersistentScriptedState scriptedState)
17+
{
18+
SetId(scriptedState.GetPersistentId());
19+
m_iLastSaved = scriptedState.GetLastSaved();
20+
}
21+
1422
//------------------------------------------------------------------------------------------------
1523
//! Utility function to write meta-data to serializer
1624
void SerializeMetaData(notnull BaseSerializationSaveContext saveContext)
@@ -31,4 +39,4 @@ class EPF_MetaDataDbEntity : EDF_DbEntity
3139
loadContext.ReadValue("m_iDataLayoutVersion", m_iDataLayoutVersion);
3240
loadContext.ReadValue("m_iLastSaved", m_iLastSaved);
3341
}
34-
};
42+
}

src/Scripts/Game/EPF_PersistenceIdGenerator.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class EPF_PersistenceIdGenerator : EDF_DbEntityIdGenerator
7272
string type = EPF_Utils.GetPrefabName(entity);
7373
if (type)
7474
{
75-
type = type.Substring(1, 16);
75+
type = EPF_Utils.GetPrefabGUID(type);
7676
}
7777
else
7878
{

src/Scripts/Game/EPF_PersistenceManager.c

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ enum EPF_EPersistenceManagerState
55
SETUP,
66
ACTIVE,
77
SHUTDOWN
8-
};
8+
}
99

1010
class EPF_PersistenceManager
1111
{
@@ -42,6 +42,9 @@ class EPF_PersistenceManager
4242
protected MapIterator m_iAutoSaveScriptedStateIt;
4343
protected ref ScriptInvoker<EPF_PersistenceManager> m_pOnAutoSaveCompleteEvent;
4444

45+
// Extensions
46+
protected ref array<EPF_PersistenceManagerExtensionBaseComponent> m_aExtensions;
47+
4548
// Setup buffers, discarded after world init
4649
protected ref map<string, EPF_PersistenceComponent> m_mBakedRoots;
4750
protected int m_iPendingLoadTypes;
@@ -686,7 +689,7 @@ class EPF_PersistenceManager
686689
}
687690

688691
//------------------------------------------------------------------------------------------------
689-
event void OnPostInit(IEntity gameMode, EPF_PersistenceManagerComponentClass settings)
692+
event void OnPostInit(IEntity gameMode, EPF_PersistenceManagerComponent managerComponent, EPF_PersistenceManagerComponentClass settings)
690693
{
691694
m_pSettings = settings;
692695

@@ -702,6 +705,15 @@ class EPF_PersistenceManager
702705
if (!m_pDbContext)
703706
return;
704707

708+
array<GenericComponent> extensions();
709+
managerComponent.FindComponents(EPF_PersistenceManagerExtensionBaseComponent, extensions);
710+
m_aExtensions = {};
711+
m_aExtensions.Reserve(extensions.Count());
712+
foreach (GenericComponent extension : extensions)
713+
{
714+
m_aExtensions.Insert(EPF_PersistenceManagerExtensionBaseComponent.Cast(extension));
715+
}
716+
705717
SetState(EPF_EPersistenceManagerState.POST_INIT);
706718
}
707719

@@ -836,15 +848,52 @@ class EPF_PersistenceManager
836848
SpawnWorldEntity(saveData);
837849
}
838850

839-
if (--m_iPendingLoadTypes == 0)
840-
OnPostSetup();
851+
if (--m_iPendingLoadTypes > 0)
852+
return;
853+
854+
// Free memory as it not needed after setup
855+
m_mBakedRoots = null;
856+
OnSetup();
857+
GetGame().GetCallqueue().CallLater(TryCompleteSetup, 100, true); // Check for completion every 100ms
858+
}
859+
860+
//------------------------------------------------------------------------------------------------
861+
//! Add EPF_PersistenceManagerExtensionBaseComponents or extend this via modded to
862+
// add custom logic that should be awaited for with IsSetupComplete()
863+
protected void OnSetup()
864+
{
865+
foreach (EPF_PersistenceManagerExtensionBaseComponent extension : m_aExtensions)
866+
{
867+
extension.OnSetup(this);
868+
}
869+
}
870+
871+
//------------------------------------------------------------------------------------------------
872+
//! Return true if additional custom setup logic is complete. Always consider super result too!
873+
protected bool IsSetupComplete()
874+
{
875+
foreach (EPF_PersistenceManagerExtensionBaseComponent extension : m_aExtensions)
876+
{
877+
if (!extension.IsSetupComplete(this))
878+
return false;
879+
}
880+
881+
return true;
882+
}
883+
884+
//------------------------------------------------------------------------------------------------
885+
protected void TryCompleteSetup()
886+
{
887+
if (!IsSetupComplete())
888+
return;
889+
890+
GetGame().GetCallqueue().Remove(TryCompleteSetup);
891+
OnPostSetup();
841892
}
842893

843894
//------------------------------------------------------------------------------------------------
844895
protected void OnPostSetup()
845896
{
846-
// Free memory as it not needed after setup
847-
m_mBakedRoots = null;
848897
SetState(EPF_EPersistenceManagerState.ACTIVE);
849898
Print("Persistence initial world load complete.", LogLevel.DEBUG);
850899
}
@@ -892,4 +941,4 @@ class EPF_PersistenceManager
892941
EPF_PersistentScriptedStateProxy.s_mProxies = null;
893942
s_pInstance = null;
894943
}
895-
};
944+
}

0 commit comments

Comments
 (0)