the symptom, in your words

"A -joinlocal dev-host client CAN read the host's Networking.SetData lobby metadata"

✓ verified on 26.07.15a
lane Writing gameplayposted

▸ SYMPTOM

You are testing local multiplayer with a -joinlocal second editor instance. The host stamps some join-time metadata on itself with Networking.SetData("h", "8870af55") (a world hash, a protocol version, whatever you want to hand a joiner before the scene fully hands off). On the client you call Networking.GetData("h", "") in OnStart — and get back the empty default.

It looks like the -joinlocal client simply cannot see host lobby data, because its per-instance fake SteamId is not a real member of the host's Steam lobby. So you assume the whole join-time metadata channel is unavailable in local testing and reach for a side channel.

▸ CAUSE

Two separate things are true and they pull in opposite directions:

  1. The -joinlocal joiner is not a real Steam lobby member — its per-instance SteamId is synthetic — so anything that keys off actual lobby membership will not find it.
  2. But Networking.GetData(key, default) does not read lobby membership. The values arrive over the ServerDataMsg the host sends every client on connect. NetworkSystem.OnReceiveServerData handles it ("we have received changed data from the server") and populates the local data store, so GetData resolves the host's SetData values even for a fake-SteamId loopback peer.

The reason a single OnStart read returns the default is timing: the ServerDataMsg can arrive a few frames after the client's own scene load / world build. Read once at OnStart and you race the message and lose.

▸ FIX

Read the value in a short poll loop rather than a single read, and start polling from your post-build guard rather than OnStart:

snippet
// client-side, after the local scene/world is built
async Task<string> AwaitHostData(string key, float timeoutSeconds = 5f)
{
    var deadline = Time.Now + timeoutSeconds;
    while (Time.Now < deadline)
    {
        var v = Networking.GetData(key, "");
        if (!string.IsNullOrEmpty(v))
            return v;
        await Task.Frame();
    }
    return "";
}

Verified live: the host stamped Networking.SetData("h", "8870af55") right after CreateLobby; the -joinlocal client's post-build guard polled Networking.GetData("h", "") and read back exactly 8870af55.

▸ WHY IT WORKS

The host-advertise / lobby-query direction (a browser listing open lobbies) really does depend on Steam lobby membership. Join-time metadata exchange does not — it rides the per-connection ServerDataMsg, which every accepted peer receives regardless of whether it is a genuine lobby member. That means the editor-host local-MP path can carry world-hash guards, protocol/version checks, and other handshake metadata during testing, not just the advertise/query direction — you just have to wait for the message instead of assuming it arrived by the time your scene finished loading.

Verified on engine 26.07.15a: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Published

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord