refactor: 移除原有的 builtin 中的各类 room 实现

This commit is contained in:
kercylan98 2023-07-27 12:07:17 +08:00
parent d06c840c46
commit ee18934768
15 changed files with 23 additions and 702 deletions

View File

@ -4,8 +4,6 @@ package game
// - 需要注意 Actor 不等于 Player // - 需要注意 Actor 不等于 Player
// - 在 Minotaur 中,每个网络连接可以表示一个 Player而每个玩家可以拥有多个 Actor // - 在 Minotaur 中,每个网络连接可以表示一个 Player而每个玩家可以拥有多个 Actor
// - Actor 并非 Player 独有,场景中也可包含各类无主的 Actor // - Actor 并非 Player 独有,场景中也可包含各类无主的 Actor
// - 内置实现builtin.Actor
// - 构建函数builtin.NewActor
type Actor interface { type Actor interface {
// SetGuid 设置对象的唯一标识符 // SetGuid 设置对象的唯一标识符
// - 需要注意的是该函数不应该主动执行,否则可能产生意想不到的情况 // - 需要注意的是该函数不应该主动执行,否则可能产生意想不到的情况

View File

@ -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)
}
}

View File

@ -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")
)

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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())
}

View File

@ -1,7 +0,0 @@
package builtin
import "errors"
var (
ErrRoomNotHasPlayer = errors.New("player not exist")
)

View File

@ -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
}
}

View File

@ -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)
)

View File

@ -4,26 +4,26 @@ import "github.com/kercylan98/minotaur/game"
type ( type (
// PlayerJoinRoomEventHandle 玩家加入房间事件处理函数 // 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 玩家离开房间事件处理函数
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 玩家被踢出房间事件处理函数
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 玩家成为房主事件处理函数
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 取消房主事件处理函数
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 改变房间人数上限事件处理函数
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 玩家座位改变事件处理函数
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 玩家座位设置事件处理函数
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 玩家座位取消事件处理函数
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]{ return &event[PID, P, R]{
playerJoinRoomEventRoomHandles: make(map[int64][]PlayerJoinRoomEventHandle[PID, P, R]), playerJoinRoomEventRoomHandles: make(map[int64][]PlayerJoinRoomEventHandle[PID, P, R]),
playerLeaveRoomEventRoomHandles: make(map[int64][]PlayerLeaveRoomEventHandle[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] playerJoinRoomEventHandles []PlayerJoinRoomEventHandle[PID, P, R]
playerJoinRoomEventRoomHandles map[int64][]PlayerJoinRoomEventHandle[PID, P, R] playerJoinRoomEventRoomHandles map[int64][]PlayerJoinRoomEventHandle[PID, P, R]
playerLeaveRoomEventHandles []PlayerLeaveRoomEventHandle[PID, P, R] playerLeaveRoomEventHandles []PlayerLeaveRoomEventHandle[PID, P, R]

View File

@ -2,7 +2,7 @@ package room
import "github.com/kercylan98/minotaur/game" 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 room R
playerLimit int // 玩家人数上限, <= 0 表示无限制 playerLimit int // 玩家人数上限, <= 0 表示无限制
owner *PlayerID // 房主 owner *PlayerID // 房主

View File

@ -3,10 +3,11 @@ package room
import ( import (
"github.com/kercylan98/minotaur/game" "github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/concurrent" "github.com/kercylan98/minotaur/utils/concurrent"
"github.com/kercylan98/minotaur/utils/generic"
) )
// NewManager 创建房间管理器 // 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]{ manager := &Manager[PID, P, R]{
event: newEvent[PID, P, R](), event: newEvent[PID, P, R](),
rooms: concurrent.NewBalanceMap[int64, *info[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 房间管理器 // 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] *event[PID, P, R]
rooms *concurrent.BalanceMap[int64, *info[PID, P, R]] // 所有房间 rooms *concurrent.BalanceMap[int64, *info[PID, P, R]] // 所有房间
players *concurrent.BalanceMap[PID, P] // 所有加入房间的玩家 players *concurrent.BalanceMap[PID, P] // 所有加入房间的玩家
@ -78,7 +79,7 @@ func (slf *Manager[PID, P, R]) CancelOwner(roomId int64) {
info.owner = nil info.owner = nil
} }
}) })
if oldOwner != nil { if !generic.IsNil(oldOwner) {
slf.OnCancelOwnerEvent(room, 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) oldOwner = slf.GetRoomPlayer(roomId, *info.owner)
} }
newOwner = slf.GetRoomPlayer(roomId, owner) newOwner = slf.GetRoomPlayer(roomId, owner)
if newOwner == nil { if generic.IsNil(newOwner) {
err = ErrRoomOrPlayerNotExist err = ErrRoomOrPlayerNotExist
return return
} }
@ -270,7 +271,7 @@ func (slf *Manager[PID, P, R]) Leave(roomId int64, player P) {
} }
// Join 使玩家加入房间 // 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 err error
var roomInfo *info[PID, P, R] var roomInfo *info[PID, P, R]
slf.rooms.Atom(func(m map[int64]*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 var executorPlayer, kickedPlayer P
slf.rp.Atom(func(m map[int64]map[PID]struct{}) { slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
executorPlayer, kickedPlayer = slf.GetRoomPlayer(roomId, executor), slf.GetRoomPlayer(roomId, kicked) executorPlayer, kickedPlayer = slf.GetRoomPlayer(roomId, executor), slf.GetRoomPlayer(roomId, kicked)
if executorPlayer == nil || kickedPlayer == nil { if generic.IsHasNil(executorPlayer, kickedPlayer) {
err = ErrRoomOrPlayerNotExist err = ErrRoomOrPlayerNotExist
return return
} }
room = slf.rooms.Get(roomId).room room = slf.rooms.Get(roomId).room
if room == nil { if generic.IsNil(room) {
err = ErrRoomNotExist err = ErrRoomNotExist
return return
} }

View File

@ -1,30 +1,7 @@
package room package room
import "github.com/kercylan98/minotaur/game"
// Room 房间类似于简版的游戏世界(World),不过没有游戏实体 // Room 房间类似于简版的游戏世界(World),不过没有游戏实体
type Room[PlayerID comparable, P game.Player[PlayerID]] interface { type Room interface {
// GetGuid 获取房间的唯一标识符 // GetGuid 获取房间的唯一标识符
GetGuid() int64 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
} }

View File

@ -7,7 +7,7 @@ import (
"sync" "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]{ roomSeat := &Seat[PlayerID, P, R]{
manager: manager, manager: manager,
room: room, room: room,
@ -18,7 +18,7 @@ func newSeat[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]](
} }
// Seat 房间座位信息 // 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] manager *Manager[PlayerID, P, R]
room R room R
event *event[PlayerID, P, R] event *event[PlayerID, P, R]

View File

@ -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
}