From 9de1ac1fb570646da0fa3f67edd6b8804f2b68c7 Mon Sep 17 00:00:00 2001 From: FoolHen Date: Tue, 18 Oct 2022 11:06:08 +0200 Subject: [PATCH 1/6] feat: rewrite mod to use custom bundles instead of json saves Removed http as new lua tables now only contain the guids of the modified vanilla objects' ReferenceObjectDatas, making the file small. Those vanilla objects are cloned and included in the custom bundle. --- .gitignore | 8 + ext/Client/__init__.lua | 6 - ext/Server/__init__.lua | 34 --- ext/Shared/Config.lua | 4 +- ext/Shared/Config.release.lua | 2 - ext/Shared/__init__.lua | 409 ++++++++-------------------------- 6 files changed, 105 insertions(+), 358 deletions(-) create mode 100644 .gitignore delete mode 100644 ext/Client/__init__.lua delete mode 100644 ext/Server/__init__.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ffea8b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.idea/ +.vscode +.vs +.vu/* +ext/Client/.vu/* +ext/Shared/.vu/* +ext/Server/.vu/* +sb \ No newline at end of file diff --git a/ext/Client/__init__.lua b/ext/Client/__init__.lua deleted file mode 100644 index c93f5e2..0000000 --- a/ext/Client/__init__.lua +++ /dev/null @@ -1,6 +0,0 @@ ----@param p_UseHttp boolean ----@param p_Mirrors string[] -NetEvents:Subscribe("CLL:HTTPINFO", function(p_UseHttp, p_Mirrors) - Config.USE_HTTP = p_UseHttp - Config.MIRRORS = p_Mirrors -end) diff --git a/ext/Server/__init__.lua b/ext/Server/__init__.lua deleted file mode 100644 index d904432..0000000 --- a/ext/Server/__init__.lua +++ /dev/null @@ -1,34 +0,0 @@ ----@param p_Command string ----@param p_Args string[] ----@return string[] -RCON:RegisterCommand("CLL.useHttp", RemoteCommandFlag.DisableAfterStartup, function(p_Command, p_Args) - local s_BooleanString = p_Args[1] - - if not s_BooleanString then - return { "OK", tostring(Config.USE_HTTP) } - end - - if s_BooleanString == "1" or s_BooleanString:lower() == "true" then - Config.USE_HTTP = true - else - Config.USE_HTTP = false - end - - return { "OK" } -end) - ----@param p_Command string ----@param p_Args string[] ----@return string[] -RCON:RegisterCommand("CLL.addMirror", RemoteCommandFlag.DisableAfterStartup, function(p_Command, p_Args) - if p_Args[1] then - table.insert(Config.MIRRORS, p_Args[1]) - end - - return { "OK" } -end) - ----@param p_Player Player -Events:Subscribe('Player:Authenticated', function(p_Player) - NetEvents:SendToLocal("CLL:HTTPINFO", p_Player, Config.USE_HTTP, Config.MIRRORS) -end) diff --git a/ext/Shared/Config.lua b/ext/Shared/Config.lua index e776544..100542b 100644 --- a/ext/Shared/Config.lua +++ b/ext/Shared/Config.lua @@ -1,6 +1,4 @@ return { - USE_HTTP = false, - MIRRORS = {}, - LOGGER_ENABLED = false, + LOGGER_ENABLED = true, CLIENT_TIMEOUT = 50 } diff --git a/ext/Shared/Config.release.lua b/ext/Shared/Config.release.lua index e776544..8c7ab4f 100644 --- a/ext/Shared/Config.release.lua +++ b/ext/Shared/Config.release.lua @@ -1,6 +1,4 @@ return { - USE_HTTP = false, - MIRRORS = {}, LOGGER_ENABLED = false, CLIENT_TIMEOUT = 50 } diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index 7f0b2b5..bf7bd59 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -1,227 +1,21 @@ ---@class CustomLevelLoaderConfig ----@field USE_HTTP boolean ----@field MIRRORS string[] ---@field LOGGER_ENABLED boolean +---@field CLIENT_TIMEOUT number Config = require "__shared/Config" local print = function(p_Message, p_IsWarning) if Config.LOGGER_ENABLED or p_IsWarning then - print(p_Message) - end -end - -local GameObjectOriginType = { - Vanilla = 1, - Custom = 2, - CustomChild = 3 -} - -local SP_TERRAIN_WORLD_PART_DATA_GUID = Guid('68D438B3-FF1B-47D7-BCB4-F484E67CA700') -local SP_TERRAIN_WORLD_PART_REFERENCE_OBJECT_DATA_GUID = Guid('93842B6D-0185-483D-9EF5-AD2B47BDABDE') - --- This is a global table that stores the save file data as a Lua table. Will be populated on-demand by --- the server via NetEvents on the client-side --- Stores LevelData DataContainer guids. -local m_PrimaryLevelGuids = {} - -local m_IndexCount = 0 -local m_LastLoadedLevelName = nil -local m_LastLoadedGameModeName = nil -local m_ObjectVariations = {} -local m_PendingVariations = {} -local m_CustomLevelData = {} - -local m_Defaults = { - variation = 0, -} - -local m_MapGameModePaths = {} - -local function PatchOriginalObject(p_Object, p_World) - if p_Object.originalRef == nil then - print("Object without original reference found, dynamic object?", true) - print(p_Object, true) - return - end - - local s_Reference = nil - - if p_Object.originalRef.partitionGuid == nil or p_Object.originalRef.partitionGuid == "nil" then -- perform a search without partitionguid - s_Reference = ResourceManager:SearchForInstanceByGuid(Guid(p_Object.originalRef.instanceGuid)) - - if s_Reference == nil then - print("Unable to find original reference: " .. p_Object.originalRef.instanceGuid, true) - return - end - else - s_Reference = ResourceManager:FindInstanceByGuid(Guid(p_Object.originalRef.partitionGuid), Guid(p_Object.originalRef.instanceGuid)) - - if s_Reference == nil then - print("Unable to find original reference: " .. p_Object.originalRef.instanceGuid .. " in partition " .. p_Object.originalRef.partitionGuid, true) - return - end - end - - s_Reference = _G[s_Reference.typeInfo.name](s_Reference) - s_Reference:MakeWritable() - - if p_Object.isDeleted then - s_Reference.excluded = true - end - - if p_Object.localTransform then - s_Reference.blueprintTransform = LinearTransform(p_Object.localTransform) -- LinearTransform(p_Object.localTransform) - else - s_Reference.blueprintTransform = LinearTransform(p_Object.transform) -- LinearTransform(p_Object.transform) - end -end - -local function AddCustomObject(p_Object, p_World, p_RegistryContainer) - local s_Blueprint = ResourceManager:FindInstanceByGuid(Guid(p_Object.blueprintCtrRef.partitionGuid), Guid(p_Object.blueprintCtrRef.instanceGuid)) - - if s_Blueprint == nil then - print('Cannot find blueprint with guid ' .. tostring(p_Object.blueprintCtrRef.instanceGuid), true) - return - end - - -- Filter BangerEntityData. - if s_Blueprint:Is('ObjectBlueprint') then - local s_ObjectBlueprint = ObjectBlueprint(s_Blueprint) - - if s_ObjectBlueprint.object and s_ObjectBlueprint.object:Is('BangerEntityData') then - return + if p_IsWarning then + p_Message = 'WARNING: ' .. p_Message end - end - - local s_Reference - - if s_Blueprint:Is('EffectBlueprint') then - s_Reference = EffectReferenceObjectData() - s_Reference.autoStart = true - else - s_Reference = ReferenceObjectData() - end - - p_RegistryContainer.referenceObjectRegistry:add(s_Reference) - if p_Object.localTransform then - s_Reference.blueprintTransform = LinearTransform(p_Object.localTransform) - else - s_Reference.blueprintTransform = LinearTransform(p_Object.transform) - end - - --print("AddCustomObject: " .. p_Object.transform) - s_Reference.blueprint = Blueprint(s_Blueprint) - -- s_Reference.blueprint:MakeWritable() - local s_Variation = p_Object.variation or m_Defaults.variation - if m_ObjectVariations[s_Variation] == nil then - m_PendingVariations[s_Variation] = s_Reference - else - s_Reference.objectVariation = m_ObjectVariations[s_Variation] + print(p_Message) end - - s_Reference.indexInBlueprint = #p_World.objects + m_IndexCount + 1 - s_Reference.isEventConnectionTarget = Realm.Realm_None - s_Reference.isPropertyConnectionTarget = Realm.Realm_None - s_Reference.castSunShadowEnable = true - s_Reference.excluded = false - - p_World.objects:add(s_Reference) end -local function CreateWorldPart(p_PrimaryLevel, p_RegistryContainer) - local s_World = WorldPartData(SP_TERRAIN_WORLD_PART_DATA_GUID) - p_RegistryContainer.blueprintRegistry:add(s_World) - - --find index - for _, l_Object in pairs(p_PrimaryLevel.objects) do - if l_Object:Is('WorldPartReferenceObjectData') then - local l_RefObjectData = WorldPartReferenceObjectData(l_Object) - - if l_RefObjectData.blueprint:Is('WorldPartData') then - local s_WorldPart = WorldPartData(l_RefObjectData.blueprint) - - if #s_WorldPart.objects ~= 0 then - local s_ROD = s_WorldPart.objects[#s_WorldPart.objects] -- last one in array - - if s_ROD and s_ROD:Is('ReferenceObjectData') then - s_ROD = ReferenceObjectData(s_ROD) - - if s_ROD.indexInBlueprint > m_IndexCount then - m_IndexCount = s_ROD.indexInBlueprint - end - end - end - end - end - end - -- m_IndexCount = 30000 - print('Index count is: ' .. tostring(m_IndexCount)) - - for _, l_Object in pairs(m_CustomLevelData.data) do - if l_Object.origin == GameObjectOriginType.Custom then - if not m_CustomLevelData.vanillaOnly then - AddCustomObject(l_Object, s_World, p_RegistryContainer) - end - elseif l_Object.origin == GameObjectOriginType.Vanilla then - PatchOriginalObject(l_Object, s_World) - end - -- TODO handle CustomChild - end - - m_LastLoadedLevelName = SharedUtils:GetLevelName() - m_LastLoadedGameModeName = SharedUtils:GetCurrentGameMode() - - local s_WorldPartReference = WorldPartReferenceObjectData(SP_TERRAIN_WORLD_PART_REFERENCE_OBJECT_DATA_GUID) - s_WorldPartReference.blueprint = s_World - - s_WorldPartReference.isEventConnectionTarget = Realm.Realm_None - s_WorldPartReference.isPropertyConnectionTarget = Realm.Realm_None - s_WorldPartReference.excluded = false - - return s_WorldPartReference -end - ----@param p_FileName string ----@return nil|string @json table -local function GetCustomLevelFromHttp(p_FileName) - if #Config.MIRRORS == 0 then - return nil - end - - local s_HttpOptions = HttpOptions({}, 20) - --ignore cert for wine users - s_HttpOptions.verifyCertificate = false - - for _, l_Address in ipairs(Config.MIRRORS) do - local s_StartTime = SharedUtils:GetTime() - - if m_MapGameModePaths[p_FileName] then - p_FileName = m_MapGameModePaths[p_FileName] - print("Adjusted file name to " .. p_FileName) - end - - local s_Path = l_Address .. p_FileName .. ".json" - local s_HttpResponse = Net:GetHTTP(s_Path, s_HttpOptions) - - local s_Duration = SharedUtils:GetTime() - s_StartTime - - if s_Duration > 25.0 then - print("It took " .. s_Duration .. " seconds to download the custom level json", true) - end - - if not s_HttpResponse then - print("Received no response from " .. s_Path, true) - elseif s_HttpResponse.status ~= 200 then - print("Received http status " .. tostring(s_HttpResponse.status) .. " from " .. s_Path, true) - else - return s_HttpResponse.body - end - end - - print('Couldn\'t find custom level data for: ' .. p_FileName) - return nil -end +---@type table? +local m_CustomLevelData = {} +local m_LazyLoadedCount = 0 ---@param p_LevelName string ---@param p_GameModeName string @@ -232,23 +26,15 @@ local function GetCustomLevel(p_LevelName, p_GameModeName) local s_PresetJson - if Config.USE_HTTP then - s_PresetJson = GetCustomLevelFromHttp(s_FileName) - - if not s_PresetJson then - return nil - end - else - local s_Path = '__shared/Levels/' .. p_LevelName .. '/' .. s_FileName + local s_Path = '__shared/Levels/' .. p_LevelName .. '/' .. s_FileName - local s_Ok - s_Ok, s_PresetJson = pcall(require, s_Path) - s_PresetJson = s_Ok and s_PresetJson or nil + local s_Ok + s_Ok, s_PresetJson = pcall(require, s_Path) + s_PresetJson = s_Ok and s_PresetJson or nil - if not s_PresetJson then - print('Couldn\'t find custom level data in path ' .. s_Path) - return nil - end + if not s_PresetJson then + print('Couldn\'t find custom level data in path ' .. s_Path) + return nil end local s_Preset = json.decode(s_PresetJson) @@ -263,124 +49,126 @@ local function GetCustomLevel(p_LevelName, p_GameModeName) return s_Preset end +Hooks:Install('ResourceManager:LoadBundles', 100, function(p_Hook, p_Bundles, p_Compartment) + print(p_Compartment) + print(p_Bundles[1]) +end) + -- nº 1 in calling order Events:Subscribe('Level:LoadResources', function() print("-----Loading resources") - m_ObjectVariations = {} - m_PendingVariations = {} + print("MountSuperBundle: CustomLevel/XP3_Shield/XP3_Shield") + ResourceManager:MountSuperBundle('CustomLevel/XP3_Shield/XP3_Shield') m_CustomLevelData = GetCustomLevel(SharedUtils:GetLevelName(), SharedUtils:GetCurrentGameMode()) -end) --- nº 2 in calling order -Events:Subscribe('Partition:Loaded', function(p_Partition) - if not m_CustomLevelData then - return - end + for l_PartitionGuid, l_Instances in pairs(m_CustomLevelData) do + for _, l_InstanceGuid in ipairs(l_Instances) do + -- print(tostring(l_PartitionGuid) .. ' - ' .. tostring(l_InstanceGuid)) - if p_Partition == nil then - return + ResourceManager:RegisterInstanceLoadHandlerOnce(Guid(l_PartitionGuid), Guid(l_InstanceGuid), function(p_Instance) + p_Instance = _G[p_Instance.typeInfo.name](p_Instance) + p_Instance:MakeWritable() + p_Instance.excluded = true + end) + end end +end) - local s_PrimaryInstance = p_Partition.primaryInstance - - if s_PrimaryInstance == nil then - print('Instance is null? ' .. p_Partition.name, true) +---Patches the level, adding a SubWorldReferenceObjectData to the level that references the SubWorld in the custom bundle +local function _PatchLevel() + local s_Data = LevelData(ResourceManager:SearchForDataContainer(SharedUtils:GetLevelName())) + s_Data:MakeWritable() + + local s_SWROD = SubWorldReferenceObjectData(Guid('6a724d44-4efd-4f7e-9249-2230121d7ecc')) + s_SWROD.bundleName = 'CustomLevel/XP3_Shield/ConquestLarge0' + s_SWROD.blueprintTransform = LinearTransform() + s_SWROD.blueprint = nil + s_SWROD.objectVariation = nil + s_SWROD.streamRealm = StreamRealm.StreamRealm_Both + s_SWROD.castSunShadowEnable = true + s_SWROD.excluded = false + s_SWROD.inclusionSettings = nil + s_SWROD.autoLoad = true + s_SWROD.isWin32SubLevel = true + s_SWROD.isXenonSubLevel = true + s_SWROD.isPs3SubLevel = true + s_SWROD.isEventConnectionTarget = 2 + s_SWROD.isPropertyConnectionTarget = 3 + + local s_Partition = s_Data.partition + + if not s_Partition then + print('Partition was nil', true) return end - -- if l_Instance:Is("Blueprint") then - --print("-------"..Blueprint(l_Instance).name) - -- end + s_Partition:AddInstance(s_SWROD) - if s_PrimaryInstance.typeInfo.name == "LevelData" then - local s_Instance = LevelData(s_PrimaryInstance) + local s_HighestIndexInPartition = 0 - if s_Instance.name == SharedUtils:GetLevelName() then - print("----Registering PrimaryLevel guids") - s_Instance:MakeWritable() + for _, l_Instance in pairs(s_Partition.instances) do + if l_Instance:Is("GameObjectData") then + l_Instance = GameObjectData(l_Instance) - m_PrimaryLevelGuids = { - instanceGuid = s_Instance.instanceGuid, - partitionGuid = s_Instance.partitionGuid - } - end - elseif s_PrimaryInstance:Is('ObjectVariation') then - -- Store all variations in a map. - local s_Variation = ObjectVariation(s_PrimaryInstance) - m_ObjectVariations[s_Variation.nameHash] = s_Variation - - if m_PendingVariations[s_Variation.nameHash] ~= nil then - for _, l_Object in pairs(m_PendingVariations[s_Variation.nameHash]) do - l_Object.objectVariation = s_Variation + if l_Instance.indexInBlueprint > s_HighestIndexInPartition and l_Instance.indexInBlueprint ~= 65535 then + s_HighestIndexInPartition = l_Instance.indexInBlueprint end - - m_PendingVariations[s_Variation.nameHash] = nil end end -end) - --- nº 3 in calling order -Events:Subscribe('Level:LoadingInfo', function(p_Info) - if p_Info == "Registering entity resources" then - print("-----Loading Info - Registering entity resources") - - if not m_CustomLevelData then - print("No custom level specified.") - return - end - - if m_PrimaryLevelGuids == nil then - print("m_PrimaryLevelGuids is nil, something went wrong", true) - return - end - local s_PrimaryLevel = ResourceManager:FindInstanceByGuid(m_PrimaryLevelGuids.partitionGuid, m_PrimaryLevelGuids.instanceGuid) + s_SWROD.indexInBlueprint = s_HighestIndexInPartition + 1 - if s_PrimaryLevel == nil then - print("Couldn\'t find PrimaryLevel DataContainer, aborting", true) - return - end + if s_Data.registryContainer ~= nil then + s_Data.registryContainer:MakeWritable() + local s_Registry = RegistryContainer(s_Data.registryContainer) + s_Registry.referenceObjectRegistry:add(s_SWROD) + end - s_PrimaryLevel = LevelData(s_PrimaryLevel) + local s_LinkConnection = LinkConnection() + s_LinkConnection.target = s_SWROD - if m_LastLoadedLevelName == SharedUtils:GetLevelName() and m_LastLoadedGameModeName == SharedUtils:GetCurrentGameMode() then - print('Same map and gamemode loading, skipping') - return - end + s_Data.linkConnections:add(s_LinkConnection) + s_Data.objects:add(s_SWROD) + print('Patched level') +end - print("Patching level") - local s_RegistryContainer = s_PrimaryLevel.registryContainer +Events:Subscribe('Partition:Loaded', function(p_Partition) + local s_LevelName = SharedUtils:GetLevelName() - if s_RegistryContainer == nil then - print('No registryContainer found, this shouldn\'t happen', true) - end + if not s_LevelName then return end - s_RegistryContainer = RegistryContainer(s_RegistryContainer) - s_RegistryContainer:MakeWritable() + if p_Partition.name == s_LevelName:lower() then + print('Patching level') - local s_WorldPartReference = CreateWorldPart(s_PrimaryLevel, s_RegistryContainer) + local s_LevelData = LevelData(p_Partition.primaryInstance) - s_WorldPartReference.indexInBlueprint = #s_PrimaryLevel.objects + for _, l_Object in ipairs(s_LevelData.objects) do + l_Object = _G[l_Object.typeInfo.name](l_Object) - s_PrimaryLevel.objects:add(s_WorldPartReference) + if l_Object.blueprint and l_Object.blueprint.isLazyLoaded then + m_LazyLoadedCount = m_LazyLoadedCount + 1 + print("LazyLoadedCount " .. m_LazyLoadedCount) + + l_Object.blueprint:RegisterLoadHandlerOnce(function (p_Instance) + m_LazyLoadedCount = m_LazyLoadedCount - 1 + if m_LazyLoadedCount == 0 then _PatchLevel() end + end) + end + end - s_RegistryContainer.referenceObjectRegistry:add(s_WorldPartReference) - print('Level patched') + if m_LazyLoadedCount == 0 then _PatchLevel() end end end) -- Remove all DataContainer references and reset vars Events:Subscribe('Level:Destroy', function() - m_ObjectVariations = {} - m_PendingVariations = {} - m_IndexCount = 0 - -- TODO: remove all custom objects from level registry and leveldata if next round is -- the same map but a different save, once that is implemented. If it's a different map -- there is no need to clear anything, as the leveldata will be unloaded and a new one loaded end) +-- Increase timeout ResourceManager:RegisterInstanceLoadHandler(Guid('C4DCACFF-ED8F-BC87-F647-0BC8ACE0D9B4'), Guid('B479A8FA-67FF-8825-9421-B31DE95B551A'), function(p_Instance) p_Instance = ClientSettings(p_Instance) p_Instance:MakeWritable() @@ -397,9 +185,4 @@ ResourceManager:RegisterInstanceLoadHandler(Guid('C4DCACFF-ED8F-BC87-F647-0BC8AC p_Instance.ingameTimeout = Config.CLIENT_TIMEOUT p_Instance.timeoutTime = Config.CLIENT_TIMEOUT print("Changed ServerSettings") -end) - ----@param p_MapGameModePaths table -Events:Subscribe("CLL:MapGameModePaths", function(p_MapGameModePaths) - m_MapGameModePaths = p_MapGameModePaths -end) +end) \ No newline at end of file From 48cd5f4fcca40904699616be48a67302a3354e7c Mon Sep 17 00:00:00 2001 From: FoolHen Date: Tue, 18 Oct 2022 19:00:16 +0200 Subject: [PATCH 2/6] fix: fix hardcoded bundle and superbundle names, now they are dynamic --- ext/Shared/__init__.lua | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index bf7bd59..2ed56ec 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -3,6 +3,17 @@ ---@field CLIENT_TIMEOUT number Config = require "__shared/Config" +BUNDLE_PREFIX = 'CustomLevels' + +function string:split(sep) + sep = sep or ":" + local fields = {} + local pattern = string.format("([^%s]+)", sep) + ---@diagnostic disable-next-line: discard-returns + self:gsub(pattern, function(c) fields[#fields + 1] = c end) + return fields +end + local print = function(p_Message, p_IsWarning) if Config.LOGGER_ENABLED or p_IsWarning then if p_IsWarning then @@ -55,12 +66,14 @@ Hooks:Install('ResourceManager:LoadBundles', 100, function(p_Hook, p_Bundles, p_ end) -- nº 1 in calling order -Events:Subscribe('Level:LoadResources', function() +Events:Subscribe('Level:LoadResources', function(p_LevelName, p_GameMode, p_IsDedicatedServer) print("-----Loading resources") - print("MountSuperBundle: CustomLevel/XP3_Shield/XP3_Shield") - ResourceManager:MountSuperBundle('CustomLevel/XP3_Shield/XP3_Shield') - m_CustomLevelData = GetCustomLevel(SharedUtils:GetLevelName(), SharedUtils:GetCurrentGameMode()) + local s_SuperBundleName = string.gsub(p_LevelName, 'Levels', BUNDLE_PREFIX) + print("MountSuperBundle: " .. s_SuperBundleName) + ResourceManager:MountSuperBundle(s_SuperBundleName) + + m_CustomLevelData = GetCustomLevel(p_LevelName, p_GameMode) for l_PartitionGuid, l_Instances in pairs(m_CustomLevelData) do for _, l_InstanceGuid in ipairs(l_Instances) do @@ -76,12 +89,14 @@ Events:Subscribe('Level:LoadResources', function() end) ---Patches the level, adding a SubWorldReferenceObjectData to the level that references the SubWorld in the custom bundle -local function _PatchLevel() +local function _PatchLevel(p_LevelName) local s_Data = LevelData(ResourceManager:SearchForDataContainer(SharedUtils:GetLevelName())) s_Data:MakeWritable() - + local s_SWROD = SubWorldReferenceObjectData(Guid('6a724d44-4efd-4f7e-9249-2230121d7ecc')) - s_SWROD.bundleName = 'CustomLevel/XP3_Shield/ConquestLarge0' + local s_SplitLevelName = p_LevelName:split('/') + + s_SWROD.bundleName = BUNDLE_PREFIX .. '/' .. s_SplitLevelName[2] .. '/' .. SharedUtils:GetCurrentGameMode() s_SWROD.blueprintTransform = LinearTransform() s_SWROD.blueprint = nil s_SWROD.objectVariation = nil @@ -152,12 +167,12 @@ Events:Subscribe('Partition:Loaded', function(p_Partition) l_Object.blueprint:RegisterLoadHandlerOnce(function (p_Instance) m_LazyLoadedCount = m_LazyLoadedCount - 1 - if m_LazyLoadedCount == 0 then _PatchLevel() end + if m_LazyLoadedCount == 0 then _PatchLevel(s_LevelName) end end) end end - if m_LazyLoadedCount == 0 then _PatchLevel() end + if m_LazyLoadedCount == 0 then _PatchLevel(s_LevelName) end end end) From 5765421950250b643453193d41ead0b5c8dfa450 Mon Sep 17 00:00:00 2001 From: FoolHen Date: Wed, 19 Oct 2022 15:45:41 +0200 Subject: [PATCH 3/6] refactor: simplify level name parsing --- ext/Shared/__init__.lua | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index 2ed56ec..92e5d77 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -5,15 +5,6 @@ Config = require "__shared/Config" BUNDLE_PREFIX = 'CustomLevels' -function string:split(sep) - sep = sep or ":" - local fields = {} - local pattern = string.format("([^%s]+)", sep) - ---@diagnostic disable-next-line: discard-returns - self:gsub(pattern, function(c) fields[#fields + 1] = c end) - return fields -end - local print = function(p_Message, p_IsWarning) if Config.LOGGER_ENABLED or p_IsWarning then if p_IsWarning then @@ -94,9 +85,8 @@ local function _PatchLevel(p_LevelName) s_Data:MakeWritable() local s_SWROD = SubWorldReferenceObjectData(Guid('6a724d44-4efd-4f7e-9249-2230121d7ecc')) - local s_SplitLevelName = p_LevelName:split('/') - s_SWROD.bundleName = BUNDLE_PREFIX .. '/' .. s_SplitLevelName[2] .. '/' .. SharedUtils:GetCurrentGameMode() + s_SWROD.bundleName = BUNDLE_PREFIX .. '/' .. p_LevelName:gsub(".*/", "") .. '/' .. SharedUtils:GetCurrentGameMode() s_SWROD.blueprintTransform = LinearTransform() s_SWROD.blueprint = nil s_SWROD.objectVariation = nil From 7e1171be32e9a43a0f285db7a302b5af78827197 Mon Sep 17 00:00:00 2001 From: FoolHen Date: Mon, 21 Nov 2022 22:59:57 +0100 Subject: [PATCH 4/6] refactor: fix some pr issus --- ext/Shared/__init__.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index 92e5d77..c0e34b5 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -66,6 +66,11 @@ Events:Subscribe('Level:LoadResources', function(p_LevelName, p_GameMode, p_IsDe m_CustomLevelData = GetCustomLevel(p_LevelName, p_GameMode) + if m_CustomLevelData == nil then + print('Could not get the level, level name: ' .. p_LevelName .. ', gamemode: ' .. p_GameMode, true) + return + end + for l_PartitionGuid, l_Instances in pairs(m_CustomLevelData) do for _, l_InstanceGuid in ipairs(l_Instances) do -- print(tostring(l_PartitionGuid) .. ' - ' .. tostring(l_InstanceGuid)) @@ -112,7 +117,7 @@ local function _PatchLevel(p_LevelName) local s_HighestIndexInPartition = 0 - for _, l_Instance in pairs(s_Partition.instances) do + for _, l_Instance in ipairs(s_Partition.instances) do if l_Instance:Is("GameObjectData") then l_Instance = GameObjectData(l_Instance) @@ -190,4 +195,5 @@ ResourceManager:RegisterInstanceLoadHandler(Guid('C4DCACFF-ED8F-BC87-F647-0BC8AC p_Instance.ingameTimeout = Config.CLIENT_TIMEOUT p_Instance.timeoutTime = Config.CLIENT_TIMEOUT print("Changed ServerSettings") -end) \ No newline at end of file +end) + From 7d33bef94a780d10dcf6615dce4912315d9b1ef8 Mon Sep 17 00:00:00 2001 From: FoolHen Date: Wed, 30 Nov 2022 20:23:51 +0100 Subject: [PATCH 5/6] refactor: refactor based on pr feedback --- ext/Shared/__init__.lua | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index c0e34b5..3028387 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -84,6 +84,22 @@ Events:Subscribe('Level:LoadResources', function(p_LevelName, p_GameMode, p_IsDe end end) +local function _GetHighestIndexInPartition(s_Partition) + local s_HighestIndexInPartition = 0 + + for _, l_Instance in ipairs(s_Partition.instances) do + if l_Instance:Is("GameObjectData") then + l_Instance = GameObjectData(l_Instance) + + if l_Instance.indexInBlueprint > s_HighestIndexInPartition and l_Instance.indexInBlueprint ~= 65535 then + s_HighestIndexInPartition = l_Instance.indexInBlueprint + end + end + end + + return s_HighestIndexInPartition +end + ---Patches the level, adding a SubWorldReferenceObjectData to the level that references the SubWorld in the custom bundle local function _PatchLevel(p_LevelName) local s_Data = LevelData(ResourceManager:SearchForDataContainer(SharedUtils:GetLevelName())) @@ -115,18 +131,7 @@ local function _PatchLevel(p_LevelName) s_Partition:AddInstance(s_SWROD) - local s_HighestIndexInPartition = 0 - - for _, l_Instance in ipairs(s_Partition.instances) do - if l_Instance:Is("GameObjectData") then - l_Instance = GameObjectData(l_Instance) - - if l_Instance.indexInBlueprint > s_HighestIndexInPartition and l_Instance.indexInBlueprint ~= 65535 then - s_HighestIndexInPartition = l_Instance.indexInBlueprint - end - end - end - + local s_HighestIndexInPartition = _GetHighestIndexInPartition(s_Partition) s_SWROD.indexInBlueprint = s_HighestIndexInPartition + 1 if s_Data.registryContainer ~= nil then From f2e35e8753f049334c3667044d774442226a29e9 Mon Sep 17 00:00:00 2001 From: FoolHen Date: Thu, 8 Dec 2022 20:21:23 +0100 Subject: [PATCH 6/6] feature(custom bundles): add support fo the new custom gamemodes --- ext/Shared/Levels/BundlesMap.lua | 28 +++++++++++++ ext/Shared/__init__.lua | 67 ++++++++++++++++++++++---------- mod.json | 14 ++++++- 3 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 ext/Shared/Levels/BundlesMap.lua diff --git a/ext/Shared/Levels/BundlesMap.lua b/ext/Shared/Levels/BundlesMap.lua new file mode 100644 index 0000000..bc0ca43 --- /dev/null +++ b/ext/Shared/Levels/BundlesMap.lua @@ -0,0 +1,28 @@ +return [[ +{ + "XP1_001/AAS_Standard": "XP1_001/ConquestAssaultLarge0", + "XP1_001/AAS_Alternative": "XP1_001/ConquestAssaultLarge0", + "XP1_001/SKR_Standard": "XP1_001/ConquestAssaultLarge0", + "XP1_002/AAS_Standard": "XP1_002/ConquestLarge0", + "XP1_002/AAS_Alternative": "XP1_002/ConquestLarge0", + "XP1_002/SKR_Standard": "XP1_002/ConquestLarge0", + "XP3_Shield/AAS_Standard": "XP3_Shield/ConquestLarge0", + "XP3_Shield/AAS_Alternative": "XP3_Shield/ConquestLarge0", + "XP3_Shield/SKR_Standard": "XP3_Shield/ConquestLarge0", + "XP3_Valley/AAS_Standard": "XP3_Valley/ConquestLarge0", + "XP3_Valley/AAS_Alternative": "XP3_Valley/ConquestLarge0", + "XP3_Valley/SKR_Standard": "XP3_Valley/ConquestLarge0", + "XP4_Parl/AAS_Standard": "XP4_Parl/ConquestLarge0", + "XP4_Parl/AAS_Alternative": "XP4_Parl/ConquestLarge0", + "XP4_Parl/SKR_Standard": "XP4_Parl/ConquestLarge0", + "XP5_001/AAS_Standard": "XP5_001/ConquestLarge0", + "XP5_001/AAS_Alternative": "XP5_001/ConquestLarge0", + "XP5_001/SKR_Standard": "XP5_001/ConquestLarge0", + "XP5_003/AAS_Standard": "XP5_003/ConquestLarge0", + "XP5_003/AAS_Alternative": "XP5_003/ConquestLarge0", + "XP5_003/SKR_Standard": "XP5_003/ConquestLarge0", + "XP5_004/AAS_Standard": "XP5_004/ConquestLarge0", + "XP5_004/AAS_Alternative": "XP5_004/ConquestLarge0", + "XP5_004/SKR_Standard": "XP5_004/ConquestLarge0" +} +]] \ No newline at end of file diff --git a/ext/Shared/__init__.lua b/ext/Shared/__init__.lua index 3028387..4b418c9 100644 --- a/ext/Shared/__init__.lua +++ b/ext/Shared/__init__.lua @@ -16,39 +16,66 @@ local print = function(p_Message, p_IsWarning) end ---@type table? -local m_CustomLevelData = {} +local m_LevelRODMap = {} local m_LazyLoadedCount = 0 +---Finds and returns the bundle name associated with the level and gamemode (@p_Path) loaded in the +---case that there is a gamemode map file, and in case the map file does not exist or the path does +---not have an entry it returns @p_Path +---@param p_Path string +---@return string +local function GetBundlePath(p_Path) + local _, s_BundlesMapJson = pcall(require, '__shared/Levels/BundlesMap.lua') + + if not s_BundlesMapJson then + return p_Path + end + + local s_BundlesMap = json.decode(s_BundlesMapJson) + + -- Replace spaces in case of custom gamemodes with spaces in their names + p_Path = p_Path:gsub(' ', '_') + + + if s_BundlesMap and s_BundlesMap[p_Path] then + print('Found custom bundle ' .. s_BundlesMap[p_Path] .. ' for gamemode ' .. p_Path .. ' in bundle map file') + return s_BundlesMap[p_Path] + end + + return p_Path +end + ---@param p_LevelName string ---@param p_GameModeName string ---@return table|nil -local function GetCustomLevel(p_LevelName, p_GameModeName) +local function GetLevelRODMap(p_LevelName, p_GameModeName) p_LevelName = p_LevelName:gsub(".*/", "") - local s_FileName = p_LevelName .. '_' .. p_GameModeName + + local s_FileName = GetBundlePath(p_LevelName .. '/' .. p_GameModeName) - local s_PresetJson + -- File name uses _ instead of / + s_FileName = s_FileName:gsub('/', '_') local s_Path = '__shared/Levels/' .. p_LevelName .. '/' .. s_FileName - local s_Ok - s_Ok, s_PresetJson = pcall(require, s_Path) - s_PresetJson = s_Ok and s_PresetJson or nil + local s_Ok, s_LevelRODMapJson = pcall(require, s_Path) + s_LevelRODMapJson = s_Ok and s_LevelRODMapJson or nil - if not s_PresetJson then - print('Couldn\'t find custom level data in path ' .. s_Path) + if not s_LevelRODMapJson then + print('Couldn\'t find ROD map file in path ' .. s_Path) return nil end - local s_Preset = json.decode(s_PresetJson) + local s_LevelRODMap = json.decode(s_LevelRODMapJson) - if not s_Preset then - error('Couldn\'t decode json preset') + if not s_LevelRODMap then + error('Couldn\'t decode json ROD map') return nil end - print("Preset found for Level: " .. p_LevelName .. " - GameMode: " .. p_GameModeName) + print("ROD map file found for Level: " .. p_LevelName .. " - GameMode: " .. p_GameModeName) - return s_Preset + return s_LevelRODMap end Hooks:Install('ResourceManager:LoadBundles', 100, function(p_Hook, p_Bundles, p_Compartment) @@ -64,17 +91,15 @@ Events:Subscribe('Level:LoadResources', function(p_LevelName, p_GameMode, p_IsDe print("MountSuperBundle: " .. s_SuperBundleName) ResourceManager:MountSuperBundle(s_SuperBundleName) - m_CustomLevelData = GetCustomLevel(p_LevelName, p_GameMode) + m_LevelRODMap = GetLevelRODMap(p_LevelName, p_GameMode) - if m_CustomLevelData == nil then + if m_LevelRODMap == nil then print('Could not get the level, level name: ' .. p_LevelName .. ', gamemode: ' .. p_GameMode, true) return end - for l_PartitionGuid, l_Instances in pairs(m_CustomLevelData) do + for l_PartitionGuid, l_Instances in pairs(m_LevelRODMap) do for _, l_InstanceGuid in ipairs(l_Instances) do - -- print(tostring(l_PartitionGuid) .. ' - ' .. tostring(l_InstanceGuid)) - ResourceManager:RegisterInstanceLoadHandlerOnce(Guid(l_PartitionGuid), Guid(l_InstanceGuid), function(p_Instance) p_Instance = _G[p_Instance.typeInfo.name](p_Instance) p_Instance:MakeWritable() @@ -106,8 +131,10 @@ local function _PatchLevel(p_LevelName) s_Data:MakeWritable() local s_SWROD = SubWorldReferenceObjectData(Guid('6a724d44-4efd-4f7e-9249-2230121d7ecc')) + + local s_Path = GetBundlePath(p_LevelName:gsub(".*/", "") .. '/' .. SharedUtils:GetCurrentGameMode()) - s_SWROD.bundleName = BUNDLE_PREFIX .. '/' .. p_LevelName:gsub(".*/", "") .. '/' .. SharedUtils:GetCurrentGameMode() + s_SWROD.bundleName = BUNDLE_PREFIX .. '/' .. s_Path s_SWROD.blueprintTransform = LinearTransform() s_SWROD.blueprint = nil s_SWROD.objectVariation = nil diff --git a/mod.json b/mod.json index 1a564d3..b7e9ec5 100644 --- a/mod.json +++ b/mod.json @@ -7,6 +7,16 @@ "HasWebUI": false, "HasVeniceEXT": true, "Dependencies": { - "veniceext": "^1.1.0" - } + "veniceext": "^1.1.0" + }, + "Superbundles": [ + "Win32/CustomLevels/XP1_001/XP1_001", + "Win32/CustomLevels/XP1_002/XP1_002", + "Win32/CustomLevels/XP3_Shield/XP3_Shield", + "Win32/CustomLevels/XP3_Valley/XP3_Valley", + "Win32/CustomLevels/XP4_Parl/XP4_Parl", + "Win32/CustomLevels/XP5_001/XP5_001", + "Win32/CustomLevels/XP5_003/XP5_003", + "Win32/CustomLevels/XP5_004/XP5_004" + ] }