package base import ( "crypto/md5" "encoding/hex" "encoding/json" "fmt" "io" "math/rand" "os" "strconv" "time" "github.com/globalsign/mgo/bson" "mongo.games.com/goserver/core/logger" "mongo.games.com/goserver/core/netlib" "mongo.games.com/goserver/core/timer" "mongo.games.com/game/common" "mongo.games.com/game/model" "mongo.games.com/game/proto" loginproto "mongo.games.com/game/protocol/login" serverproto "mongo.games.com/game/protocol/server" ) /* 登录及机器人账号更新 */ func init() { common.RegisterClockFunc(&common.ClockFunc{ OnHourTimerFunc: func() { ClientMgrSingleton.HourChange() logger.Logger.Infof("client state: %+v", ClientMgrSingleton.GetState()) }, OnDayTimerFunc: func() { ClientMgrSingleton.DayChange() }, }) } var ClientMgrSingleton = &ClientMgr{ sessionPool: make(map[string]*netlib.Session), Running: true, CycleTimeEvent: [24][]HourEvent{}, } type HourEvent struct { newAcc string //wait to open oldAcc string //wait to close session *netlib.Session } type InvalidAcc struct { Acc string Session *netlib.Session } type ClientMgr struct { sessionPool map[string]*netlib.Session Running bool CycleTimeEvent [24][]HourEvent } // 发送登录消息 func (this *ClientMgr) startLogin(acc string, s *netlib.Session) { ts := time.Now().UnixNano() csLogin := &loginproto.CSLogin{ Username: proto.String(acc), TimeStamp: proto.Int64(ts), Platform: proto.String(common.Platform_Rob), Channel: proto.String(common.Channel_Rob), } params := &model.PlayerParams{ Ip: fmt.Sprintf("%v.%v.%v.%v", 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255), 1+rand.Int31n(255)), City: RandZone(), Platform: 1, Logininmodel: "app", } data, err := json.Marshal(params) if err == nil { csLogin.Params = proto.String(string(data[:])) } h := md5.New() io.WriteString(h, fmt.Sprintf("%v%v", acc, Config.AppId)) pwd := hex.EncodeToString(h.Sum(nil)) h.Reset() io.WriteString(h, fmt.Sprintf("%v%v%v", pwd, Config.AppId, ts)) pwd = hex.EncodeToString(h.Sum(nil)) csLogin.Password = pwd csLogin.LoginType = proto.Int32(0) csLogin.Sign = proto.String(common.MakeMd5String(csLogin.GetUsername(), csLogin.GetPassword(), strconv.Itoa(int(csLogin.GetTimeStamp())), csLogin.GetParams(), Config.AppId)) s.Send(int(loginproto.LoginPacketID_PACKET_CS_LOGIN), csLogin) logger.Logger.Infof("账号 [%v] 开始登录", acc) } func (this *ClientMgr) RegisterSession(acc string, s *netlib.Session) { this.sessionPool[acc] = s StartSessionLoginTimer(s, timer.TimerActionWrapper(func(h timer.TimerHandle, ud interface{}) bool { if !s.IsConned() || !this.Running { StopSessionLoginTimer(s) return false } this.startLogin(acc, s) return true }), nil, time.Second*time.Duration(2+rand.Int31n(3)), -1) } func (this *ClientMgr) UnRegisterSession(acc string) { delete(this.sessionPool, acc) } func (this *ClientMgr) HourChange() { fileModify := false eventArr := this.CycleTimeEvent[time.Now().Hour()] for _, event := range eventArr { accChan[event.newAcc] = true //使用新的账号 cfg := NewSessionConfig() netlib.Connect(cfg) //创建新的连接 if session, ok := this.sessionPool[event.oldAcc]; ok && session != nil { //删除旧有账号数据 pack := &serverproto.RWAccountInvalid{ Acc: proto.String(event.oldAcc), } session.Send(int(loginproto.LoginPacketID_PACKET_CS_ACCOUNTINVALID), pack) // 删除机器人账号 //删号标记,不要在断线重连了 session.SetAttribute(SessionAttributeDelAccount, true) //关闭连接 session.Close() } //更新本地账号数据信息 for key, value := range accPool { if value.Acc == event.oldAcc { accPool[key] = &AccountData{ Acc: event.newAcc, Create: time.Now().UnixNano(), Time: time.Now(), } fileModify = true break } } } this.CycleTimeEvent[time.Now().Hour()] = []HourEvent{} if fileModify == true { //持久化本次的账号数据 buff, err := json.Marshal(accPool) if err != nil { logger.Logger.Error("Marshal account data error:", err) } else { err := os.WriteFile(accountFileName, buff, os.ModePerm) if err != nil { logger.Logger.Error("Write robot account file error:", err) } } } } func (this *ClientMgr) DayChange() { invalidCount := 0 //过期账号数量 updateLimit := len(accPool) * model.GameParamData.InvalidRobotAccRate / 100 //可更新的账号数量 invalidAccs := []InvalidAcc{} if updateLimit > 0 { invalideTime := time.Now().AddDate(0, 0, -model.GameParamData.InvalidRobotDay).UnixNano() for _, value := range accPool { //检查过期账号 if value.Create < invalideTime { //过期账号 invalidAccs = append(invalidAccs, InvalidAcc{ Acc: value.Acc, Session: this.sessionPool[value.Acc], }) invalidCount++ j := rand.Intn(invalidCount) i := invalidCount - 1 if i != j { invalidAccs[i], invalidAccs[j] = invalidAccs[j], invalidAccs[i] } } } if len(invalidAccs) >= updateLimit { invalidAccs = invalidAccs[:updateLimit] } } //本次需要生成的新账号 cnt := len(invalidAccs) for i := 0; i < cnt; i++ { timePoint := i % 24 eventArr := this.CycleTimeEvent[timePoint] eventArr = append(eventArr, HourEvent{ newAcc: bson.NewObjectId().Hex(), oldAcc: invalidAccs[i].Acc, session: invalidAccs[i].Session, }) this.CycleTimeEvent[timePoint] = eventArr } } type ClientState struct { SessionNum int Event map[int]int } func (this *ClientMgr) GetState() *ClientState { ret := &ClientState{ SessionNum: len(this.sessionPool), Event: make(map[int]int), } for k, v := range this.CycleTimeEvent { ret.Event[k] = len(v) } return ret }