Add bought station is ghost when spawns

This commit is contained in:
JonShard
2026-08-11 13:24:51 +02:00
parent 70d41fc21b
commit 6a591b9e3a
5 changed files with 50 additions and 30 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ transform = Transform3D(-1, 0, 8.742278e-08, 0, 1, 0, -8.742278e-08, 0, -1, 1.80
spawn_path = NodePath("../WorldContent") spawn_path = NodePath("../WorldContent")
[node name="PlayersSpawner" type="MultiplayerSpawner" parent="." unique_id=1840881658] [node name="PlayersSpawner" type="MultiplayerSpawner" parent="." unique_id=1840881658]
_spawnable_scenes = PackedStringArray("res://Player/net_player.tscn") _spawnable_scenes = PackedStringArray("uid://c58ns7csdahjy")
spawn_path = NodePath("../Players") spawn_path = NodePath("../Players")
[node name="GameOverPanel" parent="." unique_id=522901881 instance=ExtResource("12_n58f2")] [node name="GameOverPanel" parent="." unique_id=522901881 instance=ExtResource("12_n58f2")]
+3 -2
View File
@@ -62,14 +62,15 @@ move_ghost = NodePath("MoveGhost")
[node name="MoveHandle" type="RigidBody3D" parent="." unique_id=954108203 groups=["platalbe_item"]] [node name="MoveHandle" type="RigidBody3D" parent="." unique_id=954108203 groups=["platalbe_item"]]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.5, 0)
collision_layer = 4 collision_layer = 262144
collision_mask = 196615 collision_mask = 196615
axis_lock_linear_y = true axis_lock_linear_y = true
axis_lock_angular_x = true axis_lock_angular_x = true
axis_lock_angular_z = true axis_lock_angular_z = true
mass = 0.004 mass = 0.001
gravity_scale = 0.5 gravity_scale = 0.5
linear_damp = 100.0 linear_damp = 100.0
angular_damp = 100.0
script = ExtResource("2_tbkb3") script = ExtResource("2_tbkb3")
picked_up_layer = 64 picked_up_layer = 64
release_mode = 1 release_mode = 1
+18 -15
View File
@@ -15,7 +15,7 @@ var ghost_materials: Array[StandardMaterial3D] = []
# Ghost update throttling (clients send unreliable RPCs to server at this rate) # 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_interval: float = 0.1 # seconds (10 Hz)
var _ghost_send_accum: float = 0.0 var _ghost_send_accum: float = 0.0
var _was_last_pos_valid: bool = false
const GHOST_COLOR_VALID: Color = Color(0, 1, 0, 0.35) const GHOST_COLOR_VALID: Color = Color(0, 1, 0, 0.35)
const GHOST_COLOR_INVALID: Color = Color(1, 0, 0, 0.35) const GHOST_COLOR_INVALID: Color = Color(1, 0, 0, 0.35)
@@ -37,6 +37,7 @@ func _on_station_bought(_instance: Node3D):
set_move_handle_enabled(true) set_move_handle_enabled(true)
func _ready() -> void: func _ready() -> void:
SweetLogger.debug("enter")
if not station: if not station:
push_error("StationMovement is missing referece to station") push_error("StationMovement is missing referece to station")
if not move_handle: if not move_handle:
@@ -52,11 +53,11 @@ func _ready() -> void:
move_handle.dropped.connect(_handle_drop) move_handle.dropped.connect(_handle_drop)
Signals.station_bought.connect(_on_station_bought) Signals.station_bought.connect(_on_station_bought)
move_ghost.monitoring = true
move_ghost.visible = false
_cache_ghost_materials() _cache_ghost_materials()
_set_ghost_color(GHOST_COLOR_VALID) _set_ghost_color(GHOST_COLOR_VALID)
set_move_handle_enabled(GameManager.game_state == GameManager.GameState.BUILDING) set_move_handle_enabled(GameManager.game_state == GameManager.GameState.BUILDING)
move_ghost.monitoring = true
move_ghost.visible = false
Signals.game_state_changed.connect(_on_game_state_changed) Signals.game_state_changed.connect(_on_game_state_changed)
@@ -144,10 +145,6 @@ func _process(_delta: float) -> void:
if not is_moving: if not is_moving:
return 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 = Helper.get_snapped_transform(move_handle)
move_ghost.global_transform.origin.y = original_station_transform.origin.y move_ghost.global_transform.origin.y = original_station_transform.origin.y
@@ -189,6 +186,7 @@ func _is_move_position_valid() -> bool:
@rpc("any_peer", "call_remote", "reliable") @rpc("any_peer", "call_remote", "reliable")
func server_handle_drop(new_transform: Transform3D, client_thinks_valid: bool): func server_handle_drop(new_transform: Transform3D, client_thinks_valid: bool):
SweetLogger.debug("enter")
if not multiplayer.is_server(): if not multiplayer.is_server():
return return
if not (client_thinks_valid): if not (client_thinks_valid):
@@ -197,45 +195,50 @@ func server_handle_drop(new_transform: Transform3D, client_thinks_valid: bool):
server_update_station_transform.rpc(original_trans) server_update_station_transform.rpc(original_trans)
server_update_move_ghost_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_handle_transform.rpc(original_trans.translated(Vector3(0, original_handle_y_pos, 0)))
server_update_visibility.rpc(false, true) if not _was_last_pos_valid: # Edge case
SweetLogger.debug("server_handle_drop: transform rejected (invalid)") server_update_visibility.rpc(false, true)
SweetLogger.warning("Can not not reset visibility to normal when the last pos was invalid")
SweetLogger.debug("transform rejected (invalid)")
_was_last_pos_valid = false
return return
server_update_station_transform.rpc(new_transform) server_update_station_transform.rpc(new_transform)
server_update_move_ghost_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_handle_transform.rpc(new_transform.translated(Vector3(0, original_handle_y_pos, 0)))
server_update_visibility.rpc(false, true) server_update_visibility.rpc(false, true)
_was_last_pos_valid = true
SweetLogger.debug("server_handle_drop: transform applied") SweetLogger.debug("transform applied")
@rpc("any_peer", "call_local", "reliable") # Server updates clients @rpc("any_peer", "call_local", "reliable") # Server updates clients
func server_artificial_pickup(): func server_artificial_pickup():
SweetLogger.debug("enter")
_handle_pickup(self) # Arg is not used, but required of the signal. _handle_pickup(self) # Arg is not used, but required of the signal.
@rpc("any_peer", "call_local", "reliable") # Server updates clients @rpc("any_peer", "call_local", "reliable") # Server updates clients
func server_update_station_transform(new_transform: Transform3D) -> void: func server_update_station_transform(new_transform: Transform3D) -> void:
SweetLogger.debug("server_update_station_transform") SweetLogger.debug("entered")
station.global_transform = new_transform station.global_transform = new_transform
original_station_transform = new_transform original_station_transform = new_transform
@rpc("any_peer", "call_local", "reliable") # Server updates clients @rpc("any_peer", "call_local", "reliable") # Server updates clients
func server_update_handle_transform(new_transform: Transform3D) -> void: func server_update_handle_transform(new_transform: Transform3D) -> void:
SweetLogger.debug("server_update_handle_transform") SweetLogger.debug("entered")
move_handle.global_transform = new_transform move_handle.global_transform = new_transform
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func server_update_visibility(ghost_visible: bool, station_visible: bool) -> void: func server_update_visibility(ghost_visible: bool, station_visible: bool) -> void:
SweetLogger.debug("server_update_visibility") SweetLogger.debug("entered")
move_ghost.visible = ghost_visible move_ghost.visible = ghost_visible
station.visible = station_visible station.visible = station_visible
@rpc("any_peer", "call_local", "unreliable") @rpc("any_peer", "call_local", "unreliable")
func server_update_move_ghost_transform(new_transform: Transform3D, new_color: Color = Color.YELLOW) -> void: func server_update_move_ghost_transform(new_transform: Transform3D, new_color: Color = GHOST_COLOR_VALID) -> void:
SweetLogger.debug("server_update_move_ghost_transform") SweetLogger.debug("entered")
move_ghost.global_transform = new_transform move_ghost.global_transform = new_transform
_set_ghost_color(new_color) _set_ghost_color(new_color)
+23 -7
View File
@@ -16,28 +16,44 @@ func _ready() -> void:
button.icon = icon_texture button.icon = icon_texture
func _process(_delta: float) -> void:
button.disabled = GameManager.money < cost
func _on_button_pressed() -> void: func _on_button_pressed() -> void:
SweetLogger.debug("buy {0}", [station_name], "shop_station_button.gd", "_on_button_pressed") SweetLogger.debug("buy {0}", [station_name])
_buy_station() _buy_station()
func _buy_station() -> void: func _buy_station() -> void:
SweetLogger.debug("_buy_station: cost={0} current money={1}", [cost, GameManager.money], "shop_station_button.gd", "_buy_station") SweetLogger.debug("cost={0} current money={1}", [cost, GameManager.money])
GameManager.set_money(GameManager.money - cost) GameManager.set_money(GameManager.money - cost)
var transform = Helper.get_snapped_transform(XRHelpers.get_xr_origin(self).get_node_or_null("PlayerBody")) var transform = Helper.get_snapped_transform(XRHelpers.get_xr_origin(self).get_node_or_null("PlayerBody"))
var forward: Vector3 = -transform.basis.z.normalized() var forward: Vector3 = -transform.basis.z.normalized()
transform.origin += (forward * 0.6) transform.origin += (forward * 0.6)
var instance = NetworkManager.spawn_item(station_scene.resource_path, transform)
var instance
if NetworkManager.is_online() and not NetworkManager.is_server(): if NetworkManager.is_online() and not NetworkManager.is_server():
_ask_server_to_spawn.rpc_id(1, station_scene.resource_path, transform) _ask_server_to_spawn.rpc_id(1, station_scene.resource_path, transform)
else:
instance = NetworkManager.spawn_item(station_scene.resource_path, transform)
update_instance_move(instance)
Signals.station_bought.emit(instance) Signals.station_bought.emit(instance)
@rpc("any_peer", "call_remote", "reliable") @rpc("any_peer", "call_remote", "reliable")
func _ask_server_to_spawn(station_scene_path, transform): func _ask_server_to_spawn(station_scene_path, transform):
NetworkManager.spawn_item(station_scene_path, transform) if not multiplayer.is_server():
return
var instance = NetworkManager.spawn_item(station_scene_path, transform)
update_instance_move(instance)
func update_instance_move(instance: Node3D) -> void:
var move = Helper.find_first_child_of_type(instance, StationMovement) as StationMovement
if not move:
SweetLogger.error("Could not find StationMovement under station {0}", [instance.name])
move.server_artificial_pickup() # Make the new station appear as ghost, the player has to place it.
func _process(_delta: float) -> void:
button.disabled = GameManager.money < cost
+3 -3
View File
@@ -95,14 +95,14 @@ const TIMESTAMP_BG_COLOR = "#1e3a5f"
#===================================================================================# #===================================================================================#
## Column widths for alignment (in characters) ## Column widths for alignment (in characters)
const PEER_ID_COLUMN_WIDTH = 6 const PEER_ID_COLUMN_WIDTH = 6
const LOG_TYPE_COLUMN_WIDTH = 6 const LOG_TYPE_COLUMN_WIDTH = 7
## Default mm:ss:ms; with SHOW_TIMESTAMP_HOURS, hh:mm:ss:ms ## Default mm:ss:ms; with SHOW_TIMESTAMP_HOURS, hh:mm:ss:ms
const TIMESTAMP_COLUMN_WIDTH_MMSSMS = 9 const TIMESTAMP_COLUMN_WIDTH_MMSSMS = 9
const TIMESTAMP_COLUMN_WIDTH_HHMMSSMS = 12 const TIMESTAMP_COLUMN_WIDTH_HHMMSSMS = 12
const SCRIPT_FUNCTION_COLUMN_WIDTH = 45 const SCRIPT_FUNCTION_COLUMN_WIDTH = 50
const TRUNCATE_PEER_NAME = 6 const TRUNCATE_PEER_NAME = 6
const TRUNCATE_SCRIPT_NAME = 18 const TRUNCATE_SCRIPT_NAME = 20
const TRUNCATE_FUNCTION_NAME = 30 const TRUNCATE_FUNCTION_NAME = 30
#===================================================================================# #===================================================================================#