feat: bug fix social system
This commit is contained in:
@@ -7,7 +7,8 @@ signal lobby_invite_received(from_user_id: String, from_name: String, match_id:
|
||||
signal dm_message_received(from_user_id: String, from_name: String, message: String)
|
||||
|
||||
## Notification codes (must match server-side RPC)
|
||||
const NOTIF_LOBBY_INVITE := 1001
|
||||
const NOTIF_LOBBY_INVITE := 1001
|
||||
const NOTIF_FRIEND_REQUEST := 1002
|
||||
|
||||
## Friend state codes from Nakama
|
||||
const STATE_FRIEND := 0
|
||||
@@ -15,18 +16,59 @@ const STATE_INVITE_OUT := 1
|
||||
const STATE_INVITE_IN := 2
|
||||
const STATE_BLOCKED := 3
|
||||
|
||||
var friends: Array = [] # [{user_id, username, state}]
|
||||
var _friend_ids: Dictionary = {} # user_id -> true (mutual friends only)
|
||||
var friends: Array = [] # [{user_id, username, state}]
|
||||
var _friend_ids: Dictionary = {} # user_id -> true (mutual friends only)
|
||||
var _dm_channels: Dictionary = {} # user_id -> NakamaChannel
|
||||
var _has_pending_requests: bool = false # set before UI is ready
|
||||
|
||||
func _ready() -> void:
|
||||
NakamaManager.connected_to_nakama.connect(_on_nakama_connected)
|
||||
AuthManager.logged_out.connect(_on_logged_out)
|
||||
if NakamaManager.socket and NakamaManager.socket.is_connected_to_host():
|
||||
_connect_socket_signals()
|
||||
|
||||
func _on_logged_out() -> void:
|
||||
print("[FriendManager] Clearing state on logout")
|
||||
friends.clear()
|
||||
_friend_ids.clear()
|
||||
close_all_dm_channels()
|
||||
_has_pending_requests = false
|
||||
emit_signal("friends_updated", [])
|
||||
|
||||
func _on_nakama_connected() -> void:
|
||||
_connect_socket_signals()
|
||||
load_friends()
|
||||
await load_friends()
|
||||
_fetch_pending_notifications()
|
||||
|
||||
func _fetch_pending_notifications() -> void:
|
||||
if not NakamaManager.session or not NakamaManager.client:
|
||||
return
|
||||
print("[FriendManager] _fetch_pending_notifications: fetching...")
|
||||
# Retrieve all persistent notifications queued while offline (up to 100)
|
||||
var result = await NakamaManager.client.list_notifications_async(
|
||||
NakamaManager.session, 100, null)
|
||||
if result.is_exception():
|
||||
push_warning("[FriendManager] Failed to fetch notifications: " + result.get_exception().message)
|
||||
return
|
||||
|
||||
print("[FriendManager] _fetch_pending_notifications: count=%d" % result.notifications.size())
|
||||
var has_friend_request := false
|
||||
var ids_to_delete: PackedStringArray = PackedStringArray()
|
||||
|
||||
for notif in result.notifications:
|
||||
print("[FriendManager] notification: code=%d sender=%s" % [notif.code, notif.sender_id])
|
||||
ids_to_delete.append(notif.id)
|
||||
if notif.code == NOTIF_FRIEND_REQUEST:
|
||||
has_friend_request = true
|
||||
|
||||
# Acknowledge all so they are not delivered again next login
|
||||
if ids_to_delete.size() > 0:
|
||||
NakamaManager.client.delete_notifications_async(NakamaManager.session, ids_to_delete)
|
||||
|
||||
# If any were friend requests, reload the friends list to show them
|
||||
if has_friend_request:
|
||||
print("[FriendManager] _fetch_pending_notifications: reloading friends for pending request")
|
||||
load_friends()
|
||||
|
||||
func _connect_socket_signals() -> void:
|
||||
var socket = NakamaManager.socket
|
||||
@@ -43,13 +85,16 @@ func _connect_socket_signals() -> void:
|
||||
|
||||
func load_friends() -> void:
|
||||
if not NakamaManager.session:
|
||||
print("[FriendManager] load_friends: no session")
|
||||
return
|
||||
var result = await NakamaManager.client.list_friends_async(NakamaManager.session, 100, null, null)
|
||||
print("[FriendManager] load_friends: fetching...")
|
||||
var result = await NakamaManager.client.list_friends_async(NakamaManager.session, null, 100, null)
|
||||
if result.is_exception():
|
||||
push_warning("[FriendManager] Failed to load friends: " + result.get_exception().message)
|
||||
return
|
||||
friends.clear()
|
||||
_friend_ids.clear()
|
||||
_has_pending_requests = false
|
||||
for f in result.friends:
|
||||
var u = f.user
|
||||
var entry := {
|
||||
@@ -58,8 +103,12 @@ func load_friends() -> void:
|
||||
"state": f.state,
|
||||
}
|
||||
friends.append(entry)
|
||||
print("[FriendManager] friend: %s state=%d" % [u.username, f.state])
|
||||
if f.state == STATE_FRIEND:
|
||||
_friend_ids[u.id] = true
|
||||
if f.state == STATE_INVITE_IN:
|
||||
_has_pending_requests = true
|
||||
print("[FriendManager] load_friends done: %d entries" % friends.size())
|
||||
emit_signal("friends_updated", friends)
|
||||
|
||||
func get_mutual_friends() -> Array:
|
||||
@@ -71,17 +120,30 @@ func is_friend(user_id: String) -> bool:
|
||||
func add_friend_by_id(user_id: String) -> bool:
|
||||
if not NakamaManager.session:
|
||||
return false
|
||||
print("[FriendManager] add_friend_by_id: adding %s" % user_id)
|
||||
# Step 1: Add the friend relationship via native Nakama client API
|
||||
var result = await NakamaManager.client.add_friends_async(
|
||||
NakamaManager.session, PackedStringArray([user_id]), null)
|
||||
if result.is_exception():
|
||||
push_warning("[FriendManager] add_friend failed: " + result.get_exception().message)
|
||||
return false
|
||||
print("[FriendManager] add_friend_by_id: add_friends_async OK, sending notification RPC...")
|
||||
# Step 2: Notify the target via RPC
|
||||
var payload = JSON.stringify({"user_id": user_id})
|
||||
var rpc_result = await NakamaManager.client.rpc_async(NakamaManager.session, "send_friend_request", payload)
|
||||
if rpc_result.is_exception():
|
||||
push_error("[FriendManager] rpcSendFriendRequest failed: " + rpc_result.get_exception().message)
|
||||
else:
|
||||
print("[FriendManager] rpcSendFriendRequest OK: " + str(rpc_result.payload))
|
||||
|
||||
load_friends()
|
||||
return true
|
||||
|
||||
func add_friend_by_username(username: String) -> bool:
|
||||
if not NakamaManager.session:
|
||||
return false
|
||||
# Resolve username → user_id first via search, then use the RPC
|
||||
# Fall back to direct Nakama API (no notification sent to target)
|
||||
var result = await NakamaManager.client.add_friends_async(
|
||||
NakamaManager.session, null, PackedStringArray([username]))
|
||||
if result.is_exception():
|
||||
@@ -114,12 +176,18 @@ func send_lobby_invite(to_user_id: String, match_id: String) -> void:
|
||||
push_warning("[FriendManager] send_lobby_invite failed: " + result.get_exception().message)
|
||||
|
||||
func _on_notification_received(notification) -> void:
|
||||
if notification.code == NOTIF_LOBBY_INVITE:
|
||||
var content = JSON.parse_string(notification.content)
|
||||
if content:
|
||||
var from_name: String = content.get("from_name", "Someone")
|
||||
var match_id: String = content.get("match_id", "")
|
||||
emit_signal("lobby_invite_received", notification.sender_id, from_name, match_id)
|
||||
print("[FriendManager] _on_notification_received: code=%d sender=%s" % [notification.code, notification.sender_id])
|
||||
match notification.code:
|
||||
NOTIF_LOBBY_INVITE:
|
||||
var content = JSON.parse_string(notification.content)
|
||||
if content:
|
||||
var from_name: String = content.get("from_name", "Someone")
|
||||
var match_id: String = content.get("match_id", "")
|
||||
emit_signal("lobby_invite_received", notification.sender_id, from_name, match_id)
|
||||
NOTIF_FRIEND_REQUEST:
|
||||
# Refresh friends list so the incoming request appears immediately
|
||||
print("[FriendManager] friend request notification received, reloading friends...")
|
||||
load_friends()
|
||||
|
||||
# =============================================================================
|
||||
# Direct Messages
|
||||
@@ -139,6 +207,30 @@ func open_dm(user_id: String) -> Object:
|
||||
_dm_channels[user_id] = channel
|
||||
return channel
|
||||
|
||||
func get_dm_history(user_id: String, limit: int = 50) -> Array:
|
||||
if not NakamaManager.session:
|
||||
return []
|
||||
var channel = await open_dm(user_id)
|
||||
if not channel:
|
||||
return []
|
||||
var result = await NakamaManager.client.list_channel_messages_async(NakamaManager.session, channel.id, limit, true)
|
||||
if result.is_exception():
|
||||
push_warning("[FriendManager] Failed to fetch DM history: " + result.get_exception().message)
|
||||
return []
|
||||
|
||||
var history: Array = []
|
||||
for msg in result.messages:
|
||||
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
|
||||
history.append({"from": msg.sender_id, "msg": text, "username": msg.username})
|
||||
|
||||
history.reverse() # Oldest to newest
|
||||
return history
|
||||
|
||||
func send_dm(user_id: String, message: String) -> bool:
|
||||
var channel = await open_dm(user_id)
|
||||
if not channel:
|
||||
@@ -154,18 +246,17 @@ func get_dm_channel_id(user_id: String) -> String:
|
||||
return ch.id if ch else ""
|
||||
|
||||
func _on_channel_message(message) -> void:
|
||||
# Route to DM signal if this message is from a DM channel
|
||||
for user_id in _dm_channels:
|
||||
var ch = _dm_channels[user_id]
|
||||
if ch.id == message.channel_id:
|
||||
var text: String = ""
|
||||
var parsed = JSON.parse_string(message.content)
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
text = parsed.get("msg", message.content)
|
||||
else:
|
||||
text = message.content
|
||||
emit_signal("dm_message_received", message.sender_id, message.username, text)
|
||||
return
|
||||
# Ignore global chat
|
||||
if "social_global" in message.channel_id:
|
||||
return
|
||||
|
||||
var text: String = ""
|
||||
var parsed = JSON.parse_string(message.content)
|
||||
if typeof(parsed) == TYPE_DICTIONARY:
|
||||
text = parsed.get("msg", message.content)
|
||||
else:
|
||||
text = message.content
|
||||
emit_signal("dm_message_received", message.sender_id, message.username, text)
|
||||
|
||||
func close_all_dm_channels() -> void:
|
||||
var socket = NakamaManager.socket
|
||||
|
||||
Reference in New Issue
Block a user