540 lines
21 KiB
GDScript
540 lines
21 KiB
GDScript
extends Node
|
||
|
||
# ============================================================================
|
||
# MapMultiplayerController.gd - 地图远端玩家同步
|
||
# ============================================================================
|
||
# 负责把本地玩家位置发送给聊天 WebSocket,并把后端位置广播渲染为远端玩家。
|
||
# ============================================================================
|
||
|
||
const REMOTE_PLAYER_SCENE: PackedScene = preload("res://scenes/characters/remote_player.tscn")
|
||
const CHAT_BUBBLE_SCENE: PackedScene = preload("res://scenes/ui/ChatBubble.tscn")
|
||
const DEFAULT_MAP_ID: String = "whale_port"
|
||
const POSITION_SEND_INTERVAL: float = 0.12
|
||
const MIN_POSITION_DELTA: float = 4.0
|
||
const CHAT_BUBBLE_LAYER_NAME: String = "WorldChatBubbleLayer"
|
||
const CHAT_BUBBLE_TARGET_OFFSET: Vector2 = Vector2(0, -84)
|
||
const PRIVATE_CHAT_INTERACTION_DISTANCE: float = 150.0
|
||
const PRIVATE_CHAT_FORWARD_DOT_THRESHOLD: float = 0.25
|
||
|
||
@export var map_id: String = DEFAULT_MAP_ID
|
||
@export var local_player_path: NodePath = NodePath("../Characters/Players/Player")
|
||
@export var remote_players_root_path: NodePath = NodePath("../Characters/Players/RemotePlayers")
|
||
|
||
var _local_player: Node2D
|
||
var _remote_players_root: Node2D
|
||
var _remote_players: Dictionary = {}
|
||
var _pending_remote_players: Dictionary = {}
|
||
var _last_sent_position: Vector2 = Vector2.INF
|
||
var _last_sent_at_msec: int = 0
|
||
var _last_private_chat_interaction_msec: int = 0
|
||
var _last_friend_request_interaction_msec: int = 0
|
||
|
||
func _ready() -> void:
|
||
_local_player = get_node_or_null(local_player_path) as Node2D
|
||
_remote_players_root = get_node_or_null(remote_players_root_path) as Node2D
|
||
|
||
if _local_player == null:
|
||
push_warning("MapMultiplayerController: local player not found.")
|
||
if _remote_players_root == null:
|
||
push_warning("MapMultiplayerController: remote players root not found.")
|
||
|
||
var eventSystem := _get_event_system()
|
||
if eventSystem == null:
|
||
push_warning("MapMultiplayerController: EventSystem autoload is not available.")
|
||
return
|
||
|
||
eventSystem.call("connect_event", EventNames.PLAYER_MOVED, _on_local_player_moved, self)
|
||
eventSystem.call("connect_event", EventNames.CHAT_LOGIN_SUCCESS, _on_chat_login_success, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYERS_SNAPSHOT_READY, _on_remote_players_snapshot_ready, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_POSITION_UPDATED, _on_remote_player_position_updated, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_JOINED, _on_remote_player_joined, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_PLAYER_LEFT, _on_remote_player_left, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_SKIN_READY, _on_remote_skin_ready, self)
|
||
eventSystem.call("connect_event", EventNames.REMOTE_SKIN_FAILED, _on_remote_skin_failed, self)
|
||
eventSystem.call("connect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
|
||
eventSystem.call("connect_event", EventNames.INTERACT_PRESSED, _on_interact_pressed, self)
|
||
call_deferred("_send_world_ready")
|
||
|
||
func _process(_delta: float) -> void:
|
||
if Input.is_action_just_pressed("interact"):
|
||
_try_start_private_chat()
|
||
if Input.is_action_just_pressed("friend_request"):
|
||
_try_request_friend()
|
||
|
||
func _unhandled_input(event: InputEvent) -> void:
|
||
if event.is_action_pressed("interact"):
|
||
if _try_start_private_chat():
|
||
get_viewport().set_input_as_handled()
|
||
return
|
||
|
||
if event.is_action_pressed("friend_request") and _try_request_friend():
|
||
get_viewport().set_input_as_handled()
|
||
|
||
func _exit_tree() -> void:
|
||
var eventSystem := _get_event_system()
|
||
if eventSystem == null:
|
||
return
|
||
|
||
eventSystem.call("disconnect_event", EventNames.PLAYER_MOVED, _on_local_player_moved, self)
|
||
eventSystem.call("disconnect_event", EventNames.CHAT_LOGIN_SUCCESS, _on_chat_login_success, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYERS_SNAPSHOT_READY, _on_remote_players_snapshot_ready, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_POSITION_UPDATED, _on_remote_player_position_updated, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_JOINED, _on_remote_player_joined, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_PLAYER_LEFT, _on_remote_player_left, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_SKIN_READY, _on_remote_skin_ready, self)
|
||
eventSystem.call("disconnect_event", EventNames.REMOTE_SKIN_FAILED, _on_remote_skin_failed, self)
|
||
eventSystem.call("disconnect_event", EventNames.CHAT_MESSAGE_RECEIVED, _on_chat_message_received, self)
|
||
eventSystem.call("disconnect_event", EventNames.INTERACT_PRESSED, _on_interact_pressed, self)
|
||
|
||
func _get_event_system() -> Node:
|
||
return get_node_or_null("/root/EventSystem")
|
||
|
||
func _get_chat_manager() -> Node:
|
||
return get_node_or_null("/root/ChatManager")
|
||
|
||
func _get_settings_manager() -> Node:
|
||
return get_node_or_null("/root/SettingsManager")
|
||
|
||
func _find_chat_ui() -> Control:
|
||
var current: Node = self
|
||
while current != null:
|
||
var chat_ui := current.get_node_or_null("UILayer/ChatUI") as Control
|
||
if chat_ui != null:
|
||
return chat_ui
|
||
current = current.get_parent()
|
||
return null
|
||
|
||
func _is_text_input_focused() -> bool:
|
||
var focusOwner: Control = get_viewport().gui_get_focus_owner()
|
||
if focusOwner == null or not focusOwner.is_inside_tree() or not focusOwner.is_visible_in_tree():
|
||
return false
|
||
if focusOwner is LineEdit:
|
||
var lineEdit := focusOwner as LineEdit
|
||
return lineEdit.editable
|
||
if focusOwner is TextEdit:
|
||
var textEdit := focusOwner as TextEdit
|
||
return textEdit.editable
|
||
return false
|
||
|
||
func _on_interact_pressed(_data: Dictionary = {}) -> void:
|
||
_try_start_private_chat()
|
||
|
||
func _try_start_private_chat() -> bool:
|
||
if _is_text_input_focused():
|
||
return false
|
||
if not _settings_bool("allow_nearby_private", true):
|
||
return false
|
||
|
||
var now := Time.get_ticks_msec()
|
||
if _last_private_chat_interaction_msec > 0 and now - _last_private_chat_interaction_msec < 160:
|
||
return false
|
||
|
||
var target := _find_private_chat_target()
|
||
if target == null:
|
||
return false
|
||
|
||
var eventSystem := _get_event_system()
|
||
if eventSystem == null:
|
||
return false
|
||
|
||
_last_private_chat_interaction_msec = now
|
||
eventSystem.call("emit_event", EventNames.CHAT_PRIVATE_TARGET_SELECTED, {
|
||
"userId": str(target.get("userId")),
|
||
"username": str(target.get("username"))
|
||
})
|
||
return true
|
||
|
||
func _try_request_friend() -> bool:
|
||
if _is_text_input_focused():
|
||
return false
|
||
if not _settings_bool("allow_nearby_friend_requests", true):
|
||
return false
|
||
|
||
var now := Time.get_ticks_msec()
|
||
if _last_friend_request_interaction_msec > 0 and now - _last_friend_request_interaction_msec < 220:
|
||
return false
|
||
|
||
var target := _find_private_chat_target()
|
||
if target == null:
|
||
return false
|
||
|
||
var chat_ui := _find_chat_ui()
|
||
if chat_ui == null or not chat_ui.has_method("request_friend_with_target"):
|
||
return false
|
||
|
||
_last_friend_request_interaction_msec = now
|
||
return bool(chat_ui.call(
|
||
"request_friend_with_target",
|
||
str(target.get("userId")),
|
||
str(target.get("username"))
|
||
))
|
||
|
||
func _on_chat_login_success(_data: Dictionary) -> void:
|
||
_send_world_ready()
|
||
|
||
func _on_local_player_moved(data: Dictionary) -> void:
|
||
var position_variant: Variant = data.get("position", null)
|
||
if position_variant is Vector2:
|
||
_send_position(position_variant as Vector2)
|
||
|
||
func _send_world_ready() -> void:
|
||
if _local_player == null:
|
||
return
|
||
var chatManager := _get_chat_manager()
|
||
if chatManager == null or not chatManager.has_method("mark_world_ready"):
|
||
return
|
||
chatManager.call("mark_world_ready", _get_map_id(), _local_player.global_position)
|
||
|
||
func _send_position(position: Vector2, force: bool = false) -> void:
|
||
var now := Time.get_ticks_msec()
|
||
if not force and now - _last_sent_at_msec < int(POSITION_SEND_INTERVAL * 1000.0):
|
||
return
|
||
if not force and _last_sent_position != Vector2.INF and _last_sent_position.distance_to(position) < MIN_POSITION_DELTA:
|
||
return
|
||
|
||
var chatManager := _get_chat_manager()
|
||
if chatManager == null:
|
||
return
|
||
if chatManager.has_method("can_attempt_chat_connection") and not bool(chatManager.call("can_attempt_chat_connection")):
|
||
return
|
||
|
||
_last_sent_position = position
|
||
_last_sent_at_msec = now
|
||
chatManager.call("update_player_position", position.x, position.y, _get_map_id())
|
||
|
||
func _on_remote_player_joined(data: Dictionary) -> void:
|
||
if not _is_event_for_current_map(data):
|
||
return
|
||
var user_id := str(data.get("userId", "")).strip_edges()
|
||
if user_id.is_empty():
|
||
return
|
||
var remote_player := _ensure_remote_player(user_id, data)
|
||
var position_variant: Variant = data.get("position", null)
|
||
if remote_player != null and position_variant is Vector2 and remote_player.has_method("update_position"):
|
||
remote_player.call("update_position", position_variant as Vector2)
|
||
|
||
func _on_remote_players_snapshot_ready(data: Dictionary) -> void:
|
||
if not _is_event_for_current_map(data):
|
||
return
|
||
|
||
var seen_user_ids: Dictionary = {}
|
||
var players_variant: Variant = data.get("players", [])
|
||
if players_variant is Array:
|
||
for player_variant in players_variant:
|
||
if not (player_variant is Dictionary):
|
||
continue
|
||
var player_data: Dictionary = player_variant
|
||
var user_id := str(player_data.get("userId", "")).strip_edges()
|
||
if user_id.is_empty() or _is_current_user(user_id, str(player_data.get("username", ""))):
|
||
continue
|
||
seen_user_ids[user_id] = true
|
||
var remote_player := _ensure_remote_player(user_id, player_data)
|
||
var position_variant: Variant = player_data.get("position", null)
|
||
if remote_player != null and position_variant is Vector2 and remote_player.has_method("update_position"):
|
||
remote_player.call("update_position", position_variant as Vector2)
|
||
|
||
for user_id_variant in _remote_players.keys().duplicate():
|
||
var user_id := str(user_id_variant)
|
||
if seen_user_ids.has(user_id):
|
||
continue
|
||
var remote_player := _remote_players[user_id] as Node
|
||
_remote_players.erase(user_id)
|
||
if is_instance_valid(remote_player):
|
||
remote_player.queue_free()
|
||
for user_id_variant in _pending_remote_players.keys().duplicate():
|
||
var pendingUserId := str(user_id_variant)
|
||
if not seen_user_ids.has(pendingUserId):
|
||
_pending_remote_players.erase(pendingUserId)
|
||
|
||
func _on_remote_player_position_updated(data: Dictionary) -> void:
|
||
if not _is_event_for_current_map(data):
|
||
return
|
||
var user_id := str(data.get("userId", "")).strip_edges()
|
||
if user_id.is_empty():
|
||
return
|
||
if _is_current_user(user_id, str(data.get("username", ""))):
|
||
return
|
||
|
||
var position_variant: Variant = data.get("position", null)
|
||
if not (position_variant is Vector2):
|
||
return
|
||
|
||
var remote_player := _ensure_remote_player(user_id, data)
|
||
if remote_player != null and remote_player.has_method("update_position"):
|
||
remote_player.call("update_position", position_variant as Vector2)
|
||
|
||
func _on_remote_player_left(data: Dictionary) -> void:
|
||
if not _is_event_for_current_map(data):
|
||
return
|
||
var user_id := str(data.get("userId", "")).strip_edges()
|
||
if user_id.is_empty():
|
||
return
|
||
_pending_remote_players.erase(user_id)
|
||
if not _remote_players.has(user_id):
|
||
return
|
||
|
||
var remote_player := _remote_players[user_id] as Node
|
||
_remote_players.erase(user_id)
|
||
if is_instance_valid(remote_player):
|
||
remote_player.queue_free()
|
||
|
||
func _on_chat_message_received(data: Dictionary) -> void:
|
||
if not bool(data.get("show_bubble", false)):
|
||
return
|
||
if not _settings_bool("show_chat_bubbles", true):
|
||
return
|
||
|
||
var content := str(data.get("content", "")).strip_edges()
|
||
if content.is_empty():
|
||
return
|
||
|
||
var target := _resolve_chat_bubble_target(data)
|
||
if target == null:
|
||
return
|
||
|
||
_show_chat_bubble(target, content)
|
||
|
||
func _ensure_remote_player(user_id: String, data: Dictionary) -> Node2D:
|
||
if _is_current_user(user_id, str(data.get("username", ""))):
|
||
return null
|
||
var appearanceManager := get_node_or_null("/root/AppearanceManager")
|
||
var skinId := str(data.get("skin_id", data.get("skinId", ""))).strip_edges()
|
||
var skinAssetVariant: Variant = data.get("skin_asset", data.get("skinAsset", {}))
|
||
var skinAsset: Dictionary = skinAssetVariant if skinAssetVariant is Dictionary else {}
|
||
if appearanceManager != null and appearanceManager.has_method("is_remote_skin_ready"):
|
||
if not bool(appearanceManager.call("is_remote_skin_ready", skinId, skinAsset)):
|
||
_queue_pending_remote_player(user_id, data)
|
||
if _remote_players.has(user_id):
|
||
var loadingPlayer := _remote_players[user_id] as Node2D
|
||
if is_instance_valid(loadingPlayer):
|
||
loadingPlayer.hide()
|
||
if appearanceManager.has_method("request_remote_skin"):
|
||
appearanceManager.call("request_remote_skin", skinId, skinAsset)
|
||
return null
|
||
if _remote_players.has(user_id):
|
||
var existing_player := _remote_players[user_id] as Node2D
|
||
if is_instance_valid(existing_player):
|
||
_apply_remote_player_metadata(existing_player, data)
|
||
existing_player.show()
|
||
return existing_player
|
||
|
||
if _remote_players_root == null:
|
||
return null
|
||
|
||
var remote_player := REMOTE_PLAYER_SCENE.instantiate() as Node2D
|
||
if remote_player == null:
|
||
return null
|
||
|
||
remote_player.name = "RemotePlayer_%s" % user_id
|
||
remote_player.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||
_remote_players_root.add_child(remote_player)
|
||
_remote_players[user_id] = remote_player
|
||
|
||
if remote_player.has_method("setup"):
|
||
var setup_data := data.duplicate(true)
|
||
if not setup_data.has("position"):
|
||
setup_data["position"] = {"x": _local_player.global_position.x if _local_player != null else 0.0, "y": _local_player.global_position.y if _local_player != null else 0.0}
|
||
remote_player.call("setup", setup_data)
|
||
|
||
return remote_player
|
||
|
||
func _queue_pending_remote_player(userId: String, data: Dictionary) -> void:
|
||
var merged: Dictionary = (_pending_remote_players.get(userId, {}) as Dictionary).duplicate(true)
|
||
for key in data.keys():
|
||
merged[key] = data[key]
|
||
_pending_remote_players[userId] = merged
|
||
|
||
func _on_remote_skin_ready(data: Dictionary) -> void:
|
||
var skinId := str(data.get("skin_id", "")).strip_edges()
|
||
if skinId.is_empty():
|
||
return
|
||
for userIdVariant in _pending_remote_players.keys().duplicate():
|
||
var userId := str(userIdVariant)
|
||
var pendingData: Dictionary = _pending_remote_players.get(userId, {})
|
||
var pendingSkinId := str(pendingData.get("skin_id", pendingData.get("skinId", ""))).strip_edges()
|
||
if pendingSkinId != skinId:
|
||
continue
|
||
_pending_remote_players.erase(userId)
|
||
var remotePlayer := _ensure_remote_player(userId, pendingData)
|
||
var positionVariant: Variant = pendingData.get("position", null)
|
||
if remotePlayer != null and positionVariant is Vector2 and remotePlayer.has_method("update_position"):
|
||
remotePlayer.call("update_position", positionVariant as Vector2)
|
||
|
||
func _on_remote_skin_failed(data: Dictionary) -> void:
|
||
var skinId := str(data.get("skin_id", "")).strip_edges()
|
||
if skinId.is_empty():
|
||
return
|
||
for userIdVariant in _pending_remote_players.keys().duplicate():
|
||
var userId := str(userIdVariant)
|
||
var pendingData: Dictionary = _pending_remote_players.get(userId, {})
|
||
var pendingSkinId := str(pendingData.get("skin_id", pendingData.get("skinId", ""))).strip_edges()
|
||
if pendingSkinId != skinId:
|
||
continue
|
||
_pending_remote_players.erase(userId)
|
||
var fallbackData := pendingData.duplicate(true)
|
||
fallbackData["skin_id"] = ""
|
||
fallbackData["skinId"] = ""
|
||
fallbackData.erase("skin_asset")
|
||
fallbackData.erase("skinAsset")
|
||
var remotePlayer := _ensure_remote_player(userId, fallbackData)
|
||
var positionVariant: Variant = pendingData.get("position", null)
|
||
if remotePlayer != null and positionVariant is Vector2 and remotePlayer.has_method("update_position"):
|
||
remotePlayer.call("update_position", positionVariant as Vector2)
|
||
|
||
func _find_private_chat_target() -> Node2D:
|
||
if _local_player == null or _remote_players.is_empty():
|
||
return null
|
||
|
||
var local_position := _local_player.global_position
|
||
var facing_direction := _get_local_facing_direction()
|
||
var best_forward_target: Node2D = null
|
||
var best_forward_distance := INF
|
||
var best_any_target: Node2D = null
|
||
var best_any_distance := INF
|
||
|
||
for remote_player_variant in _remote_players.values():
|
||
var remote_player := remote_player_variant as Node2D
|
||
if not is_instance_valid(remote_player):
|
||
continue
|
||
|
||
var offset := remote_player.global_position - local_position
|
||
var distance := offset.length()
|
||
if distance > PRIVATE_CHAT_INTERACTION_DISTANCE:
|
||
continue
|
||
|
||
if distance < best_any_distance:
|
||
best_any_distance = distance
|
||
best_any_target = remote_player
|
||
|
||
if distance <= 0.01:
|
||
continue
|
||
|
||
var forward_dot := facing_direction.dot(offset.normalized())
|
||
if forward_dot >= PRIVATE_CHAT_FORWARD_DOT_THRESHOLD and distance < best_forward_distance:
|
||
best_forward_distance = distance
|
||
best_forward_target = remote_player
|
||
|
||
return best_forward_target if best_forward_target != null else best_any_target
|
||
|
||
func _get_local_facing_direction() -> Vector2:
|
||
if _local_player != null:
|
||
var last_direction := str(_local_player.get("lastDirection"))
|
||
match last_direction:
|
||
"up":
|
||
return Vector2.UP
|
||
"down":
|
||
return Vector2.DOWN
|
||
"left":
|
||
return Vector2.LEFT
|
||
"right":
|
||
return Vector2.RIGHT
|
||
_:
|
||
pass
|
||
return Vector2.DOWN
|
||
|
||
func _apply_remote_player_metadata(remote_player: Node2D, data: Dictionary) -> void:
|
||
var username := str(data.get("username", "")).strip_edges()
|
||
if not username.is_empty():
|
||
remote_player.set("username", username)
|
||
if data.has("skin_id") or data.has("skinId") or data.has("skin_asset") or data.has("skinAsset") or data.has("avatar_id") or data.has("avatarId") or data.has("cafe_companion") or data.has("cafeCompanion"):
|
||
if remote_player.has_method("setup"):
|
||
remote_player.call("setup", data)
|
||
|
||
func _resolve_chat_bubble_target(data: Dictionary) -> Node2D:
|
||
var from_user := str(data.get("from_user", data.get("username", ""))).strip_edges()
|
||
var user_id := str(data.get("userId", data.get("user_id", ""))).strip_edges()
|
||
var is_self := bool(data.get("is_self", false))
|
||
|
||
if is_self or (not user_id.is_empty() and _is_current_user(user_id, from_user)) or _is_current_username(from_user):
|
||
return _local_player
|
||
|
||
if not user_id.is_empty() and _remote_players.has(user_id):
|
||
var remote_by_id := _remote_players[user_id] as Node2D
|
||
if is_instance_valid(remote_by_id):
|
||
return remote_by_id
|
||
|
||
if from_user.is_empty():
|
||
return null
|
||
|
||
for remote_player_variant in _remote_players.values():
|
||
var remote_player := remote_player_variant as Node2D
|
||
if not is_instance_valid(remote_player):
|
||
continue
|
||
var remote_username := str(remote_player.get("username")).strip_edges()
|
||
if not remote_username.is_empty() and remote_username == from_user:
|
||
return remote_player
|
||
|
||
return null
|
||
|
||
func _show_chat_bubble(target: Node2D, text: String) -> void:
|
||
var bubble := CHAT_BUBBLE_SCENE.instantiate() as Control
|
||
if bubble == null:
|
||
return
|
||
|
||
var bubble_layer := _get_chat_bubble_layer()
|
||
bubble_layer.add_child(bubble)
|
||
if bubble.has_method("set_text"):
|
||
bubble.call("set_text", text, target, CHAT_BUBBLE_TARGET_OFFSET)
|
||
|
||
func _get_chat_bubble_layer() -> CanvasLayer:
|
||
return _get_or_create_canvas_layer(CHAT_BUBBLE_LAYER_NAME, 20)
|
||
|
||
func _get_or_create_canvas_layer(layerName: String, layerIndex: int) -> CanvasLayer:
|
||
var root := get_tree().root
|
||
var layer := root.get_node_or_null(layerName) as CanvasLayer
|
||
if layer != null:
|
||
return layer
|
||
|
||
layer = CanvasLayer.new()
|
||
layer.name = layerName
|
||
layer.layer = layerIndex
|
||
root.add_child(layer)
|
||
return layer
|
||
|
||
func _get_map_id() -> String:
|
||
var configured_map_id := map_id.strip_edges()
|
||
return configured_map_id if not configured_map_id.is_empty() else DEFAULT_MAP_ID
|
||
|
||
func _is_event_for_current_map(data: Dictionary) -> bool:
|
||
var event_map_id := str(data.get("mapId", data.get("map_id", ""))).strip_edges()
|
||
return event_map_id.is_empty() or event_map_id == _get_map_id()
|
||
|
||
func _is_current_user(user_id: String, username: String = "") -> bool:
|
||
var authManager := get_node_or_null("/root/AuthManager")
|
||
if authManager == null:
|
||
return false
|
||
|
||
var user: Dictionary = authManager.call("get_current_user")
|
||
var current_id := str(user.get("id", user.get("userId", user.get("user_id", "")))).strip_edges()
|
||
if not current_id.is_empty() and user_id == current_id:
|
||
return true
|
||
|
||
var current_username := str(user.get("username", "")).strip_edges()
|
||
return not current_username.is_empty() and not username.strip_edges().is_empty() and current_username == username.strip_edges()
|
||
|
||
func _is_current_username(username: String) -> bool:
|
||
var normalized_username := username.strip_edges()
|
||
if normalized_username.is_empty():
|
||
return false
|
||
|
||
var authManager := get_node_or_null("/root/AuthManager")
|
||
if authManager != null:
|
||
var user: Dictionary = authManager.call("get_current_user")
|
||
var current_username := str(user.get("username", "")).strip_edges()
|
||
if not current_username.is_empty() and current_username == normalized_username:
|
||
return true
|
||
|
||
var chatManager := _get_chat_manager()
|
||
if chatManager != null:
|
||
var chat_username := str(chatManager.get("_current_username")).strip_edges()
|
||
if not chat_username.is_empty() and chat_username == normalized_username:
|
||
return true
|
||
|
||
return false
|
||
|
||
func _settings_bool(key: String, defaultValue: bool) -> bool:
|
||
var settingsManager := _get_settings_manager()
|
||
if settingsManager != null and settingsManager.has_method("get_bool"):
|
||
return bool(settingsManager.call("get_bool", key))
|
||
return defaultValue
|