package robot import ( "context" "encoding/json" "fmt" "math/rand" "sync" "time" "bet24.com/log" "bet24.com/servers/insecureframe/gate" "bet24.com/servers/insecureframe/message" "bet24.com/servers/transaction" "bet24.com/servers/user" ) type robotInfo struct { userIndex int32 lastTick int64 } type RobotManager struct { sink gate.GateSink gate *gate.Gate robotList []robotInfo //保存机器人的index lock_robot *sync.RWMutex isStopping bool } var robotMMgr *RobotManager func NewRobotManager(s gate.GateSink, g *gate.Gate) *RobotManager { r := new(RobotManager) r.lock_robot = &sync.RWMutex{} r.sink = s r.gate = g r.isStopping = false go r.checkOnline() return r } func (rm *RobotManager) checkOnline() { for { c, cancel := context.WithTimeout(context.Background(), 5*time.Second) select { case <-c.Done(): cancel() if rm.isStopping { return } robotCount := rm.sink.GetRobotCount() rm.lock_robot.RLock() currentCount := len(rm.robotList) rm.lock_robot.RUnlock() if currentCount > robotCount { log.Debug("RobotManager.checkOnline [%d] > [%d]", currentCount, robotCount) rm.retireOneRobot(true) } if currentCount < robotCount { rm.oneRobotLogin() } rm.retireOneRobot(false) } } } func (rm *RobotManager) retireOneRobot(force bool) { //min, max := rm.sink.GetRobotGoldLimit() now := time.Now().Unix() var toRemove []int32 var backupRemove int32 rm.lock_robot.RLock() for _, v := range rm.robotList { index := v.userIndex usr := gate.GetUserInfo(index) if usr == nil { toRemove = append(toRemove, index) log.Debug("RobotManager.retireOneRobot [%d] usr == nil", index) continue } //还在游戏中,不处理 if usr.GetUserStatus() == user.UserStatus_Play || usr.GetUserStatus() == user.UserStatus_Ready { continue } //是否超出金币上下限 /*gold := usr.GetUserGold() if gold < min || (max > 0 && gold > max) { log.Debug("RobotManager.retireOneRobot [%d] [%d-%d]%d", index, min, max, gold) toRemove = append(toRemove, usr.GetUserIndex()) continue }*/ //在线时长满了 logonTime := usr.GetLogonTime() if int(now-logonTime) >= rm.sink.GetRobotOnlineSec() { log.Debug("RobotManager.retireOneRobot [%d] [%d-%d]>=%d", index, now, logonTime, rm.sink.GetRobotOnlineSec()) toRemove = append(toRemove, usr.GetUserIndex()) break } backupRemove = usr.GetUserIndex() } rm.lock_robot.RUnlock() if len(toRemove) == 0 { if !force { return } log.Debug("RobotManager.retireOneRobot [%d] forced", backupRemove) go rm.oneRobotLogout(backupRemove) return } for _, v := range toRemove { go rm.oneRobotLogout(v) } } func (rm *RobotManager) loadRobot() { robotCount := rm.sink.GetRobotCount() for i := 0; i < robotCount; i++ { if !rm.oneRobotLogin() { log.Debug("RobotManager.loadRobot failed") } } log.Debug("---------loadRobot: %d ---------", len(rm.robotList)) } func (rm *RobotManager) oneRobotLogin() bool { return rm.oneRobotLoginWithGoldLimit(rm.sink.GetRobotGoldLimit()) } func (rm *RobotManager) oneRobotLoginWithGoldLimit(min, max int) bool { if max == 0 { max = 100000000000 } obj := transaction.NewTransRobotLogin() obj.In.GameID = rm.sink.GetGameID() obj.In.ServerName = rm.sink.GetGameName() obj.In.MinBeans, obj.In.MaxBeans = min, max obj.DoAction(nil) if !obj.State { return false } log.Debug("---------get one robot from DB: %d,[%d,%d] ---------", obj.Out.UserID, min, max) if obj.Out.UserID == 0 { return false } //检查是否有重复机器人 rm.lock_robot.RLock() flag := false for _, v := range rm.robotList { u := gate.GetUserInfo(v.userIndex) if u == nil { go rm.userExit(v.userIndex) continue } if u.GetUserId() == obj.Out.UserID { flag = true break } } rm.lock_robot.RUnlock() if gate.GetUserByUserId(obj.Out.UserID) != nil { flag = true } if flag { return false } index := rm.gate.GenUserIndex() a := gate.NewClient(index, nil, rm.gate, true) go a.Run() //模拟登录 login := transaction.NewCheckUserPassword() login.IN.UserID = obj.Out.UserID login.IN.Password = obj.Out.Password d, _ := json.Marshal(login.IN) a.Login("login", string(d)) usr := gate.GetUserInfo(index) if usr == nil { return false } rm.lock_robot.Lock() rm.robotList = append(rm.robotList, robotInfo{userIndex: index, lastTick: 0}) rm.lock_robot.Unlock() // 增加机器人随机在线 usr.SetLogonTime(time.Now().Unix()) if !rm.sink.IsChipRoom() { log.Debug("机器人登录成功 %d Gold:%d", obj.Out.UserID, usr.GetUserGold()) } else { // 筹码场,服务器自行决定筹码数量 usr.SetChip(rand.Intn(max-min) + min) log.Debug("机器人登录成功 %d Chip:%d", obj.Out.UserID, usr.GetChip()) } return true } func (rm *RobotManager) getOneRobotEnterTable(tableID, min, max int, retry bool, ready bool) bool { isChipRoom := rm.sink.IsChipRoom() log.Debug("RobotManager.getOneRobotEnterTable : %d [%d-%d] ChipRoom[%v]", tableID, min, max, isChipRoom) now := time.Now().Unix() var foundUser *user.UserInfo rm.lock_robot.Lock() robotCount := len(rm.robotList) if robotCount == 0 { rm.lock_robot.Unlock() fmt.Print("游戏阶段=====robotCount",robotCount) return false } rn := rand.Intn(robotCount) for i := 0; i < robotCount; i++ { index := (rn + i) % robotCount v := rm.robotList[index] if now-v.lastTick < 5 { continue } usr := gate.GetUserInfo(v.userIndex) if usr == nil { go rm.userExit(v.userIndex) continue } if int(now-usr.GetLogonTime()) >= rm.sink.GetRobotOnlineSec() { //go rm.oneRobotLogout(v.userIndex) continue } if usr.GetUserStatus() == user.UserStatus_Free { foundUser = usr rm.robotList[index].lastTick = now break } } rm.lock_robot.Unlock() if foundUser != nil { if max <= min { max = min * 20 } gold := min + rand.Intn(max-min) if isChipRoom { foundUser.SetChip(gold) } else { foundUser.SetUserGold(gold) } log.Debug("RobotManager.getOneRobotEnterTable: %d enter table: %d -----gold=[%d]", foundUser.GetUserId(), tableID, gold) var sit message.SitTable sit.TableId = tableID sit.ChairId = -1 d, _ := json.Marshal(sit) rm.sink.OnGameMessage(foundUser.GetUserIndex(), foundUser.GetUserId(), message.Frame_Sit, string(d)) if ready { rm.sink.OnGameMessage(foundUser.GetUserIndex(), foundUser.GetUserId(), message.Frame_Ready, "") } return true } log.Debug("RobotManager.getOneRobotEnterTable no robot found %d-%d", min, max) flag := rm.oneRobotLoginWithGoldLimit(min, max) if flag && !retry { log.Debug("RobotManager.getOneRobotEnterTable enter a new Robot") rm.getOneRobotEnterTable(tableID, min, max, true, ready) } return false } func (rm *RobotManager) robotOutTable(userIndex int32, tableID int) { rm.lock_robot.RLock() l := len(rm.robotList) for i := 0; i < l; i++ { if rm.robotList[i].userIndex == userIndex { rm.lock_robot.RUnlock() usr := gate.GetUserInfo(userIndex) if usr == nil { log.Debug("ERROR: robot is no exist!") return } var sit message.SitTable sit.TableId = tableID sit.ChairId = -1 d, _ := json.Marshal(sit) rm.sink.OnGameMessage(usr.GetUserIndex(), usr.GetUserId(), message.Frame_Standup, string(d)) return } } rm.lock_robot.RUnlock() } func (rm *RobotManager) oneRobotLogout(userIndex int32) { usr := gate.GetUserInfo(userIndex) if usr == nil { rm.userExit(userIndex) return } //还在游戏中,不处理 if usr.GetUserStatus() == user.UserStatus_Play { return } obj := transaction.NewTransRobotLogout() obj.In.GameID = rm.sink.GetGameID() obj.In.ServerName = rm.sink.GetGameName() obj.In.UserID = usr.GetUserId() gate.KickUser(userIndex) go obj.DoAction(nil) //从机器人列表删除 rm.lock_robot.Lock() defer rm.lock_robot.Unlock() for i := 0; i < len(rm.robotList); i++ { if rm.robotList[i].userIndex == userIndex { rm.robotList = append(rm.robotList[:i], rm.robotList[i+1:]...) return } } } func (rm *RobotManager) Exit() { rm.lock_robot.Lock() for _, v := range rm.robotList { index := v.userIndex usr := gate.GetUserInfo(index) if usr == nil { continue } //还在游戏中,不处理 if usr.GetUserStatus() == user.UserStatus_Play { continue } log.Debug("----------exit kick user: %d ", usr.GetUserId()) obj := transaction.NewTransRobotLogout() obj.In.GameID = rm.sink.GetGameID() obj.In.ServerName = rm.sink.GetGameName() obj.In.UserID = usr.GetUserId() go gate.KickUser(index) go obj.DoAction(nil) } rm.isStopping = true rm.lock_robot.Unlock() } func (rm *RobotManager) Dump() { rm.lock_robot.RLock() log.Release("++++++++++ Robot List Count: %d +++++++++++++++", len(rm.robotList)) for _, v := range rm.robotList { usr := gate.GetUserInfo(v.userIndex) if usr == nil { continue } gold := 0 golddesc := "Gold" if rm.sink.IsChipRoom() { gold = usr.GetChip() golddesc = "Chip" } else { gold = usr.GetUserGold() } log.Release("Robot[%d]: %d, Status: %d, Online: %d 秒 %s: %d ", v.userIndex, usr.GetUserId(), usr.GetUserStatus(), time.Now().Unix()-usr.GetLogonTime(), golddesc, gold) } log.Release("++++++++++ Robot List End +++++++++++++++") rm.lock_robot.RUnlock() } func (rm *RobotManager) userExit(userIndex int32) { rm.lock_robot.Lock() defer rm.lock_robot.Unlock() for i := 0; i < len(rm.robotList); i++ { if rm.robotList[i].userIndex == userIndex { rm.robotList = append(rm.robotList[:i], rm.robotList[i+1:]...) return } } } func (rm *RobotManager) isRetired(userIndex int32) bool { usr := gate.GetUserInfo(userIndex) if usr == nil { return true } now := time.Now().Unix() logonTime := usr.GetLogonTime() return int(now-logonTime) >= rm.sink.GetRobotOnlineSec() }