feat: 房间管理器实现

This commit is contained in:
kercylan98 2023-07-26 12:03:51 +08:00
parent d26ef3aca6
commit 45c855a516
6 changed files with 473 additions and 34 deletions

12
game/room/errors.go Normal file
View File

@ -0,0 +1,12 @@
package room
import "errors"
var (
// ErrRoomNotExist 房间不存在
ErrRoomNotExist = errors.New("room not exist")
// ErrRoomPlayerFull 房间人数已满
ErrRoomPlayerFull = errors.New("room player full")
// ErrPlayerNotExist 玩家不存在
ErrPlayerNotExist = errors.New("player not exist")
)

95
game/room/events.go Normal file
View File

@ -0,0 +1,95 @@
package room
import "github.com/kercylan98/minotaur/game"
type (
// PlayerJoinRoomEventHandle 玩家加入房间事件处理函数
PlayerJoinRoomEventHandle[PID comparable, P game.Player[PID], R Room[PID, P]] func(room R, player P)
// 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)
)
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]),
}
}
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]
}
func (slf *event[PID, P, R]) unReg(guid int64) {
delete(slf.playerJoinRoomEventRoomHandles, guid)
delete(slf.playerLeaveRoomEventRoomHandles, guid)
delete(slf.playerKickedOutEventRoomHandles, guid)
}
// RegPlayerJoinRoomEvent 玩家进入房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerJoinRoomEvent(handle PlayerJoinRoomEventHandle[PID, P, R]) {
slf.playerJoinRoomEventHandles = append(slf.playerJoinRoomEventHandles, handle)
}
// OnPlayerJoinRoomEvent 玩家进入房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) OnPlayerJoinRoomEvent(room R, player P) {
for _, handle := range slf.playerJoinRoomEventHandles {
handle(room, player)
}
for _, handle := range slf.playerJoinRoomEventRoomHandles[room.GetGuid()] {
handle(room, player)
}
}
// RegPlayerJoinRoomEventWithRoom 玩家进入房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerJoinRoomEventWithRoom(room R, handle PlayerJoinRoomEventHandle[PID, P, R]) {
slf.playerJoinRoomEventRoomHandles[room.GetGuid()] = append(slf.playerJoinRoomEventRoomHandles[room.GetGuid()], handle)
}
// RegPlayerLeaveRoomEvent 玩家离开房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerLeaveRoomEvent(handle PlayerLeaveRoomEventHandle[PID, P, R]) {
slf.playerLeaveRoomEventHandles = append(slf.playerLeaveRoomEventHandles, handle)
}
// RegPlayerLeaveRoomEventWithRoom 玩家离开房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerLeaveRoomEventWithRoom(room R, handle PlayerLeaveRoomEventHandle[PID, P, R]) {
slf.playerLeaveRoomEventRoomHandles[room.GetGuid()] = append(slf.playerLeaveRoomEventRoomHandles[room.GetGuid()], handle)
}
// OnPlayerLeaveRoomEvent 玩家离开房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) OnPlayerLeaveRoomEvent(room R, player P) {
for _, handle := range slf.playerLeaveRoomEventHandles {
handle(room, player)
}
for _, handle := range slf.playerLeaveRoomEventRoomHandles[room.GetGuid()] {
handle(room, player)
}
}
// RegPlayerKickedOutEvent 玩家被踢出房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerKickedOutEvent(handle PlayerKickedOutEventHandle[PID, P, R]) {
slf.playerKickedOutEventHandles = append(slf.playerKickedOutEventHandles, handle)
}
// RegPlayerKickedOutEventWithRoom 玩家被踢出房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) RegPlayerKickedOutEventWithRoom(room R, handle PlayerKickedOutEventHandle[PID, P, R]) {
slf.playerKickedOutEventRoomHandles[room.GetGuid()] = append(slf.playerKickedOutEventRoomHandles[room.GetGuid()], handle)
}
// OnPlayerKickedOutEvent 玩家被踢出房间时将立即执行被注册的事件处理函数
func (slf *event[PID, P, R]) OnPlayerKickedOutEvent(room R, executor, kicked PID, reason string) {
for _, handle := range slf.playerKickedOutEventHandles {
handle(room, executor, kicked, reason)
}
for _, handle := range slf.playerKickedOutEventRoomHandles[room.GetGuid()] {
handle(room, executor, kicked, reason)
}
}

8
game/room/info.go Normal file
View File

@ -0,0 +1,8 @@
package room
import "github.com/kercylan98/minotaur/game"
type info[PlayerID comparable, P game.Player[PlayerID], R Room[PlayerID, P]] struct {
room R
playerLimit int // 玩家人数上限, <= 0 表示无限制
}

259
game/room/manager.go Normal file
View File

@ -0,0 +1,259 @@
package room
import (
"github.com/kercylan98/minotaur/game"
"github.com/kercylan98/minotaur/utils/concurrent"
)
// NewManager 创建房间管理器
func NewManager[PID comparable, P game.Player[PID], R Room[PID, P]]() *Manager[PID, P, R] {
manager := &Manager[PID, P, R]{
event: newEvent[PID, P, R](),
rooms: concurrent.NewBalanceMap[int64, *info[PID, P, R]](),
players: concurrent.NewBalanceMap[PID, P](),
pr: concurrent.NewBalanceMap[PID, map[int64]struct{}](),
}
return manager
}
// Manager 房间管理器
type Manager[PID comparable, P game.Player[PID], R Room[PID, P]] struct {
*event[PID, P, R]
rooms *concurrent.BalanceMap[int64, *info[PID, P, R]] // 所有房间
players *concurrent.BalanceMap[PID, P] // 所有加入房间的玩家
pr *concurrent.BalanceMap[PID, map[int64]struct{}] // 玩家所在房间
rp *concurrent.BalanceMap[int64, map[PID]struct{}] // 房间中的玩家
}
// CreateRoom 创建房间
func (slf *Manager[PID, P, R]) CreateRoom(room R) {
roomInfo := &info[PID, P, R]{room: room}
slf.rooms.Set(room.GetGuid(), roomInfo)
}
// ReleaseRoom 释放房间
func (slf *Manager[PID, P, R]) ReleaseRoom(guid int64) {
slf.unReg(guid)
slf.rooms.Delete(guid)
}
// GetRoom 获取房间
func (slf *Manager[PID, P, R]) GetRoom(guid int64) R {
return slf.rooms.Get(guid).room
}
// Exist 检查房间是否存在
func (slf *Manager[PID, P, R]) Exist(guid int64) bool {
return slf.rooms.Exist(guid)
}
// GetRooms 获取所有房间
func (slf *Manager[PID, P, R]) GetRooms() map[int64]R {
var rooms = make(map[int64]R)
slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) {
for id, info := range m {
rooms[id] = info.room
}
})
return rooms
}
// GetRoomCount 获取房间数量
func (slf *Manager[PID, P, R]) GetRoomCount() int {
return slf.rooms.Size()
}
// GetRoomPlayerCount 获取房间中玩家数量
func (slf *Manager[PID, P, R]) GetRoomPlayerCount(guid int64) int {
var count int
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
count = len(m[guid])
})
return count
}
// ExistPlayer 检查玩家是否在任一房间内
func (slf *Manager[PID, P, R]) ExistPlayer(id PID) bool {
return slf.players.Exist(id)
}
// InRoom 检查玩家是否在指定房间内
func (slf *Manager[PID, P, R]) InRoom(id PID, guid int64) bool {
var in bool
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
rooms, exist := m[id]
if !exist {
return
}
_, in = rooms[guid]
})
return in
}
// GetPlayer 获取玩家
func (slf *Manager[PID, P, R]) GetPlayer(id PID) P {
return slf.players.Get(id)
}
// GetPlayers 获取所有玩家
func (slf *Manager[PID, P, R]) GetPlayers() *concurrent.BalanceMap[PID, P] {
return slf.players
}
// GetPlayerCount 获取玩家数量
func (slf *Manager[PID, P, R]) GetPlayerCount() int {
return slf.players.Size()
}
// GetPlayerRoom 获取玩家所在房间
func (slf *Manager[PID, P, R]) GetPlayerRoom(id PID) []R {
var result = make([]R, 0)
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
rooms, exist := m[id]
if !exist {
return
}
for id := range rooms {
result = append(result, slf.rooms.Get(id).room)
}
})
return result
}
// GetPlayerRoomCount 获取玩家所在房间数量
func (slf *Manager[PID, P, R]) GetPlayerRoomCount(id PID) int {
var count int
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
count = len(m[id])
})
return count
}
// GetRoomPlayer 获取房间中的玩家
func (slf *Manager[PID, P, R]) GetRoomPlayer(roomId int64, playerId PID) P {
var player P
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
players, exist := m[roomId]
if !exist {
return
}
_, exist = players[playerId]
if !exist {
return
}
player = slf.players.Get(playerId)
})
return player
}
// GetRoomPlayers 获取房间中的玩家
func (slf *Manager[PID, P, R]) GetRoomPlayers(guid int64) map[PID]P {
var result = make(map[PID]P)
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
players, exist := m[guid]
if !exist {
return
}
for id := range players {
result[id] = slf.players.Get(id)
}
})
return result
}
// GetRoomPlayerLimit 获取房间中的玩家数量上限
func (slf *Manager[PID, P, R]) GetRoomPlayerLimit(guid int64) int {
return slf.rooms.Get(guid).playerLimit
}
// Leave 使玩家离开房间
func (slf *Manager[PID, P, R]) Leave(roomId int64, player P) {
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())
})
})
}
// Join 使玩家加入房间
func (slf *Manager[PID, P, R]) Join(player P, roomId int64) error {
var err error
slf.rooms.Atom(func(m map[int64]*info[PID, P, R]) {
room, exist := m[roomId]
if !exist {
err = ErrRoomNotExist
return
}
if room.playerLimit > 0 && room.playerLimit <= slf.GetRoomPlayerCount(roomId) {
err = ErrRoomPlayerFull
return
}
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
rooms, exist := m[player.GetID()]
if !exist {
rooms = make(map[int64]struct{})
m[player.GetID()] = rooms
}
rooms[roomId] = struct{}{}
})
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
players, exist := m[roomId]
if !exist {
players = make(map[PID]struct{})
m[roomId] = players
}
players[player.GetID()] = struct{}{}
})
slf.players.Set(player.GetID(), player)
slf.OnPlayerJoinRoomEvent(room.room, player)
})
return err
}
// KickOut 以某种原因踢出特定玩家
// - 该函数不会校验任何权限相关的内容,调用后将直接踢出玩家
func (slf *Manager[PID, P, R]) KickOut(roomId int64, executor, kicked PID, reason string) error {
var err error
var room R
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 {
return
}
room = slf.rooms.Get(roomId).room
})
if err == nil {
return err
}
slf.OnPlayerKickedOutEvent(room, executor, kicked, reason)
slf.Leave(roomId, slf.players.Get(kicked))
return nil
}

30
game/room/room.go Normal file
View File

@ -0,0 +1,30 @@
package room
import "github.com/kercylan98/minotaur/game"
// Room 房间类似于简版的游戏世界(World),不过没有游戏实体
type Room[PlayerID comparable, P game.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
}

View File

@ -21,150 +21,183 @@ func NewBalanceMap[Key comparable, value any](options ...BalanceMapOption[Key, v
type BalanceMap[Key comparable, Value any] struct {
lock sync.RWMutex
data map[Key]Value
atom bool
}
// Set 设置一个值
func (slf *BalanceMap[Key, Value]) Set(key Key, value Value) {
slf.lock.Lock()
defer slf.lock.Unlock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
slf.data[key] = value
}
// Get 获取一个值
func (slf *BalanceMap[Key, Value]) Get(key Key) Value {
slf.lock.RLock()
defer slf.lock.RUnlock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
return slf.data[key]
}
// Atom 原子操作
func (slf *BalanceMap[Key, Value]) Atom(handle func(m map[Key]Value)) {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
handle(slf.data)
slf.lock.Unlock()
}
// Exist 判断是否存在
func (slf *BalanceMap[Key, Value]) Exist(key Key) bool {
slf.lock.RLock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
_, exist := slf.data[key]
slf.lock.RUnlock()
return exist
}
// GetExist 获取一个值并判断是否存在
func (slf *BalanceMap[Key, Value]) GetExist(key Key) (Value, bool) {
slf.lock.RLock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
value, exist := slf.data[key]
slf.lock.RUnlock()
return value, exist
}
// Delete 删除一个值
func (slf *BalanceMap[Key, Value]) Delete(key Key) {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
delete(slf.data, key)
defer slf.lock.Unlock()
}
// DeleteGet 删除一个值并返回
func (slf *BalanceMap[Key, Value]) DeleteGet(key Key) Value {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
v := slf.data[key]
delete(slf.data, key)
slf.lock.Unlock()
return v
}
// DeleteGetExist 删除一个值并返回是否存在
func (slf *BalanceMap[Key, Value]) DeleteGetExist(key Key) (Value, bool) {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
v, exist := slf.data[key]
delete(slf.data, key)
defer slf.lock.Unlock()
return v, exist
}
// DeleteExist 删除一个值并返回是否存在
func (slf *BalanceMap[Key, Value]) DeleteExist(key Key) bool {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
if _, exist := slf.data[key]; !exist {
slf.lock.Unlock()
return exist
}
delete(slf.data, key)
slf.lock.Unlock()
return true
}
// Clear 清空
func (slf *BalanceMap[Key, Value]) Clear() {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
for k := range slf.data {
delete(slf.data, k)
}
slf.lock.Unlock()
}
// ClearHandle 清空并处理
func (slf *BalanceMap[Key, Value]) ClearHandle(handle func(key Key, value Value)) {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
for k, v := range slf.data {
handle(k, v)
delete(slf.data, k)
}
slf.lock.Unlock()
}
// Range 遍历所有值,如果 handle 返回 true 则停止遍历
func (slf *BalanceMap[Key, Value]) Range(handle func(key Key, value Value) bool) {
slf.lock.Lock()
if !slf.atom {
slf.lock.Lock()
defer slf.lock.Unlock()
}
for k, v := range slf.data {
key, value := k, v
if handle(key, value) {
break
}
}
slf.lock.Unlock()
}
// Keys 获取所有的键
func (slf *BalanceMap[Key, Value]) Keys() []Key {
slf.lock.RLock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
var s = make([]Key, 0, len(slf.data))
for k := range slf.data {
s = append(s, k)
}
slf.lock.RUnlock()
return s
}
// Slice 获取所有的值
func (slf *BalanceMap[Key, Value]) Slice() []Value {
slf.lock.RLock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
var s = make([]Value, 0, len(slf.data))
for _, v := range slf.data {
s = append(s, v)
}
slf.lock.RUnlock()
return s
}
// Map 转换为普通 map
func (slf *BalanceMap[Key, Value]) Map() map[Key]Value {
slf.lock.RLock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
var m = make(map[Key]Value)
for k, v := range slf.data {
m[k] = v
}
slf.lock.RUnlock()
return m
}
// Size 获取数量
func (slf *BalanceMap[Key, Value]) Size() int {
slf.lock.RLock()
defer slf.lock.RUnlock()
if !slf.atom {
slf.lock.RLock()
defer slf.lock.RUnlock()
}
return len(slf.data)
}
@ -175,11 +208,13 @@ func (slf *BalanceMap[Key, Value]) MarshalJSON() ([]byte, error) {
func (slf *BalanceMap[Key, Value]) UnmarshalJSON(bytes []byte) error {
var m = make(map[Key]Value)
if !slf.atom {
slf.lock.Lock()
slf.lock.Unlock()
}
if err := json.Unmarshal(bytes, &m); err != nil {
return err
}
slf.lock.Lock()
slf.data = m
slf.lock.Unlock()
return nil
}