diff --git a/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorage.c b/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorage.c index bd502b24..86e9cc48 100644 --- a/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorage.c +++ b/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorage.c @@ -6,13 +6,15 @@ */ class CF_ModStorage { - static const int VERSION = 4; + static const int VERSION = 5; static const int GAME_VERSION_FIRST_INSTALL = 116; - static const int GAME_VERSION_WIPE_FILE = 127; + static const int GAME_VERSION_WIPE_FILE = 129; static const int MODSTORAGE_INITIAL_IMPLEMENTATION = 2; + int m_CF_Version; + ModStructure m_Mod; int m_Version; @@ -20,9 +22,15 @@ class CF_ModStorage int m_HashA; int m_HashB; + // v4 string m_Data; string m_Value; + // v5 + ref array m_Entries; + int m_Idx; + int m_MaxIdx; + void CF_ModStorage(ModStructure mod) { m_Mod = mod; @@ -40,91 +48,151 @@ class CF_ModStorage return m_Mod; } + string GetModName() + { + if (m_Mod) + { + return m_Mod.GetName(); + } + + return "unknown<" + m_HashA + "," + m_HashB + ">"; + } + bool Read(out bool value) { - m_Data.ParseStringEx(m_Value); + if (m_CF_Version < VERSION) + { + m_Data.ParseStringEx(m_Value); - value = m_Value == "1"; + value = m_Value == "1"; + } + else + { + if (m_Idx > m_MaxIdx || m_Entries[m_Idx].ValueType() != bool) + return false; + + value = CF_ModStorageData.Cast(m_Entries[m_Idx++]).Get(); + } return true; } bool Read(out int value) { - m_Data.ParseStringEx(m_Value); + if (m_CF_Version < VERSION) + { + m_Data.ParseStringEx(m_Value); - value = m_Value.ToInt(); + value = m_Value.ToInt(); + } + else + { + if (m_Idx > m_MaxIdx || m_Entries[m_Idx].ValueType() != int) + return false; + + value = CF_ModStorageData.Cast(m_Entries[m_Idx++]).Get(); + } return true; } bool Read(out float value) { - m_Data.ParseStringEx(m_Value); + if (m_CF_Version < VERSION) + { + m_Data.ParseStringEx(m_Value); - value = m_Value.ToFloat(); + value = m_Value.ToFloat(); + } + else + { + if (m_Idx > m_MaxIdx || m_Entries[m_Idx].ValueType() != float) + return false; + + value = CF_ModStorageData.Cast(m_Entries[m_Idx++]).Get(); + } return true; } bool Read(out vector value) { - m_Data.ParseStringEx(m_Value); + if (m_CF_Version < VERSION) + { + m_Data.ParseStringEx(m_Value); - value = m_Value.ToVector(); + value = m_Value.ToVector(); + } + else + { + if (m_Idx > m_MaxIdx || m_Entries[m_Idx].ValueType() != vector) + return false; + + value = CF_ModStorageData.Cast(m_Entries[m_Idx++]).Get(); + } return true; } bool Read(out string value) { - m_Data.ParseStringEx(m_Value); + if (m_CF_Version < VERSION) + { + m_Data.ParseStringEx(m_Value); - value = m_Value; + value = m_Value; + } + else + { + if (m_Idx > m_MaxIdx || m_Entries[m_Idx].ValueType() != string) + return false; + + value = CF_ModStorageData.Cast(m_Entries[m_Idx++]).Get(); + } return true; } void Write(bool value) { - if (value) - { - m_Data += " 1"; - } - else - { - m_Data += " 0"; - } + _Insert(value); } void Write(int value) { - m_Data += " " + value.ToString(); + _Insert(value); } void Write(float value) { - m_Data += " " + value.ToString(); + _Insert(value); } void Write(vector value) { - m_Data += " \"" + value.ToString(false) + "\""; + _Insert(value); } void Write(string value) { - m_Data += " \"" + value + "\""; + _Insert(value); } void _CopyStreamTo(Serializer ctx) { + int cf_version = m_CF_Version; string data; - if (m_Data.Length() > 0) + + if (cf_version < VERSION) { - data = m_Data.Substring(1, m_Data.Length() - 1); + if (m_Data.Length() > 0) + { + data = m_Data.Substring(1, m_Data.Length() - 1); + } } + auto tmp = m_Entries; + // force resetting early so we can write the latest version _ResetStream(); @@ -133,17 +201,71 @@ class CF_ModStorage ctx.Write(m_Version); - ctx.Write(data); + if (cf_version < VERSION) + { + ctx.Write(data); + return; + } + + ctx.Write(tmp.Count()); + foreach (auto entry: tmp) + { + switch (entry.ValueType()) + { + case bool: + CF_ModStorageData entryBool = CF_ModStorageData.Cast(entry); + ctx.Write(CF_ModStorageDataType.BOOL); + ctx.Write(entryBool.Get()); + break; + case int: + CF_ModStorageData entryInt = CF_ModStorageData.Cast(entry); + ctx.Write(CF_ModStorageDataType.INT); + ctx.Write(entryInt.Get()); + break; + case float: + CF_ModStorageData entryFloat = CF_ModStorageData.Cast(entry); + ctx.Write(CF_ModStorageDataType.FLOAT); + ctx.Write(entryFloat.Get()); + break; + case vector: + CF_ModStorageData entryVector = CF_ModStorageData.Cast(entry); + ctx.Write(CF_ModStorageDataType.VECTOR); + vector v = entryVector.Get(); + ctx.Write(v[0]); + ctx.Write(v[1]); + ctx.Write(v[2]); + break; + case string: + CF_ModStorageData entryString = CF_ModStorageData.Cast(entry); + ctx.Write(CF_ModStorageDataType.STRING); + ctx.Write(entryString.Get()); + break; + default: + CF_Log.Error("Failed to write unknown data type %1 for mod %2", entry.Type().ToString(), GetModName()); + } + } } // Read and Write functions can't be called, so we can't reset the stream void _ResetStream() { + m_Idx = 0; + if (!m_Mod) { + if (!m_Entries) + { + m_Entries = new array(); + m_MaxIdx = -1; + } return; } + m_CF_Version = VERSION; + + m_Entries = new array(); + m_MaxIdx = -1; + m_Data = string.Empty; m_HashA = m_Mod.m_CF_HashA; @@ -151,4 +273,34 @@ class CF_ModStorage m_Version = m_Mod.GetStorageVersion(); } + + void _Insert(bool b) + { + m_Entries.Insert(new CF_ModStorageData(b)); + m_MaxIdx++; + } + + void _Insert(int i) + { + m_Entries.Insert(new CF_ModStorageData(i)); + m_MaxIdx++; + } + + void _Insert(float f) + { + m_Entries.Insert(new CF_ModStorageData(f)); + m_MaxIdx++; + } + + void _Insert(vector v) + { + m_Entries.Insert(new CF_ModStorageData(v)); + m_MaxIdx++; + } + + void _Insert(string s) + { + m_Entries.Insert(new CF_ModStorageData(s)); + m_MaxIdx++; + } }; diff --git a/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageData.c b/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageData.c new file mode 100644 index 00000000..52340cb0 --- /dev/null +++ b/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageData.c @@ -0,0 +1,54 @@ +enum CF_ModStorageDataType +{ + INVALID = -1, + BOOL, + INT, + FLOAT, + VECTOR, + STRING + + //! TODO + //ARRAY_BOOL, + //ARRAY_INT, + //ARRAY_FLOAT, + //ARRAY_VECTOR, + //ARRAY_STRING +}; + +class CF_ModStorageDataBase +{ + typename ValueType() + { + return typename; + } + + string ValueString() + { + return ""; + } +}; + +class CF_ModStorageData: CF_ModStorageDataBase +{ + T m_Value; + + void CF_ModStorageData(T value) + { + m_Value = value; + } + + T Get() + { + return m_Value; + } + + override typename ValueType() + { + return T; + } + + override string ValueString() + { + return string.ToString(m_Value); + } +}; diff --git a/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageModule.c b/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageModule.c deleted file mode 100644 index d699b7bf..00000000 --- a/JM/CF/Scripts/3_Game/CommunityFramework/ModStorage/CF_ModStorageModule.c +++ /dev/null @@ -1,168 +0,0 @@ -/** - * @class CF_ModStorageModule - * - * @brief Does not support unloading CF from the mods. Once loaded it can't be removed - */ -#ifndef CF_MODSTORAGE_MODULE_DISABLE -[CF_RegisterModule(CF_ModStorageModule)] -#endif -class CF_ModStorageModule : CF_ModuleGame -{ - static const string m_FileName = "modstorageids.bin"; - protected string m_FilePath; - - protected autoptr map>>>> m_IDs = new map>>>>(); - protected bool m_IsLoaded; - - protected autoptr FileSerializer m_Serializer; - - /** - * @brief Checks if the persistent ID is in the map. If they aren't in the map then add and write to the file - */ - void AddEntity(int b1, int b2, int b3, int b4) - { - Load(); - - if (!_AddEntity(b1, b2, b3, b4, false)) - { - return; - } - - m_Serializer.Write(b1); - m_Serializer.Write(b2); - m_Serializer.Write(b3); - m_Serializer.Write(b4); - } - - /** - * @brief Checks to see if the persistent ID is loaded - */ - bool IsEntity(int b1, int b2, int b3, int b4) - { - Load(); - - auto map_b1 = m_IDs[b1]; - if (!map_b1) - { - return false; - } - - auto map_b2 = map_b1[b2]; - if (!map_b2) - { - return false; - } - - auto map_b3 = map_b2[b3]; - if (!map_b3) - { - return false; - } - - auto val_b4 = map_b3[b4]; - if (!val_b4) - { - return false; - } - - return true; - } - - /** - * @brief Reads the modstorage file - */ - void Load(bool reload = false) - { - if (m_IsLoaded && !reload) - { - return; - } - - m_IsLoaded = true; - - int instanceId = g_Game.ServerConfigGetInt("instanceId"); - - string folder = "$mission:storage_" + instanceId + "/"; - if (!FileExist(folder)) - { - MakeDirectory(folder); - } - - folder += "communityframework/"; - if (!FileExist(folder)) - { - MakeDirectory(folder); - } - - m_FilePath = folder + m_FileName; - - if (m_Serializer) m_Serializer.Close(); - - // Clear existing ids - m_IDs.Clear(); - - if (FileExist(m_FilePath)) - { - FileHandle handle = OpenFile(m_FilePath, FileMode.READ); - if (handle != 0) - { - // Reads 4 ints (16 bytes) at a time - int data[4]; - while (ReadFile(handle, data, 16) > 0) - { - _AddEntity(data[0], data[1], data[2], data[3], true); - } - - CloseFile(handle); - } - - m_Serializer = new FileSerializer(); - m_Serializer.Open(m_FilePath, FileMode.APPEND); - } - else - { - m_Serializer = new FileSerializer(); - m_Serializer.Open(m_FilePath, FileMode.WRITE); - } - } - - /** - * @param loaded If the entity was added from the file - * - * @return True if newly added entity - */ - private bool _AddEntity(int b1, int b2, int b3, int b4, bool loaded) - { - auto map_b1 = m_IDs[b1]; - if (!map_b1) - { - map_b1 = new map>>>(); - m_IDs[b1] = map_b1; - } - - auto map_b2 = map_b1[b2]; - if (!map_b2) - { - map_b2 = new map>>(); - map_b1[b2] = map_b2; - } - - auto map_b3 = map_b2[b3]; - if (!map_b3) - { - map_b3 = new map>(); - map_b2[b3] = map_b3; - } - - auto val_b4 = map_b3[b4]; - if (!val_b4) - { - val_b4 = new Param1(loaded); - map_b3[b4] = val_b4; - - return true; - } - - return false; - } -}; diff --git a/JM/CF/Scripts/3_Game/CommunityFramework/Mods/ModLoader.c b/JM/CF/Scripts/3_Game/CommunityFramework/Mods/ModLoader.c index e9338358..f9edac3c 100644 --- a/JM/CF/Scripts/3_Game/CommunityFramework/Mods/ModLoader.c +++ b/JM/CF/Scripts/3_Game/CommunityFramework/Mods/ModLoader.c @@ -70,14 +70,69 @@ modded class ModLoader storage.m_HashB = hashB; } + storage.m_CF_Version = version; + if (!ctx.Read(storage.m_Version)) return false; - if (!ctx.Read(storage.m_Data)) return false; + bool hasData; + if (version < CF_ModStorage.VERSION) + { + //! Bail on old ModStorage data to avoid possible CTD if corrupted storage + int numberOfBytes; + ctx.Read(numberOfBytes); + CF_Log.Error("Reading deprecated modstorage v" + version + " is not supported, discarding " + numberOfBytes + " bytes for mod " + storage.GetModName()); + return false; + } + else + { + int entries; + if (!ctx.Read(entries)) return false; + hasData = entries > 0; + while (entries > 0) + { + int type = -1; + if (!ctx.Read(type)) return false; + switch (type) + { + case CF_ModStorageDataType.BOOL: + bool b = false; + if (!ctx.Read(b)) return false; + storage._Insert(b); + break; + case CF_ModStorageDataType.INT: + int i = 0; + if (!ctx.Read(i)) return false; + storage._Insert(i); + break; + case CF_ModStorageDataType.FLOAT: + float f = 0; + if (!ctx.Read(f)) return false; + storage._Insert(f); + break; + case CF_ModStorageDataType.VECTOR: + float x = 0, y = 0, z = 0; + if (!ctx.Read(x)) return false; + if (!ctx.Read(y)) return false; + if (!ctx.Read(z)) return false; + storage._Insert(Vector(x, y, z)); + break; + case CF_ModStorageDataType.STRING: + string s = ""; + if (!ctx.Read(s)) return false; + storage._Insert(s); + break; + default: + CF_Log.Error("Failed to read unknown data type %1 for mod %2", type.ToString(), storage.GetModName()); + return false; + } + entries--; + } + } if (exists) { loadedMods.Insert(storage.GetMod().GetName(), storage); } - else + else if (hasData) { unloadedMods[stackIndex] = storage; stackIndex++; diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/AdvancedCommunication.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/AdvancedCommunication.c index e86ceeef..8313d9a2 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/AdvancedCommunication.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/AdvancedCommunication.c @@ -1,23 +1,25 @@ modded class AdvancedCommunication { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/AnimalBase.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/AnimalBase.c index f8e33cc7..b9ac4568 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/AnimalBase.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/AnimalBase.c @@ -1,23 +1,25 @@ modded class AnimalBase { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/BoatScript.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/BoatScript.c new file mode 100644 index 00000000..f95be083 --- /dev/null +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/BoatScript.c @@ -0,0 +1,40 @@ +#ifndef DAYZ_1_25 +modded class BoatScript +{ +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif + + /** + * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave + */ + void CF_OnStoreSave(CF_ModStorageMap storage) + { + } + + /** + * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreLoad + */ + bool CF_OnStoreLoad(CF_ModStorageMap storage) + { + return true; + } +}; +#endif diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/BuildingBase.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/BuildingBase.c index b71375e5..5f66f6e9 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/BuildingBase.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/BuildingBase.c @@ -1,23 +1,25 @@ modded class BuildingBase { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/CarScript.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/CarScript.c index 22582dbb..15c98aec 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/CarScript.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/CarScript.c @@ -1,23 +1,25 @@ modded class CarScript { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/DayZPlayerImplement.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/DayZPlayerImplement.c index 61f86c66..c6509c0f 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/DayZPlayerImplement.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/DayZPlayerImplement.c @@ -1,23 +1,25 @@ modded class DayZPlayerImplement { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/HelicopterScript.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/HelicopterScript.c index bc6819f8..cd853f2e 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/HelicopterScript.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/HelicopterScript.c @@ -1,23 +1,25 @@ modded class HelicopterScript { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/ItemBase.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ItemBase.c index ff3de84d..e03ccd5a 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/ItemBase.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ItemBase.c @@ -1,23 +1,25 @@ modded class ItemBase { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @param storage Map of 'CF_ModStorage' classes diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/ManBase/PlayerBase.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ManBase/PlayerBase.c new file mode 100644 index 00000000..192c4955 --- /dev/null +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ManBase/PlayerBase.c @@ -0,0 +1,53 @@ +modded class PlayerBase +{ + protected static ref TStringArray s_CF_QueuedIdentityIDs = new TStringArray(); + protected string m_CF_QueuedIdentityID; + + static void CF_QueueIdentityId(string id) + { + s_CF_QueuedIdentityIDs.Insert(id); + } + + static string CF_DequeueIdentityId() + { + string id = s_CF_QueuedIdentityIDs[0]; + s_CF_QueuedIdentityIDs.RemoveOrdered(0); + return id; + } + + string CF_GetQueuedIdentityId() + { + //! Always prefer identity when it is available + if (GetIdentity()) + { + if (m_CF_QueuedIdentityID != GetIdentity().GetId()) + { + CF_Log.Warn("Queued identity ID %1 doesn't match actual ID %2 for player %3", m_CF_QueuedIdentityID, GetIdentity().GetId(), ToString()); + m_CF_QueuedIdentityID = GetIdentity().GetId(); + } + } + else if (!m_CF_QueuedIdentityID && s_CF_QueuedIdentityIDs.Count()) + { + array identities(); + GetDayZGame().GetPlayerIndentities(identities); + string id; + while (s_CF_QueuedIdentityIDs.Count()) + { + id = CF_DequeueIdentityId(); + if (!id) break; + //! Make sure this is not a stale ID. If it's not in the list, + //! the player didn't finish connecting (client crash, connection error etc) + foreach (PlayerIdentity identity: identities) + { + if (id == identity.GetId()) + { + m_CF_QueuedIdentityID = id; + return m_CF_QueuedIdentityID; + } + } + } + } + + return m_CF_QueuedIdentityID; + } +}; diff --git a/JM/CF/Scripts/4_World/CommunityFramework/Entities/ZombieBase.c b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ZombieBase.c index 5aa034f8..f9cb87cb 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/Entities/ZombieBase.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/Entities/ZombieBase.c @@ -1,23 +1,25 @@ modded class ZombieBase { -// autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); -// -// override void OnStoreSave(ParamsWriteContext ctx) -// { -// super.OnStoreSave(ctx); -// -// m_CF_ModStorage.OnStoreSave(ctx); -// } -// -// override bool OnStoreLoad(ParamsReadContext ctx, int version) -// { -// if (!super.OnStoreLoad(ctx, version)) -// { -// return false; -// } -// -// return m_CF_ModStorage.OnStoreLoad(ctx, version); -// } +#ifdef CF_MODSTORAGE + autoptr CF_ModStorageBase m_CF_ModStorage = new CF_ModStorageObject(this); + + override void OnStoreSave(ParamsWriteContext ctx) + { + super.OnStoreSave(ctx); + + m_CF_ModStorage.OnStoreSave(ctx); + } + + override bool OnStoreLoad(ParamsReadContext ctx, int version) + { + if (!super.OnStoreLoad(ctx, version)) + { + return false; + } + + return m_CF_ModStorage.OnStoreLoad(ctx, version); + } +#endif /** * @brief Refer to CF/ModStorage implementation of ItemBase::CF_OnStoreSave diff --git a/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageModule.c b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageModule.c new file mode 100644 index 00000000..83ba8a55 --- /dev/null +++ b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageModule.c @@ -0,0 +1,132 @@ +/** + * @class CF_ModStorageModule + * + * @brief Does not support unloading CF from the mods. Once loaded it can't be removed + */ +[CF_RegisterModule(CF_ModStorageModule)] +class CF_ModStorageModule : CF_ModuleWorld +{ + static const string m_FileName = "modstorageplayers.bin"; + protected string m_FilePath; + + //! We only need to keep track of players, because if m_FileName exists, + //! we already know that all items that are not in player inventory are OK to load from modstorage + //! because they have been saved prior (as long as the server ran for a couple minutes at least) + protected autoptr map> m_IDs = new map>(); + protected bool m_IsLoaded; + protected bool m_FileExist; + + protected autoptr FileSerializer m_Serializer; + + /** + * @brief Checks if the item has a player root and if the player ID is in the map. If they aren't in the map then add and write to the file + */ + void AddEntity(EntityAI entity) + { + if (!g_Game.IsDedicatedServer()) + return; + + Load(); + + PlayerBase player = PlayerBase.Cast(entity.GetHierarchyRootPlayer()); + if (!player || !player.CF_GetQueuedIdentityId() || !_AddPlayer(player.CF_GetQueuedIdentityId(), false)) + { + return; + } + + m_Serializer.Write(player.CF_GetQueuedIdentityId()); + } + + /** + * @brief Checks to see if the item has a player root and if the player ID is in the map + */ + bool IsEntity(EntityAI entity) + { + if (!g_Game.IsDedicatedServer()) + return true; + + Load(); + + PlayerBase player = PlayerBase.Cast(entity.GetHierarchyRootPlayer()); + if (!player || !player.CF_GetQueuedIdentityId()) + { + return m_FileExist; + } + + return m_IDs[player.CF_GetQueuedIdentityId()] != null; + } + + /** + * @brief Reads the modstorage file + */ + void Load(bool reload = false) + { + if (m_IsLoaded && !reload) + { + return; + } + + m_IsLoaded = true; + + int instanceId = g_Game.ServerConfigGetInt("instanceId"); + + string folder = "$mission:storage_" + instanceId + "/"; + if (!FileExist(folder)) + { + MakeDirectory(folder); + } + + folder += "communityframework/"; + if (!FileExist(folder)) + { + MakeDirectory(folder); + } + + m_FilePath = folder + m_FileName; + + if (m_Serializer) m_Serializer.Close(); + + // Clear existing ids + m_IDs.Clear(); + + m_FileExist = FileExist(m_FilePath); + if (m_FileExist) + { + m_Serializer = new FileSerializer(); + m_Serializer.Open(m_FilePath, FileMode.READ); + string id; + while (true) + { + m_Serializer.Read(id); // always returns true + if (!id) break; + _AddPlayer(id, true); + } + m_Serializer.Close(); + + m_Serializer = new FileSerializer(); + m_Serializer.Open(m_FilePath, FileMode.APPEND); + } + else + { + m_Serializer = new FileSerializer(); + m_Serializer.Open(m_FilePath, FileMode.WRITE); + } + } + + /** + * @param loaded If the entity was added from the file + * + * @return True if newly added entity + */ + private bool _AddPlayer(string id, bool loaded) + { + if (!m_IDs[id]) + { + m_IDs[id] = new Param1(loaded); + + return true; + } + + return false; + } +}; diff --git a/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageObject.c b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageObject.c index 1c462fac..1deb5101 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageObject.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/CF_ModStorageObject.c @@ -1,4 +1,4 @@ -#ifndef CF_MODSTORAGE_DISABLE +#ifdef CF_MODSTORAGE class CF_ModStorageObject : CF_ModStorageBase { T m_Entity; @@ -7,6 +7,8 @@ class CF_ModStorageObject : CF_ModStorageBase CF_ModStorageModule m_Module; + bool m_HasModStorage; + void CF_ModStorageObject(T entity) { m_Entity = entity; @@ -34,13 +36,14 @@ class CF_ModStorageObject : CF_ModStorageBase return; } -#ifndef CF_MODSTORAGE_MODULE_DISABLE - int b1, b2, b3, b4; - m_Entity.GetPersistentID(b1, b2, b3, b4); - // Add the entity to the file so on next load the game knows that it can read the modstorage for the entity - m_Module.AddEntity(b1, b2, b3, b4); -#endif + if (!m_HasModStorage) + { + // Add the entity to the file so on next load the game knows that it can read the modstorage for the entity + m_Module.AddEntity(m_Entity); + + m_HasModStorage = true; + } // Write the CF modstorage version ctx.Write(CF_ModStorage.VERSION); @@ -53,12 +56,21 @@ class CF_ModStorageObject : CF_ModStorageBase m_Entity.CF_OnStoreSave(ModLoader.s_CF_ModStorageMap); - ctx.Write(ModLoader.s_CF_ModStorages.Count() + m_UnloadedMods.Count()); - + int modsWithDataCount; foreach (auto mod2 : ModLoader.s_CF_ModStorages) { - // also resets the stream for next 'OnStoreSave' - mod2._CopyStreamTo(ctx); + if (mod2.m_MaxIdx > -1) modsWithDataCount++; + } + + ctx.Write(modsWithDataCount + m_UnloadedMods.Count()); + + if (modsWithDataCount) + { + foreach (auto mod3 : ModLoader.s_CF_ModStorages) + { + // also resets the stream for next 'OnStoreSave' + if (mod3.m_MaxIdx > -1) mod3._CopyStreamTo(ctx); + } } foreach (auto unloadedMod : m_UnloadedMods) @@ -83,14 +95,10 @@ class CF_ModStorageObject : CF_ModStorageBase return true; } -#ifndef CF_MODSTORAGE_MODULE_DISABLE - int b1, b2, b3, b4; - m_Entity.GetPersistentID(b1, b2, b3, b4); - // If the version is less than the wipe file, the entity will be added automatically in 'OnStoreSave' if (version >= CF_ModStorage.GAME_VERSION_WIPE_FILE) { - if (!m_Module.IsEntity(b1, b2, b3, b4)) + if (!m_Module.IsEntity(m_Entity)) { // Since the entity wasn't found we can assume that CF is freshly loaded // Highly unlikely anything else happened that can cause this @@ -99,7 +107,6 @@ class CF_ModStorageObject : CF_ModStorageBase return true; } } -#endif int cf_version; if (!ctx.Read(cf_version)) @@ -129,12 +136,15 @@ class CF_ModStorageObject : CF_ModStorageBase if (!ModLoader._CF_ReadModStorage(ctx, cf_version, m_UnloadedMods, unloadedModsRead, loadedMods)) { CF_Log.Error("Failed to read modstorage for entity Type=%1, Position=%2", m_Entity.GetType(), m_Entity.GetPosition().ToString()); - return false; + break; } } m_UnloadedMods.Resize(unloadedModsRead); + if (modsRead < numMods) + return false; + m_Entity.CF_OnStoreLoad(loadedMods); // Reset the stream for 'OnStoreSave' diff --git a/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/modstorage_prepare.c b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/modstorage_prepare.c index fbc8d1be..faa4769d 100644 --- a/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/modstorage_prepare.c +++ b/JM/CF/Scripts/4_World/CommunityFramework/ModStorage/modstorage_prepare.c @@ -1,3 +1,4 @@ +#ifndef CF_MODSTORAGE modded class BuildingBase { override void OnStoreSave(ParamsWriteContext ctx) @@ -180,4 +181,5 @@ modded class AdvancedCommunication return true; } -} \ No newline at end of file +} +#endif diff --git a/JM/CF/Scripts/5_Mission/CommunityFramework/Mission/MissionServer.c b/JM/CF/Scripts/5_Mission/CommunityFramework/Mission/MissionServer.c index 417b3dd5..72cdd449 100644 --- a/JM/CF/Scripts/5_Mission/CommunityFramework/Mission/MissionServer.c +++ b/JM/CF/Scripts/5_Mission/CommunityFramework/Mission/MissionServer.c @@ -145,6 +145,10 @@ modded class MissionServer //yaw = args.Yaw; //preloadTimeout = args.PreloadTimeout; +#ifdef CF_MODSTORAGE + PlayerBase.CF_QueueIdentityId(identity.GetId()); +#endif + // must call module code before vanilla super.OnClientPrepareEvent(identity, useDB, pos, yaw, preloadTimeout); } diff --git a/JM/CF/Scripts/config.cpp b/JM/CF/Scripts/config.cpp index cced153c..a113a9fb 100644 --- a/JM/CF/Scripts/config.cpp +++ b/JM/CF/Scripts/config.cpp @@ -39,11 +39,9 @@ class CfgMods //"CF_TRACE_ENABLED", "CF_TRACE_STACK_NAME_ASSUMPTION_FIX", "CF_GHOSTICONS", - //"CF_MODSTORAGE", + "CF_MODSTORAGE", //"CF_MODSTORAGE_TEST", //"CF_MODSTORAGE_TRACE", - "CF_MODSTORAGE_DISABLE", - "CF_MODSTORAGE_MODULE_DISABLE", "CF_SURFACES", "CF_MODULES", "CF_REF_FIX",