Uncomment net_pickable
This commit is contained in:
+208
-208
@@ -1,209 +1,209 @@
|
||||
extends MultiplayerSynchronizer
|
||||
#
|
||||
### Networked sync component for a pickable item. Added as a child literally
|
||||
### named "NetPickable" (network_manager.gd's authority RPCs already expect
|
||||
### this) of every net-synced pickable, replicating its transform and held
|
||||
### state. Only the current multiplayer authority (the server while loose, or
|
||||
### whichever peer is holding it) actually simulates physics for the item;
|
||||
### every other peer freezes their local copy and just follows the synced
|
||||
### transform.
|
||||
#
|
||||
### 0 = loose/server-simulated; otherwise the peer id currently holding it.
|
||||
### Replicated at spawn and on change so a late joiner sees the current holder.
|
||||
#var net_held_by: int = 0: set = _set_net_held_by
|
||||
#
|
||||
#var _pickable: XRToolsPickable
|
||||
#
|
||||
## This item's own baked freeze_mode (e.g. plate.tscn bakes KINEMATIC, not the
|
||||
## RigidBody3D default of STATIC) — captured once so it can be restored when
|
||||
## this peer regains ownership, instead of getting stuck on whatever
|
||||
## apply_held_state() last forced it to while non-authority.
|
||||
#var _original_freeze_mode: int
|
||||
#
|
||||
## Same idea for the pickable's authored `enabled` flag, which the non-authority
|
||||
## branch of apply_held_state() clears while someone else is holding the item.
|
||||
#var _original_enabled: bool
|
||||
#
|
||||
## Whether the "our own hand still holds this" guard has already been logged for
|
||||
## the current grab. apply_held_state() runs every network tick, so without this
|
||||
## the guard message repeats for as long as you hold the item.
|
||||
#var _grab_race_logged := false
|
||||
#
|
||||
#
|
||||
#func _ready() -> void:
|
||||
#_pickable = get_parent() as XRToolsPickable
|
||||
#if not _pickable:
|
||||
#push_error("NetPickable must be a child of an XRToolsPickable")
|
||||
#return
|
||||
#_original_freeze_mode = _pickable.freeze_mode
|
||||
#_original_enabled = _pickable.enabled
|
||||
#_pickable.picked_up.connect(_on_picked_up)
|
||||
#_pickable.dropped.connect(_on_dropped)
|
||||
## Deferred: the pickable root captures its own original_collision_mask/
|
||||
## original_collision_layer via @onready, which runs AFTER this child's
|
||||
## _ready() but BEFORE the root's _ready() body. Calling apply_held_state
|
||||
## synchronously here would freeze/mask the item before that capture runs,
|
||||
## permanently corrupting the "restore on drop" values.
|
||||
#apply_held_state.call_deferred()
|
||||
#
|
||||
#
|
||||
#func _set_net_held_by(value: int) -> void:
|
||||
#var old := net_held_by
|
||||
#net_held_by = value
|
||||
#if old != value and NetworkManager.is_online():
|
||||
#print("%s net_held_by: %d -> %d (local state: %s, authority=%d)" % [
|
||||
#_pickable.name, old, value, _holder_desc(), get_multiplayer_authority()
|
||||
#])
|
||||
#apply_held_state()
|
||||
#
|
||||
#
|
||||
### Puts the item in the right physics state for whether this peer currently
|
||||
### owns it. Called locally after net_held_by changes, and directly by
|
||||
### NetworkManager._set_item_authority right after an authority handoff.
|
||||
###
|
||||
### IMPORTANT: this runs on every network tick, not just on a real change.
|
||||
### net_held_by is replicated in ALWAYS mode, so the synchronizer assigns it every
|
||||
### tick on non-authority peers — unchanged value included — and that assignment
|
||||
### lands in _set_net_held_by(), which calls this. So every branch here has to be
|
||||
### idempotent and silent when there is nothing to do: otherwise each item logs a
|
||||
### line and rewrites four physics properties every tick on every peer that
|
||||
### doesn't own it.
|
||||
#func apply_held_state() -> void:
|
||||
#if not _pickable:
|
||||
#return
|
||||
#if not NetworkManager.is_online() or is_multiplayer_authority():
|
||||
#_grab_race_logged = false
|
||||
## We own this item's simulation (offline, loose+server, or currently
|
||||
## holding it). If it's not actively in our own hand right now, make
|
||||
## sure it isn't still left frozen/collision-less from a previous
|
||||
## non-authority period (e.g. right after regaining authority when a
|
||||
## client released it) — while actually held, XRToolsPickable's own
|
||||
## pick_up()/let_go() already manage these fields, so leave those be.
|
||||
#if not _pickable.is_picked_up():
|
||||
#var changed := _pickable.freeze_mode != _original_freeze_mode \
|
||||
#or _pickable.collision_mask != _pickable.original_collision_mask
|
||||
#if changed:
|
||||
#if NetworkManager.is_online():
|
||||
#print(
|
||||
#"%s: reclaiming ownership, restoring freeze_mode %d->%d collision_mask %d->%d" % [
|
||||
#_pickable.name, _pickable.freeze_mode, _original_freeze_mode,
|
||||
#_pickable.collision_mask, _pickable.original_collision_mask
|
||||
#]
|
||||
#)
|
||||
#_pickable.freeze_mode = _original_freeze_mode
|
||||
#_pickable.collision_mask = _pickable.original_collision_mask
|
||||
## Unlike freeze/collision (which XRToolsPickable manages itself while
|
||||
## held), `enabled` is only ever written by the non-authority branch
|
||||
## below, so it must be restored here or it stays false forever: once a
|
||||
## client grabbed this item, every other peer set enabled=false, and
|
||||
## regaining authority left it that way. On the server that silently
|
||||
## broke everything downstream — hands couldn't pick the item up again,
|
||||
## and a station snap zone would "snap" it (emitting has_picked_up, so
|
||||
## e.g. a plate still got marked dirty) while pick_up() bailed out on
|
||||
## the disabled item, leaving the zone holding an item with no grab
|
||||
## driver that then fell out of the station.
|
||||
#if _pickable.enabled != _original_enabled:
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s: reclaiming ownership, restoring enabled %s->%s" % [
|
||||
#_pickable.name, _pickable.enabled, _original_enabled
|
||||
#])
|
||||
#_pickable.enabled = _original_enabled
|
||||
#return
|
||||
## A net_held_by/position sync update can race ahead of the
|
||||
## authority-handoff RPC that's about to confirm a grab we just made
|
||||
## optimistically (they travel on different channels with no ordering
|
||||
## guarantee). Don't let a stale sync value yank an item out of our own
|
||||
## hand mid-grab — only an explicit force_release_item rejection, or
|
||||
## actually losing authority for real, should end a grab we initiated.
|
||||
#if _pickable.is_picked_up() and _pickable.get_picked_up_by() is XRToolsFunctionPickup:
|
||||
## Log once per grab, not once per tick.
|
||||
#if NetworkManager.is_online() and not _grab_race_logged:
|
||||
#_grab_race_logged = true
|
||||
#print(
|
||||
#"%s: ignoring non-authority sync (net_held_by=%d) — still actively held by our own hand (grab-race guard)" % [
|
||||
#_pickable.name, net_held_by
|
||||
#]
|
||||
#)
|
||||
#return
|
||||
#_grab_race_logged = false
|
||||
## Someone else owns it: stop simulating locally, just follow the sync.
|
||||
#if _pickable.is_picked_up():
|
||||
#if NetworkManager.is_online():
|
||||
#print(
|
||||
#"%s: was held by %s on this peer, but authority now says peer %d owns it — force-dropping" % [
|
||||
#_pickable.name, _holder_desc(), net_held_by
|
||||
#]
|
||||
#)
|
||||
#_pickable.drop()
|
||||
## Bail out when we're already in the follow-the-sync state. Without this the
|
||||
## writes below (and the line logged with them) repeated every tick for every
|
||||
## item on every non-authority peer — 90% of the log, plus four redundant
|
||||
## physics-property writes per item per tick. The comparison also means we
|
||||
## still re-apply if something else perturbs the state (e.g. let_go()
|
||||
## restoring the collision mask after a force-drop).
|
||||
#var want_enabled := (net_held_by == 0)
|
||||
#if _pickable.freeze \
|
||||
#and _pickable.freeze_mode == RigidBody3D.FREEZE_MODE_KINEMATIC \
|
||||
#and _pickable.collision_mask == 0 \
|
||||
#and _pickable.enabled == want_enabled:
|
||||
#return
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s: freezing (non-authority, owner=peer %d)" % [_pickable.name, net_held_by])
|
||||
#_pickable.freeze = true
|
||||
#_pickable.freeze_mode = RigidBody3D.FREEZE_MODE_KINEMATIC
|
||||
#_pickable.collision_mask = 0
|
||||
#_pickable.enabled = want_enabled
|
||||
#
|
||||
#
|
||||
### Local hand grab (not a station snap zone, which is server-only): request
|
||||
### authority immediately so the throw/drop can be reconciled, but let the grab
|
||||
### happen instantly here rather than waiting on the round trip.
|
||||
#func _on_picked_up(_p) -> void:
|
||||
#var by := _pickable.get_picked_up_by()
|
||||
#if not (by is XRToolsFunctionPickup):
|
||||
## e.g. a station snap zone grabbed it (server-side auto-snap, or the
|
||||
## addon's own "grab out of a snap zone" shortcut mid-cascade) — not a
|
||||
## player-initiated hand grab, so no authority request from here.
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s picked up by %s (not a hand) — no authority request" % [_pickable.name, _holder_desc()])
|
||||
#return
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s grabbed by hand (authority was peer %d), requesting authority" % [_pickable.name, net_held_by])
|
||||
#NetworkManager.request_item_authority_from(_pickable.get_path())
|
||||
#
|
||||
#
|
||||
#func _on_dropped(_p) -> void:
|
||||
## Only forward if we're actually still the authority — a drop caused by
|
||||
## apply_held_state() losing authority (see above) must not re-report.
|
||||
#if not is_multiplayer_authority():
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s dropped locally, but we aren't its authority (peer %d is) — not reporting" % [_pickable.name, net_held_by])
|
||||
#return
|
||||
#if NetworkManager.is_online():
|
||||
#print("%s dropped, reporting release to server (lin=%s ang=%s)" % [
|
||||
#_pickable.name, _pickable.linear_velocity, _pickable.angular_velocity
|
||||
#])
|
||||
## Send our own final transform too: we were the authority until now, and the
|
||||
## server's copy may not have received our last position sync yet.
|
||||
#NetworkManager.release_item_authority_from(
|
||||
#_pickable.get_path(), _pickable.linear_velocity, _pickable.angular_velocity,
|
||||
#_pickable.global_transform
|
||||
#)
|
||||
#
|
||||
#
|
||||
### Human-readable description of what's currently holding this item on THIS
|
||||
### peer, for diagnosing desyncs between a hand's own "what am I holding"
|
||||
### bookkeeping and the item's actual grab state (see the snap-zone-grab race
|
||||
### in NetworkManager._try_snap_into_station for a real example).
|
||||
#func _holder_desc() -> String:
|
||||
#if not _pickable or not _pickable.is_picked_up():
|
||||
#return "loose"
|
||||
#var by := _pickable.get_picked_up_by()
|
||||
#if not by:
|
||||
#return "held(no grabber?)"
|
||||
#if by is XRToolsFunctionPickup:
|
||||
#return "hand(%s)" % by.get_path()
|
||||
#if by is XRToolsSnapZone:
|
||||
#var station := by.get_parent()
|
||||
#return "zone(%s)" % (station.name if station else str(by.get_path()))
|
||||
#return "other(%s: %s)" % [by.get_class(), by.get_path()]
|
||||
|
||||
## Networked sync component for a pickable item. Added as a child literally
|
||||
## named "NetPickable" (network_manager.gd's authority RPCs already expect
|
||||
## this) of every net-synced pickable, replicating its transform and held
|
||||
## state. Only the current multiplayer authority (the server while loose, or
|
||||
## whichever peer is holding it) actually simulates physics for the item;
|
||||
## every other peer freezes their local copy and just follows the synced
|
||||
## transform.
|
||||
|
||||
## 0 = loose/server-simulated; otherwise the peer id currently holding it.
|
||||
## Replicated at spawn and on change so a late joiner sees the current holder.
|
||||
var net_held_by: int = 0: set = _set_net_held_by
|
||||
|
||||
var _pickable: XRToolsPickable
|
||||
|
||||
# This item's own baked freeze_mode (e.g. plate.tscn bakes KINEMATIC, not the
|
||||
# RigidBody3D default of STATIC) — captured once so it can be restored when
|
||||
# this peer regains ownership, instead of getting stuck on whatever
|
||||
# apply_held_state() last forced it to while non-authority.
|
||||
var _original_freeze_mode: int
|
||||
|
||||
# Same idea for the pickable's authored `enabled` flag, which the non-authority
|
||||
# branch of apply_held_state() clears while someone else is holding the item.
|
||||
var _original_enabled: bool
|
||||
|
||||
# Whether the "our own hand still holds this" guard has already been logged for
|
||||
# the current grab. apply_held_state() runs every network tick, so without this
|
||||
# the guard message repeats for as long as you hold the item.
|
||||
var _grab_race_logged := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_pickable = get_parent() as XRToolsPickable
|
||||
if not _pickable:
|
||||
push_error("NetPickable must be a child of an XRToolsPickable")
|
||||
return
|
||||
_original_freeze_mode = _pickable.freeze_mode
|
||||
_original_enabled = _pickable.enabled
|
||||
_pickable.picked_up.connect(_on_picked_up)
|
||||
_pickable.dropped.connect(_on_dropped)
|
||||
# Deferred: the pickable root captures its own original_collision_mask/
|
||||
# original_collision_layer via @onready, which runs AFTER this child's
|
||||
# _ready() but BEFORE the root's _ready() body. Calling apply_held_state
|
||||
# synchronously here would freeze/mask the item before that capture runs,
|
||||
# permanently corrupting the "restore on drop" values.
|
||||
apply_held_state.call_deferred()
|
||||
|
||||
|
||||
func _set_net_held_by(value: int) -> void:
|
||||
var old := net_held_by
|
||||
net_held_by = value
|
||||
if old != value and NetworkManager.is_online():
|
||||
print("%s net_held_by: %d -> %d (local state: %s, authority=%d)" % [
|
||||
_pickable.name, old, value, _holder_desc(), get_multiplayer_authority()
|
||||
])
|
||||
apply_held_state()
|
||||
|
||||
|
||||
## Puts the item in the right physics state for whether this peer currently
|
||||
## owns it. Called locally after net_held_by changes, and directly by
|
||||
## NetworkManager._set_item_authority right after an authority handoff.
|
||||
##
|
||||
## IMPORTANT: this runs on every network tick, not just on a real change.
|
||||
## net_held_by is replicated in ALWAYS mode, so the synchronizer assigns it every
|
||||
## tick on non-authority peers — unchanged value included — and that assignment
|
||||
## lands in _set_net_held_by(), which calls this. So every branch here has to be
|
||||
## idempotent and silent when there is nothing to do: otherwise each item logs a
|
||||
## line and rewrites four physics properties every tick on every peer that
|
||||
## doesn't own it.
|
||||
func apply_held_state() -> void:
|
||||
if not _pickable:
|
||||
return
|
||||
if not NetworkManager.is_online() or is_multiplayer_authority():
|
||||
_grab_race_logged = false
|
||||
# We own this item's simulation (offline, loose+server, or currently
|
||||
# holding it). If it's not actively in our own hand right now, make
|
||||
# sure it isn't still left frozen/collision-less from a previous
|
||||
# non-authority period (e.g. right after regaining authority when a
|
||||
# client released it) — while actually held, XRToolsPickable's own
|
||||
# pick_up()/let_go() already manage these fields, so leave those be.
|
||||
if not _pickable.is_picked_up():
|
||||
var changed := _pickable.freeze_mode != _original_freeze_mode \
|
||||
or _pickable.collision_mask != _pickable.original_collision_mask
|
||||
if changed:
|
||||
if NetworkManager.is_online():
|
||||
print(
|
||||
"%s: reclaiming ownership, restoring freeze_mode %d->%d collision_mask %d->%d" % [
|
||||
_pickable.name, _pickable.freeze_mode, _original_freeze_mode,
|
||||
_pickable.collision_mask, _pickable.original_collision_mask
|
||||
]
|
||||
)
|
||||
_pickable.freeze_mode = _original_freeze_mode
|
||||
_pickable.collision_mask = _pickable.original_collision_mask
|
||||
# Unlike freeze/collision (which XRToolsPickable manages itself while
|
||||
# held), `enabled` is only ever written by the non-authority branch
|
||||
# below, so it must be restored here or it stays false forever: once a
|
||||
# client grabbed this item, every other peer set enabled=false, and
|
||||
# regaining authority left it that way. On the server that silently
|
||||
# broke everything downstream — hands couldn't pick the item up again,
|
||||
# and a station snap zone would "snap" it (emitting has_picked_up, so
|
||||
# e.g. a plate still got marked dirty) while pick_up() bailed out on
|
||||
# the disabled item, leaving the zone holding an item with no grab
|
||||
# driver that then fell out of the station.
|
||||
if _pickable.enabled != _original_enabled:
|
||||
if NetworkManager.is_online():
|
||||
print("%s: reclaiming ownership, restoring enabled %s->%s" % [
|
||||
_pickable.name, _pickable.enabled, _original_enabled
|
||||
])
|
||||
_pickable.enabled = _original_enabled
|
||||
return
|
||||
# A net_held_by/position sync update can race ahead of the
|
||||
# authority-handoff RPC that's about to confirm a grab we just made
|
||||
# optimistically (they travel on different channels with no ordering
|
||||
# guarantee). Don't let a stale sync value yank an item out of our own
|
||||
# hand mid-grab — only an explicit force_release_item rejection, or
|
||||
# actually losing authority for real, should end a grab we initiated.
|
||||
if _pickable.is_picked_up() and _pickable.get_picked_up_by() is XRToolsFunctionPickup:
|
||||
# Log once per grab, not once per tick.
|
||||
if NetworkManager.is_online() and not _grab_race_logged:
|
||||
_grab_race_logged = true
|
||||
print(
|
||||
"%s: ignoring non-authority sync (net_held_by=%d) — still actively held by our own hand (grab-race guard)" % [
|
||||
_pickable.name, net_held_by
|
||||
]
|
||||
)
|
||||
return
|
||||
_grab_race_logged = false
|
||||
# Someone else owns it: stop simulating locally, just follow the sync.
|
||||
if _pickable.is_picked_up():
|
||||
if NetworkManager.is_online():
|
||||
print(
|
||||
"%s: was held by %s on this peer, but authority now says peer %d owns it — force-dropping" % [
|
||||
_pickable.name, _holder_desc(), net_held_by
|
||||
]
|
||||
)
|
||||
_pickable.drop()
|
||||
# Bail out when we're already in the follow-the-sync state. Without this the
|
||||
# writes below (and the line logged with them) repeated every tick for every
|
||||
# item on every non-authority peer — 90% of the log, plus four redundant
|
||||
# physics-property writes per item per tick. The comparison also means we
|
||||
# still re-apply if something else perturbs the state (e.g. let_go()
|
||||
# restoring the collision mask after a force-drop).
|
||||
var want_enabled := (net_held_by == 0)
|
||||
if _pickable.freeze \
|
||||
and _pickable.freeze_mode == RigidBody3D.FREEZE_MODE_KINEMATIC \
|
||||
and _pickable.collision_mask == 0 \
|
||||
and _pickable.enabled == want_enabled:
|
||||
return
|
||||
if NetworkManager.is_online():
|
||||
print("%s: freezing (non-authority, owner=peer %d)" % [_pickable.name, net_held_by])
|
||||
_pickable.freeze = true
|
||||
_pickable.freeze_mode = RigidBody3D.FREEZE_MODE_KINEMATIC
|
||||
_pickable.collision_mask = 0
|
||||
_pickable.enabled = want_enabled
|
||||
|
||||
|
||||
## Local hand grab (not a station snap zone, which is server-only): request
|
||||
## authority immediately so the throw/drop can be reconciled, but let the grab
|
||||
## happen instantly here rather than waiting on the round trip.
|
||||
func _on_picked_up(_p) -> void:
|
||||
var by := _pickable.get_picked_up_by()
|
||||
if not (by is XRToolsFunctionPickup):
|
||||
# e.g. a station snap zone grabbed it (server-side auto-snap, or the
|
||||
# addon's own "grab out of a snap zone" shortcut mid-cascade) — not a
|
||||
# player-initiated hand grab, so no authority request from here.
|
||||
if NetworkManager.is_online():
|
||||
print("%s picked up by %s (not a hand) — no authority request" % [_pickable.name, _holder_desc()])
|
||||
return
|
||||
if NetworkManager.is_online():
|
||||
print("%s grabbed by hand (authority was peer %d), requesting authority" % [_pickable.name, net_held_by])
|
||||
NetworkManager.request_item_authority_from(_pickable.get_path())
|
||||
|
||||
|
||||
func _on_dropped(_p) -> void:
|
||||
# Only forward if we're actually still the authority — a drop caused by
|
||||
# apply_held_state() losing authority (see above) must not re-report.
|
||||
if not is_multiplayer_authority():
|
||||
if NetworkManager.is_online():
|
||||
print("%s dropped locally, but we aren't its authority (peer %d is) — not reporting" % [_pickable.name, net_held_by])
|
||||
return
|
||||
if NetworkManager.is_online():
|
||||
print("%s dropped, reporting release to server (lin=%s ang=%s)" % [
|
||||
_pickable.name, _pickable.linear_velocity, _pickable.angular_velocity
|
||||
])
|
||||
# Send our own final transform too: we were the authority until now, and the
|
||||
# server's copy may not have received our last position sync yet.
|
||||
NetworkManager.release_item_authority_from(
|
||||
_pickable.get_path(), _pickable.linear_velocity, _pickable.angular_velocity,
|
||||
_pickable.global_transform
|
||||
)
|
||||
|
||||
|
||||
## Human-readable description of what's currently holding this item on THIS
|
||||
## peer, for diagnosing desyncs between a hand's own "what am I holding"
|
||||
## bookkeeping and the item's actual grab state (see the snap-zone-grab race
|
||||
## in NetworkManager._try_snap_into_station for a real example).
|
||||
func _holder_desc() -> String:
|
||||
if not _pickable or not _pickable.is_picked_up():
|
||||
return "loose"
|
||||
var by := _pickable.get_picked_up_by()
|
||||
if not by:
|
||||
return "held(no grabber?)"
|
||||
if by is XRToolsFunctionPickup:
|
||||
return "hand(%s)" % by.get_path()
|
||||
if by is XRToolsSnapZone:
|
||||
var station := by.get_parent()
|
||||
return "zone(%s)" % (station.name if station else str(by.get_path()))
|
||||
return "other(%s: %s)" % [by.get_class(), by.get_path()]
|
||||
|
||||
Reference in New Issue
Block a user