fix/scene-auth-websocket-regressions #18
@@ -102,6 +102,7 @@ var _game_token: String = "" # @deprecated 使用 _access_token 替代
|
|||||||
# 初始化管理器
|
# 初始化管理器
|
||||||
func _init() -> void:
|
func _init() -> void:
|
||||||
_load_auth_data()
|
_load_auth_data()
|
||||||
|
_connect_network_signals()
|
||||||
|
|
||||||
# 清理资源
|
# 清理资源
|
||||||
func cleanup() -> void:
|
func cleanup() -> void:
|
||||||
@@ -110,6 +111,8 @@ func cleanup() -> void:
|
|||||||
NetworkManager.cancel_request(request_id)
|
NetworkManager.cancel_request(request_id)
|
||||||
active_request_ids.clear()
|
active_request_ids.clear()
|
||||||
|
|
||||||
|
_disconnect_network_signals()
|
||||||
|
|
||||||
# ============ Token 管理 ============
|
# ============ Token 管理 ============
|
||||||
|
|
||||||
# 保存 Token 到内存
|
# 保存 Token 到内存
|
||||||
@@ -675,7 +678,7 @@ func _on_send_login_code_response(success: bool, data: Dictionary, error_info: D
|
|||||||
func _on_forgot_password_response(success: bool, data: Dictionary, error_info: Dictionary):
|
func _on_forgot_password_response(success: bool, data: Dictionary, error_info: Dictionary):
|
||||||
button_state_changed.emit("forgot_password_btn", false, "忘记密码")
|
button_state_changed.emit("forgot_password_btn", false, "忘记密码")
|
||||||
|
|
||||||
var result = ResponseHandler.handle_send_login_code_response(success, data, error_info)
|
var result = ResponseHandler.handle_forgot_password_response(success, data, error_info)
|
||||||
|
|
||||||
if result.should_show_toast:
|
if result.should_show_toast:
|
||||||
show_toast_message.emit(result.message, result.success)
|
show_toast_message.emit(result.message, result.success)
|
||||||
@@ -714,10 +717,10 @@ func _can_send_verification_code(email: String) -> bool:
|
|||||||
if not email_data.sent:
|
if not email_data.sent:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
var current_time = Time.get_time_dict_from_system()
|
var current_timestamp: int = int(Time.get_unix_time_from_system())
|
||||||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
var sent_timestamp: int = int(email_data.get("time", 0))
|
||||||
|
|
||||||
return (current_timestamp - email_data.time) >= code_cooldown
|
return float(current_timestamp - sent_timestamp) >= code_cooldown
|
||||||
|
|
||||||
# 获取剩余冷却时间
|
# 获取剩余冷却时间
|
||||||
func get_remaining_cooldown_time(email: String) -> int:
|
func get_remaining_cooldown_time(email: String) -> int:
|
||||||
@@ -725,15 +728,15 @@ func get_remaining_cooldown_time(email: String) -> int:
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
var email_data = verification_codes_sent[email]
|
var email_data = verification_codes_sent[email]
|
||||||
var current_time = Time.get_time_dict_from_system()
|
var current_timestamp: int = int(Time.get_unix_time_from_system())
|
||||||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
var sent_timestamp: int = int(email_data.get("time", 0))
|
||||||
|
var remaining: int = int(code_cooldown - float(current_timestamp - sent_timestamp))
|
||||||
|
|
||||||
return int(code_cooldown - (current_timestamp - email_data.time))
|
return maxi(0, remaining)
|
||||||
|
|
||||||
# 记录验证码发送状态
|
# 记录验证码发送状态
|
||||||
func _record_verification_code_sent(email: String):
|
func _record_verification_code_sent(email: String):
|
||||||
var current_time = Time.get_time_dict_from_system()
|
var current_timestamp: int = int(Time.get_unix_time_from_system())
|
||||||
var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second
|
|
||||||
|
|
||||||
if not verification_codes_sent.has(email):
|
if not verification_codes_sent.has(email):
|
||||||
verification_codes_sent[email] = {}
|
verification_codes_sent[email] = {}
|
||||||
@@ -758,6 +761,30 @@ func _has_sent_verification_code(email: String) -> bool:
|
|||||||
func _is_valid_identifier(identifier: String) -> bool:
|
func _is_valid_identifier(identifier: String) -> bool:
|
||||||
return StringUtils.is_valid_email(identifier) or _is_valid_phone(identifier)
|
return StringUtils.is_valid_email(identifier) or _is_valid_phone(identifier)
|
||||||
|
|
||||||
|
# 连接/断开 NetworkManager 请求信号,用于回收 active_request_ids
|
||||||
|
func _connect_network_signals() -> void:
|
||||||
|
if not NetworkManager.request_completed.is_connected(_on_network_request_completed):
|
||||||
|
NetworkManager.request_completed.connect(_on_network_request_completed)
|
||||||
|
if not NetworkManager.request_failed.is_connected(_on_network_request_failed):
|
||||||
|
NetworkManager.request_failed.connect(_on_network_request_failed)
|
||||||
|
|
||||||
|
func _disconnect_network_signals() -> void:
|
||||||
|
if NetworkManager.request_completed.is_connected(_on_network_request_completed):
|
||||||
|
NetworkManager.request_completed.disconnect(_on_network_request_completed)
|
||||||
|
if NetworkManager.request_failed.is_connected(_on_network_request_failed):
|
||||||
|
NetworkManager.request_failed.disconnect(_on_network_request_failed)
|
||||||
|
|
||||||
|
func _on_network_request_completed(request_id: String, _success: bool, _data: Dictionary) -> void:
|
||||||
|
_remove_active_request_id(request_id)
|
||||||
|
|
||||||
|
func _on_network_request_failed(request_id: String, _error_type: String, _message: String) -> void:
|
||||||
|
_remove_active_request_id(request_id)
|
||||||
|
|
||||||
|
func _remove_active_request_id(request_id: String) -> void:
|
||||||
|
var index: int = active_request_ids.find(request_id)
|
||||||
|
if index != -1:
|
||||||
|
active_request_ids.remove_at(index)
|
||||||
|
|
||||||
# 验证手机号格式
|
# 验证手机号格式
|
||||||
func _is_valid_phone(phone: String) -> bool:
|
func _is_valid_phone(phone: String) -> bool:
|
||||||
var regex = RegEx.new()
|
var regex = RegEx.new()
|
||||||
|
|||||||
@@ -44,12 +44,6 @@ var _next_spawn_name: String = "" # 下一个场景的出生
|
|||||||
# 便于统一管理和修改场景路径
|
# 便于统一管理和修改场景路径
|
||||||
var scene_paths: Dictionary = {
|
var scene_paths: Dictionary = {
|
||||||
"main": "res://scenes/MainScene.tscn", # 主场景 - 游戏入口
|
"main": "res://scenes/MainScene.tscn", # 主场景 - 游戏入口
|
||||||
"auth": "res://scenes/ui/LoginWindow.tscn", # 认证场景 - 登录窗口
|
|
||||||
"game": "res://scenes/maps/game_scene.tscn", # 游戏场景 - 主要游戏内容
|
|
||||||
"battle": "res://scenes/maps/battle_scene.tscn", # 战斗场景 - 战斗系统
|
|
||||||
"inventory": "res://scenes/ui/InventoryWindow.tscn", # 背包界面
|
|
||||||
"shop": "res://scenes/ui/ShopWindow.tscn", # 商店界面
|
|
||||||
"settings": "res://scenes/ui/SettingsWindow.tscn", # 设置界面
|
|
||||||
"square": "res://scenes/Maps/square.tscn", # 广场地图
|
"square": "res://scenes/Maps/square.tscn", # 广场地图
|
||||||
"room": "res://scenes/Maps/room.tscn", # 房间地图
|
"room": "res://scenes/Maps/room.tscn", # 房间地图
|
||||||
"fountain": "res://scenes/Maps/fountain.tscn", # 喷泉地图
|
"fountain": "res://scenes/Maps/fountain.tscn", # 喷泉地图
|
||||||
|
|||||||
@@ -106,6 +106,9 @@ var _reconnect_timer: Timer = Timer.new()
|
|||||||
# 是否为正常关闭(非异常断开)
|
# 是否为正常关闭(非异常断开)
|
||||||
var _clean_close: bool = true
|
var _clean_close: bool = true
|
||||||
|
|
||||||
|
# 当前 CLOSED 状态是否已经处理过(防止每帧重复处理 close 事件)
|
||||||
|
var _closed_state_handled: bool = false
|
||||||
|
|
||||||
# 心跳定时器
|
# 心跳定时器
|
||||||
var _heartbeat_timer: Timer = Timer.new()
|
var _heartbeat_timer: Timer = Timer.new()
|
||||||
|
|
||||||
@@ -163,13 +166,19 @@ func _exit_tree() -> void:
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# 连接到游戏服务器
|
# 连接到游戏服务器
|
||||||
func connect_to_game_server() -> void:
|
func connect_to_game_server(is_reconnect_attempt: bool = false) -> void:
|
||||||
if _connection_state == ConnectionState.CONNECTED or _connection_state == ConnectionState.CONNECTING:
|
if _connection_state == ConnectionState.CONNECTED or _connection_state == ConnectionState.CONNECTING:
|
||||||
push_warning("已经在连接或已连接状态")
|
push_warning("已经在连接或已连接状态")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if is_reconnect_attempt:
|
||||||
|
_set_connection_state(ConnectionState.RECONNECTING)
|
||||||
|
else:
|
||||||
_set_connection_state(ConnectionState.CONNECTING)
|
_set_connection_state(ConnectionState.CONNECTING)
|
||||||
_clean_close = true
|
_clean_close = true
|
||||||
|
_closed_state_handled = false
|
||||||
|
# 仅在首次/手动连接时重置重连计数,重连流程保持累计尝试次数
|
||||||
|
if not is_reconnect_attempt:
|
||||||
_reconnect_attempt = 0
|
_reconnect_attempt = 0
|
||||||
|
|
||||||
var err: Error = _websocket_peer.connect_to_url(WEBSOCKET_URL)
|
var err: Error = _websocket_peer.connect_to_url(WEBSOCKET_URL)
|
||||||
@@ -295,23 +304,40 @@ func _check_websocket_state() -> void:
|
|||||||
|
|
||||||
match state:
|
match state:
|
||||||
WebSocketPeer.STATE_CONNECTING:
|
WebSocketPeer.STATE_CONNECTING:
|
||||||
|
_closed_state_handled = false
|
||||||
|
|
||||||
# 正在连接
|
# 正在连接
|
||||||
if _connection_state != ConnectionState.CONNECTING and _connection_state != ConnectionState.RECONNECTING:
|
if _connection_state != ConnectionState.CONNECTING and _connection_state != ConnectionState.RECONNECTING:
|
||||||
_set_connection_state(ConnectionState.CONNECTING)
|
_set_connection_state(ConnectionState.CONNECTING)
|
||||||
|
|
||||||
WebSocketPeer.STATE_OPEN:
|
WebSocketPeer.STATE_OPEN:
|
||||||
|
_closed_state_handled = false
|
||||||
|
|
||||||
# 连接成功
|
# 连接成功
|
||||||
if _connection_state != ConnectionState.CONNECTED:
|
if _connection_state != ConnectionState.CONNECTED:
|
||||||
_on_websocket_connected()
|
_on_websocket_connected()
|
||||||
|
|
||||||
WebSocketPeer.STATE_CLOSING:
|
WebSocketPeer.STATE_CLOSING:
|
||||||
|
_closed_state_handled = false
|
||||||
|
|
||||||
# 正在关闭
|
# 正在关闭
|
||||||
pass
|
pass
|
||||||
|
|
||||||
WebSocketPeer.STATE_CLOSED:
|
WebSocketPeer.STATE_CLOSED:
|
||||||
# 连接关闭
|
if _closed_state_handled:
|
||||||
var code: int = _websocket_peer.get_close_code()
|
return
|
||||||
_on_websocket_closed(code != 0) # code=0 表示正常关闭
|
|
||||||
|
_closed_state_handled = true
|
||||||
|
|
||||||
|
# 仅在连接生命周期中发生的关闭才触发关闭处理
|
||||||
|
var should_handle_close: bool = (
|
||||||
|
_connection_state == ConnectionState.CONNECTED
|
||||||
|
or _connection_state == ConnectionState.CONNECTING
|
||||||
|
or _connection_state == ConnectionState.RECONNECTING
|
||||||
|
)
|
||||||
|
if should_handle_close:
|
||||||
|
var close_code: int = _websocket_peer.get_close_code()
|
||||||
|
_on_websocket_closed(_is_clean_close_code(close_code))
|
||||||
|
|
||||||
# WebSocket 连接成功处理
|
# WebSocket 连接成功处理
|
||||||
func _on_websocket_connected() -> void:
|
func _on_websocket_connected() -> void:
|
||||||
@@ -333,6 +359,11 @@ func _on_websocket_closed(clean_close: bool) -> void:
|
|||||||
else:
|
else:
|
||||||
_set_connection_state(ConnectionState.DISCONNECTED)
|
_set_connection_state(ConnectionState.DISCONNECTED)
|
||||||
|
|
||||||
|
# 判断关闭码是否为干净关闭
|
||||||
|
# Godot 中 close_code == -1 表示非干净关闭(异常断开)
|
||||||
|
func _is_clean_close_code(close_code: int) -> bool:
|
||||||
|
return close_code != -1
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 内部方法 - 重连机制
|
# 内部方法 - 重连机制
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -375,7 +406,7 @@ func _calculate_reconnect_delay() -> float:
|
|||||||
# 重连定时器超时处理
|
# 重连定时器超时处理
|
||||||
func _on_reconnect_timeout() -> void:
|
func _on_reconnect_timeout() -> void:
|
||||||
_clean_close = false
|
_clean_close = false
|
||||||
connect_to_game_server()
|
connect_to_game_server(true)
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 内部方法 - 心跳机制
|
# 内部方法 - 心跳机制
|
||||||
|
|||||||
@@ -61,8 +61,12 @@ var _player_spawned: bool = false # Track if local player has been spawned
|
|||||||
var _local_player: Node = null # Reference to the local player node
|
var _local_player: Node = null # Reference to the local player node
|
||||||
var _position_update_timer: float = 0.0 # Throttle timer for position updates
|
var _position_update_timer: float = 0.0 # Throttle timer for position updates
|
||||||
var _current_session_id: String = "" # Cache session ID to avoid race condition on exit
|
var _current_session_id: String = "" # Cache session ID to avoid race condition on exit
|
||||||
|
var _is_joining_session: bool = false
|
||||||
const POSITION_UPDATE_INTERVAL: float = 0.125 # Send at most 8 times per second
|
const POSITION_UPDATE_INTERVAL: float = 0.125 # Send at most 8 times per second
|
||||||
const PRIVATE_SCENES = ["room"] # List of scenes that should be private instances
|
const PRIVATE_SCENES = ["room"] # List of scenes that should be private instances
|
||||||
|
var _join_token_poll_interval: float = 0.5
|
||||||
|
var _join_token_wait_timeout: float = 15.0
|
||||||
|
var _join_player_wait_timeout_frames: int = 300
|
||||||
|
|
||||||
func _on_session_joined(data: Dictionary):
|
func _on_session_joined(data: Dictionary):
|
||||||
# 对账远程玩家列表,使用 position.mapId 过滤
|
# 对账远程玩家列表,使用 position.mapId 过滤
|
||||||
@@ -199,27 +203,52 @@ func _update_remote_player_position(user: Dictionary):
|
|||||||
player.update_position(Vector2(pos.x, pos.y))
|
player.update_position(Vector2(pos.x, pos.y))
|
||||||
|
|
||||||
func _join_session_with_player(session_id: String):
|
func _join_session_with_player(session_id: String):
|
||||||
# 检查是否有Token,如果没有则等待
|
if _is_joining_session:
|
||||||
if LocationManager._auth_token == "":
|
|
||||||
# 轮询等待Token就绪 (简单重试机制)
|
|
||||||
await get_tree().create_timer(0.5).timeout
|
|
||||||
_join_session_with_player(session_id)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
# 等待玩家生成完毕
|
_is_joining_session = true
|
||||||
if not _player_spawned or not _local_player:
|
|
||||||
|
# 检查是否有 Token,没有则轮询等待(带超时)
|
||||||
|
var token_wait_elapsed: float = 0.0
|
||||||
|
while LocationManager._auth_token == "":
|
||||||
|
if not is_inside_tree():
|
||||||
|
_is_joining_session = false
|
||||||
|
return
|
||||||
|
if token_wait_elapsed >= _join_token_wait_timeout:
|
||||||
|
push_warning("BaseLevel: 等待认证 Token 超时,取消加入会话 %s" % session_id)
|
||||||
|
_is_joining_session = false
|
||||||
|
return
|
||||||
|
await get_tree().create_timer(_join_token_poll_interval).timeout
|
||||||
|
token_wait_elapsed += _join_token_poll_interval
|
||||||
|
|
||||||
|
# 等待玩家生成完毕(带帧数上限)
|
||||||
|
var wait_frames: int = 0
|
||||||
|
while not _player_spawned or not _local_player:
|
||||||
|
if not is_inside_tree():
|
||||||
|
_is_joining_session = false
|
||||||
|
return
|
||||||
|
if wait_frames >= _join_player_wait_timeout_frames:
|
||||||
|
push_warning("BaseLevel: 等待本地玩家就绪超时,取消加入会话 %s" % session_id)
|
||||||
|
_is_joining_session = false
|
||||||
|
return
|
||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
_join_session_with_player(session_id)
|
wait_frames += 1
|
||||||
|
|
||||||
|
if not is_instance_valid(_local_player):
|
||||||
|
push_warning("BaseLevel: 本地玩家无效,取消加入会话 %s" % session_id)
|
||||||
|
_is_joining_session = false
|
||||||
return
|
return
|
||||||
|
|
||||||
var pos = _local_player.global_position if is_instance_valid(_local_player) else Vector2.ZERO
|
var pos = _local_player.global_position
|
||||||
|
|
||||||
LocationManager.join_session(session_id, pos)
|
LocationManager.join_session(session_id, pos)
|
||||||
|
|
||||||
# 进入会话后立即同步一次位置
|
# 进入会话后立即同步一次位置
|
||||||
await get_tree().create_timer(0.1).timeout
|
await get_tree().create_timer(0.1).timeout
|
||||||
|
if is_inside_tree():
|
||||||
LocationManager.send_position_update(session_id, pos)
|
LocationManager.send_position_update(session_id, pos)
|
||||||
|
|
||||||
|
_is_joining_session = false
|
||||||
|
|
||||||
func _process(delta):
|
func _process(delta):
|
||||||
# 发送位置更新 (节流机制)
|
# 发送位置更新 (节流机制)
|
||||||
if not _player_spawned or not _local_player:
|
if not _player_spawned or not _local_player:
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ var toast_manager: ToastManager
|
|||||||
|
|
||||||
# 验证码冷却计时器
|
# 验证码冷却计时器
|
||||||
var cooldown_timer: Timer = null
|
var cooldown_timer: Timer = null
|
||||||
|
var cooldown_email: String = ""
|
||||||
|
|
||||||
# 当前登录模式(从管理器同步)
|
# 当前登录模式(从管理器同步)
|
||||||
var current_login_mode: AuthManager.LoginMode = AuthManager.LoginMode.PASSWORD
|
var current_login_mode: AuthManager.LoginMode = AuthManager.LoginMode.PASSWORD
|
||||||
@@ -292,9 +293,6 @@ func _on_send_code_pressed():
|
|||||||
var email = register_email.text.strip_edges()
|
var email = register_email.text.strip_edges()
|
||||||
auth_manager.send_email_verification_code(email)
|
auth_manager.send_email_verification_code(email)
|
||||||
|
|
||||||
# 开始冷却计时器
|
|
||||||
_start_cooldown_timer(email)
|
|
||||||
|
|
||||||
# 获取登录验证码按钮
|
# 获取登录验证码按钮
|
||||||
func _on_get_login_code_pressed():
|
func _on_get_login_code_pressed():
|
||||||
var identifier = login_username.text.strip_edges()
|
var identifier = login_username.text.strip_edges()
|
||||||
@@ -374,8 +372,10 @@ func _on_controller_register_failed(_message: String):
|
|||||||
|
|
||||||
# 验证码发送成功处理
|
# 验证码发送成功处理
|
||||||
func _on_controller_verification_code_sent(_message: String):
|
func _on_controller_verification_code_sent(_message: String):
|
||||||
# 验证码发送成功,冷却计时器已经在按钮点击时启动
|
var email = auth_manager.current_email
|
||||||
pass
|
if email == "":
|
||||||
|
email = register_email.text.strip_edges()
|
||||||
|
_start_cooldown_timer(email)
|
||||||
|
|
||||||
# 验证码发送失败处理
|
# 验证码发送失败处理
|
||||||
func _on_controller_verification_code_failed(_message: String):
|
func _on_controller_verification_code_failed(_message: String):
|
||||||
@@ -429,12 +429,20 @@ func _on_controller_show_toast_message(message: String, is_success: bool):
|
|||||||
# ============ 验证码冷却管理 ============
|
# ============ 验证码冷却管理 ============
|
||||||
|
|
||||||
# 开始冷却计时器
|
# 开始冷却计时器
|
||||||
func _start_cooldown_timer(_email: String):
|
func _start_cooldown_timer(email: String):
|
||||||
if cooldown_timer != null:
|
if cooldown_timer != null:
|
||||||
cooldown_timer.queue_free()
|
cooldown_timer.queue_free()
|
||||||
|
cooldown_timer = null
|
||||||
|
|
||||||
|
cooldown_email = email
|
||||||
|
if cooldown_email == "":
|
||||||
|
cooldown_email = register_email.text.strip_edges()
|
||||||
|
|
||||||
send_code_btn.disabled = true
|
send_code_btn.disabled = true
|
||||||
send_code_btn.text = "重新发送(60)"
|
var remaining_time = auth_manager.get_remaining_cooldown_time(cooldown_email)
|
||||||
|
if remaining_time <= 0:
|
||||||
|
remaining_time = 60
|
||||||
|
send_code_btn.text = "重新发送(%d)" % remaining_time
|
||||||
|
|
||||||
cooldown_timer = Timer.new()
|
cooldown_timer = Timer.new()
|
||||||
add_child(cooldown_timer)
|
add_child(cooldown_timer)
|
||||||
@@ -444,7 +452,8 @@ func _start_cooldown_timer(_email: String):
|
|||||||
|
|
||||||
# 冷却计时器超时处理
|
# 冷却计时器超时处理
|
||||||
func _on_cooldown_timer_timeout():
|
func _on_cooldown_timer_timeout():
|
||||||
var remaining_time = auth_manager.get_remaining_cooldown_time(register_email.text.strip_edges())
|
var email = cooldown_email if cooldown_email != "" else register_email.text.strip_edges()
|
||||||
|
var remaining_time = auth_manager.get_remaining_cooldown_time(email)
|
||||||
|
|
||||||
if remaining_time > 0:
|
if remaining_time > 0:
|
||||||
send_code_btn.text = "重新发送(%d)" % remaining_time
|
send_code_btn.text = "重新发送(%d)" % remaining_time
|
||||||
@@ -462,6 +471,7 @@ func _reset_verification_button():
|
|||||||
cooldown_timer.queue_free()
|
cooldown_timer.queue_free()
|
||||||
cooldown_timer = null
|
cooldown_timer = null
|
||||||
|
|
||||||
|
cooldown_email = ""
|
||||||
send_code_btn.disabled = false
|
send_code_btn.disabled = false
|
||||||
send_code_btn.text = "发送验证码"
|
send_code_btn.text = "发送验证码"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://bvn8y7x2qkqxe"]
|
[gd_scene load_steps=4 format=3 uid="uid://bvn8y7x2qkqxe"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://ddb8v5c6aeqe7" path="res://tests/auth/auth_ui_test.gd" id="1_test_script"]
|
[ext_resource type="Script" uid="uid://ddb8v5c6aeqe7" path="res://tests/auth/auth_ui_test.gd" id="1_test_script"]
|
||||||
[ext_resource type="PackedScene" uid="uid://by7m8snb4xllf" path="res://scenes/ui/LoginWindow.tscn" id="2_auth_scene"]
|
[ext_resource type="PackedScene" uid="uid://by7m8snb4xllf" path="res://scenes/ui/AuthScene.tscn" id="2_auth_scene"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_feedback_info"]
|
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_feedback_info"]
|
||||||
bg_color = Color(0.2, 0.5, 0.8, 0.9)
|
bg_color = Color(0.2, 0.5, 0.8, 0.9)
|
||||||
|
|||||||
Reference in New Issue
Block a user