Four bugs from real cross-network play:
- Player/net_player.gd: the per-peer spawn-spread offset (added to avoid
stacking joiners on top of each other) could push a joining client up
to 12 units from center, but the floor is only 15x15 (~7.5 unit
half-extent) — landing a client off the edge. Bounded to ~2.7 units.
- Player/net_player.gd: the glove scenes used for remote-hand visuals
carry hand.gd (XRToolsHand), whose root node sets top_level = true and
repositions itself to its parent's transform every physics frame,
expecting to be parented under a live XRController3D. Parented under
the plain avatar node instead, it fought both the local transform copy
and the replicated sync every tick — whichever wrote last that frame
won, which read as hands stuck near origin except momentarily. Disabled
physics processing on those nodes; nothing else in the script matters
without a real controller ancestor.
- Net/net_pickable.gd: grabbing an item sends an RPC to the server to
confirm authority, but the item's regular state sync travels on a
different channel with no ordering guarantee against that RPC. A
stale "still loose" sync packet could arrive after an optimistic local
grab but before the confirmation, and was being treated as a real
authority loss, force-dropping the item — explaining "first attempt
fails, second succeeds". Now a sync update can't yank an item out of
our own hand mid-grab; only an explicit rejection or an actual loss of
authority can.
- Net/net_pickable.gd: the non-authority path zeroes collision_mask and
forces freeze_mode to KINEMATIC, but nothing ever restored either when
a peer regained authority (e.g. the server after a client's release) —
so a released item stayed collision-less forever, looking physics-less.
Now both are restored from the item's own baked values whenever a peer
owns a loose (not actively held) item again.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The world was populated before NetworkManager.world_ready() actually
called host()/join(), so owns_world() read true for every peer
(including a joining client) and each one built its own local,
unreplicated copy instead of the client receiving the server's spawn
through the MultiplayerSpawner. Population now happens after the
session is actually established.
Also adds net-log coverage for spawn/despawn, station gating, item
authority handoff, station snapping, avatar spawn/despawn, and scene
transitions, so multiplayer behavior is visible in
%TEMP%\vryhungry_net_<pid>.log instead of failing silently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>