百人场

This commit is contained in:
liuxiaobo 2025-06-13 14:54:44 +08:00
parent 0ad0125109
commit 99d726dc2a
6 changed files with 260 additions and 91 deletions

View File

@ -13,12 +13,21 @@ import (
"time"
)
type PlayerType int
const (
PT_User PlayerType = 0
PT_Robot PlayerType = 1
PT_All PlayerType = 2
)
// 房间基类,不要直接用于业务
type BaseRoom[Seat ISeat] struct {
id int
roomType int // 房间配置id 初级,中级,高级
playType int // 玩法配置id color玩法id
gameNo string
Seats []Seat
seats []Seat
processor *processor.Processor
timeProcessor *processor.TimerProcessor
@ -26,12 +35,13 @@ type BaseRoom[Seat ISeat] struct {
srv service.IService
}
func NewBaseRoom[Seat ISeat](id, roomType, playType int, srv service.IService) (*BaseRoom[Seat], pb.ErrCode) {
func NewBaseRoom[Seat ISeat](id, roomType, playType, seatNum int, srv service.IService) (*BaseRoom[Seat], pb.ErrCode) {
room := &BaseRoom[Seat]{
id: id,
roomType: roomType,
playType: playType,
gameNo: "",
seats: make([]Seat, seatNum),
timeTypes: make(map[timer.ITimeType]uint32),
srv: srv,
processor: processor.NewProcessor(),
@ -53,93 +63,86 @@ func (r *BaseRoom[Seat]) PlayType() int {
return r.playType
}
// 入座玩家数量(包含机器人)
func (r *BaseRoom[Seat]) SeatPlayerNum() int {
// 入座玩家数量
func (r *BaseRoom[Seat]) SeatPlayerNum(playerType PlayerType) int {
num := 0
for _, seat := range r.Seats {
for _, seat := range r.seats {
if !seat.Empty() {
num++
}
}
return num
}
// 真实玩家数量
func (r *BaseRoom[Seat]) RealPlayerNum() int {
num := 0
for _, seat := range r.Seats {
if !seat.Empty() && seat.Player().Robot() == nil {
num++
}
}
return num
}
// 机器人数量
func (r *BaseRoom[Seat]) RobotPlayerNum() int {
num := 0
for _, seat := range r.Seats {
if !seat.Empty() && seat.Player().Robot() != nil {
num++
switch playerType {
case PT_All:
num++
case PT_User:
if !seat.Player().IsRobot() {
num++
}
case PT_Robot:
if seat.Player().IsRobot() {
num++
}
}
}
}
return num
}
// 返回是否有空座位及空座位编号
func (r *BaseRoom[Seat]) HasEmptySeat() (bool, int) {
for _, seat := range r.Seats {
func (r *BaseRoom[Seat]) FindEmptySeat() (bool, Seat) {
for _, seat := range r.seats {
if seat.Empty() {
return true, seat.No()
return true, seat
}
}
return false, -1
return false, utils.TValue[Seat]{}.V
}
// 查找玩家及玩家所在的座位
func (r *BaseRoom[Seat]) FindPlayer(uid int64) (IPlayer, Seat) {
for _, seat := range r.Seats {
// 查找玩家所在的座位
func (r *BaseRoom[Seat]) FindSeat(uid int64) (bool, Seat) {
for _, seat := range r.seats {
if !seat.Empty() && seat.Player().Id() == uid {
return seat.Player(), seat
return true, seat
}
}
return nil, utils.TValue[Seat]{}.V
return false, utils.TValue[Seat]{}.V
}
func (r *BaseRoom[Seat]) AddPlayer(player IPlayer, seat int) {
if seat < 0 || seat >= len(r.Seats) {
log.Error(r.SeatLog(r.Seats[seat], "out of range"))
return
// 查找玩家所在的座位
func (r *BaseRoom[Seat]) GetSeat(seatNo int) (bool, Seat) {
if seatNo < 0 || seatNo >= len(r.seats) {
return false, utils.TValue[Seat]{}.V
}
r.Seats[seat].SetPlayer(player)
return
return true, r.seats[seatNo]
}
func (r *BaseRoom[Seat]) RemovePlayer(player IPlayer) {
for _, seat := range r.Seats {
if !seat.Empty() && seat.Player().Id() == player.Id() {
seat.SetPlayer(nil)
break
}
// 站起时player为nil坐下时player非nil
func (r *BaseRoom[Seat]) SitDownOrStandup(player IPlayer, seat int) bool {
if seat < 0 || seat >= len(r.seats) {
log.Error(r.SeatLog(r.seats[seat], "out of range"))
return false
}
if player != nil && r.seats[seat].Player() != nil {
log.Error(r.SeatLog(r.seats[seat], "is already here"))
return false
}
r.seats[seat].SetPlayer(player)
return true
}
func (r *BaseRoom[Seat]) RangePlayer(proc func(IPlayer) bool) {
for _, seat := range r.Seats {
if !seat.Empty() && !proc(seat.Player()) {
func (r *BaseRoom[Seat]) RangeSeat(proc func(Seat) bool) {
for _, seat := range r.seats {
if !seat.Empty() && !proc(seat) {
return
}
}
}
func (r *BaseRoom[Seat]) FilterPlayer(proc func(IPlayer) bool) []IPlayer {
players := make([]IPlayer, 0)
for _, seat := range r.Seats {
if !seat.Empty() && proc(seat.Player()) {
players = append(players, seat.Player())
func (r *BaseRoom[Seat]) FilterSeat(proc func(Seat) bool) []Seat {
seats := make([]Seat, 0)
for _, seat := range r.seats {
if !seat.Empty() && proc(seat) {
seats = append(seats, seat)
}
}
return players
return seats
}
func (r *BaseRoom[Seat]) DebugSendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) {
@ -151,19 +154,13 @@ func (r *BaseRoom[Seat]) SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message
if user.Robot() != nil {
user.Robot().OnMessage(msgId, msg)
} else {
for _, seat := range r.Seats {
if !seat.Empty() && seat.Player().Id() == user.Id() {
iMsg := ipb.MakeMsgEx(r.srv.Name(), 0, user.Id(), int32(msgId), msg)
_ = r.srv.Send(user.GateTopicName(), iMsg)
break
}
}
iMsg := ipb.MakeMsgEx(r.srv.Name(), 0, user.Id(), int32(msgId), msg)
_ = r.srv.Send(user.GateTopicName(), iMsg)
}
}
func (r *BaseRoom[Seat]) Broadcast(msgId pb.MsgId, msg proto.Message, exclude ...IPlayer) {
for _, seat := range r.Seats {
for _, seat := range r.seats {
if !seat.Empty() {
exist := false
for _, excludePlayer := range exclude {
@ -231,7 +228,7 @@ func (r *BaseRoom[Seat]) SeatLog(seat Seat, format string, a ...any) string {
func (r *BaseRoom[Seat]) UserLog(uid int64, format string, a ...any) string {
var seat Seat
exist := false
for _, st := range r.Seats {
for _, st := range r.seats {
if !st.Empty() && st.Player().Id() == uid {
seat = st
exist = true

View File

@ -0,0 +1,180 @@
package baseroom
import (
"fmt"
"game/common/proto/pb"
"github.com/fox/fox/ipb"
"github.com/fox/fox/log"
"github.com/fox/fox/processor"
"github.com/fox/fox/service"
"github.com/fox/fox/timer"
"github.com/golang/protobuf/proto"
"time"
)
// 百人场
type HundredRoom struct {
room *BaseRoom[*BaseSeat]
users map[int64]IPlayer // 所有玩家
}
func NewHundredRoom(id, roomType, playType int, srv service.IService) (*HundredRoom, pb.ErrCode) {
baseRoom, code := NewBaseRoom[*BaseSeat](id, roomType, playType, 0, srv)
if code != pb.ErrCode_OK {
return nil, code
}
room := &HundredRoom{
room: baseRoom,
}
return room, pb.ErrCode_OK
}
func (r *HundredRoom) Id() int {
return r.room.Id()
}
func (r *HundredRoom) RoomType() int {
return r.room.RoomType()
}
func (r *HundredRoom) PlayType() int {
return r.room.PlayType()
}
// 房间玩家数量
func (r *HundredRoom) GetPlayerNum(playerType PlayerType) int {
if playerType == PT_All {
return len(r.users)
}
num := 0
for _, user := range r.users {
switch playerType {
case PT_User:
if !user.IsRobot() {
num++
}
case PT_Robot:
if user.IsRobot() {
num++
}
}
}
return num
}
// 查找玩家所在的座位
func (r *HundredRoom) FindPlayer(uid int64) (IPlayer, bool) {
u, ok := r.users[uid]
return u, ok
}
// 站起时player为nil坐下时player非nil
func (r *HundredRoom) AddPayer(user IPlayer) bool {
if _, ok := r.users[user.Id()]; ok {
return false
}
r.users[user.Id()] = user
return true
}
func (r *HundredRoom) DelPlayer(uid int64) {
delete(r.users, uid)
}
func (r *HundredRoom) RangePlayer(proc func(IPlayer) bool) {
for _, seat := range r.users {
if !proc(seat) {
return
}
}
}
func (r *HundredRoom) FilterPlayer(proc func(IPlayer) bool) []IPlayer {
users := make([]IPlayer, 0)
for _, user := range r.users {
if proc(user) {
users = append(users, user)
}
}
return users
}
func (r *HundredRoom) DebugSendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) {
log.Debug(r.UserLog(user.Id(), "send msg:%v %v", msgId, msg.String()))
}
func (r *HundredRoom) SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) {
r.DebugSendMsg(user, msgId, msg)
if user.Robot() != nil {
user.Robot().OnMessage(msgId, msg)
} else {
iMsg := ipb.MakeMsgEx(r.room.srv.Name(), 0, user.Id(), int32(msgId), msg)
_ = r.room.srv.Send(user.GateTopicName(), iMsg)
}
}
func (r *HundredRoom) Broadcast(msgId pb.MsgId, msg proto.Message, exclude ...IPlayer) {
for _, user := range r.users {
exist := false
for _, excludePlayer := range exclude {
if excludePlayer.Id() == user.Id() {
exist = true
break
}
}
if !exist {
r.SendMsg(user, msgId, msg)
}
}
}
func (r *HundredRoom) NewTimer(timerType timer.ITimeType, duration time.Duration, args ...any) {
r.room.NewTimer(timerType, duration, args...)
}
func (r *HundredRoom) CancelTimer(timerType timer.ITimeType) {
r.room.CancelTimer(timerType)
}
func (r *HundredRoom) CancelAllTimer() {
r.room.CancelAllTimer()
}
func (r *HundredRoom) Log(format string, a ...any) string {
return r.room.Log(format, a...)
}
func (r *HundredRoom) UserLog(uid int64, format string, a ...any) string {
head := ""
user := r.users[uid]
if user != nil {
head = fmt.Sprintf("room:%v type:%v user:%v robot:%v ", r.Id(), r.RoomType(), uid, user.IsRobot())
} else {
head = fmt.Sprintf("room:%v type:%v ", r.Id(), r.RoomType())
}
return head + fmt.Sprintf(format, a...)
}
func (r *HundredRoom) Unmarshal(cmd int32, data []byte) (any, error) {
return r.room.Unmarshal(cmd, data)
}
func (r *HundredRoom) Dispatch(user IPlayer, cmd int32, params ...any) error {
return r.room.Dispatch(user, cmd, params...)
}
func (r *HundredRoom) RegisterMessages(metas processor.RegisterMetas) {
r.room.RegisterMessages(metas)
}
// 注册时间事件及处理
func (r *HundredRoom) RegisterTimerMessages(metas processor.RegisterTimerMetas) {
r.room.RegisterTimerMessages(metas)
}
// 初始化房间
func (r *HundredRoom) OnInit() {
}

View File

@ -11,7 +11,7 @@ type IRoom interface {
PlayType() int
OnInit()
// SeatPlayerNum() int
// HasEmptySeat() bool
// FindEmptySeat() bool
// FindPlayer(uid int64) bool
Unmarshal(cmd int32, data []byte) (any, error)
Dispatch(user IPlayer, cmd int32, params ...any) error
@ -30,6 +30,7 @@ type ISeat interface {
type IPlayer interface {
Id() int64
Robot() IRobot
IsRobot() bool
GateTopicName() string
RoomId() int
}

View File

@ -1,7 +1,6 @@
package room
import (
"game/common/baseroom"
"game/common/proto/pb"
"github.com/fox/fox/ipb"
)
@ -25,17 +24,7 @@ func (rm *ColorRoom) onMatchRoom(user *ColorPlayer, iMsg *ipb.InternalMsg, req *
}
func (rm *ColorRoom) enterRoom(user *ColorPlayer) {
// 第一次进房间分配座位
if u, _ := rm.FindPlayer(user.ID); u == nil {
var seat *baseroom.BaseSeat
if exist, no := rm.HasEmptySeat(); exist {
seat = rm.Seats[no]
} else {
rm.Seats = append(rm.Seats, baseroom.NewBaseSeat(len(rm.Seats)))
seat = rm.Seats[len(rm.Seats)-1]
}
seat.SetPlayer(user)
}
_ = rm.AddPayer(user)
rm.notifyColorRoomInfo(user)
}
@ -62,8 +51,7 @@ func (rm *ColorRoom) checkLeaveRoom(user *ColorPlayer, iMsg *ipb.InternalMsg, re
}
func (rm *ColorRoom) leaveRoom(user *ColorPlayer) {
_, st := rm.FindPlayer(user.Id())
st.SetPlayer(nil)
rm.DelPlayer(user.Id())
rm.userMgr.Del(user.ID)
}

View File

@ -35,6 +35,10 @@ func (p *ColorPlayer) Robot() baseroom.IRobot {
return nil
}
func (p *ColorPlayer) IsRobot() bool {
return false
}
func (p *ColorPlayer) GateTopicName() string {
return p.gateTopicName
}

View File

@ -12,7 +12,7 @@ import (
)
type ColorRoom struct {
*baseroom.BaseRoom[*baseroom.BaseSeat]
*baseroom.HundredRoom
userMgr *baseroom.PlayerMgr
roomCfg *game.ColorRoomConfig
@ -35,7 +35,7 @@ type ColorRoom struct {
func newColorRoom(id, roomType int, srv service.IService, userMgr *baseroom.PlayerMgr) (baseroom.IRoom, pb.ErrCode) {
playType := int(pb.ServiceTypeId_STI_ColorGame)
rm := &ColorRoom{
BaseRoom: nil,
HundredRoom: nil,
userMgr: userMgr,
roomCfg: &game.ColorRoomConfig{},
timingCfg: &game.ColorGameTiming{},
@ -50,7 +50,7 @@ func newColorRoom(id, roomType int, srv service.IService, userMgr *baseroom.Play
rm.resetGameData()
code := pb.ErrCode_OK
rm.BaseRoom, code = baseroom.NewBaseRoom[*baseroom.BaseSeat](id, roomType, playType, srv)
rm.HundredRoom, code = baseroom.NewHundredRoom(id, roomType, playType, srv)
if code != pb.ErrCode_OK {
log.ErrorF("new color room err code:%v", code)
return nil, code
@ -99,11 +99,10 @@ func (rm *ColorRoom) resetGameData() {
rm.initPrizeAreaRange()
// 清理玩家身上数据
for _, st := range rm.Seats {
if st.Player() != nil {
GetPlayer(st.Player()).resetData()
}
}
rm.RangePlayer(func(u baseroom.IPlayer) bool {
GetPlayer(u).resetData()
return true
})
// 游戏开始前清理不下注玩家
rm.kickoutUsers()