package room import ( "fmt" "samba/pkg/log" "samba/proto" "samba/server/cacheta/poker" . "samba/server/cacheta/service" "samba/server/game/baseroom" . "samba/server/game/player" "samba/stub" "samba/util/model" "samba/util/state" "samba/util/util" "sort" "time" ) func (r *GameRoom) loadRobot(user *Player, seat *GameSeat) { if user.IsRobot() { init := false for _, s := range r.Seats { if !s.Empty() && s.Player().IsRobot() && s.Player().Robot != nil { r.SetRobotTemper(seat.No(), s.Player().Robot.Cnf()) init = true break } } if !init { if cnf := r.RandRobotCnf(); cnf != stub.RteUnknown { //cnf = r.RobotCnfByTemper(stub.RteAplomb) r.SetRobotTemper(seat.No(), cnf) } else { log.Error(r.SeatLog(seat, "rand robot error. cnf is nil")) } } } } func (r *GameRoom) makePlayerToProto(seat *GameSeat, hidePoker bool) *proto.CachetaPlayer { user := seat.Player() protoPlayer := &proto.CachetaPlayer{ UserId: user.UID, Mnick: user.MNick, HeadUrl: user.HeadURL, IconId: user.IconId, AvatarFrame: user.AvatarFrame, Sex: user.Sex, TakeCoin: user.TakeCoin, ClubTakeCoin: user.TakeClubCoin, Poker: proto.CachetaHandPoker{}, Seat: seat.No(), State: util.Tie(seat.FakeLeave(), 0, 1), Point: seat.Point, } if r.ClubId() == 0 { protoPlayer.Coin = r.getCoin(user.UID) } else { protoPlayer.ClubCoin = r.getCoin(user.UID) } if !hidePoker { protoPlayer.Poker = seat.handPoker } else { for i := 0; i < len(seat.pokers); i++ { protoPlayer.Poker.Pokers = append(protoPlayer.Poker.Pokers, 0) } } return protoPlayer } func (r *GameRoom) makeProtoRoomInfo() *proto.NtfCachetaRoomInfo { protoRoom := &proto.NtfCachetaRoomInfo{ RoomId: r.Id(), RoomType: r.Type(), ClubId: r.ClubId(), Blind: r.RoomCnf.Blind, CuttingPoker: 0, WildPoker: nil, PickPokerNum: len(r.pickPokers), OutPokers: nil, State: int(r.gameStep), GameCount: r.smallGameCount, } if r.pokers.CutPoker() != nil { protoRoom.CuttingPoker = r.pokers.CutPoker().ToInt() } for _, pk := range r.pokers.WildPokers() { protoRoom.WildPoker = append(protoRoom.WildPoker, pk.ToInt()) } for _, pk := range r.outPokers { protoRoom.OutPokers = append(protoRoom.OutPokers, pk.ToInt()) } return protoRoom } // 重进入 func (r *GameRoom) checkEnterRoom(user *Player) (code proto.ErrorCode, seat *GameSeat, isReentry bool) { code = proto.RoomFull for _, st := range r.Seats { if !st.Empty() && st.Player().UID == user.UID { seat = st isReentry = true code = proto.Ok st.SetFakeLeave(false) break } } if !isReentry { if r.Status() > state.RsWait { return proto.GameStarted, nil, false } for _, st := range r.Seats { if st.Empty() { seat = st code = proto.Ok st.SetPlayer(user) st.SetFakeLeave(false) break } } } return code, seat, isReentry } // 进入房间 func (r *GameRoom) onEnterRoom(msg map[string]interface{}) { _, _, uid, _ := ParseMsg(msg) seat := r.FindSeat(uid) if seat != nil { r.OnEnterRoom(seat.Player()) } else { if player, err := NewPlayer(uid); err == nil { r.OnEnterRoom(player) } else { log.Error(r.Log(err.Error())) } } } // 加入房间 func (r *GameRoom) OnEnterRoom(user *Player) { code, seat, isReentry := r.checkEnterRoom(user) if code != proto.Ok { r.SendMsg(user, proto.RspEnterRoomId, &proto.RspEnterRoom{Code: code}, false) return } if !isReentry { if user.IsRobot() { r.loadRobot(user, seat) } r.addResource(seat.Player(), r.RoomCnf.TakeCoin, model.ResTakeCoins, model.ReasonEnter) log.Info(r.SeatLog(seat, "进入房间,携带金币:%v", r.getTakeCoin(seat.Player()))) } // 向玩家发送消息 rsp := &proto.RspEnterRoom{ Code: proto.Ok, RoomId: r.Id(), RoomType: r.Type(), PlayType: r.PlayType(), ClubId: r.ClubId(), GameInfo: r.MakeClubPlayGameInfo(), IsReentry: util.Tie(isReentry, 1, 0), } r.SendMsg(user, proto.RspEnterRoomId, rsp, false) r.notifyRoomInfo(user) r.notifyPlayerInfo(user, isReentry) // 首个真人进房间,开启等待定时器 if r.RealPlayerNum() == 1 { r.StartWaitTimer() } // 发送游戏开始消息 if !isReentry && r.SeatPlayerNum() == r.RoomCnf.MinPlayers { ntf := &proto.NtfGameStart{Time: time.Now().Unix()} r.Broadcast(proto.NtfStartCachetaGameId, ntf, false) r.SetStatus(state.RsReadyStart) r.gameStep = GsReadyGame r.NewTimer(baseroom.TtGameReadyStart, time.Duration(stub.GGlobal.TrucoGameStartTime)*time.Second) } } // kickOut:true,踢掉玩家,小局结束,分数不足时强制踢掉 func (r *GameRoom) leaveRoom(seat *GameSeat, kickOut bool) { if r.gameStep == GsWaitGame || kickOut { r.addResource(seat.Player(), -seat.Player().TakeCoin, model.ResTakeCoins, model.ReasonLeave) log.Info(r.SeatLog(seat, "归还携带金币:%v", -seat.Player().TakeCoin)) model.SetUserPlayingRoom(seat.Player().UID, r.Id(), false) // 回收机器人 if seat.Player().IsRobot() { log.Debug(r.SeatLog(seat, "清理并归还机器人")) seat.Player().CleanRobot() seat.Player().Robot = nil RobotMgr.Push(seat.Player()) } else { playingNum, err := model.DelRoomTypePlayerNum(r.RoomCnf, seat.Player().UID) if err == nil && r.RoomCnf.Mode != int(stub.RmClub) { // 金币场 广播虚假在玩人数 BroadcastMsg(proto.NtfPlayingNumId, &proto.NtfPlayingNum{RoomType: r.RoomCnf.Id, PlayingNum: model.CalculateFakeCount(playingNum)}) } } r.TriggerClubMemberPlaying(seat.Player().UID, false) r.Broadcast(proto.NtfLeaveRoomId, &proto.NtfLeaveRoom{CurrentSeat: seat.No(), RoomId: r.Id()}, false, seat.Player()) seat.SetPlayer(nil) r.TryReleaseRoom() } else { seat.SetFakeLeave(true) } } // 离开房间 func (r *GameRoom) onLeaveRoom(msg map[string]interface{}) { _, _, uid, _ := ParseMsg(msg) seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } log.Debug(r.SeatLog(seat, "玩家申请离开房间, room status:%v", r.gameStep)) // 向玩家发送消息 rsp := &proto.NtfLeaveRoom{ CurrentSeat: seat.No(), RoomId: r.Id(), } r.SendMsg(seat.Player(), proto.RspLeaveRoomId, &proto.RspLeaveRoom{Code: proto.Ok, RoomId: r.Id(), CurrentSeat: seat.No()}, false) if r.gameStep == GsWaitGame { r.Broadcast(proto.NtfLeaveRoomId, rsp, false, seat.Player()) } else { seat.SetFakeLeave(true) r.notifyFakeLeave(seat) } r.leaveRoom(seat, false) } func (r *GameRoom) checkPickPoker(seat *GameSeat) proto.ErrorCode { if r.gameStep != GsPlayerAct { log.Error(r.SeatLog(seat, "不是玩家行动期. game step:%v", r.gameStep)) return proto.BadAction } if seat.No() != r.current { log.Error(r.SeatLog(seat, "is not current:%v", r.current)) return proto.NotCurrent } if seat.ActType != SatPickPokerOrWin { log.Error(r.SeatLog(seat, "is not out poker act type:%v", seat.ActType)) return proto.BadAction } return proto.Ok } // 玩家摸牌 func (r *GameRoom) onPickPoker(msg map[string]interface{}) { _, _, uid, data := ParseMsg(msg) req, err := util.MapToStructT[proto.ReqCachetaPickPoker](data) if err != nil { log.Error(err.Error()) return } seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } if code := r.checkPickPoker(seat); code != proto.Ok { r.SendMsg(seat.Player(), proto.RspCachetaPickPokerId, proto.RspCachetaPickPoker{Code: code}, false) return } r.CancelTimer(baseroom.TtPlayerAct) seat.ActType = SatOutPoker var pickPk *poker.Poker rsp := &proto.RspCachetaPickPoker{Code: proto.Ok, IsDrop: req.IsDrop} rsp.DropPoker = r.outPokers[len(r.outPokers)-1].ToInt() // 摸牌区摸牌 if req.IsDrop == 0 { if len(r.pickPokers) == 0 { r.pickPokers = r.pickPokers[0:0] for i := len(r.outPokers) - 1; i >= 0; i-- { r.pickPokers = append(r.pickPokers, r.outPokers[i]) } r.outPokers = r.outPokers[0:0] rsp.IsTurnOver = 1 } pickPk = r.pickPokers[len(r.pickPokers)-1] r.pickPokers = r.pickPokers[:len(r.pickPokers)-1] } else { if len(r.outPokers) == 0 { log.Error(r.SeatLog(seat, "弃牌区为空,异常")) return } pickPk = r.outPokers[len(r.outPokers)-1] r.outPokers = r.outPokers[:len(r.outPokers)-1] if len(r.outPokers) != 0 { rsp.DropPoker = r.outPokers[len(r.outPokers)-1].ToInt() } else { rsp.DropPoker = 0 } } seat.pokers = append(seat.pokers, pickPk) seat.handPoker.Pokers = append(seat.handPoker.Pokers, pickPk.ToInt()) rsp.Poker = pickPk.ToInt() r.SendMsg(seat.Player(), proto.RspCachetaPickPokerId, rsp, false) log.Debug(r.SeatLog(seat, "申请摸牌:%v,玩家手牌:%v", pickPk.ToString(), poker.PokersToString(seat.pokers))) ntf := &proto.NtfCachetaPickPoker{CurrentSeat: seat.No(), IsTurnOver: 0, IsDrop: rsp.IsDrop, DropPoker: rsp.DropPoker} if len(r.pickPokers) == 0 { for i := len(r.outPokers) - 1; i >= 0; i-- { r.pickPokers = append(r.pickPokers, r.outPokers[i]) } for _, pk := range r.pickPokers { ntf.PickPokers = append(ntf.PickPokers, pk.ToInt()) } r.outPokers = nil ntf.IsTurnOver = 1 } ntf.PickPokerNum = len(r.pickPokers) r.Broadcast(proto.NtfCachetaPickPokerId, ntf, true) r.PlayerAct(seat.Player()) // 牌局回顾 ntf.Poker = pickPk.ToInt() r.record.AddAction(proto.NtfCachetaPickPokerId, ntf) } func (r *GameRoom) checkOutPoker(seat *GameSeat, req *proto.ReqCachetaOutPoker) (proto.ErrorCode, *poker.Poker) { if r.gameStep != GsPlayerAct { log.Error(r.SeatLog(seat, "不是玩家行动期. game step:%v", r.gameStep)) return proto.BadAction, nil } if seat.No() != r.current { log.Error(r.SeatLog(seat, "is not current:%v", r.current)) return proto.NotCurrent, nil } if seat.ActType != SatOutPoker { log.Error(r.SeatLog(seat, "is not out poker act type:%v", seat.ActType)) return proto.BadAction, nil } var pk *poker.Poker var ok bool if ok, pk = seat.Remove(req.Poker); !ok { log.Error(r.SeatLog(seat, "has not poker:%v 手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers))) return proto.BadParam, nil } seat.ActType = SatNone return proto.Ok, pk } // 出完牌后监测是否听牌 func (r *GameRoom) checkTingHu(seat *GameSeat) proto.ErrorCode { if seat.TingHu { return proto.BadAction } var tmpPokers []*poker.Poker tmpPokers = append(tmpPokers, seat.pokers...) tmpPokers = append(tmpPokers, r.pokers.WildPoker()) groups, high := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), tmpPokers) log.Debug(r.SeatLog(seat, "检查听牌:%s", poker.DebugGroupsHigh(groups, high, nil))) // 添加一张万能牌后,散牌数为0或1即可听牌 return util.Tie[proto.ErrorCode](len(high) < 2, proto.Ok, proto.BadAction) } // 玩家出牌 func (r *GameRoom) onOutPoker(msg map[string]interface{}) { _, _, uid, data := ParseMsg(msg) req, err := util.MapToStructT[proto.ReqCachetaOutPoker](data) if err != nil { log.Error(r.Log(err.Error())) return } seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } //log.Debug(r.SeatLog(seat, "申请出牌:%v,玩家手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers))) code, pk := r.checkOutPoker(seat, req) if code != proto.Ok { rsp := &proto.RspCachetaOutPoker{Code: code, Poker: req.Poker} r.SendMsg(seat.Player(), proto.RspCachetaOutPokerId, rsp, false) return } r.outPokers = append(r.outPokers, pk) ntf := &proto.NtfCachetaOutPoker{CurrentSeat: seat.No(), Poker: req.Poker} r.Broadcast(proto.NtfCachetaOutPokerId, ntf, false) // 牌局回顾 r.record.AddAction(proto.NtfPlayerOutPokerId, ntf) if r.checkTingHu(seat) == proto.Ok { log.Debug(r.SeatLog(seat, "听牌")) seat.TingHu = true ntf1 := &proto.NtfCachetaTingHu{Seat: seat.No()} r.Broadcast(proto.NtfCachetaTingHuId, ntf1, false) // 牌局回顾 r.record.AddAction(proto.NtfCachetaTingHuId, ntf1) } log.Debug(r.SeatLog(seat, "出牌:%v 玩家手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers))) //log.Debug(r.Log("cancel timer:TtPlayerAct")) r.CancelTimer(baseroom.TtPlayerAct) //r.NotifyCutLine() } func (r *GameRoom) checkTidyPoker(seat *GameSeat, req *proto.ReqCachetaTidyPoker) proto.ErrorCode { if r.gameStep >= GsGameSettle { log.Error(r.SeatLog(seat, "game is over")) return proto.BadAction } var clientPks []int for _, g := range req.HandPoker.Groups { for _, p := range g.Pokers { clientPks = append(clientPks, p) } } clientPks = append(clientPks, req.HandPoker.Pokers...) var serverPks []int for _, p := range seat.pokers { serverPks = append(serverPks, p.ToInt()) } sort.Ints(clientPks) sort.Ints(serverPks) if len(clientPks) != len(serverPks) { return proto.BadParam } for i, p := range clientPks { if p != serverPks[i] { return proto.BadParam } } return proto.Ok } // 客户端主动调整手牌 func (r *GameRoom) onTidyPoker(msg map[string]interface{}) { _, _, uid, data := ParseMsg(msg) req, err := util.MapToStructT[proto.ReqCachetaTidyPoker](data) if err != nil { log.Error(r.Log(err.Error())) return } seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } rsp := proto.RspCachetaTidyPoker{} rsp.Code = r.checkTidyPoker(seat, req) if rsp.Code != proto.Ok { rsp.HandPoker = seat.handPoker } else { rsp.HandPoker = req.HandPoker seat.handPoker = req.HandPoker } log.Debug(r.SeatLog(seat, "调整后手牌:%v", seat.HandPokerString())) r.SendMsg(seat.Player(), proto.RspCachetaTidyPokerId, rsp, false) } func (r *GameRoom) checkHu(seat *GameSeat, req *proto.ReqCachetaHu) (proto.ErrorCode, int) { // 不是当前玩家 if seat.No() != r.current { // 也没有插队 if r.gameStep != GsCutLine || !seat.TingHu { log.Error(r.SeatLog(seat, "is not current:%v", r.current)) return proto.NotCurrent, 0 } if r.gameStep == GsCutLine && seat.CutLineNum < 1 { log.Error(r.SeatLog(seat, "插队次数已用完")) return proto.CutLineZero, 0 } } var pokers []*poker.Poker pokers = append(pokers, seat.pokers...) if req.DropPoker > 0 { pokers = append(pokers, poker.NewPoker(req.DropPoker)) } gs, highPokers := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), pokers) log.Debug(r.SeatLog(seat, "检查胡牌:%s", poker.DebugGroupsHigh(gs, highPokers, nil))) if len(highPokers) == 0 || len(highPokers) == 1 { return proto.Ok, len(highPokers) } return proto.BadAction, 0 } // 玩家胡牌 func (r *GameRoom) onHu(msg map[string]interface{}) { _, _, uid, data := ParseMsg(msg) req, err := util.MapToStructT[proto.ReqCachetaHu](data) if err != nil { log.Error(r.Log(err.Error())) return } seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } log.Debug(r.SeatLog(seat, "申请胡牌,玩家手牌:%v", poker.PokersToString(seat.pokers))) code, highNum := r.checkHu(seat, req) if r.gameStep == GsCutLine { seat.CutLineNum = 0 } ntf := &proto.NtfCachetaHu{Code: code, Seat: seat.No()} r.Broadcast(proto.NtfCachetaHuId, ntf, true) // 牌局回顾 r.record.AddAction(proto.NtfCachetaHuId, ntf) log.Debug(r.SeatLog(seat, "申请胡牌:%v", code)) if code == proto.Ok { r.GameOver(seat, highNum) } } // 玩家断线重连 func (r *GameRoom) onReconnect(msg map[string]interface{}) { _, _, uid, _ := ParseMsg(msg) seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } seat.SetFakeLeave(false) r.notifyFakeLeave(seat) //log.Debug(r.SeatLog(seat, "seat:%v 重连", seat.No())) rsp := &proto.RspCachetaReconnect{ Code: proto.Ok, RoomInfo: r.makeProtoRoomInfo(), Players: nil, Dealer: r.dealer, } for _, st := range r.Seats { if st.Empty() { continue } rsp.Players = append(rsp.Players, r.makePlayerToProto(st, st.Player().UID != seat.Player().UID)) } r.SendMsg(seat.Player(), proto.RspCachetaReconnectId, rsp, false) if lastMsg, ok := r.LastMsg[uid]; ok { ilm := lastMsg.Msg if lm, ok1 := lastMsg.Msg.(*proto.NtfPlayerAct); ok1 && lastMsg.Tm.Unix() != 0 { lm.Countdown -= int(time.Now().Unix() - lastMsg.Tm.Unix()) lm.Countdown = util.Tie(lm.Countdown < 0, 0, lm.Countdown) } else if lm, ok1 := lastMsg.Msg.(*proto.NtfPlayerRspRaise); ok1 && lastMsg.Tm.Unix() != 0 { lm.Countdown -= int(time.Now().Unix() - lastMsg.Tm.Unix()) lm.Countdown = util.Tie(lm.Countdown < 0, 0, lm.Countdown) } r.SendMsg(seat.Player(), lastMsg.MsgId, ilm, false) log.Debug(r.SeatLog(seat, "重连补发消息:%v", lastMsg.MsgId)) } } // 服务器维护中 func (r *GameRoom) onMaintain(msg map[string]interface{}) { _, _, _, data := ParseMsg(msg) req, err := util.MapToStructT[proto.NtfMaintain](data) if err != nil { log.Error(err.Error()) return } r.CancelAllTimer() r.gameStep = GsWaitGame r.Broadcast(proto.NtfMaintainId, req, false) for _, st := range r.Seats { if !st.Empty() { msgL := util.MakeMessage(proto.ReqLeaveRoomId, &proto.ReqLeaveRoom{RoomId: r.Id()}, st.Player().UID, r.Id()) r.OnMessage(proto.ReqLeaveRoomId, msgL) } } } // gm解散房间 func (r *GameRoom) onDisbandRoom(_ map[string]interface{}) { //_, _, _, data := ParseMsg(msg) //req, err := util.MapToStructT[proto.NtfMaintain](data) //if err != nil { // log.Error(err.Error()) // return //} ntf := &proto.NtfMaintain{Code: proto.DisbandRoom} r.CancelAllTimer() r.gameStep = GsWaitGame r.Broadcast(proto.NtfMaintainId, ntf, false) for _, st := range r.Seats { if !st.Empty() { msgL := util.MakeMessage(proto.ReqLeaveRoomId, &proto.ReqLeaveRoom{RoomId: r.Id()}, st.Player().UID, r.Id()) r.OnMessage(proto.ReqLeaveRoomId, msgL) } } } func (r *GameRoom) checkEmote(user *Player, req *proto.ReqEmote, rsp *proto.NtfEmote) { rsp.Code = proto.Ok rsp.EmoteId = req.EmoteId rsp.DestSeat = req.DestSeat it, ok := stub.GPayEmote[req.EmoteId] if !ok { // 免费表情 return } if it.Value < 1 { rsp.Code = proto.Ok return } resType, err := model.IntToRes(it.Type) if err != nil { log.Error(err.Error()) rsp.Code = proto.BadParam return } rsp.ResType = it.Type rsp.CostValue = it.Value if resType == model.ResCoins { op := model.NewUserResourceOp() coins, err := op.Get(user.UID, model.ResCoins) takeCoins, err1 := op.GetTakeCoin(user.UID) if err1 != nil || err != nil { rsp.Code = proto.Internal log.Error(fmt.Sprintf("err:%v err1:%v", err, err1)) return } if coins-takeCoins < it.Value { rsp.Code = proto.NotEnough log.Debug(fmt.Sprintf("coins:%v takeCoins:%v emote cost:%v not enough", coins, takeCoins, it.Value)) return } r.addResource(user, -it.Value, model.ResCoins, model.ReasonEmote) coins -= it.Value rsp.ResVal = coins } else { op := model.NewUserResourceOp() value, err := op.Get(user.UID, resType) if err != nil { rsp.Code = proto.Internal log.Error(fmt.Sprintf("err:%v", err)) return } if value < it.Value { rsp.Code = proto.NotEnough log.Debug(fmt.Sprintf("resType:%v resValue:%v emote cost:%v not enough", resType, value, it.Value)) return } r.addResource(user, -it.Value, resType, model.ReasonEmote) value -= it.Value rsp.ResVal = value } return } // 玩家使用表情 func (r *GameRoom) onEmote(msg map[string]interface{}) { _, _, uid, data := ParseMsg(msg) seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } req, err := util.MapToStructT[proto.ReqEmote](data) if err != nil { log.Error(err.Error()) return } rsp := &proto.NtfEmote{Seat: seat.No()} r.checkEmote(seat.Player(), req, rsp) log.Debug(r.SeatLog(seat, "第%d小局, 使用表情:%v", r.smallGameCount, Emote(req.EmoteId))) if rsp.Code == proto.Ok { r.Broadcast(proto.NtfEmoteId, rsp, false) } else { r.SendMsg(seat.Player(), proto.NtfEmoteId, rsp, false) } } // 玩家准备 func (r *GameRoom) onReady(msg map[string]interface{}) { _, _, uid, _ := ParseMsg(msg) seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } log.Debug(r.SeatLog(seat, "收到玩家准备")) seat.SetReady(true) allReady := true for _, st := range r.Seats { if !st.Ready() { allReady = false break } } if allReady { r.CancelTimer(baseroom.TtGameReadyStart) if r.SeatPlayerNum() == r.RoomCnf.MinPlayers && r.Status() != state.RsGaming { r.ReInit() r.GameStart() } } // 游戏中,重连玩家发送准备后,设置为假离开后返回 if r.Status() == state.RsGaming { r.SetFakeLeave(uid, false) } }