Files
whale-town/scripts/PrivateChatSystem.gd
2025-12-05 19:00:14 +08:00

570 lines
16 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Node
class_name PrivateChatSystem
## 私聊系统
## 管理玩家之间的私人聊天功能
# 私聊会话数据结构
class PrivateConversation:
var conversation_id: String
var participant_ids: Array[String] = []
var participant_names: Dictionary = {} # id -> name
var messages: Array[Dictionary] = []
var created_at: float
var last_activity: float
var is_active: bool = true
var unread_count: int = 0
func _init(conv_id: String, participants: Array[String], names: Dictionary):
conversation_id = conv_id
participant_ids = participants.duplicate()
participant_names = names.duplicate()
created_at = Time.get_unix_time_from_system()
last_activity = created_at
# 私聊数据存储
var conversations: Dictionary = {} # conversation_id -> PrivateConversation
var user_conversations: Dictionary = {} # user_id -> Array[conversation_id]
var active_conversation: String = ""
var max_conversations: int = 50
var max_messages_per_conversation: int = 500
# 好友系统引用
var friend_system: FriendSystem
# 数据持久化
var chat_file_path: String = "user://private_chats.json"
# 信号
signal conversation_created(conversation_id: String, participants: Array[String])
signal message_sent(conversation_id: String, sender_id: String, message: String)
signal message_received(conversation_id: String, sender_id: String, message: String)
signal conversation_opened(conversation_id: String)
signal conversation_closed(conversation_id: String)
signal unread_count_changed(conversation_id: String, count: int)
signal typing_indicator(conversation_id: String, user_id: String, is_typing: bool)
func _ready():
"""初始化私聊系统"""
load_chat_data()
print("PrivateChatSystem initialized")
## 设置好友系统引用
func set_friend_system(fs: FriendSystem) -> void:
"""
设置好友系统引用
@param fs: 好友系统实例
"""
friend_system = fs
## 开始私聊
func start_private_chat(target_id: String, target_name: String) -> String:
"""
开始与指定用户的私聊
@param target_id: 目标用户ID
@param target_name: 目标用户名称
@return: 会话ID失败返回空字符串
"""
# 检查是否为好友(如果有好友系统)
if friend_system and not friend_system.is_friend(target_id):
print("Cannot start private chat: not friends with ", target_name)
return ""
# 检查是否被屏蔽
if friend_system and friend_system.is_blocked(target_id):
print("Cannot start private chat: user is blocked")
return ""
# 查找现有会话
var existing_conversation = find_conversation_with_user(target_id)
if not existing_conversation.is_empty():
print("Using existing conversation with ", target_name)
return existing_conversation
# 检查会话数量限制
if conversations.size() >= max_conversations:
print("Cannot create new conversation: limit reached")
return ""
# 创建新会话
var conversation_id = generate_conversation_id()
var participants = ["player", target_id]
var names = {"player": "You", target_id: target_name}
var conversation = PrivateConversation.new(conversation_id, participants, names)
conversations[conversation_id] = conversation
# 更新用户会话索引
_add_conversation_to_user_index("player", conversation_id)
_add_conversation_to_user_index(target_id, conversation_id)
# 保存数据
save_chat_data()
# 发射信号
conversation_created.emit(conversation_id, participants)
print("Private chat started with ", target_name, " (", conversation_id, ")")
return conversation_id
## 发送私聊消息
func send_private_message(conversation_id: String, message: String) -> bool:
"""
发送私聊消息
@param conversation_id: 会话ID
@param message: 消息内容
@return: 是否成功发送
"""
if not conversations.has(conversation_id):
print("Conversation not found: ", conversation_id)
return false
var conversation = conversations[conversation_id]
# 验证消息
if message.strip_edges().is_empty():
print("Cannot send empty message")
return false
if message.length() > 1000:
print("Message too long")
return false
# 创建消息记录
var message_record = {
"id": generate_message_id(),
"sender_id": "player",
"message": message,
"timestamp": Time.get_unix_time_from_system(),
"read": false
}
# 添加到会话历史
conversation.messages.append(message_record)
conversation.last_activity = message_record.timestamp
# 限制消息历史长度
if conversation.messages.size() > max_messages_per_conversation:
conversation.messages.pop_front()
# 记录好友互动
if friend_system:
for participant_id in conversation.participant_ids:
if participant_id != "player":
friend_system.record_interaction(participant_id, "private_chat")
# 保存数据
save_chat_data()
# 发射信号
message_sent.emit(conversation_id, "player", message)
print("Private message sent in ", conversation_id, ": ", message)
return true
## 接收私聊消息
func receive_private_message(conversation_id: String, sender_id: String, message: String) -> void:
"""
接收私聊消息
@param conversation_id: 会话ID
@param sender_id: 发送者ID
@param message: 消息内容
"""
if not conversations.has(conversation_id):
print("Conversation not found for received message: ", conversation_id)
return
var conversation = conversations[conversation_id]
# 创建消息记录
var message_record = {
"id": generate_message_id(),
"sender_id": sender_id,
"message": message,
"timestamp": Time.get_unix_time_from_system(),
"read": false
}
# 添加到会话历史
conversation.messages.append(message_record)
conversation.last_activity = message_record.timestamp
# 更新未读计数(如果不是当前活跃会话)
if active_conversation != conversation_id:
conversation.unread_count += 1
unread_count_changed.emit(conversation_id, conversation.unread_count)
# 限制消息历史长度
if conversation.messages.size() > max_messages_per_conversation:
conversation.messages.pop_front()
# 保存数据
save_chat_data()
# 发射信号
message_received.emit(conversation_id, sender_id, message)
print("Private message received in ", conversation_id, " from ", sender_id, ": ", message)
## 打开会话
func open_conversation(conversation_id: String) -> bool:
"""
打开指定会话
@param conversation_id: 会话ID
@return: 是否成功打开
"""
if not conversations.has(conversation_id):
print("Conversation not found: ", conversation_id)
return false
# 关闭当前活跃会话
if not active_conversation.is_empty():
close_conversation(active_conversation)
# 设置为活跃会话
active_conversation = conversation_id
var conversation = conversations[conversation_id]
# 标记所有消息为已读
for message in conversation.messages:
message.read = true
# 重置未读计数
if conversation.unread_count > 0:
conversation.unread_count = 0
unread_count_changed.emit(conversation_id, 0)
# 保存数据
save_chat_data()
# 发射信号
conversation_opened.emit(conversation_id)
print("Conversation opened: ", conversation_id)
return true
## 关闭会话
func close_conversation(conversation_id: String) -> void:
"""
关闭指定会话
@param conversation_id: 会话ID
"""
if active_conversation == conversation_id:
active_conversation = ""
conversation_closed.emit(conversation_id)
print("Conversation closed: ", conversation_id)
## 删除会话
func delete_conversation(conversation_id: String) -> bool:
"""
删除会话
@param conversation_id: 会话ID
@return: 是否成功删除
"""
if not conversations.has(conversation_id):
print("Conversation not found: ", conversation_id)
return false
var conversation = conversations[conversation_id]
# 从用户会话索引中移除
for participant_id in conversation.participant_ids:
_remove_conversation_from_user_index(participant_id, conversation_id)
# 如果是当前活跃会话,关闭它
if active_conversation == conversation_id:
close_conversation(conversation_id)
# 删除会话
conversations.erase(conversation_id)
# 保存数据
save_chat_data()
print("Conversation deleted: ", conversation_id)
return true
## 获取会话列表
func get_conversations_list() -> Array[Dictionary]:
"""
获取用户的会话列表
@return: 会话信息数组
"""
var conversations_list = []
# 获取用户参与的所有会话
var user_conv_ids = user_conversations.get("player", [])
for conv_id in user_conv_ids:
if conversations.has(conv_id):
var conversation = conversations[conv_id]
var last_message = ""
var last_sender = ""
if conversation.messages.size() > 0:
var last_msg = conversation.messages[-1]
last_message = last_msg.message
last_sender = last_msg.sender_id
# 获取对方用户信息
var other_participants = []
for participant_id in conversation.participant_ids:
if participant_id != "player":
other_participants.append({
"id": participant_id,
"name": conversation.participant_names.get(participant_id, "Unknown")
})
conversations_list.append({
"id": conv_id,
"participants": other_participants,
"last_message": last_message,
"last_sender": last_sender,
"last_activity": conversation.last_activity,
"unread_count": conversation.unread_count,
"message_count": conversation.messages.size(),
"is_active": conversation.is_active
})
# 按最后活动时间排序(最新的在前)
conversations_list.sort_custom(func(a, b): return a.last_activity > b.last_activity)
return conversations_list
## 获取会话消息
func get_conversation_messages(conversation_id: String, limit: int = 0) -> Array[Dictionary]:
"""
获取会话消息历史
@param conversation_id: 会话ID
@param limit: 限制返回的消息数量0表示返回全部
@return: 消息数组
"""
if not conversations.has(conversation_id):
return []
var conversation = conversations[conversation_id]
var messages = conversation.messages
if limit <= 0 or limit >= messages.size():
return messages.duplicate()
# 返回最近的消息
return messages.slice(messages.size() - limit, messages.size())
## 搜索会话消息
func search_conversation_messages(conversation_id: String, query: String) -> Array[Dictionary]:
"""
在会话中搜索消息
@param conversation_id: 会话ID
@param query: 搜索关键词
@return: 匹配的消息数组
"""
if not conversations.has(conversation_id):
return []
var conversation = conversations[conversation_id]
var results = []
var search_query = query.to_lower()
for message in conversation.messages:
if message.message.to_lower().contains(search_query):
results.append(message.duplicate())
return results
## 查找与用户的会话
func find_conversation_with_user(user_id: String) -> String:
"""
查找与指定用户的会话
@param user_id: 用户ID
@return: 会话ID如果不存在则返回空字符串
"""
var user_conv_ids = user_conversations.get("player", [])
for conv_id in user_conv_ids:
if conversations.has(conv_id):
var conversation = conversations[conv_id]
if user_id in conversation.participant_ids:
return conv_id
return ""
## 获取未读消息总数
func get_total_unread_count() -> int:
"""
获取所有会话的未读消息总数
@return: 未读消息总数
"""
var total = 0
var user_conv_ids = user_conversations.get("player", [])
for conv_id in user_conv_ids:
if conversations.has(conv_id):
total += conversations[conv_id].unread_count
return total
## 设置输入状态
func set_typing_status(conversation_id: String, is_typing: bool) -> void:
"""
设置输入状态
@param conversation_id: 会话ID
@param is_typing: 是否正在输入
"""
if conversations.has(conversation_id):
typing_indicator.emit(conversation_id, "player", is_typing)
## 处理输入状态通知
func handle_typing_notification(conversation_id: String, user_id: String, is_typing: bool) -> void:
"""
处理其他用户的输入状态通知
@param conversation_id: 会话ID
@param user_id: 用户ID
@param is_typing: 是否正在输入
"""
if conversations.has(conversation_id):
typing_indicator.emit(conversation_id, user_id, is_typing)
## 生成会话ID
func generate_conversation_id() -> String:
"""生成唯一的会话ID"""
var timestamp = Time.get_unix_time_from_system()
var random = randi()
return "conv_%d_%d" % [timestamp, random]
## 生成消息ID
func generate_message_id() -> String:
"""生成唯一的消息ID"""
var timestamp = Time.get_unix_time_from_system()
var random = randi()
return "msg_%d_%d" % [timestamp, random]
## 添加会话到用户索引
func _add_conversation_to_user_index(user_id: String, conversation_id: String) -> void:
"""
添加会话到用户索引
@param user_id: 用户ID
@param conversation_id: 会话ID
"""
if not user_conversations.has(user_id):
user_conversations[user_id] = []
var user_convs = user_conversations[user_id]
if not conversation_id in user_convs:
user_convs.append(conversation_id)
## 从用户索引移除会话
func _remove_conversation_from_user_index(user_id: String, conversation_id: String) -> void:
"""
从用户索引移除会话
@param user_id: 用户ID
@param conversation_id: 会话ID
"""
if user_conversations.has(user_id):
var user_convs = user_conversations[user_id]
user_convs.erase(conversation_id)
## 保存聊天数据
func save_chat_data() -> void:
"""保存聊天数据到本地文件"""
var data = {
"conversations": {},
"user_conversations": user_conversations,
"active_conversation": active_conversation
}
# 序列化会话数据
for conv_id in conversations:
var conversation = conversations[conv_id]
data.conversations[conv_id] = {
"participant_ids": conversation.participant_ids,
"participant_names": conversation.participant_names,
"messages": conversation.messages,
"created_at": conversation.created_at,
"last_activity": conversation.last_activity,
"is_active": conversation.is_active,
"unread_count": conversation.unread_count
}
var file = FileAccess.open(chat_file_path, FileAccess.WRITE)
if file:
var json_string = JSON.stringify(data)
file.store_string(json_string)
file.close()
print("Private chat data saved")
else:
print("Failed to save private chat data")
## 加载聊天数据
func load_chat_data() -> void:
"""从本地文件加载聊天数据"""
if not FileAccess.file_exists(chat_file_path):
print("No private chat data file found, starting fresh")
return
var file = FileAccess.open(chat_file_path, FileAccess.READ)
if file:
var json_string = file.get_as_text()
file.close()
var json = JSON.new()
var parse_result = json.parse(json_string)
if parse_result == OK:
var data = json.data
# 加载会话数据
if data.has("conversations"):
for conv_id in data.conversations:
var conv_data = data.conversations[conv_id]
var conversation = PrivateConversation.new(
conv_id,
conv_data.get("participant_ids", []),
conv_data.get("participant_names", {})
)
conversation.messages = conv_data.get("messages", [])
conversation.created_at = conv_data.get("created_at", Time.get_unix_time_from_system())
conversation.last_activity = conv_data.get("last_activity", conversation.created_at)
conversation.is_active = conv_data.get("is_active", true)
conversation.unread_count = conv_data.get("unread_count", 0)
conversations[conv_id] = conversation
# 加载用户会话索引
if data.has("user_conversations"):
user_conversations = data.user_conversations
# 加载活跃会话
if data.has("active_conversation"):
active_conversation = data.active_conversation
print("Private chat data loaded: ", conversations.size(), " conversations")
else:
print("Failed to parse private chat data JSON")
else:
print("Failed to open private chat data file")
## 获取统计信息
func get_statistics() -> Dictionary:
"""
获取私聊系统统计信息
@return: 统计信息字典
"""
var total_messages = 0
var total_unread = 0
var active_conversations = 0
for conv_id in conversations:
var conversation = conversations[conv_id]
total_messages += conversation.messages.size()
total_unread += conversation.unread_count
if conversation.is_active:
active_conversations += 1
return {
"total_conversations": conversations.size(),
"active_conversations": active_conversations,
"total_messages": total_messages,
"total_unread": total_unread,
"current_active": active_conversation,
"max_conversations": max_conversations,
"max_messages_per_conversation": max_messages_per_conversation
}