feat: update 2.1.9

This commit is contained in:
2026-04-29 01:36:49 +08:00
parent 1585b91509
commit 8a2f865ad8
76 changed files with 2628 additions and 39 deletions
+35
View File
@@ -0,0 +1,35 @@
extends HBoxContainer
## FriendRow — Single row in the friends list inside SocialPanel.
@onready var _name_label: Label = %NameLabel
@onready var _state_label: Label = %StateLabel
@onready var _dm_btn: Button = %DMBtn
@onready var _accept_btn: Button = %AcceptBtn
@onready var _decline_btn: Button = %DeclineBtn
@onready var _remove_btn: Button = %RemoveBtn
var _user_id: String
var _username: String
var _panel: Control # reference to SocialPanel for opening DM
func setup(uid: String, uname: String, state: int, panel: Control) -> void:
_user_id = uid
_username = uname
_panel = panel
_name_label.text = uname
match state:
FriendManager.STATE_FRIEND:
_dm_btn.visible = true
_remove_btn.visible = true
_dm_btn.pressed.connect(func(): panel.call("_open_dm", uid, uname))
_remove_btn.pressed.connect(func(): FriendManager.remove_friend(uid))
FriendManager.STATE_INVITE_OUT:
_state_label.text = "(invite sent)"
_state_label.visible = true
FriendManager.STATE_INVITE_IN:
_accept_btn.visible = true
_decline_btn.visible = true
_accept_btn.pressed.connect(func(): FriendManager.add_friend_by_id(uid))
_decline_btn.pressed.connect(func(): FriendManager.remove_friend(uid))
+1
View File
@@ -0,0 +1 @@
uid://blugche8rky44
+27
View File
@@ -0,0 +1,27 @@
extends Window
signal closed
@onready var _friend_rows: VBoxContainer = %FriendRows
@onready var _no_friends_label: Label = %NoFriendsLabel
@onready var _close_btn: Button = %CloseBtn
const INVITE_ROW_SCENE := preload("res://scenes/ui/invite_row.tscn")
func _ready() -> void:
_close_btn.pressed.connect(func(): emit_signal("closed"))
close_requested.connect(func(): emit_signal("closed"))
func open(friends: Array, match_id: String) -> void:
if friends.is_empty():
_no_friends_label.visible = true
popup_centered()
return
_no_friends_label.visible = false
for f in friends:
var row: Control = INVITE_ROW_SCENE.instantiate()
_friend_rows.add_child(row)
row.setup(f.get("username", "?"), f.get("user_id", ""), match_id)
popup_centered()
+1
View File
@@ -0,0 +1 @@
uid://c3aw5biordna
+12
View File
@@ -0,0 +1,12 @@
extends HBoxContainer
@onready var _name_label: Label = %NameLabel
@onready var _invite_btn: Button = %InviteBtn
func setup(username: String, user_id: String, match_id: String) -> void:
_name_label.text = username
_invite_btn.pressed.connect(func():
FriendManager.send_lobby_invite(user_id, match_id)
_invite_btn.text = "Sent!"
_invite_btn.disabled = true
)
+1
View File
@@ -0,0 +1 @@
uid://by70gfocmnnbj
+14
View File
@@ -0,0 +1,14 @@
extends ConfirmationDialog
signal accepted
signal declined
@onready var _message_label: Label = %MessageLabel
func _ready() -> void:
confirmed.connect(func(): emit_signal("accepted"))
canceled.connect(func(): emit_signal("declined"))
get_cancel_button().text = "Decline"
func setup(from_name: String) -> void:
_message_label.text = "%s invited you to join their lobby!\n\nJoin now?" % from_name
+1
View File
@@ -0,0 +1 @@
uid://okk0tebly5gr
+17 -4
View File
@@ -10,6 +10,7 @@ extends Control
@onready var google_button := %GoogleButton as Button
@onready var apple_button := %AppleButton as Button
@onready var facebook_button := %FacebookButton as Button
@onready var steam_button := %SteamButton as Button
@onready var status_label := %StatusLabel as Label
@onready var loading_spinner := %LoadingSpinner as TextureProgressBar
@@ -65,6 +66,7 @@ func _connect_signals() -> void:
google_button.pressed.connect(_on_google_pressed)
apple_button.pressed.connect(_on_apple_pressed)
facebook_button.pressed.connect(_on_facebook_pressed)
steam_button.pressed.connect(_on_steam_pressed)
# Registration buttons
register_button.pressed.connect(_on_register_pressed)
@@ -99,15 +101,18 @@ func _setup_ui() -> void:
_configure_social_buttons()
func _configure_social_buttons() -> void:
# Google - available on all platforms
google_button.visible = true
# Google - hidden until API is configured
google_button.visible = false
# Apple - iOS and macOS only (or hide if not configured)
var os := OS.get_name()
apple_button.visible = os in ["iOS", "macOS"]
# Facebook - available on all platforms
facebook_button.visible = true
# Facebook - hidden until API is configured
facebook_button.visible = false
# Steam - show when GodotSteam GDExtension is available
steam_button.visible = ClassDB.class_exists("Steam")
# =============================================================================
# Panel Switching
@@ -190,6 +195,13 @@ func _on_facebook_pressed() -> void:
# When you have the access token from Facebook SDK:
# AuthManager.login_with_facebook(access_token)
func _on_steam_pressed() -> void:
if is_loading:
return
# Steam login uses Steamworks auth ticket
AuthManager.login_with_steam()
func _on_server_option_selected(index: int) -> void:
@@ -421,6 +433,7 @@ func _set_inputs_enabled(enabled: bool) -> void:
google_button.disabled = not enabled
apple_button.disabled = not enabled
facebook_button.disabled = not enabled
steam_button.disabled = not enabled
email_input.editable = enabled
password_input.editable = enabled
reg_email_input.editable = enabled
+1
View File
@@ -231,6 +231,7 @@ func _auth_mode_name(mode: int) -> String:
AuthManager.AuthMode.GOOGLE: return "Google"
AuthManager.AuthMode.APPLE: return "Apple"
AuthManager.AuthMode.FACEBOOK: return "Facebook"
AuthManager.AuthMode.STEAM: return "Steam"
_: return "Guest"
# ─────────────────────────────────────────────────────────────
+164
View File
@@ -0,0 +1,164 @@
extends Control
## SocialPanel — Friend list with DM and global chat tabs.
## Nodes defined in social_panel.tscn; this script handles all logic.
signal closed
# ─── Node references via %UniqueName ─────────────────────────────────────
@onready var _close_btn: Button = %CloseBtn
@onready var _friends_tab_btn: Button = %FriendsTabBtn
@onready var _global_tab_btn: Button = %GlobalTabBtn
@onready var _dm_tab_btn: Button = %DMTabBtn
@onready var _friends_view: VBoxContainer = %FriendsView
@onready var _global_view: VBoxContainer = %GlobalView
@onready var _dm_view: VBoxContainer = %DMView
@onready var _add_friend_input: LineEdit = %AddFriendInput
@onready var _add_friend_btn: Button = %AddFriendBtn
@onready var _friend_list: VBoxContainer = %FriendList
@onready var _global_log: RichTextLabel = %GlobalLog
@onready var _global_input: LineEdit = %GlobalInput
@onready var _global_send_btn: Button = %GlobalSendBtn
@onready var _dm_back_btn: Button = %DMBackBtn
@onready var _dm_username_label: Label = %DMUsernameLabel
@onready var _dm_log: RichTextLabel = %DMLog
@onready var _dm_input: LineEdit = %DMInput
@onready var _dm_send_btn: Button = %DMSendBtn
# ─── State ────────────────────────────────────────────────────────────────
var _active_dm_user_id: String = ""
var _active_dm_username: String = ""
var _dm_history: Dictionary = {}
var _global_chat_channel = null
var _current_tab: String = "friends"
# ─── Lifecycle ────────────────────────────────────────────────────────────
func _ready() -> void:
_close_btn.pressed.connect(func(): emit_signal("closed"); hide())
_friends_tab_btn.pressed.connect(func(): _show_tab("friends"))
_global_tab_btn.pressed.connect(func(): _show_tab("global"))
_dm_tab_btn.pressed.connect(func(): _show_tab("dm"))
_add_friend_btn.pressed.connect(_on_add_friend_pressed)
_add_friend_input.text_submitted.connect(func(_t): _on_add_friend_pressed())
_global_send_btn.pressed.connect(_send_global_message)
_global_input.text_submitted.connect(func(_t): _send_global_message())
_dm_send_btn.pressed.connect(_send_dm)
_dm_input.text_submitted.connect(func(_t): _send_dm())
_dm_back_btn.pressed.connect(func(): _show_tab("friends"))
FriendManager.friends_updated.connect(_refresh_friend_list)
FriendManager.dm_message_received.connect(_on_dm_received)
NakamaManager.connected_to_nakama.connect(_join_global_chat)
if NakamaManager.socket and NakamaManager.socket.is_connected_to_host():
_join_global_chat()
FriendManager.load_friends()
_show_tab("friends")
func _refresh_friend_list(friends: Array) -> void:
if not _friend_list:
return
for ch in _friend_list.get_children():
ch.queue_free()
if friends.is_empty():
var empty_lbl := Label.new()
empty_lbl.text = "No friends yet. Add someone above!"
_friend_list.add_child(empty_lbl)
return
var friend_row_scene := preload("res://scenes/ui/friend_row.tscn")
for f in friends:
var uid: String = f.get("user_id", "")
var uname: String = f.get("username", "?")
var state: int = f.get("state", 0)
var row: Control = friend_row_scene.instantiate()
_friend_list.add_child(row)
row.setup(uid, uname, state, self)
func _on_add_friend_pressed() -> void:
var val := _add_friend_input.text.strip_edges()
if val.is_empty():
return
_add_friend_input.text = ""
if val.length() == 36 and val.count("-") == 4:
FriendManager.add_friend_by_id(val)
else:
FriendManager.add_friend_by_username(val)
func _join_global_chat() -> void:
if _global_chat_channel:
return
var socket = NakamaManager.socket
if not socket:
return
var channel = await socket.join_chat_async(
"social_global", NakamaSocket.ChannelType.Room, true, false)
if channel.is_exception():
return
_global_chat_channel = channel
if not socket.received_channel_message.is_connected(_on_global_message):
socket.received_channel_message.connect(_on_global_message)
func _send_global_message() -> void:
var text = _global_input.text.strip_edges()
if text.is_empty() or not _global_chat_channel:
return
_global_input.text = ""
var socket = NakamaManager.socket
if socket:
socket.write_chat_message_async(_global_chat_channel.id, {"msg": text})
func _on_global_message(msg) -> void:
if not _global_chat_channel or msg.channel_id != _global_chat_channel.id:
return
var text: String = ""
var parsed = JSON.parse_string(msg.content)
if typeof(parsed) == TYPE_DICTIONARY:
text = parsed.get("msg", msg.content)
else:
text = msg.content
var sender_name: String = msg.username if msg.username else "?"
if _global_log:
_global_log.append_text("[b]%s:[/b] %s\n" % [sender_name, text])
func _open_dm(user_id: String, username: String) -> void:
_active_dm_user_id = user_id
_active_dm_username = username
_dm_username_label.text = "DM: %s" % username
_dm_tab_btn.visible = true
# Reload history
_dm_log.clear()
var history: Array = _dm_history.get(user_id, [])
for entry in history:
var is_self = entry.get("from") == "me"
var prefix = "[b]%s:[/b]" % ("You" if is_self else username)
_dm_log.append_text("%s %s\n" % [prefix, entry.get("msg", "")])
# Open channel
FriendManager.open_dm(user_id)
_show_tab("dm")
func _send_dm() -> void:
var text = _dm_input.text.strip_edges()
if text.is_empty() or _active_dm_user_id.is_empty():
return
_dm_input.text = ""
var sent = await FriendManager.send_dm(_active_dm_user_id, text)
if sent:
if not _dm_history.has(_active_dm_user_id):
_dm_history[_active_dm_user_id] = []
_dm_history[_active_dm_user_id].append({"from": "me", "msg": text})
_dm_log.append_text("[b]You:[/b] %s\n" % text)
func _on_dm_received(from_user_id: String, from_name: String, message: String) -> void:
if not _dm_history.has(from_user_id):
_dm_history[from_user_id] = []
_dm_history[from_user_id].append({"from": from_user_id, "msg": message})
if _active_dm_user_id == from_user_id and _current_tab == "dm":
_dm_log.append_text("[b]%s:[/b] %s\n" % [from_name, message])
# ─── Tab switching ─────────────────────────────────────────────────────────
func _show_tab(tab: String) -> void:
_current_tab = tab
_friends_view.visible = tab == "friends"
_global_view.visible = tab == "global"
_dm_view.visible = tab == "dm"
+1
View File
@@ -0,0 +1 @@
uid://dyr5tlvds11ib