player_table.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. package db
  2. import (
  3. "context"
  4. "errors"
  5. capp "f1-game/internal/cherry_app"
  6. "f1-game/internal/component/db"
  7. dbFacade "f1-game/internal/component/db/facade"
  8. "f1-game/internal/component/redis"
  9. "f1-game/internal/data"
  10. "f1-game/internal/enum"
  11. nameDB "f1-game/internal/name/db"
  12. nameRedis "f1-game/internal/name/redis"
  13. "f1-game/internal/pb"
  14. "f1-game/internal/sessions"
  15. dbPlayer "f1-game/nodes/game/internal/db/data/player"
  16. "fmt"
  17. "slices"
  18. ctime "github.com/cherry-game/cherry/extend/time"
  19. cproto "github.com/cherry-game/cherry/net/proto"
  20. "go.mongodb.org/mongo-driver/v2/bson"
  21. "go.mongodb.org/mongo-driver/v2/mongo"
  22. clog "github.com/cherry-game/cherry/logger"
  23. )
  24. // PlayerTable 玩家表
  25. type (
  26. PlayerTable struct {
  27. PlayerID int64 `bson:"PlayerID,omitempty"`
  28. PID int32 `bson:"PID,omitempty"`
  29. OpenID string `bson:"OpenID,omitempty"`
  30. UID int64 `bson:"UID,omitempty"`
  31. NodeID string `bson:"NodeID,omitempty"`
  32. OriginNodeID string `bson:"OriginNodeID,omitempty"`
  33. CreateTime int64 `bson:"CreateTime,omitempty"`
  34. CreateIP string `bson:"CreateIP,omitempty"`
  35. dbPlayer.Data `bson:"Data,omitempty"`
  36. dbFacade.TableBase `bson:",inline"`
  37. }
  38. )
  39. func (*PlayerTable) TableName() string {
  40. return nameDB.Table_Player
  41. }
  42. func (p *PlayerTable) PrimaryValue() any {
  43. return p.PlayerID
  44. }
  45. func (p *PlayerTable) CheckField() {
  46. p.Data.CheckField()
  47. }
  48. func (p *PlayerTable) Save2Queue() {
  49. save2Queue(p)
  50. }
  51. func (p *PlayerTable) ToPlayerProto() pb.Player {
  52. return pb.Player{
  53. PlayerID: p.PlayerID,
  54. CreateTime: p.CreateTime,
  55. GameNodeID: p.NodeID, // TODO 这个为何要下发?
  56. HasMap: p.Map.MapNodeID != "",
  57. ChangeNameCount: p.Lord.ChangeNameCount,
  58. LastChangeNameTime: p.Lord.LastChangeNameTime,
  59. ClientSettings: p.Settings.List,
  60. }
  61. }
  62. func (p *PlayerTable) ToAOIWatcher() *pb.AOIObject {
  63. return &pb.AOIObject{
  64. ObjectID: p.Map.ObjectID,
  65. ObjectType: int32(enum.ObjectType_Player),
  66. X: p.Map.Point.X,
  67. Y: p.Map.Point.Y,
  68. IsWatcher: true,
  69. IsMarker: false,
  70. OnwerID: p.PlayerID,
  71. }
  72. }
  73. // NewPlayer 创建新玩家
  74. func NewPlayer(playerID int64, session *cproto.Session, name string) (*PlayerTable, bool) {
  75. var (
  76. pid = sessions.GetPID(session)
  77. openID = sessions.GetOpenID(session)
  78. language = session.GetString(sessions.Language)
  79. uid = session.Uid
  80. ip = session.Ip
  81. )
  82. if playerID < 1 {
  83. clog.Warnf("playerID error. [pid = %d, openID = %s, name = %s]", pid, openID, name)
  84. return nil, false
  85. }
  86. if uid < 1 || pid < 1 || openID == "" {
  87. clog.Warnf("params error. [pid = %d, openID = %s, name = %s]", pid, openID, name)
  88. return nil, false
  89. }
  90. if name == "" {
  91. name = fmt.Sprintf("%d", playerID)
  92. }
  93. player := newPlayerTable(playerID)
  94. player.PID = pid
  95. player.OpenID = openID
  96. player.UID = uid
  97. player.Lord.Name = name
  98. player.Lord.IconID = data.Const.PlayerInitIcon
  99. player.Lord.BgID = data.Const.PlayerInitBg
  100. player.CreateTime = ctime.Now().ToMillisecond()
  101. player.CreateIP = ip
  102. if language != "" {
  103. player.Settings.Language = language
  104. }
  105. player.Save2Queue() // 提交到db队列
  106. uidCache.Put(uid, player.PlayerID) // 缓存 uid->playerID
  107. uidCount.Add(1) // 用户总数+1
  108. playerNameCache.Put(name, player.PlayerID) // 缓存 playerName->playerID
  109. playerTableCache.Put(playerID, player) // 缓存 playerID->playerTable
  110. // 初始玩家相关表
  111. initTables(playerID)
  112. return player, true
  113. }
  114. func GetPlayerTable(playerID int64) (*PlayerTable, bool) {
  115. if val, ok := playerTableCache.GetIfPresent(playerID); ok {
  116. return val.(*PlayerTable), true
  117. }
  118. if playerTable, found := GetPlayerTableFromDB(playerID); found {
  119. playerTableCache.Put(playerID, playerTable)
  120. return playerTable, true
  121. }
  122. return nil, false
  123. }
  124. func GetPlayerTableFromDB(playerID int64) (*PlayerTable, bool) {
  125. playerTable := newPlayerTable(playerID)
  126. filter := bson.M{
  127. nameDB.Field_NodeID: playerTable.NodeID,
  128. nameDB.Field_PlayerID: playerTable.PlayerID,
  129. }
  130. err := dbComponent.FindOne(playerTable, filter)
  131. if err != nil {
  132. if !errors.Is(err, mongo.ErrNoDocuments) {
  133. clog.Errorf("[playerID = %d] db error = %v", playerID, err)
  134. }
  135. return nil, false
  136. }
  137. playerTable.CheckField()
  138. return playerTable, true
  139. }
  140. func newPlayerTable(playerID int64) *PlayerTable {
  141. return &PlayerTable{
  142. PlayerID: playerID,
  143. NodeID: capp.NodeID(),
  144. OriginNodeID: capp.NodeID(),
  145. Data: dbPlayer.NewData(),
  146. }
  147. }
  148. func GetPlayerIDWithUID(uid int64) (int64, bool) {
  149. if val, ok := uidCache.GetIfPresent(uid); ok {
  150. return val.(int64), true
  151. }
  152. return 0, false
  153. }
  154. // PlayerNameIsExist 玩家名全局唯一
  155. func PlayerNameIsExist(playerName string) (int64, bool) {
  156. if val, ok := playerNameCache.GetIfPresent(playerName); ok {
  157. return val.(int64), true
  158. }
  159. return 0, false
  160. }
  161. // GetPlayerIDList 批量查询玩家id(过滤不存在的)
  162. func GetPlayerIDList(ids []int64) []int64 {
  163. var list []int64
  164. for _, playerID := range ids {
  165. if _, found := GetPlayerTable(playerID); found {
  166. list = append(list, playerID)
  167. }
  168. }
  169. return list
  170. }
  171. // preloadPlayerTable 预加载所有玩家部份信息
  172. func preloadPlayerTable() {
  173. var (
  174. collection = dbComponent.Collection(nameDB.Table_Player)
  175. ctx = context.TODO()
  176. pageNum int32 = 1
  177. pageSize int32 = 100000 // 10W
  178. filter = bson.M{nameDB.Field_NodeID: capp.NodeID()}
  179. projection = bson.M{
  180. nameDB.Field_PlayerID: 1,
  181. nameDB.Field_LordName: 1,
  182. nameDB.Field_UID: 1,
  183. }
  184. count int64 = 0
  185. )
  186. for {
  187. list, _, err := db.Pagination[*PlayerTable](collection, ctx, filter, projection, nil, pageNum, pageSize, false)
  188. if err != nil {
  189. clog.Errorf("[preloadPlayerTable] err = %v", err)
  190. break
  191. }
  192. // clog.Debugf("[preloadPlayerTable] pageNum = %d, count = %d, total = %d", pageNum, len(list), total)
  193. if len(list) < 1 {
  194. break
  195. }
  196. for _, playerTable := range list {
  197. // 加载UID uid-> playerID
  198. uidCache.Put(playerTable.UID, playerTable.PlayerID)
  199. // 加载玩家名 playerName -> playerID
  200. playerNameCache.Put(playerTable.Lord.Name, playerTable.PlayerID)
  201. }
  202. pageNum++
  203. count += int64(len(list))
  204. }
  205. clog.Infof("preload player info count = %d", count)
  206. }
  207. func (p *PlayerTable) ToPlayerInfo() *pb.PlayerInfo {
  208. return &pb.PlayerInfo{
  209. GameNodeID: p.NodeID,
  210. PlayerID: p.PlayerID,
  211. PlayerName: p.Lord.Name,
  212. Level: p.Lord.Level,
  213. Power: p.Lord.Power,
  214. RegisterIP: p.CreateIP,
  215. RegisterTime: p.CreateTime,
  216. }
  217. }
  218. func (p *PlayerTable) UpdatePlayerName(newName string, curTime int64) {
  219. p.Lord.Name = newName
  220. // 删除原来名字
  221. playerNameCache.Invalidate(p.Lord.Name)
  222. playerNameCache.Put(newName, p.PlayerID)
  223. p.Lord.ChangeNameCount += 1
  224. p.Lord.LastChangeNameTime = curTime
  225. }
  226. // 检测外显过期
  227. func (p *PlayerTable) CheckShowExpire(nowSecond int64) ([]int32, []*pb.I32I32) {
  228. if p.Shows.ShowMap.Empty() {
  229. return nil, nil
  230. }
  231. expireShowIDs := p.Shows.CheckExpire(nowSecond)
  232. if len(expireShowIDs) <= 0 {
  233. return nil, nil
  234. }
  235. var (
  236. changeShows = []*pb.I32I32{}
  237. fields = map[string]any{}
  238. )
  239. // 检测头像是否过期 // TODO 过期后默认的头像、头像框和背景是否使用默认的外显ID
  240. if slices.Contains(expireShowIDs, p.Lord.IconID) {
  241. p.Lord.IconID = 0
  242. changeShows = append(changeShows, &pb.I32I32{
  243. Key: enum.ShowType_Icon,
  244. Value: 0,
  245. })
  246. fields[nameRedis.Player_IconID] = p.Lord.IconID
  247. }
  248. // 检测头像框是否过期
  249. if slices.Contains(expireShowIDs, p.Lord.FrameID) {
  250. p.Lord.FrameID = 0
  251. changeShows = append(changeShows, &pb.I32I32{
  252. Key: enum.ShowType_Frame,
  253. Value: 0,
  254. })
  255. fields[nameRedis.Player_FrameID] = p.Lord.FrameID
  256. }
  257. // 检测背景是否过期
  258. if slices.Contains(expireShowIDs, p.Lord.BgID) {
  259. p.Lord.BgID = 0
  260. changeShows = append(changeShows, &pb.I32I32{
  261. Key: enum.ShowType_Bg,
  262. Value: 0,
  263. })
  264. fields[nameRedis.Player_BgID] = p.Lord.BgID
  265. }
  266. // 更新redis数据
  267. if len(fields) > 0 {
  268. redis.Game.SavePlayerDataFields(p.PlayerID, fields)
  269. }
  270. return expireShowIDs, changeShows
  271. }