lawless/server/modules/manual.go
徐勤民 521603a899
一些检测仍在等待运行
Docs Build / build-and-deploy (push) Waiting to run
refactor(client): 删除游戏核心管理器和场景脚本
- 移除 ConfigManager 配置管理器类
- 移除 GameManager 全局单例管理器类
- 移除 NetworkManager 网络连接管理器类
- 移除 CharacterData 和 ItemData 数据模型类
- 移除 BagScene、BattleScene、LobbyScene 等场景脚本
- 移除 EncounterBubble 和 EventFeedPanel UI组件脚本
- 更新代理邀请文档中的服务器连接方式
- 更新同步状态表格中的代理任务分配信息
- 添加 MiMo 任务完成总结和审查修复记录
2026-07-03 21:34:51 +08:00

263 行
7.5 KiB
Go

package modules
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"math/rand"
"github.com/heroiclabs/nakama-common/runtime"
)
// RegisterManual 注册功法相关 RPC。
func RegisterManual(initializer runtime.Initializer) error {
if err := initializer.RegisterRpc("ManualService/ListManuals", listManuals); err != nil {
return err
}
if err := initializer.RegisterRpc("ManualService/UpgradeManual", upgradeManual); err != nil {
return err
}
if err := initializer.RegisterRpc("ManualService/SetBuffingManual", setBuffingManual); err != nil {
return err
}
return nil
}
type manualListReq struct {
CharacterID string `json:"character_id"`
IsBuffing bool `json:"is_buffing"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
}
type upgradeManualReq struct {
CharacterID string `json:"character_id"`
ManualInstanceID string `json:"manual_instance_id"`
Materials []materialCostReq `json:"materials"`
UseInsightItem bool `json:"use_insight_item"`
IdempotencyKey string `json:"idempotency_key"`
}
type buffManualReq struct {
CharacterID string `json:"character_id"`
ManualInstanceID string `json:"manual_instance_id"`
SlotIndex int32 `json:"slot_index"`
UnsetOthers bool `json:"unset_others"`
}
type manualItemData struct {
ManualInstanceID string `json:"manual_instance_id"`
ManualID string `json:"manual_id"`
Name string `json:"name"`
Category string `json:"category"`
Domain string `json:"domain"`
Element string `json:"element"`
InstanceData interface{} `json:"instance_data"`
IsBuffing bool `json:"is_buffing"`
CreatedAt string `json:"created_at"`
}
type manualListData struct {
Total int32 `json:"total"`
List []manualItemData `json:"list"`
}
type manualUpgradeData struct {
ManualInstanceID string `json:"manual_instance_id"`
OldLayer int32 `json:"old_layer"`
NewLayer int32 `json:"new_layer"`
Success bool `json:"success"`
ProficiencyLeft int32 `json:"proficiency_left"`
UnlockedSkill interface{} `json:"unlocked_skill"`
}
type buffSlotData struct {
SlotIndex int32 `json:"slot_index"`
ManualInstanceID string `json:"manual_instance_id"`
BuffSpeedBonus float64 `json:"buff_speed_bonus"`
BreakinUntil string `json:"breakin_until"`
}
func listManuals(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
var req manualListReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(5001, "invalid payload", traceID)
}
if req.Page < 1 {
req.Page = 1
}
if req.PageSize < 1 || req.PageSize > 50 {
req.PageSize = 20
}
offset := (req.Page - 1) * req.PageSize
// 查询功法列表
query := `
SELECT cm.id, cm.manual_id, m.name, m.category, m.domain, m.element,
cm.instance_data, cm.is_buffing, cm.created_at::text
FROM character_manuals cm
JOIN manuals m ON cm.manual_id = m.id
WHERE cm.character_id = $1
`
args := []interface{}{req.CharacterID}
if req.IsBuffing {
query += " AND cm.is_buffing = true"
}
query += " ORDER BY cm.created_at DESC LIMIT $2 OFFSET $3"
args = append(args, req.PageSize, offset)
rows, err := db.QueryContext(ctx, query, args...)
if err != nil {
logger.Error("list manuals failed: %v", err)
return errResp(9002, "internal error", traceID)
}
defer rows.Close()
var manuals []manualItemData
for rows.Next() {
var m manualItemData
if err := rows.Scan(
&m.ManualInstanceID, &m.ManualID, &m.Name, &m.Category,
&m.Domain, &m.Element, &m.InstanceData, &m.IsBuffing, &m.CreatedAt,
); err != nil {
continue
}
manuals = append(manuals, m)
}
// 查询总数
var total int32
err = db.QueryRowContext(ctx, `
SELECT COUNT(*) FROM character_manuals WHERE character_id = $1
`, req.CharacterID).Scan(&total)
if err != nil {
total = 0
}
return okResp(manualListData{Total: total, List: manuals}, traceID)
}
func upgradeManual(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
var req upgradeManualReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(5002, "invalid payload", traceID)
}
// 查询功法信息
var currentLayer int32
var maxLayer int32
err := db.QueryRowContext(ctx, `
SELECT cm.instance_data->>'current_layer', m.max_layers
FROM character_manuals cm
JOIN manuals m ON cm.manual_id = m.id
WHERE cm.id = $1 AND cm.character_id = $2
`, req.ManualInstanceID, req.CharacterID).Scan(&currentLayer, &maxLayer)
if err != nil {
return errResp(5003, "manual not found", traceID)
}
// 检查层数上限
if currentLayer >= maxLayer {
return errResp(5004, "already at max layer", traceID)
}
// 检查熟练度
var proficiency int32
err = db.QueryRowContext(ctx, `
SELECT COALESCE((instance_data->>'proficiency')::int, 0)
FROM character_manuals WHERE id = $1
`, req.ManualInstanceID).Scan(&proficiency)
if err != nil {
proficiency = 0
}
requiredProficiency := currentLayer * 100
if proficiency < requiredProficiency {
return errResp(5005, "insufficient proficiency", traceID)
}
// 计算成功率
successRate := 0.85
if currentLayer >= 5 {
successRate -= float64(currentLayer-4) * 0.05
}
// 判定成功/失败
success := rand.Float64() < successRate
var newLayer int32
if success {
newLayer = currentLayer + 1
// 更新功法层数和熟练度
_, err = db.ExecContext(ctx, `
UPDATE character_manuals
SET instance_data = jsonb_set(
instance_data,
'{current_layer}',
$1::jsonb
),
updated_at = NOW()
WHERE id = $2
`, fmt.Sprintf("%d", newLayer), req.ManualInstanceID)
if err != nil {
logger.Error("upgrade manual failed: %v", err)
return errResp(9002, "internal error", traceID)
}
}
logger.Info("ManualService/UpgradeManual: manual=%s success=%v", req.ManualInstanceID, success)
return okResp(manualUpgradeData{
ManualInstanceID: req.ManualInstanceID,
OldLayer: currentLayer,
NewLayer: newLayer,
Success: success,
}, traceID)
}
func setBuffingManual(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, payload string) (string, error) {
traceID := newTraceID()
var req buffManualReq
if err := json.Unmarshal([]byte(payload), &req); err != nil {
return errResp(5006, "invalid payload", traceID)
}
// 查询功法信息
var isBuffing bool
err := db.QueryRowContext(ctx, `
SELECT is_buffing FROM character_manuals
WHERE id = $1 AND character_id = $2
`, req.ManualInstanceID, req.CharacterID).Scan(&isBuffing)
if err != nil {
return errResp(5003, "manual not found", traceID)
}
// 检查是否已在加持状态
if isBuffing {
return errResp(5007, "already buffing", traceID)
}
// 更新加持状态
_, err = db.ExecContext(ctx, `
UPDATE character_manuals
SET is_buffing = true, updated_at = NOW()
WHERE id = $1 AND character_id = $2
`, req.ManualInstanceID, req.CharacterID)
if err != nil {
logger.Error("set buffing manual failed: %v", err)
return errResp(9002, "internal error", traceID)
}
logger.Info("ManualService/SetBuffingManual success: manual=%s", req.ManualInstanceID)
return okResp(buffSlotData{
SlotIndex: req.SlotIndex,
ManualInstanceID: req.ManualInstanceID,
BuffSpeedBonus: 0.08,
}, traceID)
}