feat: expand progression and multiplayer systems
Add named save slots, progression import/export, and a unified play flow. Add live friend requests, presence, invitations, and relationship controls without durable discovery-server social storage. Advance the network protocol with isolated channels, movement reconciliation, late-join recovery, fishing replication, and animation synchronization. Preserve per-species catch totals, refine generated-world startup and water recovery, and complete the related input and interface improvements.
This commit is contained in:
parent
1db1a5b754
commit
3b84bfe3a0
97 changed files with 7869 additions and 982 deletions
388
main/main.gd
388
main/main.gd
|
|
@ -13,6 +13,9 @@ const WaterRecoveryControllerType = preload(
|
|||
const PlayerSaveManagerType = preload(
|
||||
"res://save/player_save_manager.gd"
|
||||
)
|
||||
const PlayerSaveSlotCatalogType = preload(
|
||||
"res://save/player_save_slot_catalog.gd"
|
||||
)
|
||||
const PlayerSettingsManagerType = preload(
|
||||
"res://settings/player_settings_manager.gd"
|
||||
)
|
||||
|
|
@ -131,6 +134,8 @@ const TIME_CROSSING_EPSILON_HOURS: float = 0.000001
|
|||
const PLAYER_MENU_PATTERN_SCALE: float = 0.85
|
||||
const PLAYER_MENU_PATTERN_SCROLL_VELOCITY := Vector2(-7.0, -5.0)
|
||||
const SHOP_PATTERN_SCALE: float = 1.75
|
||||
const DEDICATED_IDLE_PHYSICS_TICKS_PER_SECOND: int = 10
|
||||
const DEDICATED_ACTIVE_PHYSICS_TICKS_PER_SECOND: int = 30
|
||||
|
||||
@export var fish_catalog: FishPoolType
|
||||
@export var pelican_buyer_profile: FishBuyerProfileType
|
||||
|
|
@ -234,6 +239,7 @@ const SHOP_PATTERN_SCALE: float = 1.75
|
|||
@onready var _shop_backdrop: ColorRect = %ShopBackdrop
|
||||
|
||||
var _gameplay_started: bool = false
|
||||
var _save_slots := PlayerSaveSlotCatalogType.new()
|
||||
var _shop_interaction: FishingShopInteractionType
|
||||
var _storage_interaction: PlayerStorageInteractionType
|
||||
var _title_music_tween: Tween
|
||||
|
|
@ -246,6 +252,10 @@ var _join_requested_from_pause: bool = false
|
|||
var _player_menu_backdrop_tween: Tween
|
||||
var _shop_backdrop_tween: Tween
|
||||
var _pending_join_endpoint: String = ""
|
||||
var _social_prompt_queue: Array[Dictionary] = []
|
||||
var _active_social_prompt: Dictionary = {}
|
||||
var _pending_social_invite_join: bool = false
|
||||
var _pending_social_invite_gameplay: bool = false
|
||||
var _server_trust_dialog: ConfirmationDialog
|
||||
var _pending_trust_changed: bool = false
|
||||
var _identity_notice_dialog: AcceptDialog
|
||||
|
|
@ -260,6 +270,7 @@ var _performance_profile: RuntimePerformanceProfileType
|
|||
var _pending_existing_root_path := ""
|
||||
var _local_recovery_attempt_id: String = ""
|
||||
var _dedicated_runtime: bool = false
|
||||
var _dedicated_metrics_accumulator: float = 0.0
|
||||
|
||||
@onready var _shoreline_ambience: ShorelineAmbience = %ShorelineAmbience
|
||||
var _rain_ambience: RainAmbienceType
|
||||
|
|
@ -269,6 +280,9 @@ func _ready() -> void:
|
|||
_performance_profile = RuntimePerformanceProfileType.from_environment()
|
||||
_dedicated_runtime = _is_dedicated_server_runtime()
|
||||
if _dedicated_runtime:
|
||||
Engine.max_fps = 30
|
||||
Engine.physics_ticks_per_second = 30
|
||||
_disable_dedicated_presentation_tree()
|
||||
call_deferred("_start_dedicated_server")
|
||||
return
|
||||
_world_pixelation.set_light_performance_profile(
|
||||
|
|
@ -382,6 +396,74 @@ func _is_dedicated_server_runtime() -> bool:
|
|||
return "--dedicated-server" in OS.get_cmdline_user_args()
|
||||
|
||||
|
||||
func _disable_dedicated_presentation_tree() -> void:
|
||||
for path: NodePath in [
|
||||
NodePath("TitleBackgroundLayer"),
|
||||
NodePath("PlayerMenuBackdrop"),
|
||||
NodePath("ShopBackdrop"),
|
||||
NodePath("WorldPixelationPostprocess"),
|
||||
NodePath("UIPresentation"),
|
||||
NodePath("PixelationResetOverlay"),
|
||||
NodePath("WorldTimeVisualController"),
|
||||
NodePath("GatheringController"),
|
||||
NodePath("FishingSpot"),
|
||||
NodePath("WaterRecovery"),
|
||||
NodePath("ControllerMappingManager"),
|
||||
NodePath("KeyboardMouseMappingManager"),
|
||||
]:
|
||||
var presentation_node: Node = get_node_or_null(path)
|
||||
if presentation_node == null:
|
||||
continue
|
||||
presentation_node.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
if presentation_node is CanvasItem:
|
||||
(presentation_node as CanvasItem).visible = false
|
||||
elif presentation_node is CanvasLayer:
|
||||
(presentation_node as CanvasLayer).visible = false
|
||||
for audio_path: NodePath in [
|
||||
NodePath("TitleMusic"),
|
||||
NodePath("NewGameMusic"),
|
||||
NodePath("DuskMusic"),
|
||||
NodePath("ShorelineAmbience"),
|
||||
]:
|
||||
var audio_node: Node = get_node_or_null(audio_path)
|
||||
if audio_node == null:
|
||||
continue
|
||||
if audio_node.has_method("stop"):
|
||||
audio_node.call("stop")
|
||||
audio_node.process_mode = Node.PROCESS_MODE_DISABLED
|
||||
|
||||
|
||||
func _print_dedicated_metrics() -> void:
|
||||
var metrics: Dictionary = {
|
||||
"players": _network_session.get_player_count(),
|
||||
"fps": Engine.get_frames_per_second(),
|
||||
"frame_process_ms": Performance.get_monitor(
|
||||
Performance.TIME_PROCESS
|
||||
) * 1000.0,
|
||||
"frame_physics_ms": Performance.get_monitor(
|
||||
Performance.TIME_PHYSICS_PROCESS
|
||||
) * 1000.0,
|
||||
"memory_mib": Performance.get_monitor(
|
||||
Performance.MEMORY_STATIC
|
||||
) / (1024.0 * 1024.0),
|
||||
"nodes": int(Performance.get_monitor(Performance.OBJECT_NODE_COUNT)),
|
||||
"physics_active_objects": int(Performance.get_monitor(
|
||||
Performance.PHYSICS_3D_ACTIVE_OBJECTS
|
||||
)),
|
||||
"physics_collision_pairs": int(Performance.get_monitor(
|
||||
Performance.PHYSICS_3D_COLLISION_PAIRS
|
||||
)),
|
||||
"movement": _network_session.get_movement_metrics(),
|
||||
}
|
||||
if _network_fishing.has_method("get_network_metrics"):
|
||||
metrics["fishing"] = _network_fishing.call("get_network_metrics")
|
||||
if _network_world_spawns.has_method("get_network_metrics"):
|
||||
metrics["gatherables"] = _network_world_spawns.call(
|
||||
"get_network_metrics"
|
||||
)
|
||||
print("NETfishing server metrics: %s" % JSON.stringify(metrics))
|
||||
|
||||
|
||||
func _start_dedicated_server() -> void:
|
||||
var config: DedicatedServerConfigType = DedicatedServerConfigType.from_runtime()
|
||||
if not config.is_valid():
|
||||
|
|
@ -429,6 +511,7 @@ func _start_dedicated_server() -> void:
|
|||
):
|
||||
_fail_dedicated_server("The configured world seed could not be generated.")
|
||||
return
|
||||
_test_world.set_dedicated_simulation(true)
|
||||
if not _network_session.set_host_world(
|
||||
WorldLayoutType.GENERATED,
|
||||
config.world_seed,
|
||||
|
|
@ -441,10 +524,17 @@ func _start_dedicated_server() -> void:
|
|||
return
|
||||
_initialize_application(true)
|
||||
_player.set_local_control(false)
|
||||
_player.set_network_simulation_only(true)
|
||||
_player.visible = false
|
||||
_player.collision_layer = 0
|
||||
_player.collision_mask = 0
|
||||
_player.set_physics_process(false)
|
||||
if not _network_session.peer_count_changed.is_connected(
|
||||
_on_dedicated_peer_count_changed
|
||||
):
|
||||
_network_session.peer_count_changed.connect(
|
||||
_on_dedicated_peer_count_changed
|
||||
)
|
||||
if not _network_session.start_dedicated_host(
|
||||
config.port,
|
||||
config.max_players,
|
||||
|
|
@ -456,6 +546,10 @@ func _start_dedicated_server() -> void:
|
|||
if not _network_session.set_host_open(true):
|
||||
_fail_dedicated_server("Could not open the dedicated server.")
|
||||
return
|
||||
_on_dedicated_peer_count_changed(
|
||||
_network_session.get_player_count(),
|
||||
config.max_players,
|
||||
)
|
||||
_player_jobs.begin_progression_session()
|
||||
if config.public_listing and not _discovery.set_discoverable(true):
|
||||
_network_session.disconnect_session("Discovery setup failed.")
|
||||
|
|
@ -478,15 +572,34 @@ func _start_dedicated_server() -> void:
|
|||
)
|
||||
|
||||
|
||||
func _on_dedicated_peer_count_changed(
|
||||
player_count: int,
|
||||
_max_players: int,
|
||||
) -> void:
|
||||
if not _dedicated_runtime:
|
||||
return
|
||||
Engine.physics_ticks_per_second = (
|
||||
DEDICATED_ACTIVE_PHYSICS_TICKS_PER_SECOND
|
||||
if player_count > 0
|
||||
else DEDICATED_IDLE_PHYSICS_TICKS_PER_SECOND
|
||||
)
|
||||
|
||||
|
||||
func _fail_dedicated_server(message: String) -> void:
|
||||
push_error("Dedicated server startup failed: %s" % message)
|
||||
get_tree().quit(1)
|
||||
|
||||
|
||||
func _configure_portable_stores() -> void:
|
||||
_save_manager.configure_storage(
|
||||
_data_root.path_for(&"player_save"), _data_root
|
||||
)
|
||||
if _dedicated_runtime:
|
||||
_save_manager.configure_storage(
|
||||
_data_root.path_for(&"player_save"), _data_root
|
||||
)
|
||||
elif not _save_slots.configure(_data_root, _save_manager):
|
||||
push_error(
|
||||
"Save-slot catalog setup failed: %s"
|
||||
% _save_slots.get_error_message()
|
||||
)
|
||||
_network_profile.configure_storage(
|
||||
_data_root.path_for(&"network_profile"), _data_root
|
||||
)
|
||||
|
|
@ -523,7 +636,8 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_player_spawn_service.setup(
|
||||
_players_root,
|
||||
_player,
|
||||
_test_world.get_player_spawn_transform()
|
||||
_test_world.get_player_spawn_transform(),
|
||||
dedicated,
|
||||
)
|
||||
_network_session.setup(
|
||||
_network_profile,
|
||||
|
|
@ -536,7 +650,7 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_host_bans,
|
||||
dedicated,
|
||||
)
|
||||
_discovery.setup(_network_session)
|
||||
_discovery.setup(_network_session, _relationships)
|
||||
if not dedicated:
|
||||
_world_time_visuals.setup(
|
||||
_world_time,
|
||||
|
|
@ -699,6 +813,7 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_save_manager,
|
||||
_network_item_use,
|
||||
_world_time,
|
||||
not dedicated,
|
||||
)
|
||||
_gathering_controller.setup(
|
||||
_player,
|
||||
|
|
@ -747,7 +862,8 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_save_manager,
|
||||
item_catalog,
|
||||
fish_catalog,
|
||||
_network_item_use
|
||||
_network_item_use,
|
||||
not dedicated,
|
||||
)
|
||||
var sale_buyers: Array[FishBuyerProfileType] = [
|
||||
pelican_buyer_profile,
|
||||
|
|
@ -850,7 +966,6 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
)
|
||||
_game_ui.setup_data_and_identity(
|
||||
_data_root,
|
||||
_save_manager,
|
||||
_identity_backups,
|
||||
_player_identity,
|
||||
_host_identity,
|
||||
|
|
@ -860,6 +975,18 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_game_ui.setup_controller_mapping(_controller_mapping_manager)
|
||||
_ui_pixelation.setup_controller_mapping(_controller_mapping_manager)
|
||||
_game_ui.setup_keyboard_mouse_mapping(_keyboard_mouse_mapping_manager)
|
||||
_network_player_list.friend_request_received.connect(
|
||||
_on_friend_request_received
|
||||
)
|
||||
_discovery.friend_invite_received.connect(_on_friend_invite_received)
|
||||
_discovery.public_join_prepared.connect(
|
||||
_on_social_public_join_prepared
|
||||
)
|
||||
_discovery.public_join_status_changed.connect(
|
||||
_on_social_public_join_status_changed
|
||||
)
|
||||
_game_ui.social_prompt_accepted.connect(_on_social_prompt_accepted)
|
||||
_game_ui.social_prompt_declined.connect(_on_social_prompt_declined)
|
||||
_data_root.conflict_detected.connect(_on_portable_conflict)
|
||||
if (
|
||||
PortableFileGuard.has_syncthing_conflict(
|
||||
|
|
@ -909,16 +1036,19 @@ func _initialize_application(dedicated: bool) -> void:
|
|||
_apply_runtime_settings(_settings_manager.current_settings)
|
||||
_set_gameplay_active(false)
|
||||
var title_screen: TitleScreenType = _game_ui.get_title_screen()
|
||||
title_screen.new_game_requested.connect(_on_new_game_requested)
|
||||
title_screen.continue_game_requested.connect(_on_continue_game_requested)
|
||||
title_screen.slot_play_requested.connect(_on_slot_play_requested)
|
||||
title_screen.new_slot_requested.connect(_on_new_slot_requested)
|
||||
title_screen.quit_requested.connect(_on_quit_requested)
|
||||
title_screen.setup(
|
||||
_save_manager,
|
||||
_save_slots,
|
||||
_settings_manager,
|
||||
_network_session,
|
||||
_saved_servers,
|
||||
_server_trust,
|
||||
_discovery,
|
||||
_data_root,
|
||||
_interface_fonts,
|
||||
)
|
||||
var pause_menu: PauseMenuType = _game_ui.get_pause_menu()
|
||||
pause_menu.setup(
|
||||
|
|
@ -1454,8 +1584,15 @@ func _unhandled_input(event: InputEvent) -> void:
|
|||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
func _process(delta: float) -> void:
|
||||
if _dedicated_runtime:
|
||||
_dedicated_metrics_accumulator += delta
|
||||
if _dedicated_metrics_accumulator >= 60.0:
|
||||
_dedicated_metrics_accumulator = fmod(
|
||||
_dedicated_metrics_accumulator,
|
||||
60.0,
|
||||
)
|
||||
_print_dedicated_metrics()
|
||||
return
|
||||
var show_shop_prompt := _can_show_shop_prompt()
|
||||
var shop_prompt_anchor := (
|
||||
|
|
@ -1500,7 +1637,8 @@ func _apply_runtime_settings(settings: PlayerSettingsType) -> void:
|
|||
_player.apply_camera_settings(
|
||||
settings.mouse_camera_sensitivity,
|
||||
settings.controller_camera_sensitivity,
|
||||
settings.invert_camera_y
|
||||
settings.invert_camera_y,
|
||||
settings.swap_hotbar_camera_scroll,
|
||||
)
|
||||
_fishing_spot.configure_accessibility_auto_click(
|
||||
settings.auto_click_enabled,
|
||||
|
|
@ -1760,6 +1898,97 @@ func _on_continue_game_requested() -> void:
|
|||
_enter_gameplay()
|
||||
|
||||
|
||||
func _on_slot_play_requested(slot_id: String) -> void:
|
||||
if _gameplay_started or _quit_in_progress:
|
||||
return
|
||||
if not _save_slots.activate_slot(slot_id):
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"The selected save slot could not be opened."
|
||||
)
|
||||
return
|
||||
if not _save_manager.load_player_data():
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"Failed to load that save slot. The original was preserved."
|
||||
)
|
||||
return
|
||||
var world_layout: StringName = _save_manager.get_world_layout()
|
||||
var world_seed: int = _save_manager.get_world_seed()
|
||||
if (
|
||||
not _apply_world(world_layout, world_seed, true)
|
||||
or not _prepare_host_world(world_layout, world_seed)
|
||||
):
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"The saved world could not be loaded. The save was preserved."
|
||||
)
|
||||
return
|
||||
if not _prepare_private_host():
|
||||
return
|
||||
_save_slots.mark_played(slot_id)
|
||||
_enter_gameplay()
|
||||
|
||||
|
||||
func _on_new_slot_requested(
|
||||
display_name: String,
|
||||
world_layout: StringName,
|
||||
world_seed: int,
|
||||
duplicate_source_slot_id: String,
|
||||
) -> void:
|
||||
if _gameplay_started or _quit_in_progress:
|
||||
return
|
||||
var created: Dictionary = (
|
||||
_save_slots.duplicate_slot(
|
||||
duplicate_source_slot_id,
|
||||
display_name,
|
||||
world_layout,
|
||||
world_seed,
|
||||
)
|
||||
if not duplicate_source_slot_id.is_empty()
|
||||
else _save_slots.create_empty_slot(display_name)
|
||||
)
|
||||
if not bool(created.get("ok", false)):
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
str(created.get("message", "The save slot could not be created."))
|
||||
)
|
||||
return
|
||||
var slot_id: String = str(created.get("slot_id", ""))
|
||||
if not _save_slots.activate_slot(slot_id):
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"The new save slot could not be selected."
|
||||
)
|
||||
return
|
||||
if not duplicate_source_slot_id.is_empty():
|
||||
_on_slot_play_requested(slot_id)
|
||||
return
|
||||
_start_new_game_music()
|
||||
if not _apply_world(world_layout, world_seed, true):
|
||||
_show_title_music(true)
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"Could not prepare that world. Try another seed or the starter island."
|
||||
)
|
||||
return
|
||||
if not _prepare_host_world(world_layout, world_seed):
|
||||
_show_title_music(true)
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"Could not prepare that world for hosting."
|
||||
)
|
||||
return
|
||||
if not _prepare_private_host():
|
||||
_show_title_music(true)
|
||||
return
|
||||
if (
|
||||
not _save_manager.delete_progression_save()
|
||||
or not _save_manager.initialize_new_game(world_seed, world_layout)
|
||||
):
|
||||
_network_session.disconnect_session("New Game setup failed.")
|
||||
_show_title_music(true)
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
"Could not initialize local progression."
|
||||
)
|
||||
return
|
||||
_save_slots.mark_played(slot_id)
|
||||
_enter_gameplay()
|
||||
|
||||
|
||||
func _prepare_private_host() -> bool:
|
||||
_join_requested_from_title = false
|
||||
_join_requested_from_pause = false
|
||||
|
|
@ -1816,6 +2045,12 @@ func _on_return_to_title_requested() -> void:
|
|||
func _on_title_join_game_requested(endpoint: String) -> void:
|
||||
if _quit_in_progress or _gameplay_started:
|
||||
return
|
||||
var active_slot: Dictionary = _save_slots.ensure_active_slot()
|
||||
if not bool(active_slot.get("ok", false)):
|
||||
_game_ui.get_title_screen().report_network_error(
|
||||
str(active_slot.get("message", "Local progression is unavailable."))
|
||||
)
|
||||
return
|
||||
if _network_session.state != NetworkSessionType.State.INACTIVE:
|
||||
_network_session.disconnect_session("Preparing direct connection.")
|
||||
_join_requested_from_title = true
|
||||
|
|
@ -1849,6 +2084,135 @@ func _on_pause_join_game_requested(endpoint: String) -> void:
|
|||
)
|
||||
|
||||
|
||||
func _on_friend_request_received(
|
||||
request_id: String,
|
||||
fingerprint: String,
|
||||
display_name: String,
|
||||
) -> void:
|
||||
_queue_social_prompt({
|
||||
"kind": "friend_request",
|
||||
"request_id": request_id,
|
||||
"fingerprint": fingerprint,
|
||||
"display_name": display_name,
|
||||
})
|
||||
|
||||
|
||||
func _on_friend_invite_received(
|
||||
fingerprint: String,
|
||||
display_name: String,
|
||||
room: Dictionary,
|
||||
) -> void:
|
||||
if (
|
||||
not _relationships.is_friend(fingerprint)
|
||||
or _relationships.is_blocked(fingerprint)
|
||||
):
|
||||
return
|
||||
_queue_social_prompt({
|
||||
"kind": "friend_invite",
|
||||
"fingerprint": fingerprint,
|
||||
"display_name": display_name,
|
||||
"room": room.duplicate(true),
|
||||
})
|
||||
|
||||
|
||||
func _queue_social_prompt(details: Dictionary) -> void:
|
||||
_social_prompt_queue.append(details.duplicate(true))
|
||||
_show_next_social_prompt()
|
||||
|
||||
|
||||
func _show_next_social_prompt() -> void:
|
||||
if (
|
||||
not _active_social_prompt.is_empty()
|
||||
or _game_ui.is_social_prompt_open()
|
||||
or _social_prompt_queue.is_empty()
|
||||
):
|
||||
return
|
||||
_active_social_prompt = _social_prompt_queue.pop_front()
|
||||
var kind := str(_active_social_prompt.get("kind", ""))
|
||||
var display_name := str(
|
||||
_active_social_prompt.get("display_name", "Player")
|
||||
)
|
||||
var shown: bool = false
|
||||
if kind == "friend_request":
|
||||
shown = _game_ui.show_social_prompt(
|
||||
"%s wants to add you as a friend.\n" % display_name
|
||||
+ "Friendships are saved on this device.",
|
||||
"accept",
|
||||
"decline",
|
||||
)
|
||||
elif kind == "friend_invite":
|
||||
var room: Dictionary = _active_social_prompt.get("room", {})
|
||||
var room_name := str(room.get("room_name", "their room"))
|
||||
shown = _game_ui.show_social_prompt(
|
||||
"%s invited you to join %s." % [display_name, room_name],
|
||||
"join",
|
||||
"not now",
|
||||
)
|
||||
if not shown:
|
||||
_active_social_prompt.clear()
|
||||
call_deferred("_show_next_social_prompt")
|
||||
|
||||
|
||||
func _on_social_prompt_accepted() -> void:
|
||||
var details: Dictionary = _active_social_prompt.duplicate(true)
|
||||
_active_social_prompt.clear()
|
||||
match str(details.get("kind", "")):
|
||||
"friend_request":
|
||||
_network_player_list.respond_friend_request(
|
||||
str(details.get("request_id", "")), true
|
||||
)
|
||||
"friend_invite":
|
||||
var room: Dictionary = details.get("room", {})
|
||||
_pending_social_invite_join = true
|
||||
_pending_social_invite_gameplay = _gameplay_started
|
||||
if room.is_empty() or not _discovery.prepare_public_join(room):
|
||||
_pending_social_invite_join = false
|
||||
_report_social_join_error(
|
||||
"This person needs to be online to do this."
|
||||
)
|
||||
call_deferred("_show_next_social_prompt")
|
||||
|
||||
|
||||
func _on_social_prompt_declined() -> void:
|
||||
var details: Dictionary = _active_social_prompt.duplicate(true)
|
||||
_active_social_prompt.clear()
|
||||
if str(details.get("kind", "")) == "friend_request":
|
||||
_network_player_list.respond_friend_request(
|
||||
str(details.get("request_id", "")), false
|
||||
)
|
||||
call_deferred("_show_next_social_prompt")
|
||||
|
||||
|
||||
func _on_social_public_join_prepared(endpoint: String) -> void:
|
||||
if not _pending_social_invite_join:
|
||||
return
|
||||
var from_gameplay := _pending_social_invite_gameplay
|
||||
_pending_social_invite_join = false
|
||||
_pending_social_invite_gameplay = false
|
||||
if from_gameplay:
|
||||
_on_pause_join_game_requested(endpoint)
|
||||
else:
|
||||
_on_title_join_game_requested(endpoint)
|
||||
|
||||
|
||||
func _on_social_public_join_status_changed(
|
||||
message: String,
|
||||
is_error: bool,
|
||||
) -> void:
|
||||
if not _pending_social_invite_join or not is_error:
|
||||
return
|
||||
_pending_social_invite_join = false
|
||||
_pending_social_invite_gameplay = false
|
||||
_report_social_join_error(message)
|
||||
|
||||
|
||||
func _report_social_join_error(message: String) -> void:
|
||||
if _gameplay_started:
|
||||
_game_ui.get_pause_menu().report_network_error(message)
|
||||
else:
|
||||
_game_ui.get_title_screen().report_network_error(message)
|
||||
|
||||
|
||||
func _on_network_join_authenticated() -> void:
|
||||
var connected_endpoint: ConnectionEndpoint = (
|
||||
_network_session.get_current_endpoint()
|
||||
|
|
@ -1880,6 +2244,7 @@ func _on_network_join_authenticated() -> void:
|
|||
)
|
||||
_join_requested_from_title = false
|
||||
return
|
||||
_save_slots.mark_played(_save_slots.get_active_slot_id())
|
||||
if not _apply_joined_world(server_metadata):
|
||||
return
|
||||
_fade_out_title_music()
|
||||
|
|
@ -2049,6 +2414,7 @@ func _apply_world(
|
|||
_player.velocity = Vector3.ZERO
|
||||
if _application_initialized:
|
||||
_refresh_active_world_bindings()
|
||||
_network_world_spawns.refresh_world_context()
|
||||
if _application_initialized and not _dedicated_runtime:
|
||||
_water_recovery.update_world_context(
|
||||
spawn_transform,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue