570 lines
16 KiB
GDScript
570 lines
16 KiB
GDScript
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
|
||
} |