package logic import ( "ecs/proto" "github.com/oylshe1314/framework/client/db" "github.com/oylshe1314/framework/client/rpc" "github.com/oylshe1314/framework/errors" "github.com/oylshe1314/framework/log" "github.com/oylshe1314/framework/server" "github.com/oylshe1314/framework/util" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "sort" "sync" "time" ) type manager struct { server server.Server } func (this *manager) Logger() log.Logger { return this.server.Logger() } type GateManager struct { manager setting *Setting offlineTime int64 offlineMutex sync.Mutex offlineTimer *time.Timer mongoClient db.MongoClient httpRpcClient rpc.HttpRpcClient } func NewGateManager(svr server.Server, mongoClient db.MongoClient, httpRpcClient rpc.HttpRpcClient) *GateManager { return &GateManager{ manager: manager{ server: svr, }, mongoClient: mongoClient, httpRpcClient: httpRpcClient, } } func (this *GateManager) Init() error { return nil } func (this *GateManager) Close() error { return nil } func (this *GateManager) SetSetting(setting *Setting) error { var updateOption = options.Update().SetUpsert(true) _, err := this.mongoClient.Collection(tableSetting).UpdateByID(this.mongoClient.Context(), setting.Id, bson.M{"$set": setting}, updateOption) if err != nil { this.Logger().Error(err) return proto.ErrInternalError } if setting.LoginClosed && setting.Offline { var now = util.NowUnix() this.offlineMutex.Lock() if this.offlineTimer != nil { if this.offlineTime == setting.CloseTime || now >= setting.CloseTime { this.offlineMutex.Unlock() return nil } this.offlineTimer.Stop() this.offlineTime = 0 this.offlineTimer = nil } this.offlineMutex.Unlock() var f = func() { this.offlineMutex.Lock() this.offlineTime = 0 this.offlineTimer = nil this.offlineMutex.Unlock() ar, err := this.httpRpcClient.AllPost("game_1", "/server/offline", nil, nil, nil) if err != nil { this.Logger().Errorf("Http get error, error: %v", err) } else { for appId, result := range ar { if result.Err != nil { this.Logger().Errorf("[game_1:%d] <- Server offline error, %v", appId, result.Err) continue } if result.Res.Status != errors.StatusSuccessful { this.Logger().Errorf("[game_1:%d] <- Server offline failed, %s", appId, result.Res.Message) continue } } } } if !setting.TimedClose || util.NowUnix() >= setting.CloseTime { f() } else { this.offlineMutex.Lock() this.offlineTime = setting.CloseTime this.offlineTimer = time.AfterFunc(time.Second*time.Duration(setting.CloseTime-now), f) this.offlineMutex.Unlock() } } else { this.offlineMutex.Lock() if this.offlineTimer != nil { this.offlineTimer.Stop() this.offlineTime = 0 this.offlineTimer = nil } this.offlineMutex.Unlock() } return nil } func (this *GateManager) GetSetting() (*Setting, error) { var setting = this.setting if setting == nil { setting = &Setting{Id: 1} var err = this.mongoClient.Collection(tableSetting).FindOne(this.mongoClient.Context(), bson.M{"_id": setting.Id}).Decode(setting) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { return nil, nil } return nil, proto.ErrInternalError } } return setting, nil } func (this *GateManager) serverRankFunc(serverList []*proto.GameServer, recentServer uint32) func(i, j int) bool { //var guidanceConfig = this.GetConfig() return func(i, j int) bool { var s1, s2 = serverList[i], serverList[j] //if s1.Id == recentServer { // return true //} //if s2.Id == recentServer { // return false //} // //gn1, ok1 := guidanceConfig[s1.Id] //gn2, ok2 := guidanceConfig[s2.Id] // //if ok1 && ok2 { // if s1.Charges < gn1 && s2.Charges < gn2 { // if s1.Charges != s2.Charges { // return s1.Charges > s2.Charges // } else { // return s1.Id < s2.Id // } // } // // if s1.Charges < gn1 { // return true // } // // if s2.Charges < gn2 { // return false // } // // return s1.Charges < s2.Charges //} // //if ok1 { // return s1.Charges < gn1 //} // //if ok2 { // return !(s2.Charges < gn2) //} //if s1.Online != s2.Online { // return s1.Online < s2.Online //} else { return s1.Id < s2.Id //} } } type gameServerInfo struct { appId uint32 address string online uint32 } type gameServerList struct { id uint32 platform uint32 channel uint32 area string name string online uint32 serverList []*gameServerInfo } func (this *GateManager) GetServerList(platform uint32, channel uint32, recentServer uint32) ([]*proto.GameServer, error) { var setting, err = this.GetSetting() if err != nil { return nil, err } var closedList = map[uint32]struct{}{} if setting != nil && setting.LoginClosed { if !setting.TimedClose || util.NowUnix() >= setting.CloseTime { for _, closedId := range setting.ClosedList { closedList[closedId] = struct{}{} } } } ar, err := this.httpRpcClient.AllGet("game", "/server/info", nil, &proto.MsgServerInfoAck{}) if err != nil { return nil, proto.ErrServerMaintain } var listMap = map[uint32]*gameServerList{} for appId, reply := range ar { if reply.Err != nil { this.Logger().Error("Get server online failed, ", reply.Err) continue } if reply.Res.Error() != nil { this.Logger().Errorf("Get server online failed, status: %d, message: %s", reply.Res.Status, reply.Res.Message) continue } var node = this.httpRpcClient.Node("game", appId) if node == nil { continue } var infoAck = reply.Res.Data.(*proto.MsgServerInfoAck) var gsl = listMap[infoAck.Id] if gsl == nil { gsl = &gameServerList{ id: infoAck.Id, platform: infoAck.Platform, channel: infoAck.Channel, area: infoAck.Area, name: infoAck.Name, } listMap[infoAck.Id] = gsl } gsl.online += infoAck.Online gsl.serverList = append(gsl.serverList, &gameServerInfo{appId: appId, address: node.Exter.Address, online: infoAck.Online}) } var serverList []*proto.GameServer for _, gsl := range listMap { if len(gsl.serverList) == 0 { continue } sort.Slice(gsl.serverList, func(i, j int) bool { if gsl.serverList[i].online == gsl.serverList[j].online { return gsl.serverList[i].appId < gsl.serverList[j].appId } else { return gsl.serverList[i].online < gsl.serverList[j].online } }) serverList = append(serverList, &proto.GameServer{ Id: gsl.id, Area: gsl.area, Name: gsl.name, Address: gsl.serverList[0].address, Online: gsl.online, }) } sort.Slice(serverList, this.serverRankFunc(serverList, recentServer)) return serverList, nil } func (this *GateManager) GetNotice() (string, string, string) { var setting, _ = this.GetSetting() if setting == nil { return "", "", "" } return setting.Version, setting.Title, setting.Content } //type UserManager struct { // GateManager // // locker sync.RWMutex // users map[string]*User // tokens map[uint64]string //} // //func NewUserManager(svr server.Server, mongoClient *db.MongoClient, httpRpcClient *rpc.HttpRpcClient, guideConfig []*util.Pair[uint32, uint32]) *UserManager { // return &UserManager{ // GateManager: GateManager{ // _Guidance: _Guidance{config: util.Pairs[uint32, uint32](guideConfig).ToMap()}, // logger: svr.Logger(), // server: svr, // mongoClient: mongoClient, // httpRpcClient: httpRpcClient, // }, // users: map[string]*User{}, // tokens: map[uint64]string{}, // } //} // //func (this *UserManager) Init() error { // return nil //} // //func (this *UserManager) Close() error { // return nil //} // //func (this *UserManager) saveUser(user *User) error { // _, err := this.mongoClient.Collection(TableUser).InsertOne(this.mongoClient.Context(), user) // return err //} // //func (this *UserManager) queryUser(filter bson.M) (*User, error) { // var user = new(User) // var err = this.mongoClient.Collection(TableUser).FindOne(this.mongoClient.Context(), filter).Decode(user) // if err != nil { // if err == mongo.ErrNoDocuments { // return nil, nil // } // return nil, err // } // return user, nil //} // //func (this *UserManager) updateUser(id string, set bson.M) error { // var _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), id, bson.M{"$set": set}) // return err //} // //func (this *UserManager) SignUp(origin *proto.UserOrigin, auth *proto.UserAuth) (*User, error) { // user, err := this.queryUser(bson.M{"_id": UserKey(origin.Channel, auth.Username)}) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // // if user != nil { // return nil, proto.RawErrUsernameExists // } // // return this.signUp(origin, auth, nil) //} // //func (this *UserManager) signUp(origin *proto.UserOrigin, auth *proto.UserAuth, thirdInfo map[string]interface{}) (*User, error) { // counter, err := this.mongoClient.Counter("user_id", 1) // if err != nil { // this.logger.Error(err) // return nil, err // } // // var uid = util.EncryptUid(counter) // // if len(auth.Password) > 0 { // password, err := bcrypt.GenerateFromPassword([]byte(auth.Password), bcrypt.DefaultCost) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // // auth.Password = string(password) // } // // var user = &User{ // Id: UserKey(origin.Channel, auth.Username), // UserId: uid, // Platform: origin.Platform, // Channel: origin.Channel, // Username: auth.Username, // Password: auth.Password, // CreateTime: util.Unix(), // ThirdInfo: thirdInfo, // } // // err = this.saveUser(user) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // // return user, nil //} // //func (this *UserManager) LoginThird(origin *proto.UserOrigin, third *proto.UserThird) (*LoginResult, error) { // switch proto.Channel(origin.Channel) { // case proto.ChannelWechatMiniGame: // ack, err := wechat.Code2Session(third.Token) // if err != nil { // this.logger.Error("Code to session failed, ", err) // return nil, proto.RawErrUserLoginFailed // } // // if ack.ErrCode != 0 { // this.logger.Error("Third user login failed, msg: ", ack.ErrMsg) // return nil, proto.RawErrUserLoginFailed // } // // return this.login(origin, &proto.UserAuth{Username: ack.OpenId}, map[string]any{"openid": ack.OpenId, "unionid": ack.UnionId, "sessionKey": ack.SessionKey}) // case proto.ChannelTapTap: // if third.Args == nil || third.Args.TapTap == nil || third.Args.TapTap.MacKey == "" { // return nil, proto.RawErrParameterError // } // // ack, err := taptap.AccountProfile(third.Token, third.Args.TapTap.MacKey) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrUserLoginFailed // } // // if !ack.Success { // return nil, proto.RawErrUserLoginFailed // } // // return this.login(origin, &proto.UserAuth{Username: ack.Data.Openid}, map[string]interface{}{"name": ack.Data.Name, "avatar": ack.Data.Avatar}) // default: // return nil, proto.ErrChannelError // } //} // //func (this *UserManager) Login(origin *proto.UserOrigin, auth *proto.UserAuth) (*LoginResult, error) { // return this.login(origin, auth, nil) //} // //func (this *UserManager) login(origin *proto.UserOrigin, auth *proto.UserAuth, thirdInfo map[string]interface{}) (*LoginResult, error) { // user, err := this.queryUser(bson.M{"_id": UserKey(origin.Channel, auth.Username)}) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // // if user == nil { // user, err = this.signUp(origin, auth, thirdInfo) // if err != nil { // return nil, err // } // } else { // if user.BanLogin && user.BanLoginTime > util.Unix() { // return nil, proto.RawErrUserBanLogin // } // // if len(auth.Password) > 0 { // err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(auth.Password)) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrPasswordError // } // } // // if thirdInfo != nil { // if !reflect.DeepEqual(user.ThirdInfo, thirdInfo) { // user.ThirdInfo = thirdInfo // err = this.updateUser(user.Id, bson.M{"third_info": user.ThirdInfo}) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // } // } // } // // var token = util.RandomToken() // // this.locker.Lock() // defer this.locker.Unlock() // // if expired, ok := this.tokens[user.UserId]; ok { // delete(this.users, expired) // } // // this.users[token] = user // this.tokens[user.UserId] = token // // return &LoginResult{Token: token, Username: user.Username, RecentServer: user.RecentServer}, nil //} // //func (this *UserManager) TokenVerify(token string, serverId uint32) (*User, error) { // this.locker.RLock() // var user = this.users[token] // this.locker.RUnlock() // if user == nil { // return nil, nil // } // // if user.RecentServer != serverId { // user.RecentServer = serverId // var err = this.updateUser(user.Id, bson.M{"recent_server": user.RecentServer}) // if err != nil { // return nil, err // } // } // // return user, nil //} // //func (this *UserManager) Query(channel uint32, username string) (*User, error) { // user, err := this.queryUser(bson.M{"_id": UserKey(channel, username)}) // if err != nil { // this.logger.Error(err) // return nil, proto.RawErrInternalError // } // return user, nil //} // //func (this *UserManager) kick(user *User) error { // if user.RecentServer == 0 { // return proto.RawErrUserNotOnline // } // // reply, err := this.httpRpcClient.AppIdPost("game_1", user.RecentServer, "/server/player/operate", nil, &proto.MsgPlayerOperateReq{PlayerId: user.UserId, OperateType: 3}, nil) // if err != nil { // this.logger.Error("Kick player error, ", err) // return proto.RawErrInternalError // } // // if reply.Status != errors.StatusSuccessful { // this.logger.Error("Kick player failed, ", reply.Message) // return proto.RawErrInternalError // } // // return nil //} // //func (this *UserManager) Kick(userId string) error { // user, err := this.queryUser(bson.M{"_id": userId}) // if err != nil { // this.logger.Error("Query user error, ", err) // return proto.RawErrInternalError // } // // if user == nil { // return proto.RawErrUserNotExists // } // // return this.kick(user) //} // //func (this *UserManager) Ban(userId string, banDays uint32) error { // user, err := this.queryUser(bson.M{"_id": userId}) // if err != nil { // this.logger.Error("Query user error, ", err) // return proto.RawErrInternalError // } // // if user == nil { // return proto.RawErrUserNotExists // } // // if banDays == 0 { // user.BanLogin = false // user.BanLoginTime = int64(0) // } else { // user.BanLogin = true // user.BanLoginTime = util.TodayBeginTime() + 86400*int64(banDays) // } // // _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), user.Id, bson.M{"$set": bson.M{"ban_login": user.BanLogin, "ban_login_time": user.BanLoginTime}}) // if err != nil { // this.logger.Error(err) // return proto.RawErrInternalError // } // // if banDays == 0 { // return nil // } // // return this.kick(user) //} // //func (this *UserManager) ResetPassword(userId string, password string) error { // user, err := this.queryUser(bson.M{"_id": userId}) // if err != nil { // this.logger.Error("Query user error, ", err) // return proto.RawErrInternalError // } // // if user == nil { // return proto.RawErrUserNotExists // } // // user.Password = password // // _, err = this.mongoClient.Collection(TableUser).UpdateByID(this.mongoClient.Context(), user.Id, bson.M{"$set": bson.M{"password": user.Password}}) // if err != nil { // this.logger.Error(err) // return proto.RawErrInternalError // } // // return this.kick(user) //} // //func (this *UserManager) ChargeCallback(status proto.PaymentStatus, orderId, sign, extOrderNo string, extend *proto.OrderExtend) error { // var header = client.HttpHeader{"PlayerId": []string{util.IntegerToString(extend.Uid)}} // reply, err := this.httpRpcClient.AppIdPost("game_1", extend.Sid, "/player/charge/callback", nil, &proto.MsgGateChargeCallbackReq{OrderId: orderId, Sign: sign, Status: int32(status), ExtOrderNo: extOrderNo}, nil, header) // if err != nil { // return err // } // // if reply.Status != errors.StatusSuccessful { // return errors.Error(reply.Message) // } // // return nil //} // //func (this *UserManager) WechatAccessTokenExpired(appletId string) error { // reply, err := this.httpRpcClient.RandPost("center", "/wechat/accessToken/refresh", nil, &proto.MsgRefreshWechatAccessTokenReq{AppletId: appletId}, nil) // if err != nil { // return err // } // // if reply.Status != errors.StatusSuccessful { // return errors.Error(reply.Message) // } // // return nil //} // //func (this *UserManager) GetCdkey(key string) (*Cdkey, error) { // var cdkey = &Cdkey{Key: key} // var err = this.mongoClient.Collection(TableCdkey).FindOne(this.mongoClient.Context(), bson.M{"_id": cdkey.Key}).Decode(cdkey) // if err != nil { // if err == mongo.ErrNoDocuments { // return nil, nil // } // // this.logger.Error("Query cdkey error, ", err) // return nil, proto.RawErrInternalError // } // // return cdkey, nil //} // //type PageableQueryResult[Entity any] struct { // Total []*QueryTotal `bson:"total"` // List []*Entity `bson:"list"` //} // //func (this *UserManager) PageableQueryUser(pageNo uint32, pageSize uint32, channel uint32, userId uint64) (uint32, []*User, error) { // var offset = int64(pageSize * (pageNo - 1)) // var limit = int64(pageSize) // // var pipeline bson.A // if channel > 0 { // pipeline = append(pipeline, bson.M{"$match": bson.M{"channel": channel}}) // } // if userId > 0 { // pipeline = append(pipeline, bson.M{"$match": bson.M{"user_id": userId}}) // } // pipeline = append(pipeline, bson.M{"$facet": bson.M{"total": bson.A{bson.M{"$count": "total"}}, "list": bson.A{bson.M{"$skip": offset}, bson.M{"$limit": limit}}}}) // // var result = new(PageableQueryResult[User]) // cur, err := this.mongoClient.Collection(TableUser).Aggregate(this.mongoClient.Context(), pipeline) // if err != nil { // if err == mongo.ErrNoDocuments { // return 0, nil, nil // } // // this.logger.Error("Query cdkey error, ", err) // return 0, nil, proto.RawErrInternalError // } // // if cur.Next(this.mongoClient.Context()) { // err = cur.Decode(result) // if err != nil { // this.logger.Error("Query cdkey error, ", err) // return 0, nil, proto.RawErrInternalError // } // } // // var total = uint32(0) // if len(result.Total) > 0 { // total = result.Total[0].Total // } // // return total, result.List, nil //} // //func (this *UserManager) PageableQueryCdkey(pageNo uint32, pageSize uint32, channel uint32) (uint32, []*Cdkey, error) { // var offset = int64(pageSize * (pageNo - 1)) // var limit = int64(pageSize) // // var pipeline bson.A // if channel > 0 { // pipeline = append(pipeline, bson.M{"$match": bson.M{"channel": bson.M{"$in": bson.A{0, channel}}}}) // } // pipeline = append(pipeline, bson.M{"$facet": bson.M{"total": bson.A{bson.M{"$count": "total"}}, "list": bson.A{bson.M{"$skip": offset}, bson.M{"$limit": limit}}}}) // // var result = new(PageableQueryResult[Cdkey]) // cur, err := this.mongoClient.Collection(TableCdkey).Aggregate(this.mongoClient.Context(), pipeline) // if err != nil { // if err == mongo.ErrNoDocuments { // return 0, nil, nil // } // // this.logger.Error("Query cdkey error, ", err) // return 0, nil, proto.RawErrInternalError // } // // if cur.Next(this.mongoClient.Context()) { // err = cur.Decode(result) // if err != nil { // this.logger.Error("Query cdkey error, ", err) // return 0, nil, proto.RawErrInternalError // } // } // // var total = uint32(0) // if len(result.Total) > 0 { // total = result.Total[0].Total // } // // return total, result.List, nil //} // //func (this *UserManager) AddCdkey(channel uint32, key string, validDays int64, itemId []uint32, itemNum []uint32) (*Cdkey, error) { // cur, err := this.mongoClient.Collection(TableCdkey).Find2(this.mongoClient.Context(), bson.M{"_id": key}) // if err != nil { // this.logger.Error("Query cdkey error, ", err) // return nil, proto.RawErrInternalError // } // // if cur.Next(this.mongoClient.Context()) { // this.logger.Error("Add cdkey the key has existed") // return nil, proto.RawErrCdKeyExists // } // // var expiration = int64(0) // if validDays > 0 { // expiration = util.TodayBeginTime() + util.DayTotalSeconds*validDays // } // // var cdkey = &Cdkey{ // Key: key, // Channel: channel, // Expiration: expiration, // ItemId: itemId, // ItemNum: itemNum, // } // // _, err = this.mongoClient.Collection(TableCdkey).InsertOne(this.mongoClient.Context(), cdkey) // if err != nil { // this.logger.Error("Insert cdkey error, ", err) // return nil, proto.RawErrInternalError // } // // return cdkey, nil //} // //const cdKeyCounterMax = 0b11111111111111111111111111111111111111111 // S27TO3CV <- 2199023255551 // //var ins = []int{ // 40, 37, 36, 39, 30, 35, 22, 33, 28, 27, 32, 29, 34, 31, 38, 17, 26, 1, 10, 5, 14, 23, 4, 11, 0, 19, 16, 21, 18, 7, 12, 25, 6, 15, 9, 2, 20, 3, 8, 13, 24, //} // //var chs = []byte{ // '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', //} // //func (this *UserManager) transBits(n uint64) uint64 { // var bits = make([]byte, 41) // for i := 40; n > 0; n, i = n/2, i-1 { // bits[i] = byte(n % 2) // } // // var nits = make([]byte, 41) // for ni, bi := range ins { // nits[ni] = bits[bi] // } // // n = 0 // var bi = len(nits) - 1 // for i := bi; i >= 0; i-- { // n += uint64(nits[i]) * uint64(math.Pow(2, float64(bi-i))) // } // // return n //} // //func (this *UserManager) transTo36(n uint64) string { // var bs [8]byte // var i = 7 // for ; n > 0; n, i = n/36, i-1 { // bs[i] = chs[n%36] // } // // for ; i >= 0; i-- { // bs[i] = chs[0] // } // // return string(bs[:]) //} // //func (this *UserManager) GenerateCdkey(channel uint32, count uint32, validDays int64, itemId []uint32, itemNum []uint32) ([]*Cdkey, error) { // var counter, err = this.mongoClient.Counter("cdkey_id", uint64(count)) // if err != nil { // this.logger.Error("Get counter error, ", err) // return nil, proto.RawErrInternalError // } // // var expiration = int64(0) // if validDays > 0 { // expiration = util.TodayBeginTime() + util.DayTotalSeconds*validDays // } // // var list []*Cdkey // for i := uint64(0); i < uint64(count); i++ { // var key = this.transTo36(this.transBits(counter + i)) // var cdkey = &Cdkey{ // Key: key, // Channel: channel, // Expiration: expiration, // ItemId: itemId, // ItemNum: itemNum, // } // // list = append(list, cdkey) // } // // _, err = this.mongoClient.Collection(TableCdkey).InsertMany(this.mongoClient.Context(), util.SliceConvert(list, func(cdkey *Cdkey) any { // return cdkey // })) // // return list, nil //} // //func (this *UserManager) DeleteCdkey(keys []string) error { // _, err := this.mongoClient.Collection(TableCdkey).DeleteMany(this.mongoClient.Context(), bson.M{"_id": bson.M{"$in": keys}}) // return err //}