Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| db1bad562d | |||
| dce694dac4 | |||
| 68b6d4d1dc | |||
| 6933e17f3a |
@@ -8,7 +8,7 @@
|
||||
[ext_resource type="Script" uid="uid://dvobm6vcfnqe8" path="res://addons/godot-xr-tools/hands/poses/hand_pose_settings.gd" id="4_dhtvl"]
|
||||
[ext_resource type="PackedScene" uid="uid://ctw7nbntd5pcj" path="res://addons/godot-xr-tools/objects/grab_points/grab_point_hand_right.tscn" id="5_vlcpl"]
|
||||
[ext_resource type="Animation" uid="uid://d1xnpyc08njjx" path="res://addons/godot-xr-tools/hands/animations/right/Grip 4.res" id="6_el51w"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_kek77"]
|
||||
height = 0.0635376
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="7_mde49"]
|
||||
[ext_resource type="PackedScene" uid="uid://3lr2dhy62rhk" path="res://prefabs/combinable_item.tscn" id="8_wmrff"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="10_6ypk4"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_gkni8"]
|
||||
height = 0.1
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://3lr2dhy62rhk" path="res://prefabs/combinable_item.tscn" id="7_bdp75"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="10_uygc5"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="11_vjw41"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_m1xbq"]
|
||||
size = Vector3(0.1, 0.1, 0.1)
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
[ext_resource type="Animation" uid="uid://d1xnpyc08njjx" path="res://addons/godot-xr-tools/hands/animations/right/Grip 4.res" id="7_wqxjj"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="10_l4hsd"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="10_yxtxs"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_fco8w"]
|
||||
height = 0.0338974
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://3lr2dhy62rhk" path="res://prefabs/combinable_item.tscn" id="7_wb51u"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="8_t9y3x"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="10_46e7r"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_vlqg6"]
|
||||
height = 0.10708985
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="10_6p8pj"]
|
||||
[ext_resource type="PackedScene" uid="uid://3lr2dhy62rhk" path="res://prefabs/combinable_item.tscn" id="10_ut7mg"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="10_zz42p"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="20_netpk"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="20_netpk"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_fco8w"]
|
||||
height = 0.04777527
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://ctw7nbntd5pcj" path="res://addons/godot-xr-tools/objects/grab_points/grab_point_hand_right.tscn" id="5_cu6ps"]
|
||||
[ext_resource type="Animation" uid="uid://d1xnpyc08njjx" path="res://addons/godot-xr-tools/hands/animations/right/Grip 4.res" id="6_jq7iw"]
|
||||
[ext_resource type="PackedScene" uid="uid://bfj80jh13t6e5" path="res://prefabs/food_item.tscn" id="7_fn8yh"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="8_f6bya"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="8_f6bya"]
|
||||
[ext_resource type="PackedScene" uid="uid://b5ukku8i0hilb" path="res://prefabs/despawning_item.tscn" id="9_4uadj"]
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_fco8w"]
|
||||
|
||||
+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()]
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
extends MultiplayerSpawner
|
||||
|
||||
@export var kitchen_scene: PackedScene
|
||||
|
||||
func _ready() -> void:
|
||||
if not kitchen_scene:
|
||||
push_error("StationSpawner missing kitchen_scene")
|
||||
if NetworkManager.is_server():
|
||||
|
||||
# Later we will do procedural generation here.
|
||||
# For now we just load a scene.
|
||||
var kitchen = kitchen_scene.instantiate()
|
||||
get_node(spawn_path).add_child(kitchen)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dw3bfum87xs55
|
||||
@@ -34,7 +34,7 @@ func _process(delta: float) -> void:
|
||||
# if we're held or moving, reset timer and return
|
||||
if _pickable.get_picked_up_by() or _rigid.linear_velocity.length_squared() > pow(minimum_speed_square,2):
|
||||
_time_left = time_to_despawn
|
||||
_set_visible(true)
|
||||
get_parent().visible(true)
|
||||
return
|
||||
|
||||
|
||||
@@ -50,14 +50,4 @@ func _process(delta: float) -> void:
|
||||
|
||||
# Make item blink in and out before despawning
|
||||
if _time_left < time_to_despawn / 2:
|
||||
_set_visible(int(_time_left * 5.0) % 2 == 0)
|
||||
|
||||
|
||||
# `visible` is not a replicated property, so a blink driven only here would make
|
||||
# the item flicker on the host and stay solid on clients. Until it is synced,
|
||||
# only warn when there is nobody else to disagree with.
|
||||
func _set_visible(value: bool) -> void: # TODO: Fix blinking
|
||||
if true:# NetworkManager.is_online():
|
||||
get_parent().visible = true
|
||||
return
|
||||
get_parent().visible = value
|
||||
get_parent().visible(int(_time_left * 5.0) % 2 == 0)
|
||||
|
||||
@@ -45,6 +45,9 @@ script = ExtResource("1_6gspb")
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1177077250]
|
||||
environment = SubResource("Environment_bvwq1")
|
||||
|
||||
[node name="XROrigin3D" parent="." unique_id=2055526621 groups=["local_xr_origin"] instance=ExtResource("6_b4aof")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9667189, 0)
|
||||
|
||||
[node name="GameOverPanel" parent="." unique_id=1234658657 instance=ExtResource("3_8n1fs")]
|
||||
transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -2.0281777, 2.8122003, 0)
|
||||
scene = ExtResource("4_6gspb")
|
||||
@@ -56,9 +59,6 @@ scene_properties_keys = PackedStringArray("game_over_panel.gd")
|
||||
[node name="controler" type="Node" parent="GameOverPanel" unique_id=803158176]
|
||||
script = ExtResource("5_qmys3")
|
||||
|
||||
[node name="XROrigin3D" parent="." unique_id=2055526621 groups=["local_xr_origin"] instance=ExtResource("6_b4aof")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.9667189, 0)
|
||||
|
||||
[node name="WorldContent" type="Node3D" parent="." unique_id=262398468]
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1376644384]
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://clw8ai6kngqxb"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b4ldkd3ngs0vp" path="res://scripts/main.gd" id="1_0xm2m"]
|
||||
[ext_resource type="PackedScene" uid="uid://j5s5wus7tuhb" path="res://prefabs/xr_origin.tscn" id="2_h2yge"]
|
||||
[ext_resource type="Material" uid="uid://cmia50cfqxxo4" path="res://textures/dev_material_3d.tres" id="3_272bh"]
|
||||
[ext_resource type="PackedScene" uid="uid://e4i6o5oriecx" path="res://items/pickupcube.tscn" id="3_h2yge"]
|
||||
[ext_resource type="PackedScene" uid="uid://j7caslh27nor" path="res://stations/Hob.tscn" id="3_lquwl"]
|
||||
[ext_resource type="PackedScene" uid="uid://efaec6ymgabo" path="res://stations/Counter.tscn" id="4_7mycd"]
|
||||
[ext_resource type="Sky" uid="uid://c1abtpwhdm2d5" path="res://textures/sky/NightSkyHDRI009_16K.tres" id="6_7mycd"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpot5qie20vf6" path="res://items/burger.tscn" id="8_5vw27"]
|
||||
[ext_resource type="PackedScene" uid="uid://bp3v1jl8pctro" path="res://containers/plate.tscn" id="9_kek77"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_vlqg6"]
|
||||
size = Vector3(5, 0.1, 5)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_24d3s"]
|
||||
material = ExtResource("3_272bh")
|
||||
size = Vector3(5, 0.1, 5)
|
||||
|
||||
[sub_resource type="Environment" id="Environment_bvwq1"]
|
||||
background_mode = 2
|
||||
sky = ExtResource("6_7mycd")
|
||||
reflected_light_source = 2
|
||||
tonemap_mode = 3
|
||||
ssr_enabled = true
|
||||
ssao_enabled = true
|
||||
ssil_enabled = true
|
||||
sdfgi_enabled = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_kek77"]
|
||||
size = Vector3(5.1, 10, 0.1)
|
||||
|
||||
[node name="Main" type="Node3D" unique_id=1312265607]
|
||||
script = ExtResource("1_0xm2m")
|
||||
|
||||
[node name="XROrigin3D" parent="." unique_id=2055526621 instance=ExtResource("2_h2yge")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.8820953, 0)
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1376644384]
|
||||
transform = Transform3D(-0.8660254, -0.43301278, 0.24999999, 0.3022632, -0.05510214, 0.9516306, -0.39829266, 0.8997021, 0.17860365, 0, 0, 0)
|
||||
|
||||
[node name="Floor" type="StaticBody3D" parent="." unique_id=122279514]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.05, 0)
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor" unique_id=958841648]
|
||||
shape = SubResource("BoxShape3D_vlqg6")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Floor/CollisionShape3D" unique_id=1939997056]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00018164515, 0.0006352663, -0.002532661)
|
||||
mesh = SubResource("BoxMesh_24d3s")
|
||||
|
||||
[node name="Hob" parent="." unique_id=1687971542 instance=ExtResource("3_lquwl")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.29336345, 0.8981018, -1.484)
|
||||
|
||||
[node name="Counter" parent="." unique_id=1487893288 instance=ExtResource("4_7mycd")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.73186195, 0.8981018, -1.484)
|
||||
|
||||
[node name="PickableObject" parent="." unique_id=1675596942 instance=ExtResource("3_h2yge")]
|
||||
transform = Transform3D(1, 0, -2.2351742e-08, -2.9802322e-08, 1.0000001, 0, -2.6077032e-08, 1.4901161e-08, 1, -0.70505303, 1.5761534, -1.0317149)
|
||||
|
||||
[node name="PickableObject2" parent="." unique_id=713087634 instance=ExtResource("3_h2yge")]
|
||||
transform = Transform3D(1, 0, -2.2351742e-08, -2.9802322e-08, 1.0000001, 0, -2.6077032e-08, 1.4901161e-08, 1, 0.28315204, 1.5607953, -1.0657519)
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1177077250]
|
||||
environment = SubResource("Environment_bvwq1")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=812168256]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.2856425, 1.4939771, -1.4959787)
|
||||
visible = false
|
||||
light_color = Color(1, 0.38431373, 0, 1)
|
||||
light_energy = 1.784
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="burger" parent="." unique_id=1554047278 instance=ExtResource("8_5vw27")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.482068, -1.2252241)
|
||||
|
||||
[node name="Plate" parent="." unique_id=190487773 instance=ExtResource("9_kek77")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.22971058, 1.699144, -1.8499806)
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=82945083]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=1909896160]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.688614, 2.5070028)
|
||||
shape = SubResource("BoxShape3D_kek77")
|
||||
|
||||
[node name="CollisionShape3D2" type="CollisionShape3D" parent="StaticBody3D" unique_id=39538720]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 4.688614, -2.5446289)
|
||||
shape = SubResource("BoxShape3D_kek77")
|
||||
|
||||
[node name="CollisionShape3D3" type="CollisionShape3D" parent="StaticBody3D" unique_id=1984048740]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -2.525816, 4.688614, -0.018813243)
|
||||
shape = SubResource("BoxShape3D_kek77")
|
||||
|
||||
[node name="CollisionShape3D4" type="CollisionShape3D" parent="StaticBody3D" unique_id=1484088721]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 2.525816, 4.688614, -0.018813023)
|
||||
shape = SubResource("BoxShape3D_kek77")
|
||||
@@ -4,12 +4,14 @@
|
||||
[ext_resource type="Sky" uid="uid://c1abtpwhdm2d5" path="res://textures/sky/NightSkyHDRI009_16K.tres" id="2_rtw2f"]
|
||||
[ext_resource type="PackedScene" uid="uid://j5s5wus7tuhb" path="res://prefabs/xr_origin.tscn" id="3_tbmy8"]
|
||||
[ext_resource type="PackedScene" uid="uid://clujaf3u776a3" path="res://addons/godot-xr-tools/objects/viewport_2d_in_3d.tscn" id="4_jk1qb"]
|
||||
[ext_resource type="PackedScene" path="res://ui/main_menu_panel.tscn" id="5_rtw2f"]
|
||||
[ext_resource type="PackedScene" uid="uid://jispfagyh1di" path="res://ui/main_menu_panel.tscn" id="5_rtw2f"]
|
||||
[ext_resource type="Material" uid="uid://cmia50cfqxxo4" path="res://textures/dev_material_3d.tres" id="6_oa1go"]
|
||||
|
||||
[sub_resource type="Environment" id="Environment_bvwq1"]
|
||||
background_mode = 2
|
||||
sky = ExtResource("2_rtw2f")
|
||||
ambient_light_color = Color(1, 1, 1, 1)
|
||||
ambient_light_sky_contribution = 0.49
|
||||
reflected_light_source = 2
|
||||
ssr_enabled = true
|
||||
ssao_enabled = true
|
||||
@@ -17,11 +19,11 @@ ssil_enabled = true
|
||||
sdfgi_enabled = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_vlqg6"]
|
||||
size = Vector3(115, 0.1, 15)
|
||||
size = Vector3(50, 0.1, 50)
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_24d3s"]
|
||||
material = ExtResource("6_oa1go")
|
||||
size = Vector3(15, 0.1, 15)
|
||||
size = Vector3(50, 0.1, 50)
|
||||
|
||||
[node name="Main" type="Node3D" unique_id=1312265607]
|
||||
script = ExtResource("1_rtw2f")
|
||||
@@ -37,7 +39,6 @@ transform = Transform3D(5, 0, 0, 0, 5, 0, 0, 0, 5, 0.36171648, 2.0238447, -1.975
|
||||
screen_size = Vector2(0.6, 0.4)
|
||||
scene = ExtResource("5_rtw2f")
|
||||
viewport_size = Vector2(600, 400)
|
||||
transparent = 1
|
||||
scene_properties_keys = PackedStringArray("main_menu_panel.gd")
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1376644384]
|
||||
|
||||
+44
-31
@@ -1,8 +1,22 @@
|
||||
[gd_scene format=3 uid="uid://c30i6h32w8p47"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b4ldkd3ngs0vp" path="res://scripts/main.gd" id="1_4vwpp"]
|
||||
[ext_resource type="PackedScene" uid="uid://j5s5wus7tuhb" path="res://prefabs/xr_origin.tscn" id="1_pjkx2"]
|
||||
[ext_resource type="Material" uid="uid://cmia50cfqxxo4" path="res://textures/dev_material_3d.tres" id="2_pjkx2"]
|
||||
[ext_resource type="Sky" uid="uid://c1abtpwhdm2d5" path="res://textures/sky/NightSkyHDRI009_16K.tres" id="3_4vwpp"]
|
||||
[ext_resource type="Script" uid="uid://dw3bfum87xs55" path="res://net/stations_spawner.gd" id="5_36d77"]
|
||||
[ext_resource type="PackedScene" uid="uid://cey1s10v2xufq" path="res://content/kitchen_layouts/small_line.tscn" id="6_uvo4r"]
|
||||
|
||||
[sub_resource type="Environment" id="Environment_uvo4r"]
|
||||
background_mode = 2
|
||||
sky = ExtResource("3_4vwpp")
|
||||
ambient_light_color = Color(1, 1, 1, 1)
|
||||
ambient_light_sky_contribution = 0.89
|
||||
reflected_light_source = 2
|
||||
ssr_enabled = true
|
||||
ssao_enabled = true
|
||||
ssil_enabled = true
|
||||
sdfgi_enabled = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_vlqg6"]
|
||||
size = Vector3(15, 0.1, 15)
|
||||
@@ -11,19 +25,14 @@ size = Vector3(15, 0.1, 15)
|
||||
material = ExtResource("2_pjkx2")
|
||||
size = Vector3(15, 0.1, 15)
|
||||
|
||||
[sub_resource type="Environment" id="Environment_bvwq1"]
|
||||
background_mode = 2
|
||||
sky = ExtResource("3_4vwpp")
|
||||
reflected_light_source = 2
|
||||
ssr_enabled = true
|
||||
ssao_enabled = true
|
||||
ssil_enabled = true
|
||||
sdfgi_enabled = true
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_arao0"]
|
||||
size = Vector3(15, 20, 0.1)
|
||||
|
||||
[node name="Main" type="Node3D" unique_id=1312265607]
|
||||
script = ExtResource("1_4vwpp")
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1177077250]
|
||||
environment = SubResource("Environment_uvo4r")
|
||||
|
||||
[node name="XROrigin3D" parent="." unique_id=2055526621 groups=["local_xr_origin"] instance=ExtResource("1_pjkx2")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.36171648, 0, 0.81835127)
|
||||
@@ -41,34 +50,38 @@ shape = SubResource("BoxShape3D_vlqg6")
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00018164515, 0.0006352663, -0.002532661)
|
||||
mesh = SubResource("BoxMesh_24d3s")
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=1177077250]
|
||||
environment = SubResource("Environment_bvwq1")
|
||||
[node name="InvisibleWalls" type="StaticBody3D" parent="." unique_id=4404969]
|
||||
|
||||
[node name="WorldContent" type="Node3D" parent="." unique_id=291550153]
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="InvisibleWalls" unique_id=998476672]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.197384, 7.589958)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="Players" type="Node3D" parent="." unique_id=1595463693]
|
||||
[node name="CollisionShape3D2" type="CollisionShape3D" parent="InvisibleWalls" unique_id=340303902]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.197384, -7.509142)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="CollisionShape3D3" type="CollisionShape3D" parent="InvisibleWalls" unique_id=1193703037]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -7.438612, 9.197384, -0.018813243)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="CollisionShape3D4" type="CollisionShape3D" parent="InvisibleWalls" unique_id=1431367494]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 7.502467, 9.197384, -0.018813023)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="ItemsSpawner" type="MultiplayerSpawner" parent="." unique_id=627119248]
|
||||
spawn_path = NodePath("../WorldContent")
|
||||
_spawnable_scenes = PackedStringArray("uid://dpot5qie20vf6", "uid://b3m2ag8g5rj4r", "uid://c0lknik4noobs", "uid://b3m2ag8g5rj4r", "uid://cfc4ho67u4r5e", "uid://e4i6o5oriecx")
|
||||
spawn_path = NodePath("..")
|
||||
|
||||
[node name="StationsSpawner" type="MultiplayerSpawner" parent="." unique_id=240083159]
|
||||
_spawnable_scenes = PackedStringArray("uid://c6rift56ql3f8", "uid://efaec6ymgabo", "uid://bbg7dwsbxxh1t", "uid://cnjwtnhwh0i8q", "uid://j7caslh27nor", "uid://ck5tuftqmyiue", "uid://cwnwo4i28upap", "uid://dvrk268s7gkxh", "uid://caf0xanmxbshy")
|
||||
spawn_path = NodePath("../Stations")
|
||||
script = ExtResource("5_36d77")
|
||||
kitchen_scene = ExtResource("6_uvo4r")
|
||||
|
||||
[node name="Stations" type="Node3D" parent="." unique_id=1991170324]
|
||||
|
||||
[node name="PlayersSpawner" type="MultiplayerSpawner" parent="." unique_id=106645565]
|
||||
_spawnable_scenes = PackedStringArray("res://Player/net_player.tscn")
|
||||
spawn_path = NodePath("../Players")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=4404969]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=998476672]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.197384, 7.589958)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="CollisionShape3D2" type="CollisionShape3D" parent="StaticBody3D" unique_id=340303902]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9.197384, -7.509142)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="CollisionShape3D3" type="CollisionShape3D" parent="StaticBody3D" unique_id=1193703037]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, -7.438612, 9.197384, -0.018813243)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
|
||||
[node name="CollisionShape3D4" type="CollisionShape3D" parent="StaticBody3D" unique_id=1431367494]
|
||||
transform = Transform3D(-4.371139e-08, 0, -1, 0, 1, 0, 1, 0, -4.371139e-08, 7.502467, 9.197384, -0.018813023)
|
||||
shape = SubResource("BoxShape3D_arao0")
|
||||
[node name="Players" type="Node3D" parent="." unique_id=1595463693]
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
radius = 0.3
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_24d3s"]
|
||||
height = 1.0
|
||||
radius = 0.3
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_vlqg6"]
|
||||
@@ -30,6 +31,7 @@ metadata/_custom_type_script = "uid://cquqe4f1m1sw6"
|
||||
shape = SubResource("SphereShape3D_xmbo2")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1470079357]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
shape = SubResource("CylinderShape3D_24d3s")
|
||||
|
||||
[node name="SlideOffDome" parent="." unique_id=1427609426 instance=ExtResource("4_rf1b2")]
|
||||
@@ -37,7 +39,9 @@ shape = SubResource("CylinderShape3D_24d3s")
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=1746426494]
|
||||
|
||||
[node name="CSGCylinder3D" type="CSGCylinder3D" parent="CSGCombiner3D" unique_id=858311379]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
radius = 0.3
|
||||
height = 1.0
|
||||
sides = 32
|
||||
|
||||
[node name="CSGCombiner3DBuns" type="CSGCombiner3D" parent="CSGCombiner3D" unique_id=1459017790]
|
||||
|
||||
@@ -21,10 +21,11 @@ albedo_color = Color(1.1759251, 0.741115, 0.5692133, 1)
|
||||
[node name="Counter" type="StaticBody3D" unique_id=1487893288 groups=["station"]]
|
||||
|
||||
[node name="CollisionShape3DCuttingBoard" type="CollisionShape3D" parent="." unique_id=706340473]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.49513447, 0)
|
||||
shape = SubResource("BoxShape3D_24d3s")
|
||||
|
||||
[node name="XRToolsSnapZone" type="Area3D" parent="." unique_id=1647380272 groups=["station"]]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5608841, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0560187, 0)
|
||||
collision_layer = 65536
|
||||
collision_mask = 65540
|
||||
script = ExtResource("1_dlkho")
|
||||
@@ -39,6 +40,7 @@ shape = SubResource("BoxShape3D_1ntq7")
|
||||
[node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="XRToolsSnapZone" unique_id=1786831079]
|
||||
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=451757283]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.49513447, 0)
|
||||
use_collision = true
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="CSGCombiner3D" unique_id=305755330]
|
||||
|
||||
+7
-4
@@ -13,7 +13,7 @@
|
||||
[ext_resource type="Script" uid="uid://dvobm6vcfnqe8" path="res://addons/godot-xr-tools/hands/poses/hand_pose_settings.gd" id="11_4me6o"]
|
||||
[ext_resource type="PackedScene" uid="uid://ctw7nbntd5pcj" path="res://addons/godot-xr-tools/objects/grab_points/grab_point_hand_right.tscn" id="12_tlp5u"]
|
||||
[ext_resource type="Animation" uid="uid://d1xnpyc08njjx" path="res://addons/godot-xr-tools/hands/animations/right/Grip 4.res" id="13_vttml"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://Net/net_pickable.gd" id="14_glc7b"]
|
||||
[ext_resource type="Script" uid="uid://bwd0pe2udb5xo" path="res://net/net_pickable.gd" id="14_glc7b"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_kdxnr"]
|
||||
size = Vector3(0.6, 1, 0.6)
|
||||
@@ -208,10 +208,11 @@ properties/2/replication_mode = 1
|
||||
script = ExtResource("1_34vy5")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1283846023]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.50658906, 0)
|
||||
shape = SubResource("BoxShape3D_kdxnr")
|
||||
|
||||
[node name="XRToolsSnapZone" type="Area3D" parent="." unique_id=538157739 groups=["station"]]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5270121, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0336012, 0)
|
||||
collision_layer = 65536
|
||||
collision_mask = 65540
|
||||
script = ExtResource("2_xy5oe")
|
||||
@@ -227,6 +228,7 @@ shape = SubResource("BoxShape3D_7uuqv")
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.022546828, -0.5270121, 0.36528283)
|
||||
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=70901698]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.50658906, 0)
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1111505172]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.00894776)
|
||||
@@ -311,18 +313,19 @@ size = Vector3(0.55, 0.096, 0.14)
|
||||
replication_config = SubResource("SceneReplicationConfig_np_hob")
|
||||
|
||||
[node name="ProgressBar3D" parent="." unique_id=654673176 instance=ExtResource("5_m4alr")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.74736404, -0.09216304)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.2539531, -0.09216304)
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=1525460719]
|
||||
libraries/ = SubResource("AnimationLibrary_ac5f3")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="." unique_id=1618457623]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5053487, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.0119377, 0)
|
||||
light_color = Color(1, 0.13725491, 0, 1)
|
||||
light_energy = 0.0
|
||||
omni_range = 0.5
|
||||
|
||||
[node name="Movement" type="Node3D" parent="." unique_id=1818087979]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.50658906, 0)
|
||||
script = ExtResource("7_tjce5")
|
||||
|
||||
[node name="MoveHandle" type="RigidBody3D" parent="Movement" unique_id=1675596942 groups=["platalbe_item"]]
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://cwwnbx5uat3fw"]
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" unique_id=725518829]
|
||||
@@ -9,6 +9,7 @@
|
||||
radius = 0.3
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_24d3s"]
|
||||
height = 1.0
|
||||
radius = 0.3
|
||||
|
||||
[node name="PlateDispenser" type="StaticBody3D" unique_id=710538846]
|
||||
@@ -27,6 +28,7 @@ metadata/_custom_type_script = "uid://cquqe4f1m1sw6"
|
||||
shape = SubResource("SphereShape3D_xmbo2")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=831688237]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
shape = SubResource("CylinderShape3D_24d3s")
|
||||
|
||||
[node name="SlideOffDome" parent="." unique_id=1427609426 instance=ExtResource("4_tfo2i")]
|
||||
@@ -35,7 +37,9 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.061363697, 0)
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=696108271]
|
||||
|
||||
[node name="CSGCylinder3D" type="CSGCylinder3D" parent="CSGCombiner3D" unique_id=1339763385]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
radius = 0.3
|
||||
height = 1.0
|
||||
sides = 32
|
||||
|
||||
[node name="Plates" type="Node3D" parent="." unique_id=1364909922]
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
radius = 0.3
|
||||
|
||||
[sub_resource type="CylinderShape3D" id="CylinderShape3D_24d3s"]
|
||||
height = 1.0
|
||||
radius = 0.3
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_di04w"]
|
||||
@@ -33,12 +34,15 @@ shape = SubResource("SphereShape3D_xmbo2")
|
||||
[node name="SlideOffDome" parent="." unique_id=1427609426 instance=ExtResource("5_mep2a")]
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=378049928]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
shape = SubResource("CylinderShape3D_24d3s")
|
||||
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=1275577257]
|
||||
|
||||
[node name="CSGCylinder3D" type="CSGCylinder3D" parent="CSGCombiner3D" unique_id=1973748218]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.50180435, 0)
|
||||
radius = 0.3
|
||||
height = 1.0
|
||||
sides = 32
|
||||
|
||||
[node name="CSGCylinder3D2" type="CSGCylinder3D" parent="CSGCombiner3D" unique_id=590219609]
|
||||
|
||||
+7
-4
@@ -185,11 +185,11 @@ height = 0.04
|
||||
script = ExtResource("1_7hh4b")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=964735164]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.019470215, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.4605298, 0)
|
||||
shape = SubResource("BoxShape3D_l02yb")
|
||||
|
||||
[node name="XRToolsSnapZone" type="Area3D" parent="." unique_id=1762550988 groups=["station"]]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.44614658, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.92614657, 0)
|
||||
collision_layer = 65536
|
||||
collision_mask = 65540
|
||||
script = ExtResource("2_3ocod")
|
||||
@@ -204,6 +204,7 @@ shape = SubResource("BoxShape3D_ai6d4")
|
||||
[node name="AudioStreamPlayer3D" type="AudioStreamPlayer3D" parent="XRToolsSnapZone" unique_id=1926328538]
|
||||
|
||||
[node name="CSGCombiner3D" type="CSGCombiner3D" parent="." unique_id=311064646]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.48000002, 0)
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="CSGCombiner3D" unique_id=1147823280]
|
||||
size = Vector3(0.6, 1, 0.6)
|
||||
@@ -238,9 +239,10 @@ size = Vector3(1, 1.2053223, 0.352417)
|
||||
replication_config = SubResource("SceneReplicationConfig_np_sink")
|
||||
|
||||
[node name="ProgressBar3D" parent="." unique_id=654673176 instance=ExtResource("4_1pinr")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.8164611, -0.035980098)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.2964611, -0.035980098)
|
||||
|
||||
[node name="bubbles" type="CSGCombiner3D" parent="." unique_id=1624244242]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.48000002, 0)
|
||||
visible = false
|
||||
|
||||
[node name="CSGSphere3D" type="CSGSphere3D" parent="bubbles" unique_id=1363004819]
|
||||
@@ -259,6 +261,7 @@ radius = 0.08
|
||||
material = SubResource("StandardMaterial3D_0gbf8")
|
||||
|
||||
[node name="water" type="CSGCombiner3D" parent="." unique_id=1050686091]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.48000002, 0)
|
||||
visible = false
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="water" unique_id=1224655006]
|
||||
@@ -273,7 +276,7 @@ libraries/ = SubResource("AnimationLibrary_u2x8c")
|
||||
stream = SubResource("AudioStreamWAV_ai6d4")
|
||||
|
||||
[node name="GPUParticles3D" type="GPUParticles3D" parent="." unique_id=1688415718]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0002603531, 0.6479259, -0.03759767)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.0002603531, 1.1279259, -0.03759767)
|
||||
visible = false
|
||||
process_material = SubResource("ParticleProcessMaterial_hrheb")
|
||||
draw_pass_1 = SubResource("SphereMesh_bvwq1")
|
||||
|
||||
+30
-43
@@ -5,9 +5,9 @@ extends Control
|
||||
## multiplayer game, or join one by IP, driving scene switches + NetworkManager
|
||||
## directly.
|
||||
|
||||
const JON_SCENE := "res://Scenes/JonScene.tscn"
|
||||
const MULTIPLAYER_SCENE := "res://Scenes/multiPlayer.tscn"
|
||||
const JON_SCENE := "res://Scenes/jon_scene.tscn"
|
||||
const SETTINGS_PATH := "user://vryhungry_settings.cfg"
|
||||
const MULTIPLAYER_SCENE := "res://Scenes/multiplayer.tscn"
|
||||
|
||||
var _ip := "127.0.0.1"
|
||||
|
||||
@@ -22,10 +22,12 @@ var _ip := "127.0.0.1"
|
||||
@onready var _join_btn: Button = %JoinButton
|
||||
@onready var _back_btn: Button = %BackButton
|
||||
|
||||
# store original text so we can say "connecting..." while pinging server
|
||||
var original_join_text: String
|
||||
|
||||
func _ready() -> void:
|
||||
if _bypass_menu_for_cmdline():
|
||||
return
|
||||
print("Menu Panel _ready")
|
||||
|
||||
for child in _keypad.get_children():
|
||||
if child is Button:
|
||||
child.pressed.connect(_on_key.bind(child.text))
|
||||
@@ -34,45 +36,25 @@ func _ready() -> void:
|
||||
_join_menu_btn.pressed.connect(_show_join_view)
|
||||
_join_btn.pressed.connect(_on_join_pressed)
|
||||
_back_btn.pressed.connect(_show_root_view)
|
||||
|
||||
NetworkManager.connection_failed.connect(_handle_connection_failed)
|
||||
_load_ip()
|
||||
_show_root_view()
|
||||
_refresh_ip()
|
||||
# Show why we're back here, if the last session ended unexpectedly
|
||||
# (dropped connection, failed join). Pulled rather than pushed, since
|
||||
# NetworkManager may have set this before this panel even existed.
|
||||
var status := NetworkManager.take_status()
|
||||
if not status.is_empty():
|
||||
set_status(status)
|
||||
|
||||
|
||||
## --server / --join <ip> on the command line skip straight to the multiplayer
|
||||
## scene, same as the previously headless-tested main.tscn flow.
|
||||
func _bypass_menu_for_cmdline() -> bool:
|
||||
var args := OS.get_cmdline_user_args()
|
||||
if args.has("--server"):
|
||||
NetworkManager.request_host()
|
||||
get_tree().change_scene_to_file.call_deferred(MULTIPLAYER_SCENE)
|
||||
return true
|
||||
if args.has("--join"):
|
||||
var idx := args.find("--join")
|
||||
var addr := "127.0.0.1"
|
||||
if idx + 1 < args.size():
|
||||
addr = args[idx + 1]
|
||||
NetworkManager.request_join(addr)
|
||||
get_tree().change_scene_to_file.call_deferred(MULTIPLAYER_SCENE)
|
||||
return true
|
||||
return false
|
||||
|
||||
original_join_text = _join_btn.text
|
||||
|
||||
func _show_root_view() -> void:
|
||||
print("Menu Panel show root view")
|
||||
_root_view.visible = true
|
||||
_join_view.visible = false
|
||||
|
||||
|
||||
func _show_join_view() -> void:
|
||||
print("Menu Panel show join view")
|
||||
_root_view.visible = false
|
||||
_join_view.visible = true
|
||||
set_status("Enter host IP, then Join")
|
||||
_status.text= "Enter host IP, then Join"
|
||||
_status.add_theme_color_override("font_color", Color.WHITE)
|
||||
|
||||
|
||||
func _on_key(key: String) -> void:
|
||||
@@ -90,22 +72,34 @@ func _refresh_ip() -> void:
|
||||
|
||||
|
||||
func _on_john_pressed() -> void:
|
||||
print("Menu Panel press jon_scene")
|
||||
NetworkManager.leave()
|
||||
get_tree().change_scene_to_file.call_deferred(JON_SCENE)
|
||||
|
||||
|
||||
func _on_host_pressed() -> void:
|
||||
NetworkManager.request_host()
|
||||
get_tree().change_scene_to_file.call_deferred("res://Scenes/JonScene.tscn")
|
||||
print("Menu Panel press host")
|
||||
NetworkManager.host()
|
||||
|
||||
|
||||
func _on_join_pressed() -> void:
|
||||
print("Menu Panel press join")
|
||||
if _ip.is_empty():
|
||||
set_status("Enter an IP first")
|
||||
_status.text= "Enter an IP first"
|
||||
_status.add_theme_color_override("font_color", Color.ORANGE)
|
||||
return
|
||||
_save_ip()
|
||||
NetworkManager.request_join(_ip)
|
||||
get_tree().change_scene_to_file.call_deferred(MULTIPLAYER_SCENE)
|
||||
_join_btn.disabled = true
|
||||
_join_btn.text = "Connecting..."
|
||||
NetworkManager.join(_ip)
|
||||
|
||||
|
||||
func _handle_connection_failed():
|
||||
print("Menu Panel _handle_connection_failed")
|
||||
_status.text= "Connection failed"
|
||||
_status.add_theme_color_override("font_color", Color.RED)
|
||||
_join_btn.disabled = false
|
||||
_join_btn.text = original_join_text
|
||||
|
||||
|
||||
## Remembers the last IP the player tried to join, so the keypad starts
|
||||
@@ -121,10 +115,3 @@ func _save_ip() -> void:
|
||||
cfg.load(SETTINGS_PATH)
|
||||
cfg.set_value("network", "last_ip", _ip)
|
||||
cfg.save(SETTINGS_PATH)
|
||||
|
||||
|
||||
## Called by network_manager (e.g. on connection_failed after a bounce back to
|
||||
## this menu) to show feedback.
|
||||
func set_status(text: String) -> void:
|
||||
if _status:
|
||||
_status.text = text
|
||||
|
||||
+41
-36
@@ -1,97 +1,102 @@
|
||||
[gd_scene format=3]
|
||||
[gd_scene format=3 uid="uid://jispfagyh1di"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://crs47shds8bm4" path="res://ui/main_menu_panel.gd" id="1_panel"]
|
||||
|
||||
[node name="MainMenuPanel" type="Control"]
|
||||
[node name="MainMenuPanel" type="Control" unique_id=1716965055]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_panel")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
[node name="Background" type="ColorRect" parent="." unique_id=417297929]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.09, 0.1, 0.13, 1)
|
||||
|
||||
[node name="Margin" type="MarginContainer" parent="."]
|
||||
[node name="Margin" type="MarginContainer" parent="." unique_id=122841427]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 24
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 24
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="Title" type="Label" parent="Margin"]
|
||||
[node name="Title" type="Label" parent="Margin" unique_id=1426156127]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "VRyHungry"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Status" type="Label" parent="Margin"]
|
||||
[node name="Status" type="Label" parent="Margin" unique_id=672946938]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
theme_override_colors/font_color = Color(0.8, 0.8, 0.5, 1)
|
||||
theme_override_font_sizes/font_size = 16
|
||||
text = ""
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="RootView" type="VBoxContainer" parent="Margin"]
|
||||
[node name="RootView" type="VBoxContainer" parent="Margin" unique_id=1020931014]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 14
|
||||
|
||||
[node name="Spacer" type="Control" parent="Margin/RootView"]
|
||||
[node name="Spacer" type="Control" parent="Margin/RootView" unique_id=1907673970]
|
||||
custom_minimum_size = Vector2(0, 36)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="JohnButton" type="Button" parent="Margin/RootView"]
|
||||
[node name="JohnButton" type="Button" parent="Margin/RootView" unique_id=593797044]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(0, 64)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "Play John's Scene"
|
||||
|
||||
[node name="HostButton" type="Button" parent="Margin/RootView"]
|
||||
[node name="HostButton" type="Button" parent="Margin/RootView" unique_id=2142523398]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(0, 64)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "Host Multiplayer"
|
||||
|
||||
[node name="JoinMenuButton" type="Button" parent="Margin/RootView"]
|
||||
[node name="JoinMenuButton" type="Button" parent="Margin/RootView" unique_id=1379606559]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(0, 64)
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "Join Multiplayer"
|
||||
|
||||
[node name="JoinView" type="VBoxContainer" parent="Margin"]
|
||||
[node name="JoinView" type="VBoxContainer" parent="Margin" unique_id=1763078998]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="Title" type="Label" parent="Margin/JoinView"]
|
||||
[node name="Title" type="Label" parent="Margin/JoinView" unique_id=309630334]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "Join Multiplayer"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="IPLabel" type="Label" parent="Margin/JoinView"]
|
||||
[node name="IPLabel" type="Label" parent="Margin/JoinView" unique_id=108493382]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 34
|
||||
text = "127.0.0.1"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Keypad" type="GridContainer" parent="Margin/JoinView"]
|
||||
[node name="Keypad" type="GridContainer" parent="Margin/JoinView" unique_id=1454498030]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
@@ -99,106 +104,106 @@ theme_override_constants/h_separation = 8
|
||||
theme_override_constants/v_separation = 8
|
||||
columns = 3
|
||||
|
||||
[node name="B1" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B1" type="Button" parent="Margin/JoinView/Keypad" unique_id=535729591]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "1"
|
||||
|
||||
[node name="B2" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B2" type="Button" parent="Margin/JoinView/Keypad" unique_id=439142035]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "2"
|
||||
|
||||
[node name="B3" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B3" type="Button" parent="Margin/JoinView/Keypad" unique_id=20138366]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "3"
|
||||
|
||||
[node name="B4" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B4" type="Button" parent="Margin/JoinView/Keypad" unique_id=1142576108]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "4"
|
||||
|
||||
[node name="B5" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B5" type="Button" parent="Margin/JoinView/Keypad" unique_id=422202098]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "5"
|
||||
|
||||
[node name="B6" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B6" type="Button" parent="Margin/JoinView/Keypad" unique_id=652822802]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "6"
|
||||
|
||||
[node name="B7" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B7" type="Button" parent="Margin/JoinView/Keypad" unique_id=1644038518]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "7"
|
||||
|
||||
[node name="B8" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B8" type="Button" parent="Margin/JoinView/Keypad" unique_id=2009764487]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "8"
|
||||
|
||||
[node name="B9" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B9" type="Button" parent="Margin/JoinView/Keypad" unique_id=809575757]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "9"
|
||||
|
||||
[node name="BDot" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="BDot" type="Button" parent="Margin/JoinView/Keypad" unique_id=2136562018]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "."
|
||||
|
||||
[node name="B0" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="B0" type="Button" parent="Margin/JoinView/Keypad" unique_id=2008425599]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 26
|
||||
text = "0"
|
||||
|
||||
[node name="BDel" type="Button" parent="Margin/JoinView/Keypad"]
|
||||
[node name="BDel" type="Button" parent="Margin/JoinView/Keypad" unique_id=413462771]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "DEL"
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="Margin/JoinView"]
|
||||
[node name="Buttons" type="HBoxContainer" parent="Margin/JoinView" unique_id=2121996996]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="JoinButton" type="Button" parent="Margin/JoinView/Buttons"]
|
||||
[node name="JoinButton" type="Button" parent="Margin/JoinView/Buttons" unique_id=1797756998]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 56)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
custom_minimum_size = Vector2(0, 56)
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "JOIN"
|
||||
|
||||
[node name="BackButton" type="Button" parent="Margin/JoinView/Buttons"]
|
||||
[node name="BackButton" type="Button" parent="Margin/JoinView/Buttons" unique_id=704422546]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 56)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
custom_minimum_size = Vector2(0, 56)
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "BACK"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://clujaf3u776a3"]
|
||||
[gd_scene format=3 uid="uid://clujaf3u776a3"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://eavk7267igp7" path="res://addons/godot-xr-tools/objects/viewport_2d_in_3d.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://cpl5yiyf1ypft" path="res://addons/godot-xr-tools/objects/viewport_2d_in_3d_body.gd" id="2"]
|
||||
@@ -7,41 +7,44 @@
|
||||
resource_local_to_scene = true
|
||||
size = Vector2(3, 2)
|
||||
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_ufe4n"]
|
||||
[sub_resource type="ViewportTexture" id="ViewportTexture_gvc48"]
|
||||
viewport_path = NodePath("Viewport")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_fvml0"]
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_4gj7x"]
|
||||
transparency = 1
|
||||
cull_mode = 2
|
||||
albedo_texture = SubResource("ViewportTexture_ufe4n")
|
||||
albedo_texture = SubResource("ViewportTexture_gvc48")
|
||||
texture_filter = 1
|
||||
|
||||
[sub_resource type="BoxShape3D" id="4"]
|
||||
resource_local_to_scene = true
|
||||
size = Vector3(3, 2, 0.02)
|
||||
|
||||
[node name="Viewport2Din3D" type="Node3D"]
|
||||
[node name="Viewport2Din3D" type="Node3D" unique_id=1136024888]
|
||||
script = ExtResource("1")
|
||||
transparent = 1
|
||||
alpha_scissor_threshold = 0.25
|
||||
unshaded = false
|
||||
filter = true
|
||||
scene_properties_keys = PackedStringArray()
|
||||
|
||||
[node name="Viewport" type="SubViewport" parent="."]
|
||||
[node name="Viewport" type="SubViewport" parent="." unique_id=1205536037]
|
||||
disable_3d = true
|
||||
transparent_bg = true
|
||||
gui_embed_subwindows = true
|
||||
size = Vector2i(300, 200)
|
||||
render_target_update_mode = 1
|
||||
|
||||
[node name="Screen" type="MeshInstance3D" parent="."]
|
||||
[node name="Screen" type="MeshInstance3D" parent="." unique_id=1799394652]
|
||||
mesh = SubResource("1")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_fvml0")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_4gj7x")
|
||||
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="."]
|
||||
[node name="StaticBody3D" type="StaticBody3D" parent="." unique_id=959647424]
|
||||
collision_layer = 5242881
|
||||
collision_mask = 0
|
||||
script = ExtResource("2")
|
||||
viewport_size = Vector2(300, 200)
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D"]
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="StaticBody3D" unique_id=1695382741]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.01)
|
||||
shape = SubResource("4")
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[gd_scene format=3 uid="uid://cey1s10v2xufq"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://j7caslh27nor" path="res://stations/Hob.tscn" id="1_iwheq"]
|
||||
[ext_resource type="PackedScene" uid="uid://dvrk268s7gkxh" path="res://stations/sink.tscn" id="2_ad3hj"]
|
||||
[ext_resource type="PackedScene" uid="uid://efaec6ymgabo" path="res://stations/Counter.tscn" id="3_rsv28"]
|
||||
[ext_resource type="PackedScene" uid="uid://cwnwo4i28upap" path="res://stations/raw_burger_dispenser.tscn" id="4_drmjc"]
|
||||
[ext_resource type="PackedScene" uid="uid://c6rift56ql3f8" path="res://stations/BurgerBunsDispenser.tscn" id="5_ad3hj"]
|
||||
[ext_resource type="PackedScene" uid="uid://ck5tuftqmyiue" path="res://stations/plate_dispenser.tscn" id="6_rsv28"]
|
||||
|
||||
[node name="SmallLine" type="Node3D" unique_id=50655734]
|
||||
|
||||
[node name="Hob" parent="." unique_id=1687971542 instance=ExtResource("1_iwheq")]
|
||||
|
||||
[node name="Sink" parent="." unique_id=2055277359 instance=ExtResource("2_ad3hj")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.2, 0, 0)
|
||||
|
||||
[node name="Counter" parent="." unique_id=1487893288 instance=ExtResource("3_rsv28")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.6, 0, 0)
|
||||
|
||||
[node name="RawBurgerDispenser" parent="." unique_id=235630131 instance=ExtResource("4_drmjc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1.2, 0, 0)
|
||||
|
||||
[node name="BurgerBunsDispenser" parent="." unique_id=1720683779 instance=ExtResource("5_ad3hj")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.6, 0, 0)
|
||||
|
||||
[node name="PlateDispenser" parent="." unique_id=710538846 instance=ExtResource("6_rsv28")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.8, 0, 0)
|
||||
@@ -21,6 +21,8 @@ XRToolsUserSettings="*uid://bqgb8i74tm0t"
|
||||
XRToolsRumbleManager="*uid://by853dk86g1qw"
|
||||
GlobalKeyEvents="*uid://c60unagog5oi1"
|
||||
Signals="*uid://fuux6cxfjwdc"
|
||||
NetworkManager="*uid://da1xwj5tyeii6"
|
||||
Main="uid://b4ldkd3ngs0vp"
|
||||
|
||||
[debug]
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
uid://b5y4a58f517wi
|
||||
@@ -1,10 +0,0 @@
|
||||
extends MultiplayerSpawner
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_spawnable_scene("res://Stations/csg_box_3d.tscn")
|
||||
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
pass
|
||||
@@ -1 +0,0 @@
|
||||
uid://ctsqvhicswj8m
|
||||
+3
-3
@@ -6,15 +6,15 @@ const GAME_MANAGER = preload("res://scripts/game_manager.gd")
|
||||
var xr_interface :XRInterface
|
||||
|
||||
func _ready():
|
||||
print("main: _ready")
|
||||
print("Main _ready")
|
||||
RECIPE_MANAGER.print_all_recipes()
|
||||
|
||||
GAME_MANAGER.meals_in_play = ["hamburger"]
|
||||
|
||||
xr_interface = XRServer.find_interface("OpenXR")
|
||||
if xr_interface and xr_interface.is_initialized():
|
||||
print("OpenXR started :)")
|
||||
print("Main OpenXR started :)")
|
||||
DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED)
|
||||
get_viewport().use_xr = true
|
||||
else:
|
||||
print("OpenXR failed to start. Check your headset...")
|
||||
print("Main OpenXR failed to start. Check your headset...")
|
||||
|
||||
@@ -0,0 +1,357 @@
|
||||
extends Node
|
||||
|
||||
const DEFAULT_PORT := 24565
|
||||
const MAX_CLIENTS := 7
|
||||
const MULTIPLAYER_SCENE := "res://Scenes/multiplayer.tscn"
|
||||
|
||||
|
||||
signal player_joined(peer_id: int)
|
||||
signal player_left(peer_id: int)
|
||||
signal session_started(is_server: bool)
|
||||
signal session_ended()
|
||||
signal connection_failed()
|
||||
|
||||
# World hooks, registered by main.gd once the scene tree exists.
|
||||
var _world: Node = null
|
||||
var _players_spawner: MultiplayerSpawner = null
|
||||
var _items_spawner: MultiplayerSpawner = null
|
||||
var _content_root: Node = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
multiplayer.peer_connected.connect(_on_peer_connected)
|
||||
multiplayer.peer_disconnected.connect(_on_peer_disconnected)
|
||||
multiplayer.connected_to_server.connect(_on_connected_to_server)
|
||||
multiplayer.connection_failed.connect(_on_connection_failed)
|
||||
multiplayer.server_disconnected.connect(_on_server_disconnected)
|
||||
_handle_cmdline()
|
||||
|
||||
# --- Public API ------------------------------------------------------------
|
||||
|
||||
## Start hosting. The host is peer 1 and also plays (listen server).
|
||||
func host(port: int = DEFAULT_PORT) -> Error:
|
||||
var peer := ENetMultiplayerPeer.new()
|
||||
var err := peer.create_server(port, MAX_CLIENTS)
|
||||
if err != OK:
|
||||
print("NetworkManager HOST failed to create_server on port %d: %s" % [port, error_string(err)])
|
||||
return err
|
||||
multiplayer.multiplayer_peer = peer
|
||||
print("NetworkManager HOST started on port %d (peer id %d)" % [port, multiplayer.get_unique_id()])
|
||||
session_started.emit(true)
|
||||
get_tree().change_scene_to_file.call_deferred(MULTIPLAYER_SCENE)
|
||||
return OK
|
||||
|
||||
|
||||
## Join an existing host.
|
||||
func join(address: String = "127.0.0.1", port: int = DEFAULT_PORT) -> Error:
|
||||
var peer := ENetMultiplayerPeer.new()
|
||||
var err := peer.create_client(address, port)
|
||||
if err != OK:
|
||||
print("NetworkManager JOIN failed to create_client %s:%d: %s" % [address, port, error_string(err)])
|
||||
return err
|
||||
multiplayer.multiplayer_peer = peer
|
||||
print("NetworkManager JOIN connecting to %s:%d ..." % [address, port])
|
||||
return OK
|
||||
|
||||
|
||||
## Leave the session and tear down transport.
|
||||
func leave() -> void:
|
||||
_go_offline()
|
||||
print("NetworkManager Session ended")
|
||||
session_ended.emit()
|
||||
|
||||
|
||||
# Restore Godot's default OfflineMultiplayerPeer (rather than leaving the peer
|
||||
# null), so is_multiplayer_authority()/get_unique_id() keep working while we are
|
||||
# back in single-player / menu state.
|
||||
func _go_offline() -> void:
|
||||
if multiplayer.multiplayer_peer:
|
||||
multiplayer.multiplayer_peer.close()
|
||||
multiplayer.multiplayer_peer = OfflineMultiplayerPeer.new()
|
||||
unregister_world()
|
||||
|
||||
|
||||
|
||||
# --- Item grab-authority transfer -----------------------------------------
|
||||
|
||||
## Called by NetPickable when this peer grabs an item by hand. Godot rejects
|
||||
## rpc_id() targeting your own peer id ("RPC on yourself is not allowed"), so
|
||||
## when we ARE the server this runs the logic directly instead of round-
|
||||
## tripping an RPC to ourselves — otherwise every host-side grab/drop was
|
||||
## silently failing to run its server-side half (no denial checks, and
|
||||
## crucially no auto-snap-into-station on release).
|
||||
func request_item_authority_from(item_path: NodePath) -> void:
|
||||
if is_server():
|
||||
_grant_or_reject_item_authority(item_path, multiplayer.get_unique_id())
|
||||
else:
|
||||
_request_item_authority_rpc.rpc_id(1, item_path)
|
||||
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _request_item_authority_rpc(item_path: NodePath) -> void:
|
||||
if not is_server():
|
||||
return
|
||||
_grant_or_reject_item_authority(item_path, multiplayer.get_remote_sender_id())
|
||||
|
||||
|
||||
## Runs on the server (called directly if the requester IS the server, or via
|
||||
## the RPC above otherwise). If the item was snapped into a station, the
|
||||
## station releases it so the grabber cleanly takes ownership.
|
||||
func _grant_or_reject_item_authority(item_path: NodePath, sender: int) -> void:
|
||||
var item := get_node_or_null(item_path)
|
||||
if item:
|
||||
var np := item.get_node_or_null("NetPickable")
|
||||
if np and np.net_held_by != 0 and np.net_held_by != sender:
|
||||
# Already legitimately held by a different live peer: reject the
|
||||
# requester's optimistic client-side grab instead of stealing it.
|
||||
print("NetworkManager request_item_authority: DENIED %s to peer %d (already held by %d)" % [item.name, sender, np.net_held_by])
|
||||
_force_release_item_to(sender, item_path)
|
||||
return
|
||||
print("NetworkManager request_item_authority: granting %s to peer %d" % [str(item.name) if item else str(item_path), sender])
|
||||
# Assign authority + held state first (disables the item on the server so its
|
||||
# snap zone won't re-grab it), then release it from any station.
|
||||
_set_item_authority.rpc(item_path, sender)
|
||||
if item:
|
||||
_release_from_snap_zones(item)
|
||||
|
||||
|
||||
## Called by NetPickable when this peer releases an item, forwarding its throw
|
||||
## velocity so the server can resume simulating it. Same self-RPC issue as
|
||||
## above: runs directly if we're the server.
|
||||
func release_item_authority_from(item_path: NodePath, lin: Vector3, ang: Vector3, xform: Transform3D) -> void:
|
||||
if is_server():
|
||||
_do_release_item_authority(item_path, lin, ang, xform, multiplayer.get_unique_id())
|
||||
else:
|
||||
_release_item_authority_rpc.rpc_id(1, item_path, lin, ang, xform)
|
||||
|
||||
|
||||
@rpc("any_peer", "reliable")
|
||||
func _release_item_authority_rpc(item_path: NodePath, lin: Vector3, ang: Vector3, xform: Transform3D) -> void:
|
||||
if not is_server():
|
||||
return
|
||||
_do_release_item_authority(item_path, lin, ang, xform, multiplayer.get_remote_sender_id())
|
||||
|
||||
|
||||
## Runs on the server. If released next to a station, the server snaps it in
|
||||
## (server-authoritative placement).
|
||||
func _do_release_item_authority(item_path: NodePath, lin: Vector3, ang: Vector3, xform: Transform3D, sender: int) -> void:
|
||||
print("NetworkManager release_item_authority: %s released by peer %d" % [str(item_path), sender])
|
||||
_set_item_authority.rpc(item_path, 1)
|
||||
var item := get_node_or_null(item_path)
|
||||
if item is RigidBody3D:
|
||||
# Adopt the releasing peer's own final transform rather than trusting our
|
||||
# copy's. That peer was the item's authority right up to this moment, and
|
||||
# its position updates travel on the synchronizer's separate, unordered
|
||||
# channel — this reliable RPC routinely overtakes them, leaving our copy
|
||||
# still sitting where the item was BEFORE the peer carried it away. The
|
||||
# snap decision below then reads that stale position and teleports the
|
||||
# item straight back into the station it was just picked up from.
|
||||
item.global_transform = xform
|
||||
item.freeze = false
|
||||
item.linear_velocity = lin
|
||||
item.angular_velocity = ang
|
||||
_try_snap_into_station.call_deferred(item)
|
||||
|
||||
|
||||
# All station snap zones in the world (every XRToolsSnapZone child of a node in
|
||||
# the "station" group — some stations, e.g. Table, have more than one).
|
||||
func _station_snap_zones() -> Array:
|
||||
var zones := []
|
||||
for station in get_tree().get_nodes_in_group("station"):
|
||||
for child in station.get_children():
|
||||
if child is XRToolsSnapZone:
|
||||
zones.append(child)
|
||||
return zones
|
||||
|
||||
|
||||
# If the item is snapped into any station, drop it from that station.
|
||||
func _release_from_snap_zones(item: Node) -> void:
|
||||
for zone in _station_snap_zones():
|
||||
if zone.picked_up_object == item:
|
||||
print("NetworkManager releasing %s from %s's snap zone (authority just granted elsewhere)" % [item.name, zone.get_parent().name])
|
||||
zone.drop_object()
|
||||
# Make the zone forget the item as well. These zones are snap_mode=RANGE,
|
||||
# so every frame they re-grab anything still listed in their grab area
|
||||
# that can be picked up — and Jolt does not emit body_exited when let_go()
|
||||
# switches the item's collision layer back out of the zone's mask, so the
|
||||
# entry goes stale and never clears. The station then snatches the item
|
||||
# straight back off the player who just took it, teleporting it home.
|
||||
# Bringing it near again re-adds it properly (a held item is on the layer
|
||||
# the zone watches), and releasing next to a station is handled
|
||||
# explicitly by _try_snap_into_station.
|
||||
if zone._object_in_grab_area.has(item):
|
||||
zone._object_in_grab_area.erase(item)
|
||||
|
||||
|
||||
# Snap the item into the nearest empty station snap zone within grab range.
|
||||
#
|
||||
# Called deferred from _do_release_item_authority: XRToolsFunctionPickup's own
|
||||
# "grab an item out of a snap zone" path calls zone.drop_object() BEFORE it
|
||||
# calls pick_up() on the hand's behalf. drop_object()'s let_go() synchronously
|
||||
# fires the pickable's `dropped` signal, which (via NetPickable) lands here —
|
||||
# if this ran synchronously it would immediately re-snap the item into the
|
||||
# very same zone it's still physically inside, stealing it away before the
|
||||
# hand's own pick_up() call (later in the same call stack) ever runs. That
|
||||
# leaves XRToolsFunctionPickup.picked_up_object pointing at an item whose
|
||||
# _grab_driver actually belongs to the zone — a stale reference that crashes
|
||||
# (null _grab_driver) the next time a controller button is pressed. Deferring
|
||||
# lets the hand's pick_up() go first; the is_picked_up() check below is a
|
||||
# second guard in case the item gets grabbed for real before this runs.
|
||||
func _try_snap_into_station(item: Node) -> void:
|
||||
if not (item is Node3D):
|
||||
return
|
||||
if item.has_method("is_picked_up") and item.is_picked_up():
|
||||
var by: Node = null
|
||||
if item.has_method("get_picked_up_by"):
|
||||
by = item.get_picked_up_by()
|
||||
print("NetworkManager skipped snapping %s: already held by %s (grab-race guard)" % [item.name, by.get_path() if by else "?"])
|
||||
return
|
||||
for zone in _station_snap_zones():
|
||||
if is_instance_valid(zone.picked_up_object):
|
||||
continue
|
||||
if zone.global_position.distance_to(item.global_position) <= zone.grab_distance:
|
||||
print("NetworkManager snapped %s into %s" % [item.name, zone.get_parent().name])
|
||||
zone.pick_up_object(item)
|
||||
return
|
||||
print("NetworkManager no station in range to snap %s into (or none empty)" % item.name)
|
||||
|
||||
|
||||
# Server broadcasts an authority assignment so every peer agrees on who owns the
|
||||
# item (set_multiplayer_authority is a local call and must run everywhere).
|
||||
@rpc("authority", "call_local", "reliable")
|
||||
func _set_item_authority(item_path: NodePath, peer: int) -> void:
|
||||
var item := get_node_or_null(item_path)
|
||||
if not item:
|
||||
return
|
||||
print("NetworkManager _set_item_authority: %s -> peer %d" % [item.name, peer])
|
||||
item.set_multiplayer_authority(peer) # recursive: item + synchronizer + NetPickable
|
||||
var np := item.get_node_or_null("NetPickable")
|
||||
if np:
|
||||
np.net_held_by = 0 if peer == 1 else peer
|
||||
np.apply_held_state()
|
||||
|
||||
|
||||
## Rejects peer's optimistic grab (the item was already legitimately held by
|
||||
## someone else). Same self-RPC concern: if the rejected peer is the server
|
||||
## itself, apply it directly rather than rpc_id-ing ourselves.
|
||||
func _force_release_item_to(peer: int, item_path: NodePath) -> void:
|
||||
if peer == 1:
|
||||
_do_force_release(item_path)
|
||||
else:
|
||||
force_release_item.rpc_id(peer, item_path)
|
||||
|
||||
|
||||
@rpc("authority", "reliable")
|
||||
func force_release_item(item_path: NodePath) -> void:
|
||||
_do_force_release(item_path)
|
||||
|
||||
|
||||
func _do_force_release(item_path: NodePath) -> void:
|
||||
print("NetworkManager force_release_item: dropping %s (server rejected our grab)" % str(item_path))
|
||||
var item := get_node_or_null(item_path)
|
||||
if item and item.has_method("drop"):
|
||||
item.drop()
|
||||
|
||||
|
||||
|
||||
func is_server() -> bool:
|
||||
return is_online() and multiplayer.is_server()
|
||||
|
||||
|
||||
## True only when a real ENet session is active. Godot installs a default
|
||||
## OfflineMultiplayerPeer, so a non-null peer alone does not mean "online".
|
||||
func is_online() -> bool:
|
||||
var p := multiplayer.multiplayer_peer
|
||||
return p != null and not (p is OfflineMultiplayerPeer)
|
||||
|
||||
|
||||
## True on the machine that owns authoritative world logic: the server when
|
||||
## online, or the single player when offline. Station logic and spawning should
|
||||
## only run where this is true, so state has one source of truth.
|
||||
func owns_world() -> bool:
|
||||
return not is_online() or is_server()
|
||||
|
||||
|
||||
# --- Station work-progress seam -------------------------------------------
|
||||
|
||||
## Reusable entry point for a client to contribute work to a station (e.g. a
|
||||
## future chopping/gesture station). The client detects the gesture locally and
|
||||
## calls this; the server validates and accumulates. Timer-driven stations like
|
||||
## the Hob don't need it, but it is the drop-in seam for input-driven ones.
|
||||
@rpc("any_peer", "reliable")
|
||||
func submit_work(station_path: NodePath, amount: float) -> void:
|
||||
if not is_server():
|
||||
return
|
||||
var station := get_node_or_null(station_path)
|
||||
if station and station.has_method("add_work"):
|
||||
print("NetworkManager submit_work: peer %d contributed %.2f to %s" % [multiplayer.get_remote_sender_id(), amount, station.name])
|
||||
station.add_work(multiplayer.get_remote_sender_id(), amount)
|
||||
|
||||
|
||||
## Called by the world scene once it's ready, passing its spawners. Must run
|
||||
## before world_ready()/host()/join() on every peer so the custom spawn
|
||||
## function is installed before any spawn packet can arrive.
|
||||
func register_world(world: Node, players_spawner: MultiplayerSpawner, items_spawner: MultiplayerSpawner) -> void:
|
||||
_world = world
|
||||
_players_spawner = players_spawner
|
||||
_items_spawner = items_spawner
|
||||
_content_root = items_spawner.get_node(items_spawner.spawn_path) if items_spawner else world
|
||||
print("NetworkManager World registered (players_spawner=%s items_spawner=%s)" % [str(players_spawner != null), str(items_spawner != null)])
|
||||
|
||||
|
||||
## Called when the world scene goes away (disconnect, leaving the session) so
|
||||
## the autoload doesn't hold stale/freed references across a scene reload.
|
||||
func unregister_world() -> void:
|
||||
_world = null
|
||||
_content_root = null
|
||||
_players_spawner = null
|
||||
_items_spawner = null
|
||||
|
||||
|
||||
|
||||
# --- Session signal handlers ----------------------------------------------
|
||||
|
||||
func _on_peer_connected(peer_id: int) -> void:
|
||||
print("NetworkManager peer_connected: %d" % peer_id)
|
||||
# Only the server reacts by materialising that peer's player.
|
||||
if is_server():
|
||||
print("NetworkManager player_present: %d" % peer_id)
|
||||
player_joined.emit(peer_id)
|
||||
|
||||
func _on_peer_disconnected(peer_id: int) -> void:
|
||||
print("NetworkManager peer_disconnected: %d" % peer_id)
|
||||
if is_server():
|
||||
print("NetworkManager player_absent: %d" % peer_id)
|
||||
player_left.emit(peer_id)
|
||||
|
||||
func _on_connected_to_server() -> void:
|
||||
print("NetworkManager connected_to_server (my id=%d)" % multiplayer.get_unique_id())
|
||||
session_started.emit(false)
|
||||
get_tree().change_scene_to_file.call_deferred(MULTIPLAYER_SCENE)
|
||||
|
||||
|
||||
func _on_connection_failed() -> void:
|
||||
print("NetworkManager connection_failed")
|
||||
_go_offline()
|
||||
connection_failed.emit()
|
||||
|
||||
func _on_server_disconnected() -> void:
|
||||
print("NetworkManager server_disconnected")
|
||||
_go_offline()
|
||||
session_ended.emit()
|
||||
|
||||
|
||||
# --- Command-line driven test bootstrap -----------------------------------
|
||||
func _handle_cmdline() -> void:
|
||||
print("NetworkManager _handle_cmdline")
|
||||
var args := OS.get_cmdline_user_args()
|
||||
if args.has("--server"):
|
||||
print("NetworkManager cmdline: --server")
|
||||
host()
|
||||
elif args.has("--join"):
|
||||
var idx := args.find("--join")
|
||||
var addr := "127.0.0.1"
|
||||
if idx + 1 < args.size():
|
||||
addr = args[idx + 1]
|
||||
print("NetworkManager cmdline: --join %s" % addr)
|
||||
join(addr)
|
||||
@@ -0,0 +1 @@
|
||||
uid://da1xwj5tyeii6
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://bodj8op527o2c"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d1cefhe3yyyds" path="res://Net/DEPRICATED_multiplayer_world.gd" id="1_6uucx"]
|
||||
[ext_resource type="Script" uid="uid://d1cefhe3yyyds" path="res://net/DEPRICATED_multiplayer_world.gd" id="1_6uucx"]
|
||||
[ext_resource type="PackedScene" uid="uid://j5s5wus7tuhb" path="res://prefabs/xr_origin.tscn" id="2_j7vd1"]
|
||||
[ext_resource type="Material" uid="uid://cmia50cfqxxo4" path="res://textures/dev_material_3d.tres" id="3_ssbaf"]
|
||||
[ext_resource type="Sky" uid="uid://c1abtpwhdm2d5" path="res://textures/sky/NightSkyHDRI009_16K.tres" id="4_081u3"]
|
||||
|
||||
Reference in New Issue
Block a user