创建新工程

This commit is contained in:
moyin
2025-12-05 19:00:14 +08:00
commit ff4fa5fffd
227 changed files with 32804 additions and 0 deletions

View 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
}