refactor: 移除原有的 builtin 中的各类 room 实现
This commit is contained in:
parent
d06c840c46
commit
ee18934768
|
@ -4,8 +4,6 @@ package game
|
|||
// - 需要注意 Actor 不等于 Player
|
||||
// - 在 Minotaur 中,每个网络连接可以表示一个 Player,而每个玩家可以拥有多个 Actor
|
||||
// - Actor 并非 Player 独有,场景中也可包含各类无主的 Actor
|
||||
// - 内置实现:builtin.Actor
|
||||
// - 构建函数:builtin.NewActor
|
||||
type Actor interface {
|
||||
// SetGuid 设置对象的唯一标识符
|
||||
// - 需要注意的是该函数不应该主动执行,否则可能产生意想不到的情况
|
||||
|
|
|
@ -1,166 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/log"
|
||||
)
|
||||
|
||||
// NewRoom 创建一个默认的内置游戏房间 Room
|
||||
func NewRoom[PlayerID comparable, Player game.Player[PlayerID]](guid int64, options ...RoomOption[PlayerID, Player]) *Room[PlayerID, Player] {
|
||||
room := &Room[PlayerID, Player]{
|
||||
guid: guid,
|
||||
players: concurrent.NewBalanceMap[PlayerID, Player](),
|
||||
}
|
||||
for _, option := range options {
|
||||
option(room)
|
||||
}
|
||||
return room
|
||||
}
|
||||
|
||||
// Room 默认的内置游戏房间实现
|
||||
// - 实现了最大人数控制、房主、踢出玩家、玩家维护等功能
|
||||
// - 支持并发安全和非并发安全的模式
|
||||
type Room[PlayerID comparable, Player game.Player[PlayerID]] struct {
|
||||
guid int64
|
||||
owner PlayerID
|
||||
noMaster bool
|
||||
playerLimit int
|
||||
players *concurrent.BalanceMap[PlayerID, Player]
|
||||
kickCheckHandle func(room *Room[PlayerID, Player], id, target PlayerID) error
|
||||
|
||||
playerJoinRoomEventHandles []game.PlayerJoinRoomEventHandle[PlayerID, Player]
|
||||
playerLeaveRoomEventHandles []game.PlayerLeaveRoomEventHandle[PlayerID, Player]
|
||||
playerKickedOutEventHandles []game.PlayerKickedOutEventHandle[PlayerID, Player]
|
||||
}
|
||||
|
||||
// GetGuid 获取房间唯一标识
|
||||
func (slf *Room[PlayerID, Player]) GetGuid() int64 {
|
||||
return slf.guid
|
||||
}
|
||||
|
||||
// GetPlayerLimit 获取最大玩家上限
|
||||
func (slf *Room[PlayerID, Player]) GetPlayerLimit() int {
|
||||
return slf.playerLimit
|
||||
}
|
||||
|
||||
// GetPlayer 根据玩家id获取玩家
|
||||
func (slf *Room[PlayerID, Player]) GetPlayer(id PlayerID) Player {
|
||||
return slf.players.Get(id)
|
||||
}
|
||||
|
||||
// GetPlayers 获取所有玩家
|
||||
func (slf *Room[PlayerID, Player]) GetPlayers() map[PlayerID]Player {
|
||||
return slf.players.Map()
|
||||
}
|
||||
|
||||
// GetPlayerCount 获取玩家数量
|
||||
func (slf *Room[PlayerID, Player]) GetPlayerCount() int {
|
||||
return slf.players.Size()
|
||||
}
|
||||
|
||||
// IsExistPlayer 房间内是否存在某玩家
|
||||
func (slf *Room[PlayerID, Player]) IsExistPlayer(id PlayerID) bool {
|
||||
return slf.players.Exist(id)
|
||||
}
|
||||
|
||||
// IsOwner 检查特定玩家是否是房主
|
||||
// - 当房间为无主模式(WithRoomNoMaster)时,将会始终返回false
|
||||
func (slf *Room[PlayerID, Player]) IsOwner(id PlayerID) bool {
|
||||
return !slf.noMaster && slf.owner == id
|
||||
}
|
||||
|
||||
// ChangeOwner 改变房主
|
||||
// - 当房间为无主模式(WithRoomNoMaster)时,将不会发生任何变化
|
||||
func (slf *Room[PlayerID, Player]) ChangeOwner(id PlayerID) {
|
||||
if slf.noMaster || slf.owner == id {
|
||||
return
|
||||
}
|
||||
slf.owner = id
|
||||
}
|
||||
|
||||
// Join 控制玩家加入到该房间
|
||||
func (slf *Room[PlayerID, Player]) Join(player Player) error {
|
||||
playerId := player.GetID()
|
||||
exist := slf.players.Exist(playerId)
|
||||
if !exist && slf.players.Size() >= slf.playerLimit && slf.playerLimit > 0 {
|
||||
return ErrRoomPlayerLimit
|
||||
}
|
||||
slf.players.Set(playerId, player)
|
||||
if !exist {
|
||||
log.Debug("Room.Join", log.Any("guid", slf.GetGuid()), log.Any("player", playerId))
|
||||
if slf.players.Size() == 1 && !slf.noMaster {
|
||||
slf.owner = playerId
|
||||
}
|
||||
slf.OnPlayerJoinRoomEvent(player)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Leave 控制玩家离开房间
|
||||
func (slf *Room[PlayerID, Player]) Leave(id PlayerID) {
|
||||
player, exist := slf.players.GetExist(id)
|
||||
if !exist {
|
||||
return
|
||||
}
|
||||
log.Debug("Room.Leave", log.Any("guid", slf.GetGuid()), log.Any("player", id))
|
||||
slf.OnPlayerLeaveRoomEvent(player)
|
||||
slf.players.Delete(id)
|
||||
}
|
||||
|
||||
// KickOut 以某种原因踢出特定玩家
|
||||
// - 当设置了房间踢出玩家的检查处理函数(WithRoomKickPlayerCheckHandle)时,将会根据检查结果进行处理,即便是无主模式。其他情况如下
|
||||
// - 如果是无主模式(WithRoomNoMaster),将会返回错误
|
||||
// - 如果不是房主发起的踢出玩家,将会返回错误
|
||||
func (slf *Room[PlayerID, Player]) KickOut(id, target PlayerID, reason string) error {
|
||||
player, exist := slf.players.GetExist(target)
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
if slf.kickCheckHandle != nil {
|
||||
if err := slf.kickCheckHandle(slf, id, target); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if slf.noMaster {
|
||||
return ErrRoomNoHasMaster
|
||||
} else if slf.owner != id {
|
||||
return ErrRoomNotIsOwner
|
||||
}
|
||||
|
||||
slf.OnPlayerKickedOutEvent(id, target, reason)
|
||||
slf.Leave(player.GetID())
|
||||
return nil
|
||||
}
|
||||
|
||||
// RegPlayerJoinRoomEvent 玩家进入房间时将立即执行被注册的事件处理函数
|
||||
func (slf *Room[PlayerID, Player]) RegPlayerJoinRoomEvent(handle game.PlayerJoinRoomEventHandle[PlayerID, Player]) {
|
||||
slf.playerJoinRoomEventHandles = append(slf.playerJoinRoomEventHandles, handle)
|
||||
}
|
||||
|
||||
func (slf *Room[PlayerID, Player]) OnPlayerJoinRoomEvent(player Player) {
|
||||
for _, handle := range slf.playerJoinRoomEventHandles {
|
||||
handle(slf, player)
|
||||
}
|
||||
}
|
||||
|
||||
// RegPlayerLeaveRoomEvent 玩家离开房间时将立即执行被注册的事件处理函数
|
||||
func (slf *Room[PlayerID, Player]) RegPlayerLeaveRoomEvent(handle game.PlayerLeaveRoomEventHandle[PlayerID, Player]) {
|
||||
slf.playerLeaveRoomEventHandles = append(slf.playerLeaveRoomEventHandles, handle)
|
||||
}
|
||||
|
||||
func (slf *Room[PlayerID, Player]) OnPlayerLeaveRoomEvent(player Player) {
|
||||
for _, handle := range slf.playerLeaveRoomEventHandles {
|
||||
handle(slf, player)
|
||||
}
|
||||
}
|
||||
|
||||
// RegPlayerKickedOutEvent 当玩家被踢出游戏时将立即执行被注册的事件处理函数
|
||||
func (slf *Room[PlayerID, Player]) RegPlayerKickedOutEvent(handle game.PlayerKickedOutEventHandle[PlayerID, Player]) {
|
||||
slf.playerKickedOutEventHandles = append(slf.playerKickedOutEventHandles, handle)
|
||||
}
|
||||
|
||||
func (slf *Room[PlayerID, Player]) OnPlayerKickedOutEvent(executor, kicked PlayerID, reason string) {
|
||||
for _, handle := range slf.playerKickedOutEventHandles {
|
||||
handle(slf, executor, kicked, reason)
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrRoomPlayerLimit = errors.New("the number of players in the room has reached the upper limit") // 玩家数量达到上限
|
||||
ErrRoomNoHasMaster = errors.New("room not has master, can't kick player")
|
||||
ErrRoomNotIsOwner = errors.New("not is room owner, can't kick player")
|
||||
)
|
|
@ -1,34 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func NewRoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]]() *RoomManager[PlayerID, Room] {
|
||||
return &RoomManager[PlayerID, Room]{
|
||||
rooms: concurrent.NewBalanceMap[int64, Room](),
|
||||
}
|
||||
}
|
||||
|
||||
// RoomManager 房间管理器
|
||||
type RoomManager[PlayerID comparable, Room game.Room[PlayerID, game.Player[PlayerID]]] struct {
|
||||
guid atomic.Int64
|
||||
rooms *concurrent.BalanceMap[int64, Room]
|
||||
}
|
||||
|
||||
// GenGuid 生成一个新的房间guid
|
||||
func (slf *RoomManager[PlayerID, Room]) GenGuid() int64 {
|
||||
return slf.guid.Add(1)
|
||||
}
|
||||
|
||||
// AddRoom 添加房间到房间管理器中
|
||||
func (slf *RoomManager[PlayerID, Room]) AddRoom(room Room) {
|
||||
slf.rooms.Set(room.GetGuid(), room)
|
||||
}
|
||||
|
||||
// CloseRoom 关闭特定guid的房间
|
||||
func (slf *RoomManager[PlayerID, Room]) CloseRoom(guid int64) {
|
||||
slf.rooms.Delete(guid)
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
)
|
||||
|
||||
// RoomOption 房间构建可选项
|
||||
type RoomOption[PlayerID comparable, Player game.Player[PlayerID]] func(room *Room[PlayerID, Player])
|
||||
|
||||
// WithRoomSync 通过线程安全的方式创建房间
|
||||
func WithRoomSync[PlayerID comparable, Player game.Player[PlayerID]]() RoomOption[PlayerID, Player] {
|
||||
return func(room *Room[PlayerID, Player]) {
|
||||
room.players = concurrent.NewBalanceMap[PlayerID, Player]()
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoomPlayerLimit 限制房间的玩家数量上限
|
||||
func WithRoomPlayerLimit[PlayerID comparable, Player game.Player[PlayerID]](playerLimit int) RoomOption[PlayerID, Player] {
|
||||
return func(room *Room[PlayerID, Player]) {
|
||||
room.playerLimit = playerLimit
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoomNoMaster 设置房间为无主的
|
||||
func WithRoomNoMaster[PlayerID comparable, Player game.Player[PlayerID]]() RoomOption[PlayerID, Player] {
|
||||
return func(room *Room[PlayerID, Player]) {
|
||||
room.noMaster = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoomKickPlayerCheckHandle 设置房间踢出玩家的检查处理函数
|
||||
// - 当没有设置该函数时,如果不是房主,将无法进行踢出
|
||||
func WithRoomKickPlayerCheckHandle[PlayerID comparable, Player game.Player[PlayerID]](handle func(room *Room[PlayerID, Player], id, target PlayerID) error) RoomOption[PlayerID, Player] {
|
||||
return func(room *Room[PlayerID, Player]) {
|
||||
room.kickCheckHandle = handle
|
||||
}
|
||||
}
|
|
@ -1,284 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/hash"
|
||||
"github.com/kercylan98/minotaur/utils/slice"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// NewRoomSeat 基于特定游戏房间(game.Room)的实现创建一个支持座位号管理的房间实现(RoomSeat)
|
||||
func NewRoomSeat[PlayerID comparable, Player game.Player[PlayerID]](room game.Room[PlayerID, Player], options ...RoomSeatOption[PlayerID, Player]) *RoomSeat[PlayerID, Player] {
|
||||
roomSeat := &RoomSeat[PlayerID, Player]{
|
||||
Room: room,
|
||||
seatPS: concurrent.NewBalanceMap[PlayerID, int](),
|
||||
}
|
||||
for _, option := range options {
|
||||
option(roomSeat)
|
||||
}
|
||||
return roomSeat
|
||||
}
|
||||
|
||||
// RoomSeat 包含座位号的默认内置房间实现,依赖于游戏房间(game.Room)实现
|
||||
// - 实现了对玩家座位号的管理,分别为自动管理(WithRoomSeatAutoManage)及手工管理,默认清空下为手工管理
|
||||
type RoomSeat[PlayerID comparable, Player game.Player[PlayerID]] struct {
|
||||
game.Room[PlayerID, Player]
|
||||
mutex sync.RWMutex
|
||||
vacancy []int
|
||||
seatPS *concurrent.BalanceMap[PlayerID, int]
|
||||
seatSP []*PlayerID
|
||||
duplicateLock bool
|
||||
fillIn bool
|
||||
autoMode sync.Once
|
||||
}
|
||||
|
||||
// AddSeat 为特定玩家添加座位
|
||||
// - 当座位存在空缺的时候,玩家将会优先在空缺位置坐下,否则将会在末位追加
|
||||
func (slf *RoomSeat[PlayerID, Player]) AddSeat(id PlayerID) {
|
||||
if slf.seatPS.Exist(id) {
|
||||
return
|
||||
}
|
||||
slf.mutex.Lock()
|
||||
defer slf.mutex.Unlock()
|
||||
if len(slf.vacancy) > 0 {
|
||||
seat := slf.vacancy[0]
|
||||
slf.vacancy = slf.vacancy[1:]
|
||||
slf.seatPS.Set(id, seat)
|
||||
slf.seatSP[seat] = &id
|
||||
} else {
|
||||
slf.seatPS.Set(id, len(slf.seatSP))
|
||||
slf.seatSP = append(slf.seatSP, &id)
|
||||
}
|
||||
}
|
||||
|
||||
// AddSeatWithAssign 将玩家添加到特定的座位
|
||||
// - 如果位置已经有玩家,将会与其进行更换
|
||||
func (slf *RoomSeat[PlayerID, Player]) AddSeatWithAssign(id PlayerID, seat int) {
|
||||
slf.AddSeat(id)
|
||||
_ = slf.SetSeat(id, seat)
|
||||
}
|
||||
|
||||
// RemovePlayerSeat 删除玩家座位
|
||||
// - 受补位模式(WithRoomSeatFillIn)影响
|
||||
func (slf *RoomSeat[PlayerID, Player]) RemovePlayerSeat(id PlayerID) {
|
||||
if !slf.seatPS.Exist(id) {
|
||||
return
|
||||
}
|
||||
slf.mutex.Lock()
|
||||
defer slf.mutex.Unlock()
|
||||
seat := slf.seatPS.DeleteGet(id)
|
||||
if slf.fillIn {
|
||||
slice.Del(&slf.seatSP, seat)
|
||||
for i := seat; i < len(slf.seatSP); i++ {
|
||||
slf.seatPS.Set(*slf.seatSP[i], i)
|
||||
}
|
||||
return
|
||||
}
|
||||
slf.seatSP[seat] = nil
|
||||
}
|
||||
|
||||
// RemoveSeat 删除特定座位的玩家
|
||||
// - 受补位模式(WithRoomSeatFillIn)影响
|
||||
func (slf *RoomSeat[PlayerID, Player]) RemoveSeat(seat int) {
|
||||
if seat >= len(slf.seatSP) {
|
||||
return
|
||||
}
|
||||
playerId := slf.seatSP[seat]
|
||||
if playerId == nil {
|
||||
return
|
||||
}
|
||||
slf.RemovePlayerSeat(*playerId)
|
||||
}
|
||||
|
||||
// SetSeat 设置玩家的座位号
|
||||
// - 如果玩家没有预先添加过座位将会返回错误
|
||||
// - 如果位置已经有玩家,将会与其进行更换
|
||||
func (slf *RoomSeat[PlayerID, Player]) SetSeat(id PlayerID, seat 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 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 {
|
||||
if slf.fillIn {
|
||||
seat = maxSeat + 1
|
||||
defer func() {
|
||||
slice.Del(&slf.seatSP, oldSeat)
|
||||
for i := oldSeat; i < len(slf.seatSP); i++ {
|
||||
slf.seatPS.Set(*slf.seatSP[i], i)
|
||||
}
|
||||
}()
|
||||
}
|
||||
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 nil
|
||||
}
|
||||
|
||||
// GetSeat 获取玩家座位号
|
||||
func (slf *RoomSeat[PlayerID, Player]) GetSeat(id PlayerID) (int, error) {
|
||||
seat, exist := slf.seatPS.GetExist(id)
|
||||
if !exist {
|
||||
return 0, ErrRoomNotHasPlayer
|
||||
}
|
||||
return seat, nil
|
||||
}
|
||||
|
||||
// GetPlayerIDWithSeat 获取特定座位号的玩家
|
||||
func (slf *RoomSeat[PlayerID, Player]) 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, ErrRoomNotHasPlayer
|
||||
}
|
||||
id := slf.seatSP[seat]
|
||||
if id == nil {
|
||||
return playerId, ErrRoomNotHasPlayer
|
||||
}
|
||||
return *id, nil
|
||||
}
|
||||
|
||||
// GetSeatInfo 获取所有座位号
|
||||
// - 在非补位模式(WithRoomSeatFillIn)下由于座位号可能存在缺席的情况,所以需要根据是否为空指针进行判断
|
||||
func (slf *RoomSeat[PlayerID, Player]) GetSeatInfo() []*PlayerID {
|
||||
slf.mutex.RLock()
|
||||
defer slf.mutex.RUnlock()
|
||||
return slf.seatSP
|
||||
}
|
||||
|
||||
// GetSeatInfoMap 获取座位号及其对应的玩家信息
|
||||
// - 缺席情况将被忽略
|
||||
func (slf *RoomSeat[PlayerID, Player]) 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 *RoomSeat[PlayerID, Player]) GetSeatInfoMapVacancy() map[int]*PlayerID {
|
||||
slf.mutex.RLock()
|
||||
defer slf.mutex.RUnlock()
|
||||
return hash.ToMap(slf.seatSP)
|
||||
}
|
||||
|
||||
// GetSeatInfoWithPlayerIDMap 获取玩家及其座位号信息
|
||||
func (slf *RoomSeat[PlayerID, Player]) GetSeatInfoWithPlayerIDMap() map[PlayerID]int {
|
||||
return slf.seatPS.Map()
|
||||
}
|
||||
|
||||
// GetFirstSeat 获取第一个未缺席的座位号
|
||||
func (slf *RoomSeat[PlayerID, Player]) GetFirstSeat() int {
|
||||
for seat, playerId := range slf.seatSP {
|
||||
if playerId != nil {
|
||||
return seat
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// GetNextSeat 获取特定座位号下一个未缺席的座位号
|
||||
func (slf *RoomSeat[PlayerID, Player]) 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 *RoomSeat[PlayerID, Player]) 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 *RoomSeat[PlayerID, Player]) 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 *RoomSeat[PlayerID, Player]) 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
|
||||
}
|
||||
|
||||
func (slf *RoomSeat[PlayerID, Player]) onJoinRoom(room game.Room[PlayerID, Player], player Player) {
|
||||
slf.AddSeat(player.GetID())
|
||||
}
|
||||
|
||||
func (slf *RoomSeat[PlayerID, Player]) onLeaveRoom(room game.Room[PlayerID, Player], player Player) {
|
||||
slf.RemovePlayerSeat(player.GetID())
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrRoomNotHasPlayer = errors.New("player not exist")
|
||||
)
|
|
@ -1,26 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import "github.com/kercylan98/minotaur/game"
|
||||
|
||||
type RoomSeatOption[PlayerID comparable, Player game.Player[PlayerID]] func(seat *RoomSeat[PlayerID, Player])
|
||||
|
||||
// WithRoomSeatAutoManage 通过自动管理的方式创建带有座位号的房间
|
||||
// - 默认情况下需要自行维护房间用户的座位号信息
|
||||
// - 自动管理模式下,将注册房间的 Room.RegPlayerJoinRoomEvent 和 Room.RegPlayerLeaveRoomEvent 事件以便玩家在加入或者离开时维护座位信息
|
||||
func WithRoomSeatAutoManage[PlayerID comparable, Player game.Player[PlayerID]]() RoomSeatOption[PlayerID, Player] {
|
||||
return func(seatRoom *RoomSeat[PlayerID, Player]) {
|
||||
seatRoom.autoMode.Do(func() {
|
||||
seatRoom.RegPlayerJoinRoomEvent(seatRoom.onJoinRoom)
|
||||
seatRoom.RegPlayerLeaveRoomEvent(seatRoom.onLeaveRoom)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// WithRoomSeatFillIn 通过补位的方式创建带有座位号的房间
|
||||
// - 默认情况下玩家离开座位不会影响其他玩家
|
||||
// - 补位情况下,靠前的玩家离开座位将有后方玩家向前补位
|
||||
func WithRoomSeatFillIn[PlayerID comparable, Player game.Player[PlayerID]]() RoomSeatOption[PlayerID, Player] {
|
||||
return func(seatRoom *RoomSeat[PlayerID, Player]) {
|
||||
seatRoom.fillIn = true
|
||||
}
|
||||
}
|
44
game/room.go
44
game/room.go
|
@ -1,44 +0,0 @@
|
|||
package game
|
||||
|
||||
// Room 房间类似于简版的游戏世界(World),不过没有游戏实体
|
||||
type Room[PlayerID comparable, P Player[PlayerID]] interface {
|
||||
// GetGuid 获取房间的唯一标识符
|
||||
GetGuid() int64
|
||||
// GetPlayerLimit 获取玩家人数上限
|
||||
GetPlayerLimit() int
|
||||
// GetPlayer 根据玩家id获取玩家
|
||||
GetPlayer(id PlayerID) P
|
||||
// GetPlayers 获取房间中的所有玩家
|
||||
GetPlayers() map[PlayerID]P
|
||||
// GetPlayerCount 获取玩家数量
|
||||
GetPlayerCount() int
|
||||
// IsExistPlayer 检查房间中是否存在特定玩家
|
||||
IsExistPlayer(id PlayerID) bool
|
||||
// IsOwner 检查玩家是否是房主
|
||||
IsOwner(id PlayerID) bool
|
||||
// ChangeOwner 设置玩家为房主
|
||||
ChangeOwner(id PlayerID)
|
||||
|
||||
// Join 使特定玩家加入房间
|
||||
Join(player P) error
|
||||
// Leave 使特定玩家离开房间
|
||||
Leave(id PlayerID)
|
||||
// KickOut 将特定玩家踢出房间
|
||||
KickOut(id, target PlayerID, reason string) error
|
||||
|
||||
// RegPlayerJoinRoomEvent 玩家进入房间时将立即执行被注册的事件处理函数
|
||||
RegPlayerJoinRoomEvent(handle PlayerJoinRoomEventHandle[PlayerID, P])
|
||||
OnPlayerJoinRoomEvent(player P)
|
||||
// RegPlayerLeaveRoomEvent 玩家离开房间时将立即执行被注册的事件处理函数
|
||||
RegPlayerLeaveRoomEvent(handle PlayerLeaveRoomEventHandle[PlayerID, P])
|
||||
OnPlayerLeaveRoomEvent(player P)
|
||||
// RegPlayerKickedOutEvent 当玩家被踢出游戏时将立即执行被注册的事件处理函数
|
||||
RegPlayerKickedOutEvent(handle PlayerKickedOutEventHandle[PlayerID, P])
|
||||
OnPlayerKickedOutEvent(executor, kicked PlayerID, reason string)
|
||||
}
|
||||
|
||||
type (
|
||||
PlayerJoinRoomEventHandle[ID comparable, P Player[ID]] func(room Room[ID, P], player P)
|
||||
PlayerLeaveRoomEventHandle[ID comparable, P Player[ID]] func(room Room[ID, P], player P)
|
||||
PlayerKickedOutEventHandle[ID comparable, P Player[ID]] func(room Room[ID, P], executor, kicked ID, reason string)
|
||||
)
|
|
@ -4,26 +4,26 @@ import "github.com/kercylan98/minotaur/game"
|
|||
|
||||
type (
|
||||
// PlayerJoinRoomEventHandle 玩家加入房间事件处理函数
|
||||
PlayerJoinRoomEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P)
|
||||
PlayerJoinRoomEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, player P)
|
||||
// PlayerLeaveRoomEventHandle 玩家离开房间事件处理函数
|
||||
PlayerLeaveRoomEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P)
|
||||
PlayerLeaveRoomEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, player P)
|
||||
// PlayerKickedOutEventHandle 玩家被踢出房间事件处理函数
|
||||
PlayerKickedOutEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, executor, kicked P, reason string)
|
||||
PlayerKickedOutEventHandle[PID comparable, P game.Player[PID], R Room] 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)
|
||||
PlayerUpgradeOwnerEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, oldOwner, newOwner P)
|
||||
// CancelOwnerEventHandle 取消房主事件处理函数
|
||||
CancelOwnerEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, oldOwner P)
|
||||
CancelOwnerEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, oldOwner P)
|
||||
// ChangePlayerLimitEventHandle 改变房间人数上限事件处理函数
|
||||
ChangePlayerLimitEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, oldLimit, newLimit int)
|
||||
ChangePlayerLimitEventHandle[PID comparable, P game.Player[PID], R Room] 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)
|
||||
PlayerSeatChangeEventHandle[PID comparable, P game.Player[PID], R Room] 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)
|
||||
PlayerSeatSetEventHandle[PID comparable, P game.Player[PID], R Room] 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)
|
||||
PlayerSeatCancelEventHandle[PID comparable, P game.Player[PID], R Room] func(room R, player P, seat int)
|
||||
)
|
||||
|
||||
func newEvent[PID comparable, P game.Player[PID], R Room[PID, P]]() *event[PID, P, R] {
|
||||
func newEvent[PID comparable, P game.Player[PID], R Room]() *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]),
|
||||
|
@ -36,7 +36,7 @@ func newEvent[PID comparable, P game.Player[PID], R Room[PID, P]]() *event[PID,
|
|||
}
|
||||
}
|
||||
|
||||
type event[PID comparable, P game.Player[PID], R Room[PID, P]] struct {
|
||||
type event[PID comparable, P game.Player[PID], R Room] struct {
|
||||
playerJoinRoomEventHandles []PlayerJoinRoomEventHandle[PID, P, R]
|
||||
playerJoinRoomEventRoomHandles map[int64][]PlayerJoinRoomEventHandle[PID, P, R]
|
||||
playerLeaveRoomEventHandles []PlayerLeaveRoomEventHandle[PID, P, R]
|
||||
|
|
|
@ -2,7 +2,7 @@ package room
|
|||
|
||||
import "github.com/kercylan98/minotaur/game"
|
||||
|
||||
type info[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]] struct {
|
||||
type info[PlayerID comparable, P game.Player[PlayerID], R Room] struct {
|
||||
room R
|
||||
playerLimit int // 玩家人数上限, <= 0 表示无限制
|
||||
owner *PlayerID // 房主
|
||||
|
|
|
@ -3,10 +3,11 @@ package room
|
|||
import (
|
||||
"github.com/kercylan98/minotaur/game"
|
||||
"github.com/kercylan98/minotaur/utils/concurrent"
|
||||
"github.com/kercylan98/minotaur/utils/generic"
|
||||
)
|
||||
|
||||
// NewManager 创建房间管理器
|
||||
func NewManager[PID comparable, P game.Player[PID], R Room[PID, P]]() *Manager[PID, P, R] {
|
||||
func NewManager[PID comparable, P game.Player[PID], R Room]() *Manager[PID, P, R] {
|
||||
manager := &Manager[PID, P, R]{
|
||||
event: newEvent[PID, P, R](),
|
||||
rooms: concurrent.NewBalanceMap[int64, *info[PID, P, R]](),
|
||||
|
@ -18,7 +19,7 @@ func NewManager[PID comparable, P game.Player[PID], R Room[PID, P]]() *Manager[P
|
|||
}
|
||||
|
||||
// Manager 房间管理器
|
||||
type Manager[PID comparable, P game.Player[PID], R Room[PID, P]] struct {
|
||||
type Manager[PID comparable, P game.Player[PID], R Room] struct {
|
||||
*event[PID, P, R]
|
||||
rooms *concurrent.BalanceMap[int64, *info[PID, P, R]] // 所有房间
|
||||
players *concurrent.BalanceMap[PID, P] // 所有加入房间的玩家
|
||||
|
@ -78,7 +79,7 @@ func (slf *Manager[PID, P, R]) CancelOwner(roomId int64) {
|
|||
info.owner = nil
|
||||
}
|
||||
})
|
||||
if oldOwner != nil {
|
||||
if !generic.IsNil(oldOwner) {
|
||||
slf.OnCancelOwnerEvent(room, oldOwner)
|
||||
}
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ func (slf *Manager[PID, P, R]) SetOwner(roomId int64, owner PID) error {
|
|||
oldOwner = slf.GetRoomPlayer(roomId, *info.owner)
|
||||
}
|
||||
newOwner = slf.GetRoomPlayer(roomId, owner)
|
||||
if newOwner == nil {
|
||||
if generic.IsNil(newOwner) {
|
||||
err = ErrRoomOrPlayerNotExist
|
||||
return
|
||||
}
|
||||
|
@ -270,7 +271,7 @@ func (slf *Manager[PID, P, R]) Leave(roomId int64, player P) {
|
|||
}
|
||||
|
||||
// Join 使玩家加入房间
|
||||
func (slf *Manager[PID, P, R]) Join(player P, roomId int64) error {
|
||||
func (slf *Manager[PID, P, R]) Join(roomId int64, player P) error {
|
||||
var err error
|
||||
var roomInfo *info[PID, P, R]
|
||||
slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) {
|
||||
|
@ -315,12 +316,12 @@ func (slf *Manager[PID, P, R]) KickOut(roomId int64, executor, kicked PID, reaso
|
|||
var executorPlayer, kickedPlayer P
|
||||
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
|
||||
executorPlayer, kickedPlayer = slf.GetRoomPlayer(roomId, executor), slf.GetRoomPlayer(roomId, kicked)
|
||||
if executorPlayer == nil || kickedPlayer == nil {
|
||||
if generic.IsHasNil(executorPlayer, kickedPlayer) {
|
||||
err = ErrRoomOrPlayerNotExist
|
||||
return
|
||||
}
|
||||
room = slf.rooms.Get(roomId).room
|
||||
if room == nil {
|
||||
if generic.IsNil(room) {
|
||||
err = ErrRoomNotExist
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,30 +1,7 @@
|
|||
package room
|
||||
|
||||
import "github.com/kercylan98/minotaur/game"
|
||||
|
||||
// Room 房间类似于简版的游戏世界(World),不过没有游戏实体
|
||||
type Room[PlayerID comparable, P game.Player[PlayerID]] interface {
|
||||
type Room interface {
|
||||
// GetGuid 获取房间的唯一标识符
|
||||
GetGuid() int64
|
||||
// GetPlayerLimit 获取玩家人数上限
|
||||
GetPlayerLimit() int
|
||||
// GetPlayer 根据玩家id获取玩家
|
||||
GetPlayer(id PlayerID) P
|
||||
// GetPlayers 获取房间中的所有玩家
|
||||
GetPlayers() map[PlayerID]P
|
||||
// GetPlayerCount 获取玩家数量
|
||||
GetPlayerCount() int
|
||||
// IsExistPlayer 检查房间中是否存在特定玩家
|
||||
IsExistPlayer(id PlayerID) bool
|
||||
// IsOwner 检查玩家是否是房主
|
||||
IsOwner(id PlayerID) bool
|
||||
// ChangeOwner 设置玩家为房主
|
||||
ChangeOwner(id PlayerID)
|
||||
|
||||
// Join 使特定玩家加入房间
|
||||
Join(player P) error
|
||||
// Leave 使特定玩家离开房间
|
||||
Leave(id PlayerID)
|
||||
// KickOut 将特定玩家踢出房间
|
||||
KickOut(id, target PlayerID, reason string) error
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"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] {
|
||||
func newSeat[PlayerID comparable, P game.Player[PlayerID], R Room](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,
|
||||
|
@ -18,7 +18,7 @@ func newSeat[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]](
|
|||
}
|
||||
|
||||
// Seat 房间座位信息
|
||||
type Seat[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]] struct {
|
||||
type Seat[PlayerID comparable, P game.Player[PlayerID], R Room] struct {
|
||||
manager *Manager[PlayerID, P, R]
|
||||
room R
|
||||
event *event[PlayerID, P, R]
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
package game
|
||||
|
||||
// RoomSeat 带有座位号的房间实现
|
||||
type RoomSeat[PlayerID comparable, P Player[PlayerID]] interface {
|
||||
Room[PlayerID, P]
|
||||
// AddSeat 将玩家添加到座位号中
|
||||
AddSeat(id PlayerID)
|
||||
// AddSeatWithAssign 将玩家添加到座位号中,并分配特定的座位号
|
||||
AddSeatWithAssign(id PlayerID, seat int)
|
||||
// RemovePlayerSeat 移除玩家的座位号
|
||||
RemovePlayerSeat(id PlayerID)
|
||||
// RemoveSeat 移除特定座位号
|
||||
RemoveSeat(seat int)
|
||||
// SetSeat 设置玩家座位号,当玩家没有座位号时,将会返回错误信息
|
||||
// - 如果座位号有其他玩家,他们的位置将互换
|
||||
SetSeat(id PlayerID, seat int) error
|
||||
// GetSeat 获取玩家座位号
|
||||
GetSeat(id PlayerID) (int, error)
|
||||
// GetPlayerIDWithSeat 根据座位号获取玩家ID
|
||||
GetPlayerIDWithSeat(seat int) (PlayerID, error)
|
||||
// GetSeatInfo 获取座位信息,空缺的位置将为空
|
||||
GetSeatInfo() []*PlayerID
|
||||
// GetSeatInfoMap 以map的方式获取座位号
|
||||
GetSeatInfoMap() map[int]PlayerID
|
||||
// GetSeatInfoMapVacancy 以map的方式获取座位号,空缺的位置将被保留为nil
|
||||
GetSeatInfoMapVacancy() map[int]*PlayerID
|
||||
// GetSeatInfoWithPlayerIDMap 获取座位信息,将以玩家ID作为key
|
||||
GetSeatInfoWithPlayerIDMap() map[PlayerID]int
|
||||
// GetFirstSeat 获取第一个非空缺座位号,不存在时将返回-1
|
||||
GetFirstSeat() int
|
||||
// GetNextSeat 获取下一个座位号,空缺的位置将会被跳过
|
||||
// - 超出范围将返回-1
|
||||
// - 当没有下一个座位号时将始终返回本身
|
||||
GetNextSeat(seat int) int
|
||||
// GetNextSeatVacancy 获取下一个座位号,空缺的位置将被保留
|
||||
// - 超出范围将返回-1
|
||||
// - 当没有下一个座位号时将始终返回本身
|
||||
GetNextSeatVacancy(seat int) int
|
||||
// GetPrevSeat 获取上一个座位号,空缺的位置将会被跳过
|
||||
// - 超出范围将返回-1
|
||||
// - 当没有上一个座位号时将始终返回本身
|
||||
GetPrevSeat(seat int) int
|
||||
// GetPrevSeatVacancy 获取上一个座位号,空缺的位置将被保留
|
||||
// - 超出范围将返回-1
|
||||
// - 当没有上一个座位号时将始终返回本身
|
||||
GetPrevSeatVacancy(seat int) int
|
||||
}
|
Loading…
Reference in New Issue