Sending Instance to Client Doesn't Register Sometimes

When I create a part on the server and try to send it to the client, it’s USUALLY successful, but in somewhat rare cases, it results in nil.

I have streaming enabled off
I parented the part before firing the remote

Any solutions? Happy to provide more context if needed

Hello!
If I were you, I would put a “task.wait()” after creating the part because these rare cases are caused because you fire the client before the client render the part, I guess. Is it possible to you put a “task.wait()”?

Wouldn’t it take longer to render if the player were lagging, making the wait useless?

I don’t think so.

local part = Instance.new("Part") -- Creates the part for server, rendering to players

SendPartForPlayer:FireClient(player, part) -- Not rendered yet
local part = Instance.new("Part") -- Creates the part for server, rendering to players

task.wait() -- Wait a little for render

SendPartForPlayer:FireClient(player, part) -- Rendered

It makes sense for me, but I’m probably wrong. Another suggestion is trying to get the part without sending it to player by a RemotEvent. Like, putting it on a folder and getting it by Folder:WaitForChild("part"). Is this possible?

@OP

I imagine this means that the part isn’t on the client yet. I’m not sure what the intended way to way for it is, perhaps you could use the Workspace.DescendentAdded event, though task.wait should work fine too (it would also be good to have a timeout set so the code doesn’t wait forever in edge cases).


I don’t believe this is exactly how the engine works, the part variable should contain the part after the call to Instance.new. I would guess the bottle neck is probably replication to the client, not something on the server.

1 Like

Well, in server, the variable contains the Part. The problem is that, for laggy players, the server’s actions don’t happen instantly and for they, the part doesn’t exist and it returns nil.

1 Like

The problem though is that the part isn’t being replicated to them at the same time as the remote event is getting to them. Waiting for a frame on the server would be beneficial in the laggy case, but would be almost just as inconsistent.

They should do something like this on the client:

local function waitForInstance(instance, timeOut)
    local start = tick()
    while tick() < start + timeOut do
        task.wait()
    end
    if tick() >= start + timeOut then
        warn("Function timed out, instance never replicated!")
    end
    return instance
end

Edit: Actually, because of how Lua does nil (because it’s primitive), the nil object should always be nil. I believe that means it’s not possible to wait for the object. I’m not sure how to fix this. Waiting on the server might be a good idea, though I’m pretty sure it shouldn’t replicate this way…

1 Like

Yes, you’re right! I think the most effective method to solve that is creating a folder with the parts with distinct names and only pass the name of the created part for player, then it can calls:

RemoteEvent.OnClientEvent:Connect(function(partName)

    Folder:WaitForChild(partName)

end)

With this method, every player can do it in its time.

1 Like

@Kakazin19010
Yeah, that is a good solution.


@OP
It appears the parameter is nil when the object isn’t replicated according to the docs:

Some options are:

  • Make the instance identifiable so the client can tell when it’s been added (e.g. by name like Kakazin said)
  • Send a RemoteEvent back to the server asking for the server to resend that data
  • Send data about the instance instead of the actual instance (if possible) (i.e. send it’s position if that’s the only information the client actually needs)
  • Use an ObjectValue, then on the client use ObjectValue.Changed to wait for the instance to be added if the ObjectValue.Value is nil

I’ve looked through some similar threads and there doesn’t seem to be a great solution for this.

2 Likes

Wouldn’t sending an object value result in nil as well?

If you send the ObjectValue through the remote event, then yes.

However, if you have an ObjectValue that already exists, then you set the ObjectValue’s Value to the object, then you can send objects to the client like that. That way, even if the instance hasn’t replicated, you can wait for the instance using ObjectValue.Chagned:Wait().

(The problem with the nil value that the remote event sends is that it can’t ever become a none nil value (kind of like if you have a value that’s 1, 1 is always just one. The number 1 and nil are called primitive objects, which have this behavior). The ObjectValue can be used as a wrapper for the primitive, such that the value it represents can be updated, even if it’s initially nil.)


If you want to exactly simulate a RemoteEvent sending the data, you could give get a unique ID for each time you send an instance to a client, then add an ObjectValue to a folder in PlayerGui (which replicates only to the single player) with a name of the unique ID sent with the remote event.

That way, when your client script gets the unique ID, it knows it needs to wait for an ObjectValue in PlayerGui, then wait for the ObjectValue to have a non-nil value using .Changed.

To do the unique IDs, you could just have a counter that increase each time an instance is sent (and maybe reset it after a very long time, when all instances should have already been sent and client code waiting for instances should have decided to time out the waiting).

I passed the day thinking and I think you could try:

--In server

coroutine.wrap(function()

local player --= player
local part = Instance.new("Part", workspace)

SendPartForPlayer:FireClient(player, part)

SendPartForPlayer.OnServerEvent:Connect(function(playerAsked, action)

    if playerAsked == player and action == "NeedPart" then

        SendPartForPlayer:FireClient(player, part)

    end

end)

end)()
--In player

SendPartForPlayer.OnClientEvent:Connect(function(obj)

     if not obj then

        SendPartForPlayer:FireServer("NeedPart")

        return
    end

    --The rest of the code

end)

Yeah I gave the parts unique ids using GenerateGUID, and on the client used WaitForChild to search for the part in a folder. I guess it’s somewhat a solution, the only problem being that the visuals will be pretty delayed/not appear at all depending how laggy the player is

2 Likes

Yeah.

What people often do for visuals is send the information for each client to create the visuals on their end, instead of replicating a whole instance over to them. (E.g. do :FireAllClients("PurpleExplosion", explosionStartTime, explosionPosition, explosionSize) on the server then each client gets that event and makes an explosion effect at the position.)

Fair enough, that is definitely a difficult case to handle. Your solution is pretty good in this case, since not doing an effect on a part if the part doesn’t exist on the client yet is probably fine.

If it is a problem, you could send the last position of the part in the remote event, so that even if the client doesn’t have the exact position of the part, at least it can use some value.

But yeah, that’s a tough problem, and the best solution problem depends on the exact case (e.g. if it’s really important you could pseudo replicate the position with UnreliableRemoteEvents constantly).

1 Like

In some cases I can’t always do this. For example, sometimes I need a visual to constantly follow a part that’s moving/changing position. There’s probably better ways to handle this, but I never found an issue until now

1 Like