Do you have StreamingEnabled turned on?
PreloadAsync is meant to load images, sounds, probably meshes as well
Do you have StreamingEnabled turned on?
PreloadAsync is meant to load images, sounds, probably meshes as well
StreamingEnabled = false
It’s quite a small map (3000 parts)
are you using meshparts or the roblox parts?
If you are using the roblox parts, they are already stored inside the roblox files, so they don’t have to be loaded. It might be that the client is just not rendering it perhaps?
mainly meshparts
normal parts as well but mainly meshparts
Try loading each asset individually.
local assets = workspace:GetDescendants()
for i = 1, #assets do
local asset = assets[i]
ContentProvider:PreloadAsync({asset})
end
The solution to this issue is more complicated than what the previous answers elude to, and has nothing to do with PreloadAsync
except timing.
The map loads into the workspace on the server, then replicated to the client. After you load the assets on the client, you need to wait for a remote event on the client to pause execution until the server fires that event after the map is loaded. Then when the GUI is destroyed, the map appears to be fully loaded.
Server
-- ******** Services
local replicatedStorage = game:GetService("ReplicatedStorage")
-- ******** Local Variables
local eventFolder = replicatedStorage:FindFirstChild("Events")
local eventLoading = eventFolder:FindFirstChild("LoadCharacter")
local eventMapLoad = eventFolder:FindFirstChild("MapLoaded")
local mapLoaded = false
-- ******** Events
-- Called when the client has finished loading assets and is ready to load
-- the player's character model.
eventLoading.OnServerEvent:Connect(function(player)
while mapLoaded == false do
task.wait(0.1)
end
eventMapLoad:FireClient(player, mapLoaded)
player:LoadCharacter()
end)
Client
-- ******** Services
local replicatedFirst = game:GetService("ReplicatedFirst")
local replicatedStorage = game:GetService("ReplicatedStorage")
local playerService = game:GetService("Players")
-- ******** Local Variables
local mapLoaded = false
local localPlayer = playerService.LocalPlayer
local playerGui = localPlayer:WaitForChild("PlayerGui")
local loadingScreen = replicatedFirst:WaitForChild("LoadingScreenGui")
local eventFolder = replicatedStorage:WaitForChild("Events")
local eventLoading = eventFolder:WaitForChild("LoadCharacter")
local eventMap = eventFolder:WaitForChild("MapLoaded")
-- ******** Events
-- Called when the server completes loading the game map.
replicatedStorage.MapLoaded:Connect(function(loaded)
mapLoaded = loaded
end)
-- ******** Run
local clientGui = loadingScreen:Clone()
clientGui.Parent = playerGui
replicatedFirst:RemoveDefaultLoadingScreen()
-- Perform PreloadAsync
eventLoading:FireServer()
while mapLoaded == false do
task.wait(0.1)
end
clientGui:Destroy()
Something like that should be what you need. When PreloadAsync
has completed, it will send a request to the server indicating that it’s ready to proceed. On the server, the response is held until the mapLoaded
variable is true. mapLoaded
is set to true when the code that loads the map is done. When that happens, all the clients that have signaled that they are done loading assets are then responded to. When the clients receive the new value, they remove the loading screen Gui.
For clients that connect afterwards, they proceed immediately since the server has long completed the map loading. If there’s other conditions to check to see if the clients can be loaded, those can also be checked for too. You could probably use a remote function for this, but I prefer using remote events. If you’re using Parallel LUA
, on the server, you can have the busy-wait loop run in parallel mode so as to free up the main thread, but the script has to be running under an actor for that to work.
I’m confused as to what’s setting mapLoaded
to true on the server? Seems like it will wait infinitely.
I probably should have made that more clear. The routine that’s loading the map does that. It may be better to make that a global variable with the _G property so no matter what script your map building routines are in, it can be accessed.
Are saying that the problem is that the client has loaded which is why the ContentProvider:PreloadAsync
stopped yielding. But the server hasn’t fully loaded? I don’t understand because I thought it loads to the client being replicated from the server so how is it possible that the client has loaded in without the server having loaded in?
More or less. PreloadAsync
doesn’t load in the map to the client. That’s done through the normal replication process. PreloadAsync
just loads in the assets that are used in the map (images/textures, sounds, and meshes). If the asset load completes before 1) the server has finished building the map, or 2) the map hasn’t been replicated to the client yet, when the preload is finished and the loading gui goes away, visually the map hasn’t finished loading in, from the client’s perspective. The issue is more apparent when the server is first spun up since during server startup, as a lot of things are going on at the same time.
In other words, PreloadAsync
is the what as in what’s being used. Replication is the how as how those assets are being used.
Ideally, both the asset preload and the replication happen at the same time, but in practice it doesn’t. When a player first logs in, there’s a massive data dump from the server to the client. This includes scripts, assets, instances, etc… There are a number of causes. Mainly limited network bandwidth or network congestion. Server lag can also be an issue, as is client performance since the client has to process all that data.
Assuming they are not the first person to join the server and the server has already loaded:
Is the problem that the PreloadAsync() is finishing before the server → client replication process?
I thought game.Loaded:Wait() waits for the client replication process to complete:
I have
if not game:IsLoaded() then game.Loaded:Wait() end
G.ContentProvider:PreloadAsync({Icon, GameTitle})
G.ContentProvider:PreloadAsync, G.GetDescendantsOfClass(workspace.Map.MainMap, "MeshPart"))
LoadingScreen:Destroy()
Yet the client sometimes looks like this after the loading screen completes (alot hasn’t loaded in yet)
In my case, the server does other things after the map loads, so it’s still working on things so I have to hold the client at the loading screen until the server finishes.
I’m confused how you’re measureing when the server has finished
Tweak this script to make it work for your situation.
local ContentProvider = game:GetService("ContentProvider")
local Players = game:GetService("Players")
local loadingScreen = script:WaitForChild("LoadingScreen"):Clone()
repeat task.wait() until game:IsLoaded()
local assets = workspace:GetDescendants()
local maxAssets = #assets
local client = Players.LocalPlayer
local plrGui = client:WaitForChild("PlayerGui")
loadingScreen.Parent = plrGui
for i, assetToLoad in assets do
ContentProvider:PreloadAsync({assetToLoad})
loadingScreen:WaitForChild("Assets").Text = "Assets loaded: "..i.."/"..maxAssets
end
loadingScreen["Loading Text"].Text = "Successfully loaded assets!"
loadingScreen.Assets.Visible = false
task.wait(5)
loadingScreen:Remove()
Hope this helped!
There is no measurement. When the last startup task is finished and the game is ready to play, then it spawns all the players in game and sends the event to release the loading screen. In other words, the startup sequence broadly follows this:
Of course, every game is different.
How is that any different to my script which isn’t working
You’re just using a for loop instead which if anything would make it slower
ContentProvider also tends to yield in my case forever (Happened to a lot of my players), I would just add an automatic skip after 60 seconds has passed, which in this case would most likely be after all of the map parts have already replicated/loaded.
Seems like a non-ideal work around rather than a solution
There has to be better way as Pet Simulator X loading screen is short and always works perfectly and is never 60 seconds long.
Well, it was my only solution to ContentProvider hanging forever, you can also just load specific classes if needed.
local ToLoad = {}
local CC = game:GetService("ContentProvider")
for _,v in workspace:GetDescendants() do
if v:IsA("Sound") then
table.insert(ToLoad, v)
continue
end
if v:IsA("MeshPart") then
table.insert(ToLoad, v)
continue
end
end
for _,v in ToLoad do
CC:PreloadAsync({v})
end
-- Make sure to clear ToLoad after usage.
Tried only loading meshparts (that’s the only thing I want to load) but it doesn’t seem to make the situation any better