Fix StationMovement in Multiplayer, still works singleplayer
This commit is contained in:
+24
-24
@@ -10,34 +10,34 @@
|
||||
[ext_resource type="PackedScene" uid="uid://cqhw276realc" path="res://addons/godot-xr-tools/functions/function_pointer.tscn" id="6_ptr"]
|
||||
[ext_resource type="PackedScene" uid="uid://bjcxf427un2wp" path="res://addons/godot-xr-tools/player/poke/poke.tscn" id="7_1fmci"]
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_ouruy"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_1fmci"]
|
||||
animation = &"Grip"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_i0gvw"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_itrmp"]
|
||||
animation = &"Grip"
|
||||
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_5mmou"]
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_nrfrd"]
|
||||
filter_enabled = true
|
||||
filters = ["Armature/Skeleton3D:Little_Distal_L", "Armature/Skeleton3D:Little_Intermediate_L", "Armature/Skeleton3D:Little_Metacarpal_L", "Armature/Skeleton3D:Little_Proximal_L", "Armature/Skeleton3D:Middle_Distal_L", "Armature/Skeleton3D:Middle_Intermediate_L", "Armature/Skeleton3D:Middle_Metacarpal_L", "Armature/Skeleton3D:Middle_Proximal_L", "Armature/Skeleton3D:Ring_Distal_L", "Armature/Skeleton3D:Ring_Intermediate_L", "Armature/Skeleton3D:Ring_Metacarpal_L", "Armature/Skeleton3D:Ring_Proximal_L", "Armature/Skeleton3D:Thumb_Distal_L", "Armature/Skeleton3D:Thumb_Metacarpal_L", "Armature/Skeleton3D:Thumb_Proximal_L", "Armature/Skeleton:Little_Distal_L", "Armature/Skeleton:Little_Intermediate_L", "Armature/Skeleton:Little_Proximal_L", "Armature/Skeleton:Middle_Distal_L", "Armature/Skeleton:Middle_Intermediate_L", "Armature/Skeleton:Middle_Proximal_L", "Armature/Skeleton:Ring_Distal_L", "Armature/Skeleton:Ring_Intermediate_L", "Armature/Skeleton:Ring_Proximal_L", "Armature/Skeleton:Thumb_Distal_L", "Armature/Skeleton:Thumb_Proximal_L"]
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_yfij0"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_73fu8"]
|
||||
animation = &"Grip 5"
|
||||
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_2ebvs"]
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_ouruy"]
|
||||
filter_enabled = true
|
||||
filters = ["Armature/Skeleton3D:Index_Distal_L", "Armature/Skeleton3D:Index_Intermediate_L", "Armature/Skeleton3D:Index_Metacarpal_L", "Armature/Skeleton3D:Index_Proximal_L", "Armature/Skeleton:Index_Distal_L", "Armature/Skeleton:Index_Intermediate_L", "Armature/Skeleton:Index_Proximal_L"]
|
||||
|
||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_l8v2a"]
|
||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_i0gvw"]
|
||||
graph_offset = Vector2(-536, 11)
|
||||
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_ouruy")
|
||||
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_1fmci")
|
||||
nodes/ClosedHand1/position = Vector2(-600, 300)
|
||||
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_i0gvw")
|
||||
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_itrmp")
|
||||
nodes/ClosedHand2/position = Vector2(-360, 300)
|
||||
nodes/Grip/node = SubResource("AnimationNodeBlend2_5mmou")
|
||||
nodes/Grip/node = SubResource("AnimationNodeBlend2_nrfrd")
|
||||
nodes/Grip/position = Vector2(0, 20)
|
||||
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_yfij0")
|
||||
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_73fu8")
|
||||
nodes/OpenHand/position = Vector2(-600, 100)
|
||||
nodes/Trigger/node = SubResource("AnimationNodeBlend2_2ebvs")
|
||||
nodes/Trigger/node = SubResource("AnimationNodeBlend2_ouruy")
|
||||
nodes/Trigger/position = Vector2(-360, 20)
|
||||
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
|
||||
|
||||
@@ -45,34 +45,34 @@ node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &
|
||||
transparency = 1
|
||||
albedo_color = Color(1, 1, 1, 0.5882353)
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_qpjda"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_5mmou"]
|
||||
animation = &"Grip"
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_37hbl"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_yfij0"]
|
||||
animation = &"Grip"
|
||||
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_to5tn"]
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_2ebvs"]
|
||||
filter_enabled = true
|
||||
filters = ["Armature/Skeleton3D:Little_Distal_R", "Armature/Skeleton3D:Little_Intermediate_R", "Armature/Skeleton3D:Little_Metacarpal_R", "Armature/Skeleton3D:Little_Proximal_R", "Armature/Skeleton3D:Middle_Distal_R", "Armature/Skeleton3D:Middle_Intermediate_R", "Armature/Skeleton3D:Middle_Metacarpal_R", "Armature/Skeleton3D:Middle_Proximal_R", "Armature/Skeleton3D:Ring_Distal_R", "Armature/Skeleton3D:Ring_Intermediate_R", "Armature/Skeleton3D:Ring_Metacarpal_R", "Armature/Skeleton3D:Ring_Proximal_R", "Armature/Skeleton3D:Thumb_Distal_R", "Armature/Skeleton3D:Thumb_Metacarpal_R", "Armature/Skeleton3D:Thumb_Proximal_R", "Armature/Skeleton:Little_Distal_R", "Armature/Skeleton:Little_Intermediate_R", "Armature/Skeleton:Little_Proximal_R", "Armature/Skeleton:Middle_Distal_R", "Armature/Skeleton:Middle_Intermediate_R", "Armature/Skeleton:Middle_Proximal_R", "Armature/Skeleton:Ring_Distal_R", "Armature/Skeleton:Ring_Intermediate_R", "Armature/Skeleton:Ring_Proximal_R", "Armature/Skeleton:Thumb_Distal_R", "Armature/Skeleton:Thumb_Proximal_R"]
|
||||
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_nlnu7"]
|
||||
[sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_l8v2a"]
|
||||
animation = &"Grip 5"
|
||||
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_we6ah"]
|
||||
[sub_resource type="AnimationNodeBlend2" id="AnimationNodeBlend2_qpjda"]
|
||||
filter_enabled = true
|
||||
filters = ["Armature/Skeleton3D:Index_Distal_R", "Armature/Skeleton3D:Index_Intermediate_R", "Armature/Skeleton3D:Index_Metacarpal_R", "Armature/Skeleton3D:Index_Proximal_R", "Armature/Skeleton:Index_Distal_R", "Armature/Skeleton:Index_Intermediate_R", "Armature/Skeleton:Index_Proximal_R"]
|
||||
|
||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_dtgvk"]
|
||||
[sub_resource type="AnimationNodeBlendTree" id="AnimationNodeBlendTree_37hbl"]
|
||||
graph_offset = Vector2(-552.664, 107.301)
|
||||
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_qpjda")
|
||||
nodes/ClosedHand1/node = SubResource("AnimationNodeAnimation_5mmou")
|
||||
nodes/ClosedHand1/position = Vector2(-600, 300)
|
||||
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_37hbl")
|
||||
nodes/ClosedHand2/node = SubResource("AnimationNodeAnimation_yfij0")
|
||||
nodes/ClosedHand2/position = Vector2(-360, 300)
|
||||
nodes/Grip/node = SubResource("AnimationNodeBlend2_to5tn")
|
||||
nodes/Grip/node = SubResource("AnimationNodeBlend2_2ebvs")
|
||||
nodes/Grip/position = Vector2(0, 40)
|
||||
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_nlnu7")
|
||||
nodes/OpenHand/node = SubResource("AnimationNodeAnimation_l8v2a")
|
||||
nodes/OpenHand/position = Vector2(-600, 100)
|
||||
nodes/Trigger/node = SubResource("AnimationNodeBlend2_we6ah")
|
||||
nodes/Trigger/node = SubResource("AnimationNodeBlend2_qpjda")
|
||||
nodes/Trigger/position = Vector2(-360, 40)
|
||||
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
|
||||
|
||||
@@ -120,7 +120,7 @@ bone_idx = 9
|
||||
[node name="Poke" parent="XRControllerLeftHand/LeftHand/Hand_low_L/Armature/Skeleton3D/BoneAttachment3D" unique_id=259928841 instance=ExtResource("7_1fmci")]
|
||||
|
||||
[node name="AnimationTree" parent="XRControllerLeftHand/LeftHand" index="1"]
|
||||
tree_root = SubResource("AnimationNodeBlendTree_l8v2a")
|
||||
tree_root = SubResource("AnimationNodeBlendTree_i0gvw")
|
||||
|
||||
[node name="MovementDirect" parent="XRControllerLeftHand" unique_id=1865515938 instance=ExtResource("2_q1xvu")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.009340696, -0.0074635968, 0.024033919)
|
||||
@@ -181,7 +181,7 @@ bone_idx = 9
|
||||
[node name="Poke" parent="XRControllerRightHand/RightHand/Hand_low_R/Armature/Skeleton3D/BoneAttachment3D" unique_id=303927399 instance=ExtResource("7_1fmci")]
|
||||
|
||||
[node name="AnimationTree" parent="XRControllerRightHand/RightHand" index="1"]
|
||||
tree_root = SubResource("AnimationNodeBlendTree_dtgvk")
|
||||
tree_root = SubResource("AnimationNodeBlendTree_37hbl")
|
||||
|
||||
[node name="MovementTurn" parent="XRControllerRightHand" unique_id=154529119 instance=ExtResource("4_rd8py")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.0074635968, 0.024033919)
|
||||
|
||||
@@ -7,11 +7,15 @@ extends Node
|
||||
|
||||
var move_handle_rigid: RigidBody3D
|
||||
var original_collision_layer: int = 0
|
||||
var original_y_pos: float = 0.0
|
||||
var original_handle_y_pos: float = 0.0
|
||||
var original_station_transform: Transform3D = Transform3D.IDENTITY
|
||||
@export var moving: bool = false
|
||||
var ghost_materials: Array[StandardMaterial3D] = []
|
||||
|
||||
# Ghost update throttling (clients send unreliable RPCs to server at this rate)
|
||||
var ghost_send_interval: float = 0.1 # seconds (10 Hz)
|
||||
var _ghost_send_accum: float = 0.0
|
||||
|
||||
const GHOST_COLOR_VALID: Color = Color(0, 1, 0, 0.35)
|
||||
const GHOST_COLOR_INVALID: Color = Color(1, 0, 0, 0.35)
|
||||
|
||||
@@ -42,7 +46,7 @@ func _ready() -> void:
|
||||
|
||||
move_handle_rigid = move_handle as RigidBody3D
|
||||
original_collision_layer = station.collision_layer
|
||||
original_y_pos = move_handle.transform.origin.y
|
||||
original_handle_y_pos = move_handle.transform.origin.y
|
||||
original_station_transform = station.global_transform
|
||||
move_handle.picked_up.connect(_handle_pickup)
|
||||
move_handle.dropped.connect(_handle_drop)
|
||||
@@ -93,16 +97,21 @@ func _handle_pickup(_by: Node) -> void:
|
||||
if move_handle.get_picked_up_by() is XRToolsSnapZone:
|
||||
print("StationMovement handle pickup by snap zone, dropping and resetting position")
|
||||
move_handle.drop()
|
||||
move_handle.global_position = station.global_position + Vector3(0, original_y_pos, 0)
|
||||
move_handle.global_position = station.global_position + Vector3(0, original_handle_y_pos, 0)
|
||||
move_handle.rotation = station.rotation
|
||||
return
|
||||
|
||||
moving = true
|
||||
move_ghost.visible = true
|
||||
station.visible = false
|
||||
station.collision_layer = 0
|
||||
original_station_transform = station.global_transform
|
||||
move_ghost.global_transform = original_station_transform
|
||||
if NetworkManager.is_online():
|
||||
# Ask the server to update visibility (reliable)
|
||||
server_update_visibility.rpc_id(1, true, false)
|
||||
else:
|
||||
# If not connected to server (single-player or test), apply locally
|
||||
move_ghost.visible = true
|
||||
station.visible = false
|
||||
|
||||
|
||||
func _handle_drop(_by: Node) -> void:
|
||||
@@ -111,24 +120,43 @@ func _handle_drop(_by: Node) -> void:
|
||||
move_ghost.visible = false
|
||||
station.visible = true
|
||||
station.collision_layer = original_collision_layer
|
||||
if _is_move_position_valid():
|
||||
station.global_transform = move_ghost.global_transform
|
||||
else:
|
||||
station.global_transform = original_station_transform
|
||||
move_handle.global_position = station.global_position + Vector3(0, original_y_pos, 0)
|
||||
move_handle.rotation = station.rotation
|
||||
|
||||
var is_valid: bool = _is_move_position_valid()
|
||||
if NetworkManager.is_online():
|
||||
server_handle_drop.rpc_id(1, move_ghost.global_transform, is_valid)
|
||||
else:
|
||||
# If not connected to server (single-player or test), apply locally only when valid
|
||||
if is_valid:
|
||||
station.global_transform = move_ghost.global_transform
|
||||
move_handle.global_transform = move_ghost.global_transform.translated(Vector3(0, original_handle_y_pos, 0))
|
||||
print("StationMovement handle drop (local apply), station authority: ", station.get_multiplayer_authority(), " move_handle authority: ", move_handle.get_multiplayer_authority())
|
||||
else:
|
||||
station.global_transform = original_station_transform
|
||||
move_handle.global_transform = original_station_transform.translated(Vector3(0, original_handle_y_pos, 0))
|
||||
print("StationMovement handle drop (local reject), restoring original position")
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if not moving:
|
||||
return
|
||||
|
||||
# Update the ghost locally for instant feedback and send throttled unreliable RPCs to the server
|
||||
move_ghost.visible = true
|
||||
station.visible = false
|
||||
|
||||
move_ghost.global_transform = Helper.get_snapped_transform(move_handle)
|
||||
move_ghost.global_transform.origin.y = original_station_transform.origin.y
|
||||
if _is_move_position_valid():
|
||||
_set_ghost_color(GHOST_COLOR_VALID)
|
||||
else:
|
||||
_set_ghost_color(GHOST_COLOR_INVALID)
|
||||
|
||||
var ghost_color = GHOST_COLOR_VALID if _is_move_position_valid() else GHOST_COLOR_INVALID
|
||||
_set_ghost_color(ghost_color)
|
||||
|
||||
# Throttle and send ghost transform updates to server so other clients see the preview
|
||||
_ghost_send_accum += _delta
|
||||
if _ghost_send_accum >= ghost_send_interval:
|
||||
_ghost_send_accum = 0.0
|
||||
# Unreliable RPC to the server to update the server-side ghost; server will replicate to clients
|
||||
# Use rpc_unreliable_id to avoid blocking traffic
|
||||
if NetworkManager.is_online():
|
||||
server_update_move_ghost_transform.rpc_id(1, move_ghost.global_transform, ghost_color)
|
||||
|
||||
|
||||
func _is_move_position_valid() -> bool:
|
||||
@@ -146,9 +174,59 @@ func _is_move_position_valid() -> bool:
|
||||
return false
|
||||
|
||||
for area in move_ghost.get_overlapping_areas():
|
||||
print("Overlapping area: ", area)
|
||||
#print("Overlapping area: ", area)
|
||||
if area == move_ghost:
|
||||
continue
|
||||
return false
|
||||
|
||||
return true
|
||||
|
||||
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func server_handle_drop(new_transform: Transform3D, client_thinks_valid: bool):
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
if not (client_thinks_valid):
|
||||
# Reset state for everyone, including server
|
||||
var original_trans = original_station_transform # Make a copy to avoid server_update_station_transform setting it before move handle uses it
|
||||
server_update_station_transform.rpc(original_trans)
|
||||
server_update_move_ghost_transform.rpc(original_trans)
|
||||
server_update_handle_transform.rpc(original_trans.translated(Vector3(0, original_handle_y_pos, 0)))
|
||||
server_update_visibility.rpc(false, true)
|
||||
print("StationMovement server_handle_drop: transform rejected (invalid)")
|
||||
return
|
||||
|
||||
server_update_station_transform.rpc(new_transform)
|
||||
server_update_move_ghost_transform.rpc(new_transform)
|
||||
server_update_handle_transform.rpc(new_transform.translated(Vector3(0, original_handle_y_pos, 0)))
|
||||
server_update_visibility.rpc(false, true)
|
||||
|
||||
print("StationMovement server_handle_drop: transform applied")
|
||||
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable") # Server updates clients
|
||||
func server_update_station_transform(new_transform: Transform3D) -> void:
|
||||
print("StationMovement server_update_station_transform")
|
||||
station.global_transform = new_transform
|
||||
original_station_transform = new_transform
|
||||
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable") # Server updates clients
|
||||
func server_update_handle_transform(new_transform: Transform3D) -> void:
|
||||
print("StationMovement server_update_handle_transform")
|
||||
move_handle.global_transform = new_transform
|
||||
|
||||
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func server_update_visibility(ghost_visible: bool, station_visible: bool) -> void:
|
||||
print("StationMovement server_update_visibility")
|
||||
move_ghost.visible = ghost_visible
|
||||
station.visible = station_visible
|
||||
|
||||
|
||||
@rpc("any_peer", "call_local", "unreliable")
|
||||
func server_update_move_ghost_transform(new_transform: Transform3D, new_color: Color = Color.YELLOW) -> void:
|
||||
print("StationMovement server_update_move_ghost_transform")
|
||||
move_ghost.global_transform = new_transform
|
||||
_set_ghost_color(new_color)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
if OS.has_feature("editor"):
|
||||
# Force subwindows to be native OS windows instead of embedded viewports
|
||||
get_tree().root.gui_embed_subwindows = false
|
||||
get_tree().create_timer(0.05).timeout.connect(_position_window)
|
||||
|
||||
func _position_window() -> void:
|
||||
var is_server: bool = OS.get_cmdline_args().has("--server")
|
||||
var screen_rect: Rect2i = DisplayServer.screen_get_usable_rect(DisplayServer.window_get_current_screen())
|
||||
|
||||
var window_width: int = screen_rect.size.x / 2
|
||||
var window_height: int = int(screen_rect.size.y * (2.0 / 3.0))
|
||||
|
||||
DisplayServer.window_set_size(Vector2i(window_width - 10, window_height))
|
||||
|
||||
var x_pos: int = screen_rect.position.x + window_width if is_server else screen_rect.position.x
|
||||
DisplayServer.window_set_position(Vector2i(x_pos, screen_rect.position.y))
|
||||
@@ -0,0 +1 @@
|
||||
uid://de8j52uus1pbv
|
||||
@@ -2,7 +2,7 @@ extends Node
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("quit_game"):
|
||||
get_tree().quit()
|
||||
all_quit_game.rpc()
|
||||
if event.is_action_pressed("satisfy_table_orders"):
|
||||
satisfy_table_orders()
|
||||
if event.is_action_pressed("set_building_mode"):
|
||||
@@ -12,6 +12,10 @@ func _unhandled_input(event: InputEvent) -> void:
|
||||
if event.is_action_pressed("set_game_over"):
|
||||
set_game_over()
|
||||
|
||||
# Kill all clients
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func all_quit_game():
|
||||
get_tree().quit()
|
||||
|
||||
# Finds any node of class/type Table and calls satisfyAllOrders()
|
||||
func satisfy_table_orders() -> void:
|
||||
|
||||
Reference in New Issue
Block a user