创建新工程
This commit is contained in:
570
scripts/PrivateChatSystem.gd
Normal file
570
scripts/PrivateChatSystem.gd
Normal file
@@ -0,0 +1,570 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user