go_dreamfactory/modules/user/module.go
2022-12-31 14:56:50 +08:00

775 lines
18 KiB
Go

// 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 err := this.modelSession.GetList(comm.RDS_EMPTY, &cache); err != nil {
return nil, err
}
return cache, nil
}
// 跨服玩家列表
func (this *User) CrossUserOnlineList() ([]*pb.CacheUser, error) {
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
err error
)
if this.IsCross() {
user, err = this.getRemoteUser(uid)
if err != nil {
return
}
} else {
user = this.modelUser.GetUser(uid)
}
if user == 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)
}
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},
)
// code = pb.ErrorCode_ReqParameterError
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 {
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.SociatyCoin,
ArenaCoin: userEx.ArenaCoin,
Ps: user.Ps,
}
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.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.SociatyCoin+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,
}
//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))
userEx, err := this.GetUserExpand(session.GetUserId())
if err != nil {
return sz
}
for i, heroid := range heros {
if _, ok := userEx.Tujian[heroid]; ok {
sz[i] = true
}
}
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) RecoverUserVitStart(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.recoverUserVit(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) recoverUserVit(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.LastRecoverVitSec == 0 {
update := map[string]interface{}{
"lastRecoverVitSec": cur,
}
this.modelUser.Change(u.Uid, update)
return
} else {
diff := cur - u.LastRecoverVitSec
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{}{
"lastRecoverVitSec": 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()), "vitchanged",
&pb.UserVitChangedPush{Ps: changed}, u.Uid); err != nil {
this.Error("玩家体力变化 UserVitChangedPush推送失败",
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
}