Add transactional RV decor synchronization
This commit is contained in:
parent
ee992d9f0c
commit
6578d3e870
7 changed files with 663 additions and 48 deletions
|
|
@ -47,6 +47,12 @@ func setup(
|
|||
_on_local_space_changed
|
||||
):
|
||||
_network_home.local_space_changed.connect(_on_local_space_changed)
|
||||
if not _network_home.local_manifest_rejected.is_connected(
|
||||
_on_local_manifest_rejected
|
||||
):
|
||||
_network_home.local_manifest_rejected.connect(
|
||||
_on_local_manifest_rejected
|
||||
)
|
||||
if not _home_state.changed.is_connected(_on_home_changed):
|
||||
_home_state.changed.connect(_on_home_changed)
|
||||
if not _toolbar.placement_mode_requested.is_connected(
|
||||
|
|
@ -84,7 +90,13 @@ func _input(event: InputEvent) -> void:
|
|||
and pointer_button.button_index == MOUSE_BUTTON_LEFT
|
||||
and not pointer_button.pressed
|
||||
):
|
||||
_finish_drag()
|
||||
# A gameplay menu can take ownership between the press and release. Do
|
||||
# not let that release finalize decor behind the menu; preserve the
|
||||
# original transform instead.
|
||||
if _can_decorate():
|
||||
_finish_drag()
|
||||
else:
|
||||
_cancel_drag()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
|
|
@ -120,14 +132,17 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
func _process(delta: float) -> void:
|
||||
if _drag_instance_id.is_empty():
|
||||
return
|
||||
# Menus and other gameplay input handoffs must cancel an active drag before
|
||||
# evaluating a held/released pointer. This prevents a same-frame TAB press
|
||||
# followed by left-button release from committing a move behind the UI.
|
||||
if not _can_decorate():
|
||||
_cancel_drag()
|
||||
return
|
||||
if not Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT):
|
||||
_finish_drag()
|
||||
return
|
||||
if not _drag_target_valid:
|
||||
return
|
||||
if not _can_decorate():
|
||||
_cancel_drag()
|
||||
return
|
||||
var current: Dictionary = _home_world.get_decor_preview_transform(
|
||||
_drag_owner_fingerprint, _drag_instance_id
|
||||
)
|
||||
|
|
@ -146,6 +161,9 @@ func _process(delta: float) -> void:
|
|||
|
||||
|
||||
func _begin_drag(screen_position: Vector2) -> bool:
|
||||
if _network_home.has_pending_local_decor_mutation():
|
||||
_toolbar.report_status("waiting for the previous decor update")
|
||||
return false
|
||||
var owner_fingerprint: String = (
|
||||
_network_home.get_local_home_owner_fingerprint()
|
||||
)
|
||||
|
|
@ -331,22 +349,34 @@ static func _placement_position(placement: Dictionary) -> Vector3:
|
|||
|
||||
|
||||
func _place_carried_decor(decor_id: StringName) -> void:
|
||||
if _network_home.has_pending_local_decor_mutation():
|
||||
_toolbar.report_status("waiting for the previous decor update")
|
||||
return
|
||||
var owner_fingerprint: String = (
|
||||
_network_home.get_local_home_owner_fingerprint()
|
||||
)
|
||||
var forward: Vector3 = -_player.global_basis.z
|
||||
forward.y = 0.0
|
||||
forward = forward.normalized()
|
||||
# RV transitions keep the gameplay root axis-aligned so its transform can be
|
||||
# synchronized independently from the presentation. Use the visual facing
|
||||
# direction instead; otherwise carried decor is always placed along world -Z
|
||||
# instead of in front of the character the player sees.
|
||||
var forward: Vector3 = _player.get_facing_direction()
|
||||
var global_position: Vector3 = _player.global_position + forward * 1.45
|
||||
var local_position: Vector3 = _home_world.interior_global_to_local(
|
||||
owner_fingerprint, global_position
|
||||
)
|
||||
var placement_yaw: float = atan2(-forward.x, -forward.z)
|
||||
var instance_id: String = _network_home.place_local_decor(
|
||||
decor_id, local_position, _player.global_rotation.y, _mode
|
||||
decor_id, local_position, placement_yaw, _mode
|
||||
)
|
||||
if instance_id.is_empty():
|
||||
_toolbar.report_status("make room in front of you to place this decor")
|
||||
return
|
||||
# A local host can reject its own optimistic manifest synchronously. Do not
|
||||
# claim success after that rollback or leave the player looking for decor that
|
||||
# was never accepted into the room.
|
||||
if _home_state.get_placement(instance_id).is_empty():
|
||||
_toolbar.report_status("decor placement was not accepted")
|
||||
return
|
||||
_select_instance(instance_id)
|
||||
_toolbar.report_status("decor placed")
|
||||
|
||||
|
|
@ -366,6 +396,9 @@ func _select_instance(instance_id: String) -> void:
|
|||
func _rotate_selected() -> void:
|
||||
if not _can_decorate() or _selected_instance_id.is_empty():
|
||||
return
|
||||
if _network_home.has_pending_local_decor_mutation():
|
||||
_toolbar.report_status("waiting for the previous decor update")
|
||||
return
|
||||
var placement: Dictionary = _home_state.get_placement(
|
||||
_selected_instance_id
|
||||
)
|
||||
|
|
@ -392,6 +425,9 @@ func _rotate_selected() -> void:
|
|||
func _store_selected() -> void:
|
||||
if _selected_instance_id.is_empty():
|
||||
return
|
||||
if _network_home.has_pending_local_decor_mutation():
|
||||
_toolbar.report_status("waiting for the previous decor update")
|
||||
return
|
||||
if _network_home.remove_local_decor(_selected_instance_id):
|
||||
_clear_selection()
|
||||
_toolbar.report_status("decor packed into storage")
|
||||
|
|
@ -418,6 +454,12 @@ func _on_local_space_changed(
|
|||
_refresh_toolbar()
|
||||
|
||||
|
||||
func _on_local_manifest_rejected(message: String) -> void:
|
||||
_clear_selection()
|
||||
if _toolbar != null and _toolbar.visible:
|
||||
_toolbar.report_status(message)
|
||||
|
||||
|
||||
func _on_home_changed() -> void:
|
||||
if (
|
||||
not _selected_instance_id.is_empty()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ const EXTERIOR_INTERACTION_DISTANCE: float = 2.7
|
|||
const INTERIOR_EXIT_DISTANCE: float = 2.1
|
||||
const INTERIOR_ORIGIN := Vector3(1000.0, 300.0, 1000.0)
|
||||
const INTERIOR_SPACING: float = 14.0
|
||||
const INTERIOR_MAX_SPAWN_SLOTS: int = 64
|
||||
const INTERIOR_FLOOR_SIZE := Vector3(8.0, 0.18, 4.5)
|
||||
const INTERIOR_WALL_HEIGHT: float = 2.7
|
||||
const MIN_EXTERIOR_TRIANGLE_AREA: float = 1.0
|
||||
|
|
@ -461,13 +462,17 @@ func get_interior_spawn_transform(
|
|||
visitor_offset: int = 0,
|
||||
) -> Transform3D:
|
||||
var anchor: Vector3 = get_interior_anchor(owner_fingerprint)
|
||||
var result := Transform3D(Basis.IDENTITY, anchor)
|
||||
result.origin += Vector3(
|
||||
-1.0 + float(posmod(visitor_offset, 4)) * 0.65,
|
||||
# `visitor_offset` is allocated by the host as a compact room-local slot.
|
||||
# Keep it bounded as a last line of defense if a future caller accidentally
|
||||
# supplies an engine peer ID instead of a slot.
|
||||
var visitor_slot: int = posmod(visitor_offset, INTERIOR_MAX_SPAWN_SLOTS)
|
||||
# Build the origin as a value rather than mutating Transform3D.origin.
|
||||
var spawn_origin: Vector3 = anchor + Vector3(
|
||||
-1.0 + float(posmod(visitor_slot, 4)) * 0.65,
|
||||
0.25,
|
||||
0.4 + float(floori(float(visitor_offset) / 4.0)) * 0.55,
|
||||
0.4 + float(floori(float(visitor_slot) / 4.0)) * 0.55,
|
||||
)
|
||||
return result
|
||||
return Transform3D(Basis.IDENTITY, spawn_origin)
|
||||
|
||||
|
||||
func get_interior_anchor(owner_fingerprint: String) -> Vector3:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue