76 lines
2.6 KiB
GDScript
76 lines
2.6 KiB
GDScript
class_name NetPickable
|
|
extends Node
|
|
|
|
## Networks an XRToolsPickable (RigidBody3D). Default authority is the server,
|
|
## which simulates free items and syncs their transform. Grabbing an item with a
|
|
## hand transfers authority to the grabbing client (its local kinematic grab
|
|
## driver then drives the synced transform); releasing returns authority and the
|
|
## throw velocity to the server. Non-authority peers keep the body frozen and
|
|
## follow the synced transform, so only one machine ever simulates an item.
|
|
|
|
@onready var _item: XRToolsPickable = get_parent() as XRToolsPickable
|
|
|
|
var net_held_by := 0 # peer id currently holding (0 = free); kept consistent via NetworkManager
|
|
var _was_authority := true
|
|
|
|
|
|
func _ready() -> void:
|
|
if not _item:
|
|
push_error("NetPickable must be a child of an XRToolsPickable")
|
|
return
|
|
_item.grabbed.connect(_on_grabbed)
|
|
_item.released.connect(_on_released)
|
|
_was_authority = _item.is_multiplayer_authority()
|
|
_apply_authority_state()
|
|
|
|
|
|
## Enable the item only where it is free or where we are its current holder, so
|
|
## a peer's snap zone / hand never grabs an item another peer is holding. Called
|
|
## by NetworkManager whenever net_held_by changes. Note: net_held_by is set per
|
|
## peer by RPC (not by a synchronizer), so the holder keeps its item enabled
|
|
## while everyone else disables their copy.
|
|
func apply_held_state() -> void:
|
|
if not _item:
|
|
return
|
|
_item.enabled = net_held_by == 0 or net_held_by == multiplayer.get_unique_id()
|
|
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if not NetworkManager.is_online():
|
|
return
|
|
var mine := _item.is_multiplayer_authority()
|
|
if mine != _was_authority:
|
|
_was_authority = mine
|
|
_apply_authority_state()
|
|
|
|
|
|
# Configure physics for whether we own this item or merely mirror it.
|
|
func _apply_authority_state() -> void:
|
|
if not _item:
|
|
return
|
|
if _item.is_multiplayer_authority():
|
|
# We own physics: simulate the item whenever it is not held.
|
|
if not _item.is_picked_up():
|
|
_item.freeze = false
|
|
else:
|
|
# Someone else owns it: follow the synced transform kinematically.
|
|
_item.freeze = true
|
|
_item.freeze_mode = RigidBody3D.FREEZE_MODE_KINEMATIC
|
|
|
|
|
|
func _on_grabbed(_pickable: Node3D, by: Node3D) -> void:
|
|
if not NetworkManager.is_online():
|
|
return
|
|
# Only hand grabs transfer authority; snap-zone grabs are server-driven.
|
|
if not (by is XRToolsFunctionPickup):
|
|
return
|
|
NetworkManager.request_item_authority.rpc_id(1, _item.get_path())
|
|
|
|
|
|
func _on_released(_pickable: Node3D, by: Node3D) -> void:
|
|
if not NetworkManager.is_online():
|
|
return
|
|
if not (by is XRToolsFunctionPickup):
|
|
return
|
|
NetworkManager.release_item_authority.rpc_id(1, _item.get_path(), _item.linear_velocity, _item.angular_velocity)
|