一些检测仍在等待运行
Docs Build / build-and-deploy (push) Waiting to run
- 移除 ConfigManager 配置管理器类 - 移除 GameManager 全局单例管理器类 - 移除 NetworkManager 网络连接管理器类 - 移除 CharacterData 和 ItemData 数据模型类 - 移除 BagScene、BattleScene、LobbyScene 等场景脚本 - 移除 EncounterBubble 和 EventFeedPanel UI组件脚本 - 更新代理邀请文档中的服务器连接方式 - 更新同步状态表格中的代理任务分配信息 - 添加 MiMo 任务完成总结和审查修复记录
218 行
6.6 KiB
Go
218 行
6.6 KiB
Go
package modules
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
|
|
"github.com/heroiclabs/nakama-common/runtime"
|
|
)
|
|
|
|
// RegisterCharacter 注册角色相关 RPC。
|
|
func RegisterCharacter(initializer runtime.Initializer) error {
|
|
if err := initializer.RegisterRpc("CharacterService/CreateCharacter", createCharacter); err != nil {
|
|
return err
|
|
}
|
|
if err := initializer.RegisterRpc("CharacterService/GetCharacter", getCharacter); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type createCharacterReq struct {
|
|
Name string `json:"name"`
|
|
RaceID string `json:"race_id"`
|
|
BirthWorldTier int32 `json:"birth_world_tier"`
|
|
}
|
|
|
|
type getCharacterReq struct {
|
|
CharacterID string `json:"character_id"`
|
|
WithSnapshot bool `json:"with_snapshot"`
|
|
}
|
|
|
|
type characterData struct {
|
|
CharacterID string `json:"character_id"`
|
|
PlayerID string `json:"player_id"`
|
|
Name string `json:"name"`
|
|
RaceID string `json:"race_id"`
|
|
WorldTier int32 `json:"world_tier"`
|
|
RealmTier int32 `json:"realm_tier"`
|
|
MinorRealm int32 `json:"minor_realm"`
|
|
RealmStatus string `json:"realm_status"`
|
|
Level int32 `json:"level"`
|
|
Exp int64 `json:"exp"`
|
|
Status string `json:"status"`
|
|
BaseStats string `json:"base_stats"`
|
|
BattleStats string `json:"battle_stats"`
|
|
SanCurrent int32 `json:"san_current"`
|
|
SanMax int32 `json:"san_max"`
|
|
CrimeScore int32 `json:"crime_score"`
|
|
HeavenlyValue int32 `json:"heavenly_value"`
|
|
KarmaValue int32 `json:"karma_value"`
|
|
ReputationScore int32 `json:"reputation_score"`
|
|
LastOnlineAt string `json:"last_online_at"`
|
|
CreatedAt string `json:"created_at"`
|
|
}
|
|
|
|
func createCharacter(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 createCharacterReq
|
|
if err := json.Unmarshal([]byte(payload), &req); err != nil {
|
|
return errResp(2001, "invalid payload", traceID)
|
|
}
|
|
|
|
// 校验参数
|
|
if req.Name == "" || len(req.Name) > 32 {
|
|
return errResp(2002, "invalid name length", traceID)
|
|
}
|
|
if req.RaceID == "" {
|
|
return errResp(2003, "race_id required", traceID)
|
|
}
|
|
if req.BirthWorldTier < 1 || req.BirthWorldTier > 5 {
|
|
req.BirthWorldTier = 1 // 默认第1层
|
|
}
|
|
|
|
// 检查种族是否可创建
|
|
var canCreate bool
|
|
err := db.QueryRowContext(ctx, `
|
|
SELECT can_create FROM race_templates WHERE id = $1
|
|
`, req.RaceID).Scan(&canCreate)
|
|
if err != nil {
|
|
return errResp(2004, "invalid race", traceID)
|
|
}
|
|
if !canCreate {
|
|
return errResp(2005, "race not creatable", traceID)
|
|
}
|
|
|
|
// 检查角色名是否已存在
|
|
var nameCount int32
|
|
err = db.QueryRowContext(ctx, `
|
|
SELECT COUNT(*) FROM characters WHERE name = $1
|
|
`, req.Name).Scan(&nameCount)
|
|
if err == nil && nameCount > 0 {
|
|
return errResp(2006, "name already exists", traceID)
|
|
}
|
|
|
|
// 获取玩家ID
|
|
var playerID string
|
|
err = db.QueryRowContext(ctx, `
|
|
SELECT id FROM players WHERE nakama_user_id = $1
|
|
`, uid).Scan(&playerID)
|
|
if err != nil {
|
|
return errResp(4002, "player not found", traceID)
|
|
}
|
|
|
|
// 获取种族初始属性
|
|
var baseStatsJSON string
|
|
err = db.QueryRowContext(ctx, `
|
|
SELECT COALESCE(description, '{}') FROM race_templates WHERE id = $1
|
|
`, req.RaceID).Scan(&baseStatsJSON)
|
|
if err != nil {
|
|
baseStatsJSON = `{"str":10,"vit":10,"wis":10,"agi":10,"spi":10,"luk":10}`
|
|
}
|
|
|
|
// 创建角色
|
|
var charID string
|
|
err = db.QueryRowContext(ctx, `
|
|
INSERT INTO characters (
|
|
player_id, name, race_id, birth_race_id, birth_world_tier,
|
|
world_tier, realm_tier, minor_realm, realm_status,
|
|
level, exp, status, base_stats, battle_stats,
|
|
san_current, san_max, crime_score, heavenly_value, karma_value,
|
|
reputation_score, last_online_at, created_at, updated_at
|
|
) VALUES (
|
|
$1, $2, $3, $3, $4,
|
|
$4, 1, 1, 'normal',
|
|
1, 0, 'active', $5, '{}',
|
|
100, 100, 0, 0, 0,
|
|
0, NOW(), NOW(), NOW()
|
|
)
|
|
RETURNING id::text
|
|
`, playerID, req.Name, req.RaceID, req.BirthWorldTier, baseStatsJSON).Scan(&charID)
|
|
if err != nil {
|
|
logger.Error("create character failed: %v", err)
|
|
return errResp(9002, "internal error", traceID)
|
|
}
|
|
|
|
// 创建种族状态记录
|
|
_, err = db.ExecContext(ctx, `
|
|
INSERT INTO character_race_states (character_id, main_race_id, bloodline_data, race_talents, hidden_talents)
|
|
VALUES ($1, $2, '{}', '{}', '{}')
|
|
`, charID, req.RaceID)
|
|
if err != nil {
|
|
logger.Error("create race state failed: %v", err)
|
|
}
|
|
|
|
// 创建货币余额记录
|
|
_, err = db.ExecContext(ctx, `
|
|
INSERT INTO currency_balances (character_id, currency_code, amount, total_earned, total_spent)
|
|
VALUES ($1, 'copper', 100, 100, 0)
|
|
`, charID)
|
|
if err != nil {
|
|
logger.Error("create currency balance failed: %v", err)
|
|
}
|
|
|
|
logger.Info("CharacterService/CreateCharacter success: char_id=%s name=%s race=%s", charID, req.Name, req.RaceID)
|
|
return okResp(characterData{
|
|
CharacterID: charID,
|
|
PlayerID: playerID,
|
|
Name: req.Name,
|
|
RaceID: req.RaceID,
|
|
WorldTier: req.BirthWorldTier,
|
|
RealmTier: 1,
|
|
MinorRealm: 1,
|
|
Level: 1,
|
|
Exp: 0,
|
|
Status: "active",
|
|
}, traceID)
|
|
}
|
|
|
|
func getCharacter(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 getCharacterReq
|
|
if err := json.Unmarshal([]byte(payload), &req); err != nil {
|
|
return errResp(2005, "invalid payload", traceID)
|
|
}
|
|
|
|
// 查询玩家ID
|
|
var playerID string
|
|
err := db.QueryRowContext(ctx, `
|
|
SELECT id FROM players WHERE nakama_user_id = $1
|
|
`, uid).Scan(&playerID)
|
|
if err != nil {
|
|
return errResp(4002, "player not found", traceID)
|
|
}
|
|
|
|
// 查询角色
|
|
var char characterData
|
|
err = db.QueryRowContext(ctx, `
|
|
SELECT id, player_id, name, race_id, world_tier, realm_tier, minor_realm,
|
|
realm_status, level, exp, status, base_stats, battle_stats,
|
|
san_current, san_max, crime_score, heavenly_value, karma_value,
|
|
reputation_score, last_online_at, created_at
|
|
FROM characters
|
|
WHERE id = $1 AND player_id = $2
|
|
`, req.CharacterID, playerID).Scan(
|
|
&char.CharacterID, &char.PlayerID, &char.Name, &char.RaceID,
|
|
&char.WorldTier, &char.RealmTier, &char.MinorRealm,
|
|
&char.RealmStatus, &char.Level, &char.Exp, &char.Status,
|
|
&char.BaseStats, &char.BattleStats,
|
|
&char.SanCurrent, &char.SanMax, &char.CrimeScore,
|
|
&char.HeavenlyValue, &char.KarmaValue, &char.ReputationScore,
|
|
&char.LastOnlineAt, &char.CreatedAt,
|
|
)
|
|
if err != nil {
|
|
return errResp(2007, "character not found", traceID)
|
|
}
|
|
|
|
return okResp(char, traceID)
|
|
}
|