feat: bug fix social system

This commit is contained in:
2026-04-30 04:18:46 +08:00
parent 2a1a76e682
commit 54be7bbb25
9 changed files with 607 additions and 235 deletions
+46 -21
View File
@@ -125,7 +125,7 @@ var _bot_names: Dictionary = {}
# =============================================================================
# Chat System
# =============================================================================
const GLOBAL_CHAT_ROOM := "global_lobby"
const GLOBAL_CHAT_ROOM := "social_global"
var _chat_channel = null
var _chat_messages: Array = []
@@ -1311,15 +1311,30 @@ func _join_global_chat() -> void:
if not socket.received_channel_message.is_connected(_on_chat_message_received):
socket.received_channel_message.connect(_on_chat_message_received)
# Load history and render (Nakama sends recent messages on join via received_channel_message)
# Load history
_chat_messages.clear()
var history_result = await NakamaManager.client.list_channel_messages_async(NakamaManager.session, _chat_channel.id, 50, false)
if not history_result.is_exception() and history_result.messages:
var msgs = history_result.messages.duplicate()
msgs.reverse() # Oldest to newest
for msg in msgs:
_add_chat_message(msg, false)
_trim_old_messages()
_refresh_chat_display()
func _on_chat_message_received(message) -> void:
"""Nakama socket signal: a message arrived on any channel."""
# message is ApiChannelMessage — use direct property access, NOT .get()
if _chat_channel == null or message.channel_id != _chat_channel.id:
return
# Ignore messages from ourselves (we inject them locally for instant feedback)
if NakamaManager.session and message.sender_id == NakamaManager.session.user_id:
return
_add_chat_message(message, true)
func _add_chat_message(message, refresh_display: bool) -> void:
# content is a String (JSON) — parse to extract our "msg" field
var text: String = ""
var parsed = JSON.parse_string(message.content)
@@ -1330,8 +1345,10 @@ func _on_chat_message_received(message) -> void:
# Sender: use username property directly (falls back to first 8 chars of sender_id)
var sender: String = message.username
if sender.is_empty():
if sender.is_empty() and message.sender_id:
sender = message.sender_id.substr(0, 8)
elif sender.is_empty():
sender = "Unknown"
# Timestamp → HH:MM
var ts_str: String = _format_nakama_time(message.create_time)
@@ -1340,11 +1357,12 @@ func _on_chat_message_received(message) -> void:
"sender": sender,
"content": text,
"ts": ts_str,
"date": message.create_time.substr(0, 10)
"date": message.create_time.substr(0, 10) if message.create_time else Time.get_date_string_from_system()
})
_trim_old_messages()
_refresh_chat_display()
if refresh_display:
_trim_old_messages()
_refresh_chat_display()
func _on_chat_send_pressed() -> void:
"""Send a message to the global chat channel."""
@@ -1355,10 +1373,11 @@ func _on_chat_send_pressed() -> void:
chat_input.text = ""
chat_input.grab_focus()
# Instantly show locally for best UX
_inject_local_message(text)
var socket = NakamaManager.socket
if not socket or _chat_channel == null:
# Offline fallback: show locally only
_inject_local_message(text)
return
# Nakama GDScript SDK: write_chat_message_async takes a Dictionary, not a JSON string
@@ -1366,8 +1385,6 @@ func _on_chat_send_pressed() -> void:
var result = await socket.write_chat_message_async(_chat_channel.id, content)
if result.is_exception():
push_warning("[Chat] Failed to send message: " + result.get_exception().message)
# Still show it locally
_inject_local_message(text)
func _inject_local_message(text: String) -> void:
"""Display a message as the local player when offline/fallback."""
@@ -1382,9 +1399,9 @@ func _inject_local_message(text: String) -> void:
_refresh_chat_display()
func _trim_old_messages() -> void:
"""Remove messages from previous calendar days (daily clear)."""
var today: String = Time.get_date_string_from_system()
_chat_messages = _chat_messages.filter(func(m): return m.get("date", today) == today)
"""Keep only the most recent 100 messages to prevent memory/UI bloat."""
if _chat_messages.size() > 100:
_chat_messages = _chat_messages.slice(-100)
func _refresh_chat_display() -> void:
"""Re-render the RichTextLabel with all buffered messages."""
@@ -1402,20 +1419,28 @@ func _refresh_chat_display() -> void:
# Scroll to bottom
await get_tree().process_frame
chat_display.scroll_to_line(chat_display.get_line_count())
if chat_display:
var scrollbar = chat_display.get_v_scroll_bar()
if scrollbar:
chat_display.scroll_to_line(chat_display.get_line_count())
func _format_nakama_time(iso_str: String) -> String:
"""Convert Nakama ISO timestamp '2026-04-14T10:30:00Z' → local 'HH:MM'."""
# Parse the UTC time components
if iso_str.length() < 19:
func _format_nakama_time(time_str: String) -> String:
"""Convert Nakama time to local 'HH:MM'."""
# Nakama returns UNIX epoch as string (e.g. "1714418656") or ISO string.
if time_str.is_valid_int():
var unix_time = time_str.to_int()
var dict = Time.get_time_dict_from_unix_time(unix_time)
return "%02d:%02d" % [dict.hour, dict.minute]
# Fallback for ISO strings or empty
if time_str.length() < 19:
return _get_local_time_hhmm()
var t_parts = iso_str.split("T")
var t_parts = time_str.split("T")
if t_parts.size() < 2:
return _get_local_time_hhmm()
var time_part = t_parts[1].replace("Z", "").split(":")
if time_part.size() < 2:
return _get_local_time_hhmm()
# Use UTC hours/minutes directly (simple, avoids TZ complexity in Godot)
return "%s:%s" % [time_part[0], time_part[1]]
func _get_local_time_hhmm() -> String: