actor_player.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. package player
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/aws/aws-sdk-go/aws"
  7. "github.com/aws/aws-sdk-go/aws/credentials"
  8. "github.com/aws/aws-sdk-go/aws/session"
  9. "github.com/aws/aws-sdk-go/service/s3/s3manager"
  10. jsoniter "github.com/json-iterator/go"
  11. mhayaString "github.com/mhaya/extend/string"
  12. mhayaTime "github.com/mhaya/extend/time"
  13. "github.com/mhaya/game/game_cluster/internal/code"
  14. "github.com/mhaya/game/game_cluster/internal/constant"
  15. "github.com/mhaya/game/game_cluster/internal/data"
  16. "github.com/mhaya/game/game_cluster/internal/event"
  17. "github.com/mhaya/game/game_cluster/internal/mdb"
  18. "github.com/mhaya/game/game_cluster/internal/mdb/models"
  19. "github.com/mhaya/game/game_cluster/internal/param"
  20. "github.com/mhaya/game/game_cluster/internal/token"
  21. "github.com/mhaya/game/game_cluster/nodes/game/module/online"
  22. clog "github.com/mhaya/logger"
  23. "github.com/mhaya/net/parser/pomelo"
  24. "go.mongodb.org/mongo-driver/bson"
  25. "io"
  26. "net/http"
  27. "net/url"
  28. "time"
  29. )
  30. type (
  31. // actorPlayer 每位登录的玩家对应一个子actor
  32. actorPlayer struct {
  33. pomelo.ActorBase
  34. isOnline bool // 玩家是否在线
  35. playerId string
  36. uid int64
  37. index int
  38. dirty bool
  39. exitTime time.Duration
  40. Player *models.Player
  41. Account *models.Account
  42. }
  43. )
  44. func (p *actorPlayer) OnInit() {
  45. p.exitTime = time.Minute * 30
  46. clog.Debugf("[actorPlayer] path = %s init!", p.PathString())
  47. // 注册 session关闭的remote函数(网关触发连接断开后,会调用RPC发送该消息)
  48. p.Remote().Register("sessionClose", p.sessionClose)
  49. p.Remote().Register("login", p.login) // 注册 查看角色
  50. p.Remote().Register("start", p.start) // 主页
  51. p.Remote().Register("guide", p.guide) // 主页关闭信息
  52. p.Remote().Register("roll", p.roll) // roll
  53. p.Remote().Register("lottery", p.lottery) // 转盘
  54. p.Remote().Register("signIn", p.signIn) // 签到
  55. p.Remote().Register("rank", p.rank) // 排行榜
  56. p.Remote().Register("invite", p.invite) // 邀请
  57. p.Remote().Register("inviteRecord", p.inviteRecord) // 邀请累计信息
  58. p.Remote().Register("share", p.share) // 分享
  59. p.Remote().Register("claim", p.claim) // 领取奖励
  60. p.Remote().Register("cashOut", p.cashOut) //提现
  61. p.Remote().Register("jump", p.jump) // 成就1相关的跳转服务
  62. p.Remote().Register("savex", p.savex) // 更新推特
  63. p.Remote().Register("savetonwall", p.savetonwall) // 更新钱包
  64. p.Timer().Add(10*time.Second, p.refreshRoll)
  65. p.Timer().Add(1*time.Second, p.SetRewardToDB)
  66. p.Timer().Add(1*time.Minute, p.setRedis)
  67. p.Timer().Add(1*time.Second, p.setDb)
  68. p.Timer().Add(30*time.Second, p.setInviteReward)
  69. p.Timer().Add(1*time.Second, p.addRoll)
  70. p.Timer().Add(10*time.Second, p.sessionClose)
  71. //p.Timer().Add(30*time.Second, p.getAndUpdateAvatar) //排行榜头像
  72. }
  73. func (p *actorPlayer) addRoll() {
  74. if p.isOnline {
  75. if p.Player.Item[models.ItemRoll].Num < 10 {
  76. p.Player.Item[models.ItemRoll].Num += 100
  77. }
  78. if p.Player.Item[models.ItemDrawsNumber].Num < 10 {
  79. p.Player.Item[models.ItemDrawsNumber].Num += 100
  80. }
  81. p.dirty = true
  82. }
  83. }
  84. func (p *actorPlayer) OnStop() {
  85. clog.Debugf("[actorPlayer] path = %s exit!", p.PathString())
  86. }
  87. func (p *actorPlayer) setInviteReward() {
  88. if p.isOnline {
  89. p.Player.SetInviteReward()
  90. }
  91. }
  92. func (p *actorPlayer) setRedis() {
  93. if p.isOnline {
  94. var playerBase = param.PlayerBase{
  95. UserName: p.Player.UserName,
  96. NickName: p.Player.NickName,
  97. UserNameMaybe: p.Player.UserNameMaybe,
  98. IsLeader: p.Player.IsLeader,
  99. Avatar: p.Player.Avatar,
  100. OpenId: p.Player.OpenId,
  101. }
  102. d, _ := jsoniter.Marshal(&playerBase)
  103. mdb.RDB.Set(context.Background(), models.GetPlayBaseKey(p.Player.UserName), d, 30*24*time.Hour)
  104. }
  105. }
  106. func (p *actorPlayer) SetRewardToDB() {
  107. if p.Player != nil {
  108. for k, r := range p.Player.PlayerReward {
  109. _, err := mdb.MDB.Collection(constant.CNamePlayerReward).InsertOne(context.Background(), r)
  110. if err != nil {
  111. clog.Errorf("[actorPlayer] set invite to db error: %v", err)
  112. }
  113. delete(p.Player.PlayerReward, k)
  114. }
  115. }
  116. }
  117. func (p *actorPlayer) setDb() {
  118. if p.isOnline && p.dirty {
  119. ret, err := mdb.MDB.Collection(constant.CNamePlayer).UpdateOne(context.Background(), bson.M{"userName": p.Player.UserName}, bson.M{"$set": p.Player})
  120. if err != nil {
  121. clog.Debugf("[actorPlayer] setDb ret=%v, err = %s exit!", ret, err)
  122. }
  123. p.dirty = false
  124. }
  125. }
  126. // 获取和更新头像
  127. func (p *actorPlayer) getAndUpdateAvatar() {
  128. if p.isOnline {
  129. openid, _ := mhayaString.ToInt64(p.Player.OpenId)
  130. proxyURL, err := url.Parse("http://127.0.0.1:22307")
  131. if err != nil {
  132. clog.Debugf("Failed to parse proxy URL: %v", err.Error())
  133. return
  134. }
  135. // 创建代理配置
  136. proxy := http.ProxyURL(proxyURL)
  137. // 创建自定义的 Transport
  138. tr := &http.Transport{
  139. Proxy: proxy,
  140. TLSHandshakeTimeout: 5 * time.Second,
  141. }
  142. // 使用自定义 Transport 创建 HTTP 客户端
  143. client := &http.Client{
  144. Transport: tr,
  145. Timeout: 10 * time.Second, // 设置超时时间
  146. }
  147. sdkConfig := data.SdkConfig.Get(3).Params
  148. url := fmt.Sprintf("https://api.telegram.org/bot%s/getUserProfilePhotos?user_id=%d", sdkConfig.BotToken, openid)
  149. // 发起 HTTP 请求
  150. req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
  151. if err != nil {
  152. clog.Error("Failed to create request: %v", err)
  153. return
  154. }
  155. resp, err := client.Do(req)
  156. if err != nil {
  157. clog.Error("Failed to send request: %v", err)
  158. return
  159. }
  160. defer resp.Body.Close()
  161. body, err := io.ReadAll(resp.Body)
  162. if err != nil {
  163. clog.Error("Error reading body: %v", err)
  164. return
  165. }
  166. var result struct {
  167. Ok bool `json:"ok"`
  168. Result struct {
  169. Photos [][]struct {
  170. FileID string `json:"file_id"`
  171. } `json:"photos"`
  172. } `json:"result"`
  173. }
  174. err = json.Unmarshal(body, &result)
  175. if err != nil {
  176. clog.Error("Error unmarshalling JSON: %v", err)
  177. return
  178. }
  179. if !result.Ok {
  180. clog.Error("Error: %s", string(body))
  181. return
  182. }
  183. if len(result.Result.Photos) == 0 {
  184. clog.Error("No photos found for user %d", openid)
  185. return
  186. }
  187. if len(result.Result.Photos) > 0 {
  188. // 获取第一张照片的最大尺寸
  189. photo := result.Result.Photos[0][len(result.Result.Photos[0])-1]
  190. //根据数据库记录判断是否需要更新图片库
  191. if photo.FileID != p.Player.Avatar {
  192. if p.uploadPhotoToS3(client, sdkConfig.BotToken, photo.FileID, openid) == true {
  193. p.Player.Avatar = photo.FileID
  194. }
  195. }
  196. }
  197. }
  198. }
  199. func (p *actorPlayer) uploadPhotoToS3(client *http.Client, botToken, fileId string, openid int64) bool {
  200. s3 := data.SdkConfig.Get(3).S3
  201. url := fmt.Sprintf("https://api.telegram.org/bot%s/getFile?file_id=%s", botToken, fileId)
  202. // 发起 HTTP 请求
  203. req, err := http.NewRequestWithContext(context.Background(), "GET", url, nil)
  204. if err != nil {
  205. clog.Error("Failed to create request: %v", err)
  206. return false
  207. }
  208. resp, err := client.Do(req)
  209. if err != nil {
  210. clog.Error("Failed to send request: %v", err)
  211. return false
  212. }
  213. defer resp.Body.Close()
  214. body, err := io.ReadAll(resp.Body)
  215. if err != nil {
  216. clog.Error("Error reading body: %v", err)
  217. return false
  218. }
  219. var fileInfo struct {
  220. Ok bool `json:"ok"`
  221. Result struct {
  222. FilePath string `json:"file_path"`
  223. } `json:"result"`
  224. }
  225. err = json.Unmarshal(body, &fileInfo)
  226. if err != nil {
  227. clog.Error("Error unmarshalling JSON: %v", err)
  228. return false
  229. }
  230. if !fileInfo.Ok {
  231. clog.Error("Error: %s", string(body))
  232. return false
  233. }
  234. downloadUrl := fmt.Sprintf("https://api.telegram.org/file/bot%s/%s", botToken, fileInfo.Result.FilePath)
  235. resp, err = client.Get(downloadUrl)
  236. if err != nil {
  237. clog.Error("Error: %v", err)
  238. return false
  239. }
  240. defer resp.Body.Close()
  241. // 配置 AWS 凭证
  242. sess, err := session.NewSession(&aws.Config{
  243. Credentials: credentials.NewStaticCredentials(
  244. s3.AccessKey,
  245. s3.SecretKey,
  246. "",
  247. ),
  248. Region: aws.String(s3.Region), // 替换为你的 S3 区域
  249. })
  250. uploader := s3manager.NewUploader(sess)
  251. // 上传文件到 S3
  252. _, err = uploader.Upload(&s3manager.UploadInput{
  253. Bucket: aws.String(s3.BucketName), // S3 存储桶名称
  254. Key: aws.String(fmt.Sprintf("%d.jpg", openid)), //当前目录存储文件
  255. Body: resp.Body,
  256. ACL: aws.String("private"), // 可以根据需要设置 ACL
  257. })
  258. if err != nil {
  259. clog.Error("Error uploading file: %v", err)
  260. return false
  261. }
  262. return true
  263. }
  264. func (p *actorPlayer) refreshRoll() {
  265. if p.isOnline {
  266. if p.Player.RefreshRoll() {
  267. p.dirty = true
  268. }
  269. }
  270. }
  271. // sessionClose 接收角色session关闭处理
  272. func (p *actorPlayer) sessionClose() {
  273. deadline := time.Now().Add(-p.exitTime).Unix()
  274. if p.LastAt() < deadline {
  275. online.UnBindPlayer(p.uid)
  276. p.isOnline = false
  277. p.Exit()
  278. logoutEvent := event.NewPlayerLogout(p.ActorID(), p.playerId)
  279. p.PostEvent(&logoutEvent)
  280. }
  281. }
  282. func (p *actorPlayer) Init(req *token.Token) {
  283. clog.Debugf("[actorPlayer] init token = %s", req)
  284. var account models.Account
  285. mdb.MDB.Collection(constant.CNameAccount).FindOne(context.Background(), bson.M{"userName": req.PlayerID}).Decode(&account)
  286. p.Account = &account
  287. var player models.Player
  288. mdb.MDB.Collection(constant.CNamePlayer).FindOne(context.Background(), bson.M{"userName": req.PlayerID}).Decode(&player)
  289. if player.OpenId != "" {
  290. p.Player = &player
  291. } else {
  292. user := models.NewPlayer(req)
  293. mdb.MDB.Collection(constant.CNamePlayer).InsertOne(context.Background(), user)
  294. p.Player = user
  295. }
  296. if len(p.Player.PlayerReward) == 0 {
  297. p.Player.PlayerReward = make(map[int]*models.PlayerRewardBase)
  298. }
  299. }
  300. func (p *actorPlayer) InitBase(uid string) int32 {
  301. clog.Debugf("[actorPlayer] InitBase uid = %s", uid)
  302. var player models.Player
  303. mdb.MDB.Collection(constant.CNamePlayer).FindOne(context.Background(), bson.M{"userName": uid}).Decode(&player)
  304. if player.OpenId != "" {
  305. p.Player = &player
  306. if len(p.Player.PlayerReward) == 0 {
  307. p.Player.PlayerReward = make(map[int]*models.PlayerRewardBase)
  308. }
  309. return code.OK
  310. } else {
  311. return code.Error
  312. }
  313. }
  314. func (p *actorPlayer) login(req *token.Token) (*models.Player, int32) {
  315. if !p.isOnline {
  316. p.Init(req)
  317. }
  318. p.isOnline = true
  319. p.dirty = true
  320. p.playerId = req.PlayerID
  321. uid, _ := mhayaString.ToInt64(req.OpenID)
  322. p.uid = uid
  323. p.Player.Init()
  324. p.Player.NickName = req.Nickname
  325. if !mhayaTime.CreateFromTimestamp(p.Player.LoginTime).IsToday() {
  326. p.Player.Successions += 1
  327. if mhayaTime.CreateFromTimestamp(p.Player.LoginTime).DiffInDays(mhayaTime.Now()) > 1 {
  328. p.Player.MaxSuccessions = 1
  329. } else {
  330. p.Player.MaxSuccessions += 1
  331. }
  332. }
  333. p.Player.LoginTime = mhayaTime.Now().Unix()
  334. //刷新roll
  335. p.Player.RefreshRoll()
  336. p.Player.InitDaily()
  337. p.Player.InitWeekly()
  338. p.Player.InitAchieveTask()
  339. // 保存进入游戏的玩家对应的agentPath
  340. online.BindPlayer(p.Player.UserName, p.uid, req.TargetPath)
  341. // 角色登录事件
  342. loginEvent := event.NewPlayerLogin(p.ActorID(), req)
  343. p.PostEvent(&loginEvent)
  344. return p.Player, code.OK
  345. }