// package // 用户 // 赵长远 package user import ( "context" "fmt" "go_dreamfactory/comm" "go_dreamfactory/modules" "go_dreamfactory/pb" "go_dreamfactory/sys/db" "go_dreamfactory/utils" "strings" "sync" "time" "go_dreamfactory/lego/base" "go_dreamfactory/lego/core" "go_dreamfactory/lego/sys/event" "go_dreamfactory/lego/sys/log" cfg "go_dreamfactory/sys/configure/structs" "github.com/pkg/errors" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) const ( // 跨服玩家 Rpc_GetAllOnlineUser string = "Rpc_GetAllOnlineUser" // 跨服用户 Rpc_GetCrossUser string = "Rpc_GetCrossUser" // 跨服用户会话 Rpc_GetCrossUserSession string = "Rpc_GetCrossUserSession" // 搜索用户 Rpc_QueryUser = "Rpc_QueryUser" ) var _ comm.IUser = (*User)(nil) func NewModule() core.IModule { m := new(User) return m } type User struct { modules.ModuleBase api *apiComp modelUser *ModelUser modelSession *ModelSession modelSetting *ModelSetting modelExpand *ModelExpand service base.IRPCXService configure *configureComp globalConf *cfg.GameGlobalData modelSign *ModelSign // 签到 timerLock sync.Mutex timerMap map[string]*time.Ticker } func (this *User) GetType() core.M_Modules { return comm.ModuleUser } func (this *User) Init(service core.IService, module core.IModule, options core.IModuleOptions) (err error) { err = this.ModuleBase.Init(service, module, options) this.service = service.(base.IRPCXService) this.timerMap = make(map[string]*time.Ticker) return } func (this *User) Start() (err error) { err = this.ModuleBase.Start() event.RegisterGO(comm.EventUserOffline, this.CleanSession) this.service.RegisterFunctionName(Rpc_GetAllOnlineUser, this.RpcGetAllOnlineUser) this.service.RegisterFunctionName(Rpc_GetCrossUser, this.RpcGetCrossUser) this.service.RegisterFunctionName(Rpc_GetCrossUserSession, this.RpcGetCrossUserSession) this.service.RegisterFunctionName(Rpc_QueryUser, this.RpcQueryUser) this.globalConf = this.configure.GetGlobalConf() if this.globalConf == nil { err = errors.New("global config not found") } this.ResetSession() return } func (this *User) OnInstallComp() { this.ModuleBase.OnInstallComp() this.api = this.RegisterComp(new(apiComp)).(*apiComp) this.modelUser = this.RegisterComp(new(ModelUser)).(*ModelUser) this.modelSession = this.RegisterComp(new(ModelSession)).(*ModelSession) this.modelSetting = this.RegisterComp(new(ModelSetting)).(*ModelSetting) this.modelExpand = this.RegisterComp(new(ModelExpand)).(*ModelExpand) this.modelSign = this.RegisterComp(new(ModelSign)).(*ModelSign) this.configure = this.RegisterComp(new(configureComp)).(*configureComp) } // 获取用户数据 func (this *User) GetUser(uid string) (user *pb.DBUser) { var err error if this.IsCross() { user, err = this.getRemoteUser(uid) if err != nil { return nil } } else { user = this.modelUser.GetUser(uid) } if user.Id == "" { return nil } return user } // 获取跨服用户数据 func (this *User) GetCrossUser(uid string) (*pb.DBUser, error) { reply := &pb.DBUser{} err := this.service.AcrossClusterRpcCall(context.Background(), this.GetCrossTag(), comm.Service_Worker, Rpc_GetCrossUser, &pb.UIdReq{Uid: uid}, reply) return reply, err } // 获取远程用户数据 func (this *User) getRemoteUser(uid string) (*pb.DBUser, error) { reply := &pb.DBUser{} if err := this.getUserFromRemoteDb(uid, reply); err != nil { return nil, err } return reply, nil } // 获取用户会话 func (this *User) GetUserSession(uid string) *pb.CacheUser { return this.modelSession.getUserSession(uid) } func (this *User) ResetSession() { us, err := this.UserOnlineList() if err != nil { return } for _, v := range us { this.modelSession.DelListlds(comm.RDS_EMPTY, v.Uid) } } // 清除session func (this *User) CleanSession(session comm.IUserSession) { this.stopTicker(session.GetUserId()) if !this.IsCross() { this.modelUser.updateOfflineTime(session.GetUserId()) } sId := fmt.Sprintf("%s-%s", comm.RDS_EMPTY, session.GetUserId()) this.modelSession.Del(sId, db.SetDBMgoLog(false)) this.modelSession.DelListlds(comm.RDS_EMPTY, session.GetUserId()) this.modelUser.DelByUId(session.GetUserId(), db.SetDBMgoLog(false)) this.modelExpand.DelByUId(session.GetUserId(), db.SetDBMgoLog(false)) this.modelSetting.DelByUId(session.GetUserId(), db.SetDBMgoLog(false)) } // 在线玩家列表 func (this *User) UserOnlineList() ([]*pb.CacheUser, error) { var cache []*pb.CacheUser if !this.IsCross() { if err := this.modelSession.GetList(comm.RDS_EMPTY, &cache); err != nil { return nil, err } } else { var err error if cache, err = this.CrossUserOnlineList(); err != nil { return nil, err } } return cache, nil } // 跨服玩家列表 func (this *User) CrossUserOnlineList() ([]*pb.CacheUser, error) { conn, err := db.Local() if err != nil { return nil, err } model := db.NewDBModel(comm.TableSession, 0, conn) var cache []*pb.CacheUser if err := model.GetList(comm.RDS_EMPTY, &cache); err != nil { return nil, err } this.Debug("在线玩家数FromDB", log.Field{Key: "count", Value: len(cache)}) return cache, nil // reply := &pb.UserOnlineResp{} // err := this.service.AcrossClusterRpcCall(context.Background(), this.GetCrossTag(), // comm.Service_Worker, Rpc_GetAllOnlineUser, nil, reply) // if err != nil { // return nil, err // } // return reply.Users, err } // 跨服玩家会话 func (this *User) CrossUserSession(uid string) *pb.CacheUser { cacheUser := &pb.CacheUser{} err := this.service.AcrossClusterRpcCall(context.Background(), this.GetCrossTag(), comm.Service_Worker, Rpc_GetCrossUserSession, &pb.UIdReq{Uid: uid}, cacheUser) if err != nil { return nil } return cacheUser } // 跨服搜索用户 func (this *User) CrossSearchUser(nickName string) ([]*pb.DBUser, error) { name := strings.TrimSpace(nickName) if name == "" { return nil, errors.New("search name is empty") } reply := &pb.UserDataListResp{} err := this.service.AcrossClusterRpcCall(context.Background(), this.GetCrossTag(), comm.Service_Worker, Rpc_QueryUser, &pb.NameReq{Name: name}, reply) if err != nil { return nil, err } return reply.Users, nil } // 远程搜索用户 func (this *User) SearchRmoteUser(nickname string) ([]*pb.DBUser, error) { reply := &pb.UserDataListResp{} if err := this.queryUserFromRemoteDb(nickname, reply); err != nil { return nil, err } return reply.Users, nil } // 查询用户属性值 例如 金币 经验 func (this *User) QueryAttributeValue(uid string, attr string) (value int64) { var ( user *pb.DBUser userEx *pb.DBUserExpand err error ) user = this.modelUser.GetUser(uid) userEx, err = this.GetUserExpand(uid) if err != nil { return } if user == nil || userEx == nil { return } switch attr { case comm.ResGold: return user.Gold case comm.ResExp: return user.Exp case comm.VipExp: return user.Vipexp case comm.StarCoin: return user.Starcoin case comm.ResDiamond: return user.Diamond case comm.ResPs: return int64(user.Ps) case comm.SociatyCoin: return int64(userEx.Guildcoin) case comm.ArenaCoin: return int64(userEx.Arenacoin) case comm.ResFriend: return int64(userEx.FriendPoint) case comm.Moongold: return int64(user.Moongold) } return } func (this *User) change(session comm.IUserSession, attr string, add int32) (change *pb.UserResChangedPush, code pb.ErrorCode) { if add == 0 { log.Warn("attr no changed", log.Field{Key: "uid", Value: session.GetUserId()}, log.Field{Key: "attr", Value: attr}, log.Field{Key: "add", Value: add}, ) return } uid := session.GetUserId() var ( user *pb.DBUser userEx *pb.DBUserExpand err error ) user = this.GetUser(uid) userEx, err = this.GetUserExpand(uid) if err != nil { code = pb.ErrorCode_UserExpandNull return } if user == nil || userEx == nil { code = pb.ErrorCode_UserSessionNobeing return } change = &pb.UserResChangedPush{ Gold: user.Gold, Exp: user.Exp, Vipexp: user.Vipexp, Diamond: user.Diamond, Friend: userEx.FriendPoint, Starcoin: user.Starcoin, Guildcoin: userEx.Guildcoin, Arenacoin: userEx.Arenacoin, Ps: user.Ps, Moongold: user.Moongold, } switch attr { case comm.ResGold: if add < 0 { if user.Gold+int64(add) < 0 { code = pb.ErrorCode_GoldNoEnough return } } change.Gold += int64(add) case comm.ResExp: if add < 0 { if user.Exp+int64(add) < 0 { code = pb.ErrorCode_UserExpNoEnough return } } change.Exp += int64(add) case comm.VipExp: if add < 0 { if user.Vipexp+int64(add) < 0 { code = pb.ErrorCode_UserExpNoEnough return } } change.Vipexp += int64(add) case comm.ResDiamond: if add < 0 { if user.Diamond+int64(add) < 0 { code = pb.ErrorCode_DiamondNoEnough return } } change.Diamond += int64(add) case comm.Moongold: if add < 0 { if user.Moongold+int32(add) < 0 { code = pb.ErrorCode_UserMoongoldNoEnough pb.ErrorCode_PayRenewTimeErr.Enum().Descriptor().ReservedNames() } } change.Moongold += int32(add) case comm.ResFriend: if add < 0 { if userEx.FriendPoint+add < 0 { code = pb.ErrorCode_UserFriendNoEnough return } } change.Friend += add case comm.StarCoin: if add < 0 { if user.Starcoin+int64(add) < 0 { code = pb.ErrorCode_UserFriendNoEnough return } } change.Starcoin += int64(add) case comm.SociatyCoin: if add < 0 { if userEx.Guildcoin+add < 0 { code = pb.ErrorCode_UserSociatyCoinNoEnough return } } change.Guildcoin += add case comm.ArenaCoin: if add < 0 { if userEx.Arenacoin+add < 0 { code = pb.ErrorCode_UserArenaCoinNoEnough return } } change.Arenacoin += add case comm.ResPs: if add < 0 { if user.Ps+add < 0 { code = pb.ErrorCode_UserVitNoEnough return } } ggd := this.configure.GetGlobalConf() if ggd == nil { return } if change.Ps+add > ggd.PsUl { code = pb.ErrorCode_UserVitLimit return } change.Ps += add default: err = errors.New(fmt.Sprintf("%s no supported", attr)) return } //user update := map[string]interface{}{ comm.ResGold: change.Gold, comm.ResDiamond: change.Diamond, comm.ResExp: change.Exp, comm.VipExp: change.Vipexp, comm.StarCoin: change.Starcoin, comm.ResPs: change.Ps, comm.Moongold: change.Moongold, } //user ex updateEx := map[string]interface{}{ comm.ResFriend: change.Friend, comm.SociatyCoin: change.Guildcoin, comm.ArenaCoin: change.Arenacoin, } if err := this.modelUser.updateUserAttr(uid, update); err != nil { this.Errorf("AddAttributeValue err:%v", err) code = pb.ErrorCode_DBError } if err := this.modelExpand.ChangeUserExpand(uid, updateEx); err != nil { this.Errorf("AddAttributeValue ex err:%v", err) code = pb.ErrorCode_DBError } return } // 用户资源 func (this *User) AddAttributeValue(session comm.IUserSession, attr string, add int32, bPush bool) (code pb.ErrorCode) { var _change *pb.UserResChangedPush _change, code = this.change(session, attr, add) if code != pb.ErrorCode_Success { return } if _change == nil { return } if bPush { //推送玩家账号信息变化消息 session.SendMsg(string(this.GetType()), "reschanged", _change) } return } // 用户资源 func (this *User) AddAttributeValues(session comm.IUserSession, attrs map[string]int32, bPush bool) (code pb.ErrorCode) { for key, add := range attrs { var _change *pb.UserResChangedPush _change, code = this.change(session, key, add) if code != pb.ErrorCode_Success { return } if _change == nil { continue } if bPush { //推送玩家账号信息变化消息 session.SendMsg(string(this.GetType()), "reschanged", _change) } if key == comm.ResExp { this.ModuleUser.EventUserChanged(session) } else if key == comm.VipExp { this.ModuleUser.EventUserVipChanged(session) } } return } // 用户事件变化 func (this *User) EventUserVipChanged(session comm.IUserSession) { ul := new(UserListen) user := this.GetUser(session.GetUserId()) if user != nil { ul.session = session ul.name = user.Name ul.vipexp = user.Vipexp ul.viplv = user.Vip } this.modelUser.EventApp.Dispatch(comm.EventUserVipChanged, ul) } // 用户事件变化 func (this *User) EventUserChanged(session comm.IUserSession) { ul := new(UserListen) user := this.GetUser(session.GetUserId()) if user != nil { ul.session = session ul.exp = user.Exp ul.lv = user.Lv ul.name = user.Name } this.modelUser.EventApp.Dispatch(comm.EventUserChanged, ul) } func (this *User) GetUserExpand(uid string) (result *pb.DBUserExpand, err error) { return this.modelExpand.GetUserExpand(uid) } func (this *User) ChangeUserExpand(uid string, value map[string]interface{}) error { return this.modelExpand.ChangeUserExpand(uid, value) } // 从远程库查询用户 func (this *User) getUserFromRemoteDb(uid string, rsp *pb.DBUser) error { sid, _, ok := utils.UIdSplit(uid) if !ok { return errors.New("sid split error") } conn, err := db.ServerDBConn(sid) if err != nil { return err } model := db.NewDBModel(comm.TableUser, 0, conn) if err := model.Get(uid, rsp); err != nil { return err } return nil } func (this *User) getUserExpandFromRemoteDb(uid string, rsp *pb.DBUserExpand) error { sid, _, ok := utils.UIdSplit(uid) if !ok { return errors.New("sid split error") } conn, err := db.ServerDBConn(sid) if err != nil { return err } model := db.NewDBModel(comm.TableUserExpand, 0, conn) if err := model.Get(uid, rsp); err != nil { return err } return nil } func (this *User) changeUserExpandFromRemoteDb(uid string, data map[string]interface{}) error { sid, _, ok := utils.UIdSplit(uid) if !ok { return errors.New("sid split error") } conn, err := db.ServerDBConn(sid) if err != nil { return err } model := db.NewDBModel(comm.TableUserExpand, 0, conn) if err := model.Change(uid, data); err != nil { return err } return nil } func (this *User) queryUserFromRemoteDb(name string, reply *pb.UserDataListResp) error { // 区服列表 for _, tag := range db.GetServerTags() { conn, err := db.ServerDBConn(tag) if err != nil { return fmt.Errorf("db tag:%v err:%v", tag, err) } //查询用户 filter := bson.M{ "name": name, } sr := conn.Mgo.FindOne(comm.TableUser, filter) user := &pb.DBUser{} if err = sr.Decode(user); err != nil { if err != mongo.ErrNoDocuments { return err } } if user.Uid != "" { reply.Users = append(reply.Users, user) } } return nil } func (this *User) RpcGetAllOnlineUser(ctx context.Context, args *pb.EmptyReq, reply *pb.UserOnlineResp) error { conn, err := db.Local() if err != nil { return err } model := db.NewDBModel(comm.TableSession, 0, conn) var cache []*pb.CacheUser if err := model.GetList(comm.RDS_EMPTY, &cache); err != nil { return err } reply.Users = cache return nil } func (this *User) RpcGetCrossUser(ctx context.Context, req *pb.UIdReq, reply *pb.DBUser) error { return this.getUserFromRemoteDb(req.Uid, reply) } func (this *User) RpcGetCrossUserSession(ctx context.Context, req *pb.UIdReq, reply *pb.CacheUser) error { conn, err := db.Local() if err != nil { return err } model := db.NewDBModel(comm.TableSession, 0, conn) if err := model.GetListObj(comm.RDS_EMPTY, req.Uid, reply); err != nil { if err != mongo.ErrNoDocuments { return err } return nil } return nil } func (this *User) RpcQueryUser(ctx context.Context, req *pb.NameReq, reply *pb.UserDataListResp) error { return this.queryUserFromRemoteDb(req.Name, reply) } func (this *User) CheckTujianHero(session comm.IUserSession, heros []string) []bool { sz := make([]bool, len(heros)) index := 0 list := this.ModuleHero.GetHeroList(session.GetUserId()) for _, v1 := range heros { for _, h := range list { if v1 == h.HeroID { sz[index] = true index++ break } } } return sz } func (this *User) BingoSetUserLv(session comm.IUserSession, lv int32) error { if lv <= 0 { return comm.NewCustomError(pb.ErrorCode_ReqParameterError) } update := map[string]interface{}{ "lv": lv, "exp": 0, } if err := this.modelUser.Change(session.GetUserId(), update); err == nil { if err := session.SendMsg(string(this.GetType()), UserSubTypeLvChangedPush, &pb.UserLvChangedPush{Uid: session.GetUserId(), Exp: 0, Lv: lv}); err != nil { this.Error("Bingo玩家等级变化 UserSubTypeLvChangedPush推送失败", log.Field{Key: "uid", Value: session.GetUserId()}, log.Field{Key: "exp", Value: 0}, log.Field{Key: "lv", Value: lv}, ) } } return nil } // func (this *User) Update() { // if this.IsCross() { // return // } // cu, err := this.UserOnlineList() // if err != nil { // return // } // for _, v := range cu { // if isession, ok := this.ModuleBase.GetUserSession(v.Uid); ok { // //del session // log.Debug("del session", log.Field{Key: "uid", Value: v.Uid}, log.Field{Key: "isLogin", Value: isession.IsLogin()}) // } // } // } // 玩家体力恢复 func (this *User) RecoverUserPsStart(uid string) { go func(uid string) { timeSec := time.NewTicker(time.Second * 30) this.timerLock.Lock() this.timerMap[uid] = timeSec this.timerLock.Unlock() for { select { case <-timeSec.C: this.recoverUserPs(uid) } } }(uid) } // 停止计时 func (this *User) stopTicker(uid string) { if t, ok := this.timerMap[uid]; ok { if t != nil { t.Stop() delete(this.timerMap, uid) } } } // 玩家体力恢复 func (this *User) recoverUserPs(uid string) { if this.IsCross() { return } u := this.GetUser(uid) if u == nil { return } ggd := this.configure.GetGlobalConf() if ggd == nil { return } var ( yu int32 add int32 changed int32 ) cur := time.Now().Unix() if u.LastRecoverPsSec == 0 { update := map[string]interface{}{ "lastRecoverPsSec": cur, } this.modelUser.Change(u.Uid, update) return } else { diff := cur - u.LastRecoverPsSec yu = int32(diff / int64(ggd.PsRecovery)) } if yu > 0 { pconf := this.configure.GetPlayerlvConf(u.Lv) if pconf == nil { return } if u.Ps < pconf.PsCeiling { total := u.Ps + yu if total > pconf.PsCeiling { add = pconf.PsCeiling - u.Ps changed = pconf.PsCeiling } else { add = yu changed = total } } else { add = 0 } update := map[string]interface{}{ "lastRecoverPsSec": cur, } if add > 0 { u.Ps += add update["ps"] = u.Ps } if err := this.modelUser.Change(u.Uid, update); err == nil { if changed > 0 { if err := this.SendMsgToUser(string(this.GetType()), "pschanged", &pb.UserPsChangedPush{Ps: changed}, u.Uid); err != nil { this.Error("玩家体力变化 UserPsChangedPush推送失败", log.Field{Key: "uid", Value: u.Uid}, log.Field{Key: comm.ResPs, Value: changed}, ) } } } } } func (this *User) BingoSetUserVipLv(session comm.IUserSession, lv int32) error { if lv <= 0 { return comm.NewCustomError(pb.ErrorCode_ReqParameterError) } update := map[string]interface{}{ "vip": lv, "vipexp": 0, } if err := this.modelUser.Change(session.GetUserId(), update); err == nil { if err := session.SendMsg(string(this.GetType()), UserSubTypeLvChangedPush, &pb.UserVipChangedPush{Uid: session.GetUserId(), VipExp: 0, VipLv: lv}); err != nil { this.Error("Bingo玩家等级变化 UserVipChangedPush推送失败", log.Field{Key: "uid", Value: session.GetUserId()}, log.Field{Key: "vipexp", Value: 0}, log.Field{Key: "viplv", Value: lv}, ) } } return nil }