Before reading, if you have a good method/suggestion for loading in big maps then please let me know.
I have placed a Boolean value called “mapLoaded” inside of the ServerStorage that I want to be marked to true once the map has loaded.
I want to ensure that the map is loaded before spawning players. Since there are so many assets, the player falls through the ground when they spawn in a new server.
There are roughly 70k workspace assets in total
Here are some shots of the map if youre wondering how a map can have 70k assets
I have CharacterAutoLoad disabled. The players character is loaded from a function inside of my DataManager module. This function runs but halts if mapLoaded
is false. It gives their player the attribute “waiting”
Now the concept is:
In a server script called “PlayerLoader”, I intend on calling content providers “PreloadAsync” for each asset inside of the players workspace.
& Once each asset is loaded, I set the “mapLoaded” value to true and call the character load function in the DataManager module for every waiting player.
First I attempted to preload all 70k assets and 2 issues occurred.
-
The callback function I used prints (“LoadedAssets”) but it was fired for every single asset. (Im not sure if that is how the callback function for PreloadAsync works)
-
The 2nd issue was that the script yielded forever after only printing “LoadedAssets” around 3k times.
The 2nd issue being the main issue I decided to preload the assets in chunks because I speculated that the volume was too much to handle.
- I set the max amount of collected assets to 100 just to test the problem… & each chunk size was 10.
- I also scripted timeout functionality for each PreloadAsync call. This found an error but it just returned “nil”.
Here is the code for this:
local SSS = game:GetService("ServerScriptService")
local GameModules = SSS.GameModules
local DataManager = require(GameModules.Managers.DataManager)
local function PlayerAdded(Player)
DataManager.PlayerAdded(Player)
end
local function PlayerRemoving(Player)
DataManager.PlayerRemoving(Player)
end
local function collectAssets(parent)
local assets = {}
for _, object in pairs(parent:GetDescendants()) do
if object:IsA("Model") or object:IsA("Decal") or object:IsA("Texture") or object:IsA("Sound") then
if #assets >= 100 then continue end
table.insert(assets, object)
end
end
print("Assets Collected", #assets)
return assets
end
local function onAssetsPreloaded()
print("Assets have been loaded")
--task.wait(5)
local LoadSignal = SS.LoadSignal
LoadSignal.Value = true
for _, player in pairs(Players:GetPlayers()) do
print("checked")
if player:GetAttribute("waiting") then
warn("reloading")
DataManager.Reload(player)
end
end
end
local function timeOutPreload(chunk, timeout)
local completed = false
local success, err
local function preloadAssets()
print("function called")
success, err = pcall(function()
game:GetService("ContentProvider"):PreloadAsync(chunk)
end)
completed = true
end
spawn(preloadAssets)
local startTime = tick()
while not completed and tick() - startTime < timeout do -- to timeout the preload async call
wait(0.1)
end
if not completed then
print("not completed", chunk, err) -- error here is "nil"
for _, asset in ipairs(chunk) do --doing this to attempt to pinpoint asset thats erroring
local succ, errmsg = pcall(function()
game:GetService("ContentProvider"):PreloadAsync({asset})
end)
--It yields forever here because PreloadAsync errored. So the code below never gets read
if not succ then
print("Error preloading asset:", asset:GetFullName(), errmsg)
end
end
elseif not success then
print("Error preloading asset:", err)
end
end
local function loadChunk(assets, chunkSize, waitTime, timeout)
local totalAssets = #assets
local currentIndex = 1
print(totalAssets)
while currentIndex <= totalAssets do
local chunk = {}
for i=0, chunkSize -1 do
if currentIndex + i <= totalAssets then
table.insert(chunk, assets[currentIndex + i])
else
print("broken")
break
end
end
print(#chunk)
timeOutPreload(chunk, timeout)
currentIndex += chunkSize
wait(waitTime)
end
print("AssetsLoaded")
onAssetsPreloaded()
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
print("ScriptStarted")
local assets = collectAssets(workspace)
local chunkSize = 10
local waitTime = 0.1
local timeout = 5
coroutine.wrap(function()
loadChunk(assets, chunkSize, waitTime, timeout)
end)()
I printed the chunk that was in iteration when the error occurred and it had 10 values inside of it (The correct amount). So im not sure why the error printed “nil”. I tried to find exact asset that causes the issue but like before the rest of the script yielded forever.
The last thing I tried was doing all of this on the client from a local script inside of ReplicatedFirst. However, the map still wasnt fully loaded and the first couple of players that join fall through the ground.
I dont know if this issue stems from me testing in studio or whether im using preloaded async incorrectly.
Ultimately a 15 second wait time for the 1st couple of players that join worked for the most part however my brain tells me this isnt something I should do. I have no idea why but I feel like relying on a wait shouldnt be done. I dont know where it came from (maybe because it feels too easy of a solution?)
Overall, if you have a better way of handling this or a fix for this issue, please let me know.