I made a server script that creates a part and parents it to the workspace. After that, the script fires a remote to a client with an instance as a parameter:
Server
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataRelatedToThePart = {}
-- The instance
local Part = Instance.new("Part", workspace)
-- Firing the remote
ReplicatedStorage.RemoteEvent:FireAllClients(Part, DataRelatedToThePart)
The problem is the part is nil when the client receives the event.
Client
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Receiving the event
ReplicatedStorage.RemoteEvent.OnClientEvent:Connect(function(Part:Part, DataRelatedToThePart:{any})
print(Part, DataRelatedToThePart) -- nil, {}
end)
If I use task.wait(4), the part will not be nil. But I don’t want the event to be called as soon as it can.
What is the best way to have the part replicated before receiving a RemoteEvent?
Here is where you would add task.wait() for it to work. Server
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataRelatedToThePart = {}
-- The instance
local Part = Instance.new("Part", workspace)
-- Leave time for replication to complete
task.wait(4)
-- Firing the remote
ReplicatedStorage.RemoteEvent:FireAllClients(Part, DataRelatedToThePart)
Do you have streaming enabled? If not, there’s no problem with using WaitForChild() while specifying the name and place to wait.
When streaming is disabled, the part will be sent if it was successfully parented to workspace on the server by the engine beforehand (because of the replication order).
When streaming is enabled, there are no guarantees. In some cases it might never stream in. There’s a trick with object values and ObjectValue.Changed:Wait(), but it’s best to avoid sending parts in the first place.
Ok, assuming the player is already joined and the client script loaded by the time the server script runs, then your original post, should contain the part data.
There must be something else going on with your scripts.
EDIT : I forgot about streaming, I was assuming the part was near the player. @fellow_meerkat has a good suggestion.
So in the Server after creating the part you could try WaitForChild(workspace.Part) but idk if that would work. What would work is giving the Part on the Server a Name and then you can search it on the client and use waitForChild on it
Better to find some workaround then disable streaming - although I believe it still needs some more development to equip us with better control for cases like this.
Just to elaborate, an object has to be scheduled for replication before the remote is fired in order to exist in the same frame when the signal is received.
So we’re left with workarounds:
If we’re certain that the object is going to be in player’s replication radius, it’s alright to send its name and the place to look (that already exists locally).
The ObjectValue trick involves setting its value to the part and parenting both to a replicated place before sending the ObjectValue. It should be available to the client immediately, and the part 1) should be referred by Object.Value right away or 2) referred once .Changed fires.
For multiple parts we can use CollectionService.
I think @SelDraken’s idea is great. It’s definitely a reliable approach. And yes, you can also make it persistent for individuals (model:AddPersistentPlayer(player))
@fellow_meerkat showed a :AddPersistentPlayer() method that allows you to set the players the model will stream as ‘Enum.ModelStreamingMode.Persistent’ to.
local Player -- Player instance
local Model -- Model instance
Model.ModelStreamingMode = Enum.ModelStreamingMode.PersistentPerPlayer
Model:AddPersistentPlayer(Player)
Is this expected to have the same result as disabling streaming in workspace, except it is for the model’s descendants?
Disabling streaming fixed the replication issue but setting ModelStreamingMode to Enum.ModelStreamingMode.PersistentPerPlayer does not fix it.