From c8f181f63eaad5310d263621f222985baad35fd1 Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Thu, 27 Jul 2023 10:34:06 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20room=20=E5=8C=85=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=9B=B4=E5=A4=9A=E7=9A=84=E4=BA=8B=E4=BB=B6=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=BA=A7=E4=BD=8D=E5=8F=B7=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/room/errors.go | 6 +- game/room/events.go | 177 ++++++++++++++++++++++++++++-- game/room/info.go | 4 +- game/room/manager.go | 150 ++++++++++++++++++++------ game/room/seat.go | 249 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 542 insertions(+), 44 deletions(-) create mode 100644 game/room/seat.go diff --git a/game/room/errors.go b/game/room/errors.go index 2d7abeb..5d849b9 100644 --- a/game/room/errors.go +++ b/game/room/errors.go @@ -7,6 +7,8 @@ var ( ErrRoomNotExist = errors.New("room not exist") // ErrRoomPlayerFull 房间人数已满 ErrRoomPlayerFull = errors.New("room player full") - // ErrPlayerNotExist 玩家不存在 - ErrPlayerNotExist = errors.New("player not exist") + // ErrPlayerNotInRoom 玩家不在房间中 + ErrPlayerNotInRoom = errors.New("player not in room") + // ErrRoomOrPlayerNotExist 房间不存在或玩家不在房间中 + ErrRoomOrPlayerNotExist = errors.New("room or player not exist") ) diff --git a/game/room/events.go b/game/room/events.go index c8355cd..7f3fe33 100644 --- a/game/room/events.go +++ b/game/room/events.go @@ -8,30 +8,65 @@ type ( // PlayerLeaveRoomEventHandle 玩家离开房间事件处理函数 PlayerLeaveRoomEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P) // PlayerKickedOutEventHandle 玩家被踢出房间事件处理函数 - PlayerKickedOutEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, executor, kicked PID, reason string) + PlayerKickedOutEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, executor, kicked P, reason string) + // PlayerUpgradeOwnerEventHandle 玩家成为房主事件处理函数 + PlayerUpgradeOwnerEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, oldOwner, newOwner P) + // CancelOwnerEventHandle 取消房主事件处理函数 + CancelOwnerEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, oldOwner P) + // ChangePlayerLimitEventHandle 改变房间人数上限事件处理函数 + ChangePlayerLimitEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, oldLimit, newLimit int) + // PlayerSeatChangeEventHandle 玩家座位改变事件处理函数 + PlayerSeatChangeEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P, oldSeat, newSeat int) + // PlayerSeatSetEventHandle 玩家座位设置事件处理函数 + PlayerSeatSetEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P, seat int) + // PlayerSeatCancelEventHandle 玩家座位取消事件处理函数 + PlayerSeatCancelEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P, seat int) ) func newEvent[PID comparable, P game.Player[PID], R Room[PID, P]]() *event[PID, P, R] { return &event[PID, P, R]{ - playerJoinRoomEventRoomHandles: make(map[int64][]PlayerJoinRoomEventHandle[PID, P, R]), - playerLeaveRoomEventRoomHandles: make(map[int64][]PlayerLeaveRoomEventHandle[PID, P, R]), - playerKickedOutEventRoomHandles: make(map[int64][]PlayerKickedOutEventHandle[PID, P, R]), + playerJoinRoomEventRoomHandles: make(map[int64][]PlayerJoinRoomEventHandle[PID, P, R]), + playerLeaveRoomEventRoomHandles: make(map[int64][]PlayerLeaveRoomEventHandle[PID, P, R]), + playerKickedOutEventRoomHandles: make(map[int64][]PlayerKickedOutEventHandle[PID, P, R]), + playerUpgradeOwnerEventRoomHandles: make(map[int64][]PlayerUpgradeOwnerEventHandle[PID, P, R]), + cancelOwnerEventRoomHandles: make(map[int64][]CancelOwnerEventHandle[PID, P, R]), + changePlayerLimitEventRoomHandles: make(map[int64][]ChangePlayerLimitEventHandle[PID, P, R]), + playerSeatChangeEventRoomHandles: make(map[int64][]PlayerSeatChangeEventHandle[PID, P, R]), + playerSeatSetEventRoomHandles: make(map[int64][]PlayerSeatSetEventHandle[PID, P, R]), } } type event[PID comparable, P game.Player[PID], R Room[PID, P]] struct { - playerJoinRoomEventHandles []PlayerJoinRoomEventHandle[PID, P, R] - playerJoinRoomEventRoomHandles map[int64][]PlayerJoinRoomEventHandle[PID, P, R] - playerLeaveRoomEventHandles []PlayerLeaveRoomEventHandle[PID, P, R] - playerLeaveRoomEventRoomHandles map[int64][]PlayerLeaveRoomEventHandle[PID, P, R] - playerKickedOutEventHandles []PlayerKickedOutEventHandle[PID, P, R] - playerKickedOutEventRoomHandles map[int64][]PlayerKickedOutEventHandle[PID, P, R] + playerJoinRoomEventHandles []PlayerJoinRoomEventHandle[PID, P, R] + playerJoinRoomEventRoomHandles map[int64][]PlayerJoinRoomEventHandle[PID, P, R] + playerLeaveRoomEventHandles []PlayerLeaveRoomEventHandle[PID, P, R] + playerLeaveRoomEventRoomHandles map[int64][]PlayerLeaveRoomEventHandle[PID, P, R] + playerKickedOutEventHandles []PlayerKickedOutEventHandle[PID, P, R] + playerKickedOutEventRoomHandles map[int64][]PlayerKickedOutEventHandle[PID, P, R] + playerUpgradeOwnerEventHandles []PlayerUpgradeOwnerEventHandle[PID, P, R] + playerUpgradeOwnerEventRoomHandles map[int64][]PlayerUpgradeOwnerEventHandle[PID, P, R] + cancelOwnerEventHandles []CancelOwnerEventHandle[PID, P, R] + cancelOwnerEventRoomHandles map[int64][]CancelOwnerEventHandle[PID, P, R] + changePlayerLimitEventHandles []ChangePlayerLimitEventHandle[PID, P, R] + changePlayerLimitEventRoomHandles map[int64][]ChangePlayerLimitEventHandle[PID, P, R] + playerSeatChangeEventHandles []PlayerSeatChangeEventHandle[PID, P, R] + playerSeatChangeEventRoomHandles map[int64][]PlayerSeatChangeEventHandle[PID, P, R] + playerSeatSetEventHandles []PlayerSeatSetEventHandle[PID, P, R] + playerSeatSetEventRoomHandles map[int64][]PlayerSeatSetEventHandle[PID, P, R] + playerSeatCancelEventHandles []PlayerSeatCancelEventHandle[PID, P, R] + playerSeatCancelEventRoomHandles map[int64][]PlayerSeatCancelEventHandle[PID, P, R] } func (slf *event[PID, P, R]) unReg(guid int64) { delete(slf.playerJoinRoomEventRoomHandles, guid) delete(slf.playerLeaveRoomEventRoomHandles, guid) delete(slf.playerKickedOutEventRoomHandles, guid) + delete(slf.playerUpgradeOwnerEventRoomHandles, guid) + delete(slf.cancelOwnerEventRoomHandles, guid) + delete(slf.changePlayerLimitEventRoomHandles, guid) + delete(slf.playerSeatChangeEventRoomHandles, guid) + delete(slf.playerSeatSetEventRoomHandles, guid) + delete(slf.playerSeatCancelEventRoomHandles, guid) } // RegPlayerJoinRoomEvent 玩家进入房间时将立即执行被注册的事件处理函数 @@ -85,7 +120,7 @@ func (slf *event[PID, P, R]) RegPlayerKickedOutEventWithRoom(room R, handle Play } // OnPlayerKickedOutEvent 玩家被踢出房间时将立即执行被注册的事件处理函数 -func (slf *event[PID, P, R]) OnPlayerKickedOutEvent(room R, executor, kicked PID, reason string) { +func (slf *event[PID, P, R]) OnPlayerKickedOutEvent(room R, executor, kicked P, reason string) { for _, handle := range slf.playerKickedOutEventHandles { handle(room, executor, kicked, reason) } @@ -93,3 +128,123 @@ func (slf *event[PID, P, R]) OnPlayerKickedOutEvent(room R, executor, kicked PID handle(room, executor, kicked, reason) } } + +// RegPlayerUpgradeOwnerEvent 玩家成为房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerUpgradeOwnerEvent(handle PlayerUpgradeOwnerEventHandle[PID, P, R]) { + slf.playerUpgradeOwnerEventHandles = append(slf.playerUpgradeOwnerEventHandles, handle) +} + +// RegPlayerUpgradeOwnerEventWithRoom 玩家成为房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerUpgradeOwnerEventWithRoom(room R, handle PlayerUpgradeOwnerEventHandle[PID, P, R]) { + slf.playerUpgradeOwnerEventRoomHandles[room.GetGuid()] = append(slf.playerUpgradeOwnerEventRoomHandles[room.GetGuid()], handle) +} + +// OnPlayerUpgradeOwnerEvent 玩家成为房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnPlayerUpgradeOwnerEvent(room R, executor, newOwner P) { + for _, handle := range slf.playerUpgradeOwnerEventHandles { + handle(room, executor, newOwner) + } + for _, handle := range slf.playerUpgradeOwnerEventRoomHandles[room.GetGuid()] { + handle(room, executor, newOwner) + } +} + +// RegCancelOwnerEvent 取消房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegCancelOwnerEvent(handle CancelOwnerEventHandle[PID, P, R]) { + slf.cancelOwnerEventHandles = append(slf.cancelOwnerEventHandles, handle) +} + +// RegCancelOwnerEventWithRoom 取消房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegCancelOwnerEventWithRoom(room R, handle CancelOwnerEventHandle[PID, P, R]) { + slf.cancelOwnerEventRoomHandles[room.GetGuid()] = append(slf.cancelOwnerEventRoomHandles[room.GetGuid()], handle) +} + +// OnCancelOwnerEvent 取消房主时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnCancelOwnerEvent(room R, oldOwner P) { + for _, handle := range slf.cancelOwnerEventHandles { + handle(room, oldOwner) + } + for _, handle := range slf.cancelOwnerEventRoomHandles[room.GetGuid()] { + handle(room, oldOwner) + } +} + +// RegChangePlayerLimitEvent 修改玩家上限时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegChangePlayerLimitEvent(handle ChangePlayerLimitEventHandle[PID, P, R]) { + slf.changePlayerLimitEventHandles = append(slf.changePlayerLimitEventHandles, handle) +} + +// RegChangePlayerLimitEventWithRoom 修改玩家上限时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegChangePlayerLimitEventWithRoom(room R, handle ChangePlayerLimitEventHandle[PID, P, R]) { + slf.changePlayerLimitEventRoomHandles[room.GetGuid()] = append(slf.changePlayerLimitEventRoomHandles[room.GetGuid()], handle) +} + +// OnChangePlayerLimitEvent 修改玩家上限时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnChangePlayerLimitEvent(room R, oldLimit, newLimit int) { + for _, handle := range slf.changePlayerLimitEventHandles { + handle(room, oldLimit, newLimit) + } + for _, handle := range slf.changePlayerLimitEventRoomHandles[room.GetGuid()] { + handle(room, oldLimit, newLimit) + } +} + +// RegPlayerSeatChangeEvent 玩家座位改变时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatChangeEvent(handle PlayerSeatChangeEventHandle[PID, P, R]) { + slf.playerSeatChangeEventHandles = append(slf.playerSeatChangeEventHandles, handle) +} + +// RegPlayerSeatChangeEventWithRoom 玩家座位改变时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatChangeEventWithRoom(room R, handle PlayerSeatChangeEventHandle[PID, P, R]) { + slf.playerSeatChangeEventRoomHandles[room.GetGuid()] = append(slf.playerSeatChangeEventRoomHandles[room.GetGuid()], handle) +} + +// OnPlayerSeatChangeEvent 玩家座位改变时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnPlayerSeatChangeEvent(room R, player P, oldSeat, newSeat int) { + for _, handle := range slf.playerSeatChangeEventHandles { + handle(room, player, oldSeat, newSeat) + } + for _, handle := range slf.playerSeatChangeEventRoomHandles[room.GetGuid()] { + handle(room, player, oldSeat, newSeat) + } +} + +// RegPlayerSeatSetEvent 玩家座位设置时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatSetEvent(handle PlayerSeatSetEventHandle[PID, P, R]) { + slf.playerSeatSetEventHandles = append(slf.playerSeatSetEventHandles, handle) +} + +// RegPlayerSeatSetEventWithRoom 玩家座位设置时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatSetEventWithRoom(room R, handle PlayerSeatSetEventHandle[PID, P, R]) { + slf.playerSeatSetEventRoomHandles[room.GetGuid()] = append(slf.playerSeatSetEventRoomHandles[room.GetGuid()], handle) +} + +// OnPlayerSeatSetEvent 玩家座位设置时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnPlayerSeatSetEvent(room R, player P, seat int) { + for _, handle := range slf.playerSeatSetEventHandles { + handle(room, player, seat) + } + for _, handle := range slf.playerSeatSetEventRoomHandles[room.GetGuid()] { + handle(room, player, seat) + } +} + +// RegPlayerSeatCancelEvent 玩家座位取消时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatCancelEvent(handle PlayerSeatCancelEventHandle[PID, P, R]) { + slf.playerSeatCancelEventHandles = append(slf.playerSeatCancelEventHandles, handle) +} + +// RegPlayerSeatCancelEventWithRoom 玩家座位取消时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) RegPlayerSeatCancelEventWithRoom(room R, handle PlayerSeatCancelEventHandle[PID, P, R]) { + slf.playerSeatCancelEventRoomHandles[room.GetGuid()] = append(slf.playerSeatCancelEventRoomHandles[room.GetGuid()], handle) +} + +// OnPlayerSeatCancelEvent 玩家座位取消时将立即执行被注册的事件处理函数 +func (slf *event[PID, P, R]) OnPlayerSeatCancelEvent(room R, player P, seat int) { + for _, handle := range slf.playerSeatCancelEventHandles { + handle(room, player, seat) + } + for _, handle := range slf.playerSeatCancelEventRoomHandles[room.GetGuid()] { + handle(room, player, seat) + } +} diff --git a/game/room/info.go b/game/room/info.go index 35fedd9..a546fac 100644 --- a/game/room/info.go +++ b/game/room/info.go @@ -4,5 +4,7 @@ import "github.com/kercylan98/minotaur/game" type info[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]] struct { room R - playerLimit int // 玩家人数上限, <= 0 表示无限制 + playerLimit int // 玩家人数上限, <= 0 表示无限制 + owner *PlayerID // 房主 + seat *Seat[PlayerID, P, R] } diff --git a/game/room/manager.go b/game/room/manager.go index f343c75..8ad06da 100644 --- a/game/room/manager.go +++ b/game/room/manager.go @@ -29,7 +29,10 @@ type Manager[PID comparable, P game.Player[PID], R Room[PID, P]] struct { // CreateRoom 创建房间 func (slf *Manager[PID, P, R]) CreateRoom(room R) { - roomInfo := &info[PID, P, R]{room: room} + roomInfo := &info[PID, P, R]{ + room: room, + seat: newSeat[PID, P, R](slf, room, slf.event), + } slf.rooms.Set(room.GetGuid(), roomInfo) } @@ -39,6 +42,73 @@ func (slf *Manager[PID, P, R]) ReleaseRoom(guid int64) { slf.rooms.Delete(guid) } +// SetPlayerLimit 设置房间人数上限 +func (slf *Manager[PID, P, R]) SetPlayerLimit(roomId int64, limit int) { + if limit <= 0 { + return + } + var room R + var oldLimit int + slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { + info, ok := m[roomId] + if !ok { + return + } + oldLimit = info.playerLimit + info.playerLimit = limit + }) + if oldLimit == limit { + return + } + slf.OnChangePlayerLimitEvent(room, oldLimit, limit) +} + +// CancelOwner 取消房主 +func (slf *Manager[PID, P, R]) CancelOwner(roomId int64) { + var room R + var oldOwner P + slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { + info, ok := m[roomId] + if !ok { + return + } + room = info.room + if info.owner != nil { + oldOwner = slf.GetRoomPlayer(roomId, *info.owner) + info.owner = nil + } + }) + if oldOwner != nil { + slf.OnCancelOwnerEvent(room, oldOwner) + } +} + +// SetOwner 设置房主 +func (slf *Manager[PID, P, R]) SetOwner(roomId int64, owner PID) error { + var err error + var oldOwner, newOwner P + var room R + slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { + info, ok := m[roomId] + if !ok { + err = ErrRoomNotExist + return + } + room = info.room + if info.owner != nil { + oldOwner = slf.GetRoomPlayer(roomId, *info.owner) + } + newOwner = slf.GetRoomPlayer(roomId, owner) + if newOwner == nil { + err = ErrRoomOrPlayerNotExist + return + } + info.owner = &owner + }) + slf.OnPlayerUpgradeOwnerEvent(room, oldOwner, newOwner) + return err +} + // GetRoom 获取房间 func (slf *Manager[PID, P, R]) GetRoom(guid int64) R { return slf.rooms.Get(guid).room @@ -170,32 +240,39 @@ func (slf *Manager[PID, P, R]) GetRoomPlayerLimit(guid int64) int { // Leave 使玩家离开房间 func (slf *Manager[PID, P, R]) Leave(roomId int64, player P) { + var roomInfo *info[PID, P, R] slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { room, exist := m[roomId] if !exist { return } - slf.OnPlayerLeaveRoomEvent(room.room, player) - slf.pr.Atom(func(m map[PID]map[int64]struct{}) { - rooms, exist := m[player.GetID()] - if !exist { - return - } - delete(rooms, roomId) - }) - slf.rp.Atom(func(m map[int64]map[PID]struct{}) { - players, exist := m[roomId] - if !exist { - return - } - delete(players, player.GetID()) - }) + roomInfo = room + }) + if roomInfo == nil { + return + } + slf.OnPlayerLeaveRoomEvent(roomInfo.room, player) + roomInfo.seat.removePlayerSeat(player.GetID()) + slf.pr.Atom(func(m map[PID]map[int64]struct{}) { + rooms, exist := m[player.GetID()] + if !exist { + return + } + delete(rooms, roomId) + }) + slf.rp.Atom(func(m map[int64]map[PID]struct{}) { + players, exist := m[roomId] + if !exist { + return + } + delete(players, player.GetID()) }) } // Join 使玩家加入房间 func (slf *Manager[PID, P, R]) Join(player P, roomId int64) error { var err error + var roomInfo *info[PID, P, R] slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { room, exist := m[roomId] if !exist { @@ -223,8 +300,10 @@ func (slf *Manager[PID, P, R]) Join(player P, roomId int64) error { players[player.GetID()] = struct{}{} }) slf.players.Set(player.GetID(), player) - slf.OnPlayerJoinRoomEvent(room.room, player) + roomInfo = room }) + roomInfo.seat.addSeat(player.GetID()) + slf.OnPlayerJoinRoomEvent(roomInfo.room, player) return err } @@ -233,27 +312,38 @@ func (slf *Manager[PID, P, R]) Join(player P, roomId int64) error { func (slf *Manager[PID, P, R]) KickOut(roomId int64, executor, kicked PID, reason string) error { var err error var room R + var executorPlayer, kickedPlayer P slf.rp.Atom(func(m map[int64]map[PID]struct{}) { - players, exist := m[roomId] - if !exist { - err = ErrPlayerNotExist - return - } - _, exist = players[executor] - if !exist { - err = ErrPlayerNotExist - return - } - _, exist = players[kicked] - if !exist { + executorPlayer, kickedPlayer = slf.GetRoomPlayer(roomId, executor), slf.GetRoomPlayer(roomId, kicked) + if executorPlayer == nil || kickedPlayer == nil { + err = ErrRoomOrPlayerNotExist return } room = slf.rooms.Get(roomId).room + if room == nil { + err = ErrRoomNotExist + return + } }) if err == nil { return err } - slf.OnPlayerKickedOutEvent(room, executor, kicked, reason) + slf.OnPlayerKickedOutEvent(room, executorPlayer, kickedPlayer, reason) slf.Leave(roomId, slf.players.Get(kicked)) return nil } + +// GetSeatInfo 获取座位信息 +func (slf *Manager[PID, P, R]) GetSeatInfo(roomId int64) (*Seat[PID, P, R], error) { + var result *Seat[PID, P, R] + var err error + slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) { + room, exist := m[roomId] + if !exist { + err = ErrRoomNotExist + return + } + result = room.seat + }) + return result, err +} diff --git a/game/room/seat.go b/game/room/seat.go new file mode 100644 index 0000000..e96197b --- /dev/null +++ b/game/room/seat.go @@ -0,0 +1,249 @@ +package room + +import ( + "github.com/kercylan98/minotaur/game" + "github.com/kercylan98/minotaur/utils/concurrent" + "github.com/kercylan98/minotaur/utils/hash" + "sync" +) + +func newSeat[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]](manager *Manager[PlayerID, P, R], room R, event *event[PlayerID, P, R]) *Seat[PlayerID, P, R] { + roomSeat := &Seat[PlayerID, P, R]{ + manager: manager, + room: room, + event: event, + seatPS: concurrent.NewBalanceMap[PlayerID, int](), + } + return roomSeat +} + +// Seat 房间座位信息 +type Seat[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]] struct { + manager *Manager[PlayerID, P, R] + room R + event *event[PlayerID, P, R] + mutex sync.RWMutex + vacancy []int + seatPS *concurrent.BalanceMap[PlayerID, int] + seatSP []*PlayerID + duplicateLock bool + autoMode sync.Once +} + +// addSeat 为特定玩家添加座位 +// - 当座位存在空缺的时候,玩家将会优先在空缺位置坐下,否则将会在末位追加 +func (slf *Seat[PlayerID, P, R]) addSeat(id PlayerID) { + if slf.seatPS.Exist(id) { + return + } + var seat int + slf.mutex.Lock() + if len(slf.vacancy) > 0 { + seat = slf.vacancy[0] + slf.vacancy = slf.vacancy[1:] + slf.seatPS.Set(id, seat) + slf.seatSP[seat] = &id + } else { + seat = len(slf.seatSP) + slf.seatPS.Set(id, seat) + slf.seatSP = append(slf.seatSP, &id) + } + slf.mutex.Unlock() + slf.event.OnPlayerSeatSetEvent(slf.room, slf.manager.GetPlayer(id), seat) +} + +// removePlayerSeat 删除玩家座位 +func (slf *Seat[PlayerID, P, R]) removePlayerSeat(id PlayerID) { + if !slf.seatPS.Exist(id) { + return + } + slf.event.OnPlayerSeatCancelEvent(slf.room, slf.manager.GetPlayer(id), slf.seatPS.Get(id)) + slf.mutex.Lock() + defer slf.mutex.Unlock() + seat := slf.seatPS.DeleteGet(id) + slf.seatSP[seat] = nil +} + +// SetSeat 设置玩家的座位号 +// - 如果玩家没有预先添加过座位将会返回错误 +// - 如果位置已经有玩家,将会与其进行更换 +func (slf *Seat[PlayerID, P, R]) SetSeat(id PlayerID, seat int) error { + oldSeat, err := slf.setSeat(id, seat) + if err != nil { + return err + } + slf.event.OnPlayerSeatChangeEvent(slf.room, slf.manager.GetPlayer(id), oldSeat, seat) + return nil +} + +func (slf *Seat[PlayerID, P, R]) setSeat(id PlayerID, seat int) (int, error) { + slf.mutex.Lock() + slf.duplicateLock = true + defer func() { + slf.mutex.Unlock() + slf.duplicateLock = false + }() + oldSeat, err := slf.GetSeat(id) + if err != nil { + return oldSeat, err + } + playerId, err := slf.GetPlayerIDWithSeat(seat) + if err != nil { + ov := slf.seatSP[oldSeat] + slf.seatSP[oldSeat] = slf.seatSP[seat] + slf.seatSP[seat] = ov + slf.seatPS.Set(id, seat) + slf.seatPS.Set(playerId, oldSeat) + } else { + maxSeat := len(slf.seatSP) - 1 + if seat > maxSeat { + count := seat - maxSeat + slf.seatSP = append(slf.seatSP, make([]*PlayerID, count)...) + } + slf.seatSP[seat] = slf.seatSP[oldSeat] + slf.seatSP[oldSeat] = nil + slf.seatPS.Set(id, seat) + } + return oldSeat, nil +} + +// GetSeat 获取玩家座位号 +func (slf *Seat[PlayerID, P, R]) GetSeat(id PlayerID) (int, error) { + seat, exist := slf.seatPS.GetExist(id) + if !exist { + return 0, ErrPlayerNotInRoom + } + return seat, nil +} + +// GetPlayerIDWithSeat 获取特定座位号的玩家 +func (slf *Seat[PlayerID, P, R]) GetPlayerIDWithSeat(seat int) (playerId PlayerID, err error) { + if !slf.duplicateLock { + slf.mutex.RLock() + defer slf.mutex.RUnlock() + } + if seat > len(slf.seatSP)-1 { + return playerId, ErrPlayerNotInRoom + } + id := slf.seatSP[seat] + if id == nil { + return playerId, ErrPlayerNotInRoom + } + return *id, nil +} + +// GetSeatInfo 获取所有座位号 +// - 在非补位模式(WithRoomSeatFillIn)下由于座位号可能存在缺席的情况,所以需要根据是否为空指针进行判断 +func (slf *Seat[PlayerID, P, R]) GetSeatInfo() []*PlayerID { + slf.mutex.RLock() + defer slf.mutex.RUnlock() + return slf.seatSP +} + +// GetSeatInfoMap 获取座位号及其对应的玩家信息 +// - 缺席情况将被忽略 +func (slf *Seat[PlayerID, P, R]) GetSeatInfoMap() map[int]PlayerID { + var seatInfo = make(map[int]PlayerID) + slf.mutex.RLock() + defer slf.mutex.RUnlock() + for seat, playerId := range slf.seatSP { + if playerId == nil { + continue + } + seatInfo[seat] = *playerId + } + return seatInfo +} + +// GetSeatInfoMapVacancy 获取座位号及其对应的玩家信息 +// - 缺席情况将不会被忽略 +func (slf *Seat[PlayerID, P, R]) GetSeatInfoMapVacancy() map[int]*PlayerID { + slf.mutex.RLock() + defer slf.mutex.RUnlock() + return hash.ToMap(slf.seatSP) +} + +// GetSeatInfoWithPlayerIDMap 获取玩家及其座位号信息 +func (slf *Seat[PlayerID, P, R]) GetSeatInfoWithPlayerIDMap() map[PlayerID]int { + return slf.seatPS.Map() +} + +// GetFirstSeat 获取第一个未缺席的座位号 +func (slf *Seat[PlayerID, P, R]) GetFirstSeat() int { + for seat, playerId := range slf.seatSP { + if playerId != nil { + return seat + } + } + return -1 +} + +// GetNextSeat 获取特定座位号下一个未缺席的座位号 +func (slf *Seat[PlayerID, P, R]) GetNextSeat(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + var target = seat + for { + target++ + if target >= l { + target = 0 + } + if target == seat { + return seat + } + if slf.seatSP[target] != nil { + return target + } + } +} + +// GetNextSeatVacancy 获取特定座位号下一个座位号 +// - 缺席将不会被忽略 +func (slf *Seat[PlayerID, P, R]) GetNextSeatVacancy(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + seat++ + if seat >= l { + seat = 0 + } + return seat +} + +// GetPrevSeat 获取特定座位号上一个未缺席的座位号 +func (slf *Seat[PlayerID, P, R]) GetPrevSeat(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + var target = seat + for { + target-- + if target < 0 { + target = l - 1 + } + if target == seat { + return seat + } + if slf.seatSP[target] != nil { + return target + } + } +} + +// GetPrevSeatVacancy 获取特定座位号上一个座位号 +// - 缺席将不会被忽略 +func (slf *Seat[PlayerID, P, R]) GetPrevSeatVacancy(seat int) int { + l := len(slf.seatSP) + if l == 0 || seat >= l || seat < 0 { + return -1 + } + seat-- + if seat < 0 { + seat = l - 1 + } + return seat +}