tag

#wrong-behavior

21 items (21 fixes)

fixes
26.07.15a
"A code-built world can run TWICE on a network join and stack a second world on the first"

A joining client can build a code-generated world twice -- once on local StartupScene load, once on the networked scene handoff. A non-idempotent world author that clears its tracking lists but never destroys prior GameObjects stacks a whole second world. Make the author idempotent, and compute any sync hash from the build recipe, not a live scene scan.

26.07.22
"A component that reads Input in OnUpdate fires on every instance at once"

Input.Pressed and Input.Down read a global input state, not a per-entity one. A component that reads them in its own OnUpdate samples that global state once per instance. With one entity on screen this is correct. With many non-networked instances of the same component, one keypress fires the action on every instance at once. Gating only on IsProxy does not help, because IsProxy answers whether the entity is replicated, not whether this instance should own the keypress. Gate the input read on genuine local ownership instead.

26.07.22
"A dedicated server never creates a Steam lobby, so lobby-based join code does nothing"

Networking.CreateLobbyAsync branches on Application.IsDedicatedServer straight into the dedicated-server path and never opens a lobby, so lobby-creation code written for peer-hosted play does nothing useful on a dedicated server.

26.07.15a
"A second s&box instance on the same Steam account can make P2P joins fail rendezvous"

Running a second s&box process under the same Steam account (an editor or dev instance alongside the published client) correlates with P2P joins to a live remote host failing rendezvous inside the engine's fixed ~3.5s connect budget. Close every second same-account instance before drawing any conclusion from a P2P connectivity failure.

26.07.15a
"An accumulator guard that resets on a transient flicker never fires"

A per-run accumulator guard (budget, cooldown, or streak counter) that resets on a transient state flicker never actually fires, because the reset zeroes it faster than it can fill. Reset only on a signal that truly ends the episode (sustained air, real forward progress), never on a per-tick proxy the episode itself trips constantly.

26.07.15a
"Character stacking: seat at the visual mesh head-top, not the physics capsule height"

When one character stands on another that uses a collider-less trace mover, standing can't happen physically -- you synthesise it by pinning the rider's feet to the carrier's head each fixed step. Seat at the VISUAL mesh head-top, not the physics capsule height, or the rider sinks into the carrier's head and reads as two merged characters.

26.07.08e
"CreateLobby is async: IsActive is still false on the same frame"

Networking.CreateLobby is async: Networking.IsActive is still false on the same frame, so any branch on IsActive takes the wrong path. Gate on your own synchronous mode enum instead.

26.07.15a
"Dev-host streaming skips loose data files, breaking deterministic sync"

Dev-host streaming sends assemblies and compiled assets but NOT loose data files read via FileSystem.Mounted: a joining client that loads a raw JSON manifest builds differently, silently.

26.07.08e
"FromHost field on a runtime singleton does not replicate without NetworkSpawn"

A [Sync(SyncFlags.FromHost)] field on a runtime-created singleton never replicates: the object needs NetworkSpawn, not just NetworkMode.Snapshot.

26.07.22
"GameObject.Destroy() is deferred: a destroyed object still holds its exclusive claims for the rest of the frame"

GameObject.Destroy() is deferred to the end of the frame, so an object you just destroyed still exists, still answers, and still holds any exclusive claim it owns (IsMainCamera, a singleton slot, a registry entry) for the rest of that frame. Clear exclusive claims on the outgoing owner BEFORE calling Destroy, and never read a leak census in the same frame as the teardown it checks.

26.07.15a
"Gate the exact lobby you connect to, not query result [0]"

When a lobby query drives a connect, validate the exact lobby candidate you will connect to (not results[0]), or a peer can connect to a lobby it never validated (build-skew, wrong-host connect that no gate catches).

26.07.15a
"Idempotent world rebuild needs DestroyImmediate, not Destroy"

Deferred Destroy() leaves the old code-built world overlapping the fresh one for a frame: use DestroyImmediate for teardown before rebuild, and pair it with a recipe hash for belt-and-suspenders join verification.

26.07.15a
"Joining client's singleton claim grabs the host's character"

A static Instance claimed behind if (!IsProxy) in OnStart grabs the host's character on a joining client: the client camera follows the wrong player forever.

26.07.08e
"Joining client's static state wiped by networked scene handoff"

A joining client's static join state (invite code, mode, attempt ID) gets wiped by the networked scene handoff: the bootstrap's OnEnabled resets statics before the join handshake uses them.

26.07.15
"QueryLobbies never finds your Hidden lobby"

Networking.QueryLobbies appends a hidden:0 filter unless you pass a truthy hidden key, so a Hidden lobby is structurally excluded from every ordinary query. The bool overload param is includeServers, not hidden-inclusion. And editor hosts force Private privacy, which no filter overrides.

26.07.22
"Read collision direction from the impulse, not the struck object's velocity"

A collision rule that keys its direction on the struck object's own velocity is correct for the object that caused the hit and silently backwards for the object that got hit. A car rolling forward at 2 m/s and rammed from behind reads as moving forward, so it dents its nose instead of its rear. Derive the direction from the negated impulse delta-v, which points from the struck face inward for both bodies.

26.07.22
"ScreenPanel.ZIndex orders paint only: pointer input falls through"

ScreenPanel.ZIndex orders paint only: pointer input is NOT routed by cross-root stacking, so a lower-ZIndex root keeps swallowing clicks even when a higher-ZIndex modal draws over it.

26.07.22
"Setting a [ConVar] to the value it already holds does nothing"

Re-setting a [ConVar] to the value it already holds is a silent no-op: the C# setter never runs, so a convar-triggered action does nothing, with zero console output. Statics survive Play stop/start, so a prior session leaves the value already set.

26.07.08e
"Spawning a joiner's character clobbers the host's camera target"

On the host, spawning a joiner's character clobbers the host's own camera-target singleton because IsProxy is false at Components.Create time: re-resolve the claim at the first OnFixedUpdate.

26.07.15a
"Steam lobby metadata survives host game switch"

A Steam lobby survives its host switching to a different s&box game with stale metadata: a joiner connects and loads the wrong game's content.

26.07.15a
"The P2P join handshake has a fixed ~3-second connect budget"

The joiner's engine-side connect budget after entering a Steam lobby is a fixed ~3 seconds that game code cannot extend: any host-side delay exhausts it fast, so join recovery must be a fresh retry, never a longer wait.

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