| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- package redis
- import (
- "errors"
- "f1-game/internal/enum"
- nameRedis "f1-game/internal/name/redis"
- "f1-game/internal/pb"
- "fmt"
- "slices"
- cmapStructure "github.com/cherry-game/cherry/extend/mapstructure"
- cstring "github.com/cherry-game/cherry/extend/string"
- clog "github.com/cherry-game/cherry/logger"
- cprofile "github.com/cherry-game/cherry/profile"
- "github.com/go-redis/redis/v8"
- jsoniter "github.com/json-iterator/go"
- )
- type (
- game struct {
- *client
- playerDataKey string // 玩家数据key
- playerTeamKey string // 玩家部队key
- playerHeroKey string // 玩家英雄key
- battleReportGroupKey string // 战报组key
- battleReportKey string // 战报key
- settingKey string // 玩家设置
- mapExchangeLogKey string // 地图交易所日志key
- }
- )
- var notNilFunc = func(v any) bool { return v != nil }
- func newGame() *game {
- return &game{}
- }
- func (p *game) setKey() {
- prefix := fmt.Sprintf("%s-game:", cprofile.Env())
- p.playerDataKey = prefix + "player_data:%d" // type = hash, redisKey = player_data:<playerID> value = playerData
- p.playerTeamKey = prefix + "player_team:%d" // type = hash, redisKey = player_team:<playerID> value = pb.BattleTeam
- p.playerHeroKey = prefix + "player_hero:%d" // type = hash, redisKey = player_hero:<playerID> value = pb.PlayerHero
- p.battleReportGroupKey = prefix + "battle_report_group:%s" // type = list, redisKey = battle_report_group:<groupID> value = reportID 列表
- p.battleReportKey = prefix + "battle_report:%s" // Key = battle_report:<reportID> value = pb.Report
- p.settingKey = prefix + "setting:%d" // type = hash, redisKey = setting:<playerID> value = setting
- p.mapExchangeLogKey = prefix + "map_exchange_log:%s:%d:%d" // type = list, redisKey = map_exchange_log:<mapNodeID>:<playerID>:<logType> value = pb.MapExchangeLog
- }
- func (p *game) ConfigID() string {
- return "game_redis_id"
- }
- func (p *game) load(client *client) {
- p.client = client
- p.setKey()
- }
- func (p *game) GetPlayerDataHash(playerID int64, fields ...string) map[string]any {
- if len(fields) == 0 {
- return nil
- }
- key := p.buildPlayerDataKey(playerID)
- result, err := p.HMGet(p.Context(), key, fields...).Result()
- if err != nil {
- clog.Warnf("[GetPlayerData] error: %v", err)
- return nil
- }
- maps := make(map[string]any, len(fields))
- for i := range len(fields) {
- maps[fields[i]] = result[i]
- }
- return maps
- }
- // GetPlayerData 获取玩家数据
- func (p *game) GetPlayerData(playerID int64, fields ...string) *pb.PlayerData {
- dataMaps := p.GetPlayerDataHash(playerID, fields...)
- if dataMaps == nil {
- return nil
- }
- var playerData pb.PlayerData
- if err := cmapStructure.WeakDecode(dataMaps, &playerData); err != nil {
- clog.Errorf("[GetPlayerData] decode error: %v", err)
- return nil
- }
- return &playerData
- }
- // GetChatPlayer 获取玩家聊天数据信息
- func (p *game) GetChatPlayer(playerID int64) *pb.ChatPlayer {
- dataMaps := p.GetPlayerDataHash(playerID, nameRedis.PlayerChatFields...)
- var chatPlayer pb.ChatPlayer
- if err := cmapStructure.WeakDecode(dataMaps, &chatPlayer); err != nil {
- clog.Errorf("[GetChatPlayer] decode error: %v", err)
- return nil
- }
- return &chatPlayer
- }
- // GetPlayerDataList 批量获取玩家数据列表
- func (p *game) GetPlayerDataList(playerIDS []int64, fields ...string) []*pb.PlayerData {
- if len(fields) == 0 {
- return nil
- }
- pipeline := p.Pipeline()
- defer pipeline.Close()
- cmds := make([]*redis.SliceCmd, 0, len(playerIDS))
- for _, playerID := range playerIDS {
- key := p.buildPlayerDataKey(playerID)
- cmds = append(cmds, pipeline.HMGet(p.Context(), key, fields...))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetPlayerDataMap] error: %v", err)
- return nil
- }
- players := make([]*pb.PlayerData, 0, len(playerIDS))
- for i, playerID := range playerIDS {
- result, err := cmds[i].Result()
- if err != nil {
- continue
- }
- maps := make(map[string]any, len(fields))
- for i := range len(fields) {
- maps[fields[i]] = result[i]
- }
- // 添加玩家 ID 字段
- maps[nameRedis.Player_PlayerID] = playerID
- var playerData pb.PlayerData
- if err := cmapStructure.WeakDecode(maps, &playerData); err != nil {
- continue
- }
- players = append(players, &playerData)
- }
- return players
- }
- // 获取玩家数据(聊天玩家基础信息)
- func (p *game) GetChatPlayerMap(playerIDs ...int64) map[int64]*pb.ChatPlayer {
- if len(playerIDs) == 0 {
- return nil
- }
- fields := nameRedis.PlayerChatFields
- pipeline := p.Pipeline()
- defer pipeline.Close()
- cmds := make([]*redis.SliceCmd, 0, len(playerIDs))
- for _, playerID := range playerIDs {
- key := p.buildPlayerDataKey(playerID)
- cmds = append(cmds, pipeline.HMGet(p.Context(), key, fields...))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetPlayerDataMap] error: %v", err)
- return nil
- }
- players := make(map[int64]*pb.ChatPlayer, len(playerIDs))
- for i, playerID := range playerIDs {
- result, err := cmds[i].Result()
- if err != nil {
- continue
- }
- // hmget 的机制:当 key 不存在时,返回一个全为 nil 的 slice
- if !slices.ContainsFunc(result, notNilFunc) {
- continue
- }
- maps := make(map[string]any, len(fields))
- for i := range len(fields) {
- maps[fields[i]] = result[i]
- }
- // 添加玩家 ID 字段
- maps[nameRedis.Player_PlayerID] = playerID
- var chatPlayer pb.ChatPlayer
- if err := cmapStructure.WeakDecode(maps, &chatPlayer); err != nil {
- continue
- }
- players[playerID] = &chatPlayer
- }
- return players
- }
- func (p *game) getPlayerDataMap(pipeline redis.Pipeliner, playerIDs []int64, fields ...string) map[int64]*pb.PlayerData {
- if len(fields) == 0 {
- return nil
- }
- cmds := make([]*redis.SliceCmd, 0, len(playerIDs))
- for _, playerID := range playerIDs {
- key := p.buildPlayerDataKey(playerID)
- cmds = append(cmds, pipeline.HMGet(p.Context(), key, fields...))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetPlayerDataMap] error: %v", err)
- return nil
- }
- players := make(map[int64]*pb.PlayerData, len(playerIDs))
- for i, playerID := range playerIDs {
- result, err := cmds[i].Result()
- if err != nil {
- continue
- }
- // hmget 的机制:当 key 不存在时,返回一个全为 nil 的 slice
- if !slices.ContainsFunc(result, notNilFunc) {
- continue
- }
- maps := make(map[string]any, len(fields))
- for i := range len(fields) {
- maps[fields[i]] = result[i]
- }
- // 添加玩家 ID 字段
- maps[nameRedis.Player_PlayerID] = playerID
- var playerData pb.PlayerData
- if err := cmapStructure.WeakDecode(maps, &playerData); err != nil {
- continue
- }
- players[playerID] = &playerData
- }
- return players
- }
- func (p *game) GetPlayerDataMap(playerIDs []int64, fields ...string) map[int64]*pb.PlayerData {
- pipeline := p.Pipeline()
- defer pipeline.Close()
- return p.getPlayerDataMap(pipeline, playerIDs, fields...)
- }
- // SavePlayerDataFields 保存玩家数据
- func (p *game) SavePlayerDataFields(playerID int64, fields map[string]any) bool {
- if len(fields) == 0 {
- return false
- }
- key := p.buildPlayerDataKey(playerID)
- _, err := p.HSet(p.Context(), key, fields).Result()
- if err != nil {
- clog.Errorf("[SavePlayerDataFields] error: %v", err)
- return false
- }
- return true
- }
- // 更新玩家的联盟信息,支持多个
- func (p *game) UpdatePlayerDataLeagueInfo(playerLeagueInfoList ...*pb.PlayerLeagueInfo) {
- if len(playerLeagueInfoList) == 0 {
- return
- }
- // 批量修改玩家联盟ID和名称字段
- pipeline := p.Pipeline()
- defer pipeline.Close()
- cmds := make([]*redis.BoolCmd, 0, len(playerLeagueInfoList))
- for _, playerLeagueInfo := range playerLeagueInfoList {
- key := p.buildPlayerDataKey(playerLeagueInfo.PlayerID)
- cmds = append(cmds, pipeline.HMSet(p.Context(), key, map[string]any{
- nameRedis.Player_LeagueID: playerLeagueInfo.LeagueID,
- nameRedis.Player_LeagueName: playerLeagueInfo.LeagueName,
- nameRedis.Player_LeagueAbbName: playerLeagueInfo.LeagueAbbName,
- nameRedis.Player_LeagueJob: playerLeagueInfo.LeagueJob,
- }))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[UpdatePlayerDataLeagueInfo] error: %v", err)
- return
- }
- }
- // UpdatePlayerDataField 更新玩家数据
- func (p *game) UpdatePlayerDataField(playerID int64, field string, value any) bool {
- key := p.buildPlayerDataKey(playerID)
- _, err := p.HSet(p.Context(), key, field, value).Result()
- if err != nil {
- clog.Errorf("[UpdatePlayerDataField] error: %v", err)
- return false
- }
- return true
- }
- // DeletePlayerDataFields 删除玩家数据字段
- func (p *game) DeletePlayerDataFields(playerID int64, fields ...string) bool {
- if len(fields) == 0 {
- return false
- }
- key := p.buildPlayerDataKey(playerID)
- _, err := p.HDel(p.Context(), key, fields...).Result()
- if err != nil {
- clog.Errorf("[DeletePlayerDataFields] error: %v", err)
- return false
- }
- return true
- }
- // 获取部队信息
- func (p *game) GetPlayerTeamsByID(playerID int64, teamIDList ...int32) map[int32]*pb.BattleTeam {
- if len(teamIDList) == 0 {
- return nil
- }
- key := p.buildPlayerTeamKey(playerID)
- fields := make([]string, 0, len(teamIDList))
- for _, teamID := range teamIDList {
- fields = append(fields, cstring.ToString(teamID))
- }
- result, err := p.HMGet(p.Context(), key, fields...).Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerTeamsByID] playerID = %d, error: %v", playerID, err)
- }
- return nil
- }
- ret := map[int32]*pb.BattleTeam{}
- for _, teamStr := range result {
- if teamStr == nil {
- continue
- }
- team := &pb.BattleTeam{}
- if err := jsoniter.UnmarshalFromString(teamStr.(string), team); err != nil {
- clog.Errorf("[GetPlayerTeamsByID] playerID = %d, unmarshal error: %v", playerID, err)
- return nil
- }
- ret[team.TeamID] = team
- }
- return ret
- }
- // 获取部队信息
- func (p *game) GetPlayerTeamByID(playerID int64, teamID int32) (*pb.BattleTeam, bool) {
- m := p.GetPlayerTeamsByID(playerID, teamID)
- if m == nil {
- return nil, false
- }
- team, ok := m[teamID]
- return team, ok
- }
- // 更新玩家队伍
- func (p *game) UpdatePlayerTeams(playerID int64, battleTeams ...*pb.BattleTeam) {
- if len(battleTeams) == 0 {
- return
- }
- key := p.buildPlayerTeamKey(playerID)
- values := make([]any, 0, len(battleTeams)*2)
- for _, team := range battleTeams {
- values = append(values, cstring.ToString(team.TeamID))
- jsonStr, err := jsoniter.MarshalToString(team)
- if err != nil {
- clog.Errorf("[UpdatePlayerTeams] player = %d, marshal error: %v", playerID, err)
- return
- }
- values = append(values, jsonStr)
- }
- err := p.HSet(p.Context(), key, values).Err()
- if err != nil {
- clog.Errorf("[UpdatePlayerTeams] player = %d, error: %v", playerID, err)
- }
- }
- // 获取玩家英雄
- func (p *game) GetPlayerHeros(playerID int64, heroIDList ...int64) map[int64]*pb.PlayerHero {
- if len(heroIDList) == 0 {
- return nil
- }
- key := p.buildPlayerHeroKey(playerID)
- fields := make([]string, 0, len(heroIDList))
- for _, heroID := range heroIDList {
- fields = append(fields, cstring.ToString(heroID))
- }
- result, err := p.HMGet(p.Context(), key, fields...).Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerHeros] playerID = %d, error: %v", playerID, err)
- }
- return nil
- }
- ret := map[int64]*pb.PlayerHero{}
- for _, playerHeroStr := range result {
- if playerHeroStr == nil {
- continue
- }
- playerHero := &pb.PlayerHero{}
- if err := jsoniter.UnmarshalFromString(playerHeroStr.(string), playerHero); err != nil {
- clog.Errorf("[GetPlayerHeros] playerID = %d, unmarshal error: %v", playerID, err)
- return nil
- }
- ret[playerHero.Id] = playerHero
- }
- return ret
- }
- // 更新玩家英雄信息
- func (p *game) UpdatePlayerHeros(playerID int64, heroList ...*pb.PlayerHero) {
- if len(heroList) == 0 {
- return
- }
- key := p.buildPlayerHeroKey(playerID)
- values := make([]any, 0, len(heroList)*2)
- for _, hero := range heroList {
- values = append(values, cstring.ToString(hero.Id))
- jsonStr, err := jsoniter.MarshalToString(hero)
- if err != nil {
- clog.Errorf("[UpdatePlayerHeros] player = %d, marshal error: %v", playerID, err)
- return
- }
- values = append(values, jsonStr)
- }
- err := p.HSet(p.Context(), key, values).Err()
- if err != nil {
- clog.Errorf("[UpdatePlayerHeros] player = %d, error: %v", playerID, err)
- }
- }
- // 删除玩家英雄
- func (p *game) DeletePlayerHeros(playerID int64, heroIDList ...int64) {
- if len(heroIDList) == 0 {
- return
- }
- key := p.buildPlayerHeroKey(playerID)
- fields := make([]string, 0, len(heroIDList))
- for _, heroID := range heroIDList {
- fields = append(fields, cstring.ToString(heroID))
- }
- err := p.HDel(p.Context(), key, fields...).Err()
- if err != nil {
- clog.Errorf("[DeletePlayerHeros] player = %d, error: %v", playerID, err)
- }
- }
- // 根据玩家战斗部队数据获取部队和英雄数据
- func (p *game) GetPlayerTeamHeroData(playerID int64, teamID int32, heroGUID int64) (*pb.PlayerData, *pb.BattleTeam, *pb.PlayerHero) {
- pipeline := p.Pipeline()
- defer pipeline.Close()
- // 获取玩家数据
- playerKey := p.buildPlayerDataKey(playerID)
- playerCmd := pipeline.HMGet(p.Context(), playerKey, nameRedis.PlayerBaseFields...)
- // 获取队伍数据
- teamKey := p.buildPlayerTeamKey(playerID)
- teamCmd := pipeline.HGet(p.Context(), teamKey, cstring.ToString(teamID))
- // 获取英雄数据
- heroKey := p.buildPlayerHeroKey(playerID)
- heroCmd := pipeline.HGet(p.Context(), heroKey, cstring.ToString(heroGUID))
- if _, err := pipeline.Exec(p.Context()); err != nil && !errors.Is(err, redis.Nil) {
- clog.Warnf("[GetPlayerTeamHeroData] err = %v", err)
- return nil, nil, nil
- }
- playerData, _ := p.decodePlayerData(playerID, nameRedis.PlayerBaseFields, playerCmd)
- teamData, _ := p.decodeBattleTeam(teamID, teamCmd)
- heroData, _ := p.decodePlayerHero(heroGUID, heroCmd)
- return playerData, teamData, heroData
- }
- // 根据玩家战斗部队数据获取部队和英雄数据
- func (p *game) GetPlayerBattleData(battleTeamDataList ...*pb.BattleTeamData) (map[int64]*pb.BattleTeam, map[int64]*pb.PlayerHero) {
- if len(battleTeamDataList) == 0 {
- return nil, nil
- }
- // 收集每个玩家对应的 teamID(每个玩家只有一个部队)和 heroID 列表
- playerTeamIDs := map[int64]int32{}
- playerHeroIDs := map[int64][]int64{}
- for _, teamData := range battleTeamDataList {
- if teamData == nil {
- continue
- }
- playerTeamIDs[teamData.PlayerID] = teamData.TeamID
- playerHeroIDs[teamData.PlayerID] = teamData.TeamHeros
- }
- // 去重玩家列表并保持顺序(用于 pipeline 对应)
- playerIDs := make([]int64, 0, len(playerTeamIDs))
- for pid := range playerTeamIDs {
- playerIDs = append(playerIDs, pid)
- }
- pipeline := p.Pipeline()
- defer pipeline.Close()
- // 为每个玩家创建 HGET 请求(因为每个玩家只有一个部队字段)
- teamCmds := make([]*redis.StringCmd, 0, len(playerIDs))
- heroCmds := make([]*redis.SliceCmd, 0, len(playerIDs))
- for _, pid := range playerIDs {
- teamKey := p.buildPlayerTeamKey(pid)
- field := cstring.ToString(playerTeamIDs[pid])
- teamCmds = append(teamCmds, pipeline.HGet(p.Context(), teamKey, field))
- // 为每个玩家创建 HMGET 请求(因为每个玩家有多个英雄字段)
- heroKey := p.buildPlayerHeroKey(pid)
- var fields []string
- for _, hid := range playerHeroIDs[pid] {
- fields = append(fields, cstring.ToString(hid))
- }
- heroCmds = append(heroCmds, pipeline.HMGet(p.Context(), heroKey, fields...))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetPlayerBattleData] pipeline exec error: %v", err)
- return nil, nil
- }
- teamsMap := map[int64]*pb.BattleTeam{}
- heroesMap := map[int64]*pb.PlayerHero{}
- // 解析部队结果
- for i, cmd := range teamCmds {
- playerID := playerIDs[i]
- str, err := cmd.Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerBattleData] get team error player = %d, err = %v", playerID, err)
- }
- continue
- }
- if str == "" {
- continue
- }
- var team pb.BattleTeam
- if err := jsoniter.UnmarshalFromString(str, &team); err != nil {
- clog.Errorf("[GetPlayerBattleData] unmarshal team error player = %d, err = %v", playerID, err)
- continue
- }
- teamsMap[playerID] = &team
- }
- // 解析英雄结果
- for _, cmd := range heroCmds {
- result, err := cmd.Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerBattleData] get heroes error: %v", err)
- }
- } else {
- for _, item := range result {
- if item == nil {
- continue
- }
- str, ok := item.(string)
- if !ok || str == "" {
- continue
- }
- var hero pb.PlayerHero
- if err := jsoniter.UnmarshalFromString(str, &hero); err != nil {
- clog.Errorf("[GetPlayerBattleData] unmarshal hero error, err = %v", err)
- continue
- }
- heroesMap[hero.Id] = &hero
- }
- }
- }
- return teamsMap, heroesMap
- }
- // 保存战报数据,并追加到战报组数据中
- func (p *game) AddReports(groupID string, reports ...*pb.Report) bool {
- if len(reports) == 0 {
- return false
- }
- groupKey := p.buildBattleReportGroupKey(groupID)
- pipeline := p.Pipeline()
- defer pipeline.Close()
- var idValues []any
- for _, report := range reports {
- if report == nil {
- continue
- }
- jsonStr, err := jsoniter.MarshalToString(report)
- if err != nil {
- clog.Errorf("[AddReports] marshal error: %v", err)
- continue
- }
- // 先写入战报 key
- reportKey := p.buildBattleReportKey(report.ReportID)
- pipeline.Set(p.Context(), reportKey, jsonStr, NoExpire)
- // 只有序列化成功的战报ID才追加到组列表
- idValues = append(idValues, report.ReportID)
- }
- // 如果没有任何要写入的 id,则仍可能成功(但无实际写入),根据需要可视为失败,这里视为失败
- if len(idValues) == 0 {
- clog.Warnf("[AddReports] no valid reports to save for group = %s", groupID)
- return false
- }
- // 将所有战报ID 追加到组列表
- pipeline.LPush(p.Context(), groupKey, idValues...)
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[AddReports] pipeline error: %v", err)
- return false
- }
- return true
- }
- // 获取战报组的所有战报ID集合
- func (p *game) GetReportIDListOfGroup(groupIDList ...string) map[string][]string {
- resultMap := map[string][]string{}
- if len(groupIDList) == 0 {
- return resultMap
- }
- pipeline := p.Pipeline()
- defer pipeline.Close()
- cmds := make([]*redis.StringSliceCmd, 0, len(groupIDList))
- for _, groupID := range groupIDList {
- key := p.buildBattleReportGroupKey(groupID)
- cmds = append(cmds, pipeline.LRange(p.Context(), key, 0, -1))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetReportIDListOfGroup] pipeline error: %v", err)
- return resultMap
- }
- for i, groupID := range groupIDList {
- result, err := cmds[i].Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetReportIDListOfGroup] get error: %v", err)
- }
- continue
- }
- var idList []string
- for _, id := range result {
- if id != "" {
- idList = append(idList, id)
- }
- }
- resultMap[groupID] = idList
- }
- return resultMap
- }
- // 获取战报
- func (p *game) GetBattleReports(reportIDList ...string) map[string]*pb.Report {
- reportMap := map[string]*pb.Report{}
- if len(reportIDList) == 0 {
- return reportMap
- }
- pipeline := p.Pipeline()
- defer pipeline.Close()
- cmds := make([]*redis.StringCmd, 0, len(reportIDList))
- for _, reportID := range reportIDList {
- key := p.buildBattleReportKey(reportID)
- cmds = append(cmds, pipeline.Get(p.Context(), key))
- }
- if _, err := pipeline.Exec(p.Context()); err != nil {
- clog.Errorf("[GetBattleReports] pipeline error: %v", err)
- return reportMap
- }
- for i, reportID := range reportIDList {
- result, err := cmds[i].Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetBattleReports] get error: %v", err)
- }
- continue
- }
- var report pb.Report
- if err := jsoniter.UnmarshalFromString(result, &report); err != nil {
- clog.Errorf("[GetBattleReports] unmarshal error: %v", err)
- continue
- }
- reportMap[reportID] = &report
- }
- return reportMap
- }
- // 更新玩家设置信息
- func (p *game) UpdatePlayerSetting(playerID int64, setting *pb.LordShowDataList) {
- key := p.buildPlayerSettingKey(playerID)
- value, err := jsoniter.MarshalToString(setting)
- if err != nil {
- clog.Errorf("[UpdatePlayerSetting] player = %d, marshal error: %v", playerID, err)
- return
- }
- err = p.Set(p.Context(), key, value, 0).Err()
- if err != nil {
- clog.Errorf("[UpdatePlayerSetting] player = %d, error: %v", playerID, err)
- }
- }
- // 获取玩家设置信息
- func (p *game) GetPlayerSetting(playerID int64) *pb.LordShowDataList {
- key := p.buildPlayerSettingKey(playerID)
- value, err := p.Get(p.Context(), key).Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerSetting] player = %d, error: %v", playerID, err)
- }
- return nil
- }
- list := &pb.LordShowDataList{}
- if err := jsoniter.UnmarshalFromString(value, list); err != nil {
- clog.Errorf("[GetBattleReports] unmarshal error: %v", err)
- return nil
- }
- return list
- }
- func (p *game) buildPlayerDataKey(playerID int64) string {
- return fmt.Sprintf(p.playerDataKey, playerID)
- }
- func (p *game) buildPlayerTeamKey(playerID int64) string {
- return fmt.Sprintf(p.playerTeamKey, playerID)
- }
- func (p *game) buildPlayerHeroKey(playerID int64) string {
- return fmt.Sprintf(p.playerHeroKey, playerID)
- }
- func (p *game) buildBattleReportGroupKey(groupID string) string {
- return fmt.Sprintf(p.battleReportGroupKey, groupID)
- }
- func (p *game) buildBattleReportKey(reportID string) string {
- return fmt.Sprintf(p.battleReportKey, reportID)
- }
- func (p *game) buildPlayerSettingKey(playerID int64) string {
- return fmt.Sprintf(p.settingKey, playerID)
- }
- func (p *game) decodePlayerData(playerID int64, fields []string, value *redis.SliceCmd) (*pb.PlayerData, bool) {
- result, err := value.Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[GetPlayerData] player = %d, error: %v", playerID, err)
- }
- return nil, false
- }
- maps := make(map[string]any, len(fields))
- for i := range len(fields) {
- maps[fields[i]] = result[i]
- }
- // 添加玩家 ID 字段
- maps[nameRedis.Player_PlayerID] = playerID
- var playerData pb.PlayerData
- if err := cmapStructure.WeakDecode(maps, &playerData); err != nil {
- clog.Errorf("[GetPlayerData] player = %d, error: %v", playerID, err)
- return nil, false
- }
- return &playerData, true
- }
- func (p *game) decodeBattleTeam(teamID int32, value *redis.StringCmd) (*pb.BattleTeam, bool) {
- result, err := value.Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[decodeBattleTeam] team = %d, error: %v", teamID, err)
- }
- return nil, false
- }
- var teamData pb.BattleTeam
- if err := jsoniter.UnmarshalFromString(result, &teamData); err != nil {
- clog.Errorf("[decodeBattleTeam] team = %d, error: %v", teamID, err)
- return nil, false
- }
- return &teamData, true
- }
- func (p *game) decodePlayerHero(playerID int64, value *redis.StringCmd) (*pb.PlayerHero, bool) {
- result, err := value.Result()
- if err != nil {
- if !errors.Is(err, redis.Nil) {
- clog.Errorf("[decodePlayerHero] player = %d, error: %v", playerID, err)
- }
- return nil, false
- }
- var heroData pb.PlayerHero
- if err := jsoniter.UnmarshalFromString(result, &heroData); err != nil {
- clog.Errorf("[decodePlayerHero] player = %d, error: %v", playerID, err)
- return nil, false
- }
- return &heroData, true
- }
- func (p *game) buildMapExchangeLogKey(mapNodeID string, playerID int64, logType enum.ExchangeLogType) string {
- return fmt.Sprintf(p.mapExchangeLogKey, mapNodeID, playerID, logType)
- }
- // 获取地图交易所日志列表
- // lastLogID > 0 获取最近pageSize条消息,不包括lastLogID
- // lastLogID = 0 获取最新的pageSize条消息
- func (p *game) GetMapExchangeLogList(mapNodeID string, playerID int64, logType enum.ExchangeLogType, lastLogID int64) *pb.ExchangeLogList {
- key := p.buildMapExchangeLogKey(mapNodeID, playerID, logType)
- return p.getMapExchangeLogList(key, lastLogID)
- }
- // 获取地图交易所日志列表
- // lastLogID > 0 获取最近pageSize条消息,不包括lastLogID
- // lastLogID = 0 获取最新的pageSize条消息
- func (p *game) getMapExchangeLogList(key string, lastLogID int64) *pb.ExchangeLogList {
- zSliceToChatMsgs := func(zs []redis.Z, isReverse bool) *pb.ExchangeLogList {
- logList := &pb.ExchangeLogList{}
- for _, v := range zs {
- if v.Member == nil {
- continue
- }
- var log pb.ExchangeLog
- if err := jsoniter.UnmarshalFromString(v.Member.(string), &log); err == nil {
- logList.List = append(logList.List, &log)
- }
- }
- // 反转
- if isReverse {
- for i, j := 0, len(logList.List)-1; i < j; i, j = i+1, j-1 {
- logList.List[i], logList.List[j] = logList.List[j], logList.List[i]
- }
- }
- return logList
- }
- if lastLogID > 0 {
- // 获取 lastLogID 之前的 20 条
- values, err := p.ZRevRangeByScoreWithScores(p.Context(), key, &redis.ZRangeBy{
- Max: fmt.Sprintf("(%d", lastLogID), // 开区间,排除自身
- Min: "-inf",
- Offset: 0,
- Count: pageSize,
- }).Result()
- if err != nil && err != redis.Nil {
- clog.Warnf("[getMapExchangeLogList] ZRevRangeByScoreWithScores error. [key = %s, lastLogID = %d, error: %v]", key, lastLogID, err)
- return nil
- }
- return zSliceToChatMsgs(values, false)
- } else {
- // 最新 20 条,降序
- values, err := p.ZRevRangeWithScores(p.Context(), key, 0, pageSize-1).Result()
- if err != nil && err != redis.Nil {
- clog.Warnf("[getMapExchangeLogList] ZRevRangeWithScores error. [key = %s, lastLogID = %d, error: %v]", key, lastLogID, err)
- return nil
- }
- // 反转成升序
- return zSliceToChatMsgs(values, false)
- }
- }
- // 添加一条地图交易所日志
- func (p *game) AddMapExchangeLog(mapNodeID string, playerID int64, logType enum.ExchangeLogType, maxLimit int64, log *pb.ExchangeLog) {
- key := p.buildMapExchangeLogKey(mapNodeID, playerID, logType)
- p.addMapExchangeLog(key, maxLimit, log)
- }
- // 保存一条日志消息
- func (p *game) addMapExchangeLog(key string, maxLimit int64, log *pb.ExchangeLog) {
- data, err := jsoniter.MarshalToString(log)
- if err != nil {
- clog.Warnf("[addMapExchangeLog] marshal error. [key = %s, maxLimit = %d, error: %v]", key, maxLimit, err)
- return
- }
- // 使用 LogID 作为 score,保证顺序
- err = p.ZAdd(p.Context(), key, &redis.Z{
- Score: float64(log.LogID),
- Member: data,
- }).Err()
- if err != nil {
- clog.Warnf("[addMapExchangeLog] zadd error. [key = %s, maxLimit = %d, error: %v]", key, maxLimit, err)
- return
- }
- // 裁剪到最多 maxLimit 条
- err = p.ZRemRangeByRank(p.Context(), key, 0, -maxLimit-1).Err()
- if err != nil {
- clog.Warnf("[addMapExchangeLog] zremrangebyrank error. [key = %s, maxLimit = %d, error: %v]", key, maxLimit, err)
- }
- }
|