lawless/server/modules/chat.go

244 行
6.6 KiB
Go

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

// Package modules - 聊天系统模块
// 对齐GDD-29 聊天与信息传递系统设计
package modules
import (
"context"
"database/sql"
"encoding/json"
"time"
"github.com/heroiclabs/nakama-common/runtime"
)
// RegisterChat 注册聊天相关 RPC。
func RegisterChat(initializer runtime.Initializer) error {
rpcs := map[string]func(runtime.Initializer) error{
"ChatService/SendMessage": sendMessage,
"ChatService/GetMessages": getMessages,
"ChatService/GetChatChannels": getChatChannels,
"ChatService/JoinChannel": joinChannel,
"ChatService/LeaveChannel": leaveChannel,
}
for path, fn := range rpcs {
if err := initializer.RegisterRpc(path, fn); err != nil {
return err
}
}
return nil
}
type sendMessageReq struct {
ChannelID string `json:"channel_id"` // channel_id 或 player_id私聊
Content string `json:"content"`
MsgType string `json:"msg_type"` // text/system/emote
}
type getMessagesReq struct {
ChannelID string `json:"channel_id"`
Limit int32 `json:"limit"`
Before string `json:"before"` // 分页游标
}
type getChatChannelsReq struct {
CharacterID string `json:"character_id"`
}
type joinChannelReq struct {
ChannelID string `json:"channel_id"`
}
type leaveChannelReq struct {
ChannelID string `json:"channel_id"`
}
type chatMessageData struct {
MessageID string `json:"message_id"`
SenderID string `json:"sender_id"`
SenderName string `json:"sender_name"`
SenderRace string `json:"sender_race"`
ChannelID string `json:"channel_id"`
ChannelType string `json:"channel_type"` // world/region/guild/party/private/system
Content string `json:"content"`
MsgType string `json:"msg_type"` // text/system/emote
Timestamp time.Time `json:"timestamp"`
}
type chatChannelData struct {
ChannelID string `json:"channel_id"`
ChannelType string `json:"channel_type"`
Name string `json:"name"`
MemberCount int32 `json:"member_count"`
IsMuted bool `json:"is_muted"`
}
// 频道配置
var channelConfig = map[string]chatChannelData{
"world": {
ChannelID: "world", ChannelType: "world", Name: "世界频道", MemberCount: 0,
},
"region_1": {
ChannelID: "region_1", ChannelType: "region", Name: "凡界频道", MemberCount: 0,
},
"region_2": {
ChannelID: "region_2", ChannelType: "region", Name: "灵界频道", MemberCount: 0,
},
"region_3": {
ChannelID: "region_3", ChannelType: "region", Name: "道界频道", MemberCount: 0,
},
"region_4": {
ChannelID: "region_4", ChannelType: "region", Name: "圣界频道", MemberCount: 0,
},
}
func sendMessage(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
uid := userIDFromCtx(ctx)
if uid == "" {
return errResp(1001, "missing token", traceID)
}
var req sendMessageReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(2001, "invalid payload", traceID)
}
// 获取发送者信息
var charID string
var charName string
var raceID string
err := hhdbPool.QueryRow(ctx, `
SELECT id, name, race_id FROM characters WHERE player_id = $1 AND status = 'active'
ORDER BY created_at DESC LIMIT 1
`, uid).Scan(&charID, &charName, &raceID)
if err != nil {
return errResp(4002, "character not found", traceID)
}
// 检查消息长度
if len(req.Content) > 500 {
return errResp(8801, "message too long", traceID)
}
// 发送消息简化版,实际应使用Nakama实时多人游戏API
message := chatMessageData{
MessageID: genUUID(),
SenderID: charID,
SenderName: charName,
SenderRace: raceID,
ChannelID: req.ChannelID,
ChannelType: getChannelType(req.ChannelID),
Content: req.Content,
MsgType: req.MsgType,
Timestamp: time.Now(),
}
logger.Info("Chat message: sender=%s channel=%s content=%s", charName, req.ChannelID, req.Content)
return okResp(message, traceID)
}
func getMessages(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
uid := userIDFromCtx(ctx)
if uid == "" {
return errResp(1001, "missing token", traceID)
}
var req getMessagesReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(2001, "invalid payload", traceID)
}
if req.Limit < 1 || req.Limit > 100 {
req.Limit = 50
}
// 返回空消息列表(实际应从数据库/缓存读取)
return okResp(map[string]interface{}{
"channel_id": req.ChannelID,
"messages": []chatMessageData{},
"count": 0,
"has_more": false,
}, traceID)
}
func getChatChannels(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
uid := userIDFromCtx(ctx)
if uid == "" {
return errResp(1001, "missing token", traceID)
}
var req getChatChannelsReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(2001, "invalid payload", traceID)
}
// 返回可用频道列表
var channels []chatChannelData
for _, ch := range channelConfig {
channels = append(channels, ch)
}
return okResp(map[string]interface{}{
"channels": channels,
"count": len(channels),
}, traceID)
}
func joinChannel(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
uid := userIDFromCtx(ctx)
if uid == "" {
return errResp(1001, "missing token", traceID)
}
var req joinChannelReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(2001, "invalid payload", traceID)
}
// 加入频道(简化版)
logger.Info("Join channel: channel=%s", req.ChannelID)
return okResp(map[string]interface{}{
"success": true,
"channel_id": req.ChannelID,
"message": "已加入频道",
}, traceID)
}
func leaveChannel(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
uid := userIDFromCtx(ctx)
if uid == "" {
return errResp(1001, "missing token", traceID)
}
var req leaveChannelReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(2001, "invalid payload", traceID)
}
// 离开频道(简化版)
logger.Info("Leave channel: channel=%s", req.ChannelID)
return okResp(map[string]interface{}{
"success": true,
"channel_id": req.ChannelID,
"message": "已离开频道",
}, traceID)
}
// 辅助函数
func getChannelType(channelID string) string {
if channelID == "world" {
return "world"
}
if len(channelID) > 6 && channelID[:6] == "region" {
return "region"
}
return "other"
}