Merge branch 'develop'
This commit is contained in:
commit
2d35b283b8
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/kercylan98/minotaur/utils/random"
|
"github.com/kercylan98/minotaur/utils/random"
|
||||||
"github.com/kercylan98/minotaur/utils/slice"
|
"github.com/kercylan98/minotaur/utils/slice"
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
"github.com/kercylan98/minotaur/utils/timer"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -65,6 +66,8 @@ type Round[Data RoundData] struct {
|
||||||
changeEventHandles []RoundChangeEvent[Data] // 游戏回合变更事件
|
changeEventHandles []RoundChangeEvent[Data] // 游戏回合变更事件
|
||||||
actionTimeoutEventHandles []RoundActionTimeoutEvent[Data] // 行动超时事件
|
actionTimeoutEventHandles []RoundActionTimeoutEvent[Data] // 行动超时事件
|
||||||
actionRefreshEventHandles []RoundActionRefreshEvent[Data] // 行动刷新事件
|
actionRefreshEventHandles []RoundActionRefreshEvent[Data] // 行动刷新事件
|
||||||
|
|
||||||
|
actionMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetData 获取游戏数据
|
// GetData 获取游戏数据
|
||||||
|
@ -77,6 +80,8 @@ func (slf *Round[Data]) GetData() Data {
|
||||||
func (slf *Round[Data]) Start() {
|
func (slf *Round[Data]) Start() {
|
||||||
slf.currentEntity = -1
|
slf.currentEntity = -1
|
||||||
slf.round = 1
|
slf.round = 1
|
||||||
|
slf.actionMutex.Lock()
|
||||||
|
defer slf.actionMutex.Unlock()
|
||||||
slf.loop(false)
|
slf.loop(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +162,11 @@ func (slf *Round[Data]) SkipCamp() {
|
||||||
// ActionRefresh 刷新行动超时时间
|
// ActionRefresh 刷新行动超时时间
|
||||||
func (slf *Round[Data]) ActionRefresh() {
|
func (slf *Round[Data]) ActionRefresh() {
|
||||||
slf.actionTimeoutTime = time.Now().Add(slf.actionTimeout).Unix()
|
slf.actionTimeoutTime = time.Now().Add(slf.actionTimeout).Unix()
|
||||||
slf.ticker.After(slf.actionTimeoutTickerName, slf.actionTimeout, slf.loop, true)
|
slf.ticker.After(slf.actionTimeoutTickerName, slf.actionTimeout, func(timeout bool) {
|
||||||
|
slf.actionMutex.Lock()
|
||||||
|
defer slf.actionMutex.Unlock()
|
||||||
|
slf.loop(timeout)
|
||||||
|
}, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionFinish 结束行动
|
// ActionFinish 结束行动
|
||||||
|
@ -166,6 +175,8 @@ func (slf *Round[Data]) ActionFinish() {
|
||||||
if slf.shareAction {
|
if slf.shareAction {
|
||||||
slf.currentEntity = -1
|
slf.currentEntity = -1
|
||||||
}
|
}
|
||||||
|
slf.actionMutex.Lock()
|
||||||
|
defer slf.actionMutex.Unlock()
|
||||||
slf.loop(false)
|
slf.loop(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,9 @@ func (slf *Manager[PID, P, R]) ReleaseRoom(guid int64) {
|
||||||
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
|
slf.pr.Atom(func(m map[PID]map[int64]struct{}) {
|
||||||
for playerId := range players {
|
for playerId := range players {
|
||||||
delete(m[playerId], guid)
|
delete(m[playerId], guid)
|
||||||
|
if len(m[playerId]) == 0 {
|
||||||
|
slf.players.Delete(playerId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -364,6 +367,9 @@ func (slf *Manager[PID, P, R]) Leave(roomId int64, player P) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
delete(rooms, roomId)
|
delete(rooms, roomId)
|
||||||
|
if len(rooms) == 0 {
|
||||||
|
slf.players.Delete(player.GetID())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
|
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
|
||||||
players, exist := m[roomId]
|
players, exist := m[roomId]
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package room_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/kercylan98/minotaur/game/room"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewManager(t *testing.T) {
|
||||||
|
m := room.NewManager[string, *Player, *Room]()
|
||||||
|
r := &Room{}
|
||||||
|
m.CreateRoom(r)
|
||||||
|
helper := m.GetHelper(r)
|
||||||
|
|
||||||
|
helper.Join(&Player{ID: "player_01"})
|
||||||
|
helper.Join(&Player{ID: "player_02"})
|
||||||
|
helper.Join(&Player{ID: "player_03"})
|
||||||
|
helper.Leave(helper.GetPlayer("player_02"))
|
||||||
|
helper.Join(&Player{ID: "player_02"})
|
||||||
|
|
||||||
|
helper.BroadcastExcept(func(player *Player) {
|
||||||
|
fmt.Println(player.GetID())
|
||||||
|
}, func(player *Player) bool {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
|
@ -8,10 +8,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Player struct {
|
type Player struct {
|
||||||
|
ID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Player) GetID() string {
|
func (slf *Player) GetID() string {
|
||||||
return ""
|
return slf.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Player) GetConn() *server.Conn {
|
func (slf *Player) GetConn() *server.Conn {
|
||||||
|
|
|
@ -0,0 +1,352 @@
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
"github.com/kercylan98/minotaur/utils/slice"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newRoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]](manager *RoomManager[EntityID, RoomID, Entity, Room], room Room, options *RoomControllerOptions) *RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
|
controller := &RoomController[EntityID, RoomID, Entity, Room]{
|
||||||
|
manager: manager,
|
||||||
|
options: options,
|
||||||
|
entities: make(map[EntityID]Entity),
|
||||||
|
room: room,
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.roomsRWMutex.Lock()
|
||||||
|
defer manager.roomsRWMutex.Unlock()
|
||||||
|
manager.rooms[room.GetId()] = controller
|
||||||
|
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoomController 对房间进行操作的控制器,由 RoomManager 接管后返回
|
||||||
|
type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct {
|
||||||
|
manager *RoomManager[EntityID, RoomID, Entity, Room]
|
||||||
|
options *RoomControllerOptions
|
||||||
|
room Room
|
||||||
|
entities map[EntityID]Entity
|
||||||
|
entitiesRWMutex sync.RWMutex
|
||||||
|
|
||||||
|
vacancy []int // 空缺的座位
|
||||||
|
seat []*EntityID // 座位上的玩家
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinSeat 设置特定对象加入座位,当具体的座位不存在的时候,将会自动分配座位
|
||||||
|
// - 当目标座位存在玩家或未添加到房间中的时候,将会返回错误
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) JoinSeat(entityId EntityID, seat ...int) error {
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
_, exist := slf.entities[entityId]
|
||||||
|
if !exist {
|
||||||
|
return ErrNotInRoom
|
||||||
|
}
|
||||||
|
var targetSeat int
|
||||||
|
if len(seat) > 0 {
|
||||||
|
targetSeat = seat[0]
|
||||||
|
if targetSeat < len(slf.seat) && slf.seat[targetSeat] != nil {
|
||||||
|
return ErrSeatNotEmpty
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(slf.vacancy) > 0 {
|
||||||
|
targetSeat = slf.vacancy[0]
|
||||||
|
slf.vacancy = slf.vacancy[1:]
|
||||||
|
} else {
|
||||||
|
targetSeat = len(slf.seat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetSeat >= len(slf.seat) {
|
||||||
|
slf.seat = append(slf.seat, make([]*EntityID, targetSeat-len(slf.seat)+1)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.seat[targetSeat] = &entityId
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LeaveSeat 离开座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) LeaveSeat(entityId EntityID) {
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
slf.leaveSeat(entityId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// leaveSeat 离开座位(无锁)
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) leaveSeat(entityId EntityID) {
|
||||||
|
for i, seat := range slf.seat {
|
||||||
|
if seat != nil && *seat == entityId {
|
||||||
|
slf.seat[i] = nil
|
||||||
|
slf.vacancy = append(slf.vacancy, i)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeat 获取座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeat(entityId EntityID) int {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
for i, seat := range slf.seat {
|
||||||
|
if seat != nil && *seat == entityId {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNotEmptySeat 获取非空座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []int {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
var seats []int
|
||||||
|
for i, player := range slf.seat {
|
||||||
|
if player != nil {
|
||||||
|
seats = append(seats, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seats
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEmptySeat 获取空座位
|
||||||
|
// - 空座位需要在有对象离开座位后才可能出现
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int {
|
||||||
|
return slice.Copy(slf.vacancy)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSeat 判断是否有座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) HasSeat(entityId EntityID) bool {
|
||||||
|
return slf.GetSeat(entityId) != -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeatEntityCount 获取座位上的实体数量
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntityCount() int {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
var count int
|
||||||
|
for _, seat := range slf.seat {
|
||||||
|
if seat != nil {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeatEntities 获取座位上的实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntities() map[EntityID]Entity {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
var entities = make(map[EntityID]Entity)
|
||||||
|
for _, entityId := range slf.seat {
|
||||||
|
if entityId != nil {
|
||||||
|
entities[*entityId] = slf.entities[*entityId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entities
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeatEntitiesByOrdered 有序的获取座位上的实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntitiesByOrdered() []Entity {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
var entities = make([]Entity, 0, len(slf.seat))
|
||||||
|
for _, entityId := range slf.seat {
|
||||||
|
if entityId != nil {
|
||||||
|
entities = append(entities, slf.entities[*entityId])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entities
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeatEntitiesByOrderedAndContainsEmpty 获取有序的座位上的实体,包含空座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntitiesByOrderedAndContainsEmpty() []Entity {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
var entities = make([]Entity, len(slf.seat))
|
||||||
|
for i, entityId := range slf.seat {
|
||||||
|
if entityId != nil {
|
||||||
|
entities[i] = slf.entities[*entityId]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entities
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSeatEntity 获取座位上的实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntity(seat int) (entity Entity) {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
if seat < len(slf.seat) {
|
||||||
|
eid := slf.seat[seat]
|
||||||
|
if eid != nil {
|
||||||
|
return slf.entities[*eid]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return entity
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainEntity 房间内是否包含实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) ContainEntity(id EntityID) bool {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
_, exist := slf.entities[id]
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoom 获取原始房间实例
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoom() Room {
|
||||||
|
return slf.room
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntities 获取所有实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEntities() map[EntityID]Entity {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
return hash.Copy(slf.entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasEntity 判断是否有实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) HasEntity(id EntityID) bool {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
_, exist := slf.entities[id]
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntity 获取实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEntity(id EntityID) Entity {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
return slf.entities[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntityIDs 获取所有实体ID
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEntityIDs() []EntityID {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
return hash.KeyToSlice(slf.entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntityCount 获取实体数量
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEntityCount() int {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
return len(slf.entities)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangePassword 修改房间密码
|
||||||
|
// - 当房间密码为 nil 时,将会取消密码
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) ChangePassword(password *string) {
|
||||||
|
old := slf.options.password
|
||||||
|
slf.options.password = password
|
||||||
|
slf.manager.OnRoomChangePasswordEvent(slf, old, slf.options.password)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEntity 添加实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) AddEntity(entity Entity) error {
|
||||||
|
if slf.options.password != nil {
|
||||||
|
return ErrRoomPasswordNotMatch
|
||||||
|
}
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
|
||||||
|
if slf.options.maxEntityCount != nil && len(slf.entities) > *slf.options.maxEntityCount {
|
||||||
|
return ErrRoomFull
|
||||||
|
}
|
||||||
|
slf.entities[entity.GetId()] = entity
|
||||||
|
|
||||||
|
slf.manager.OnRoomAddEntityEvent(slf, entity)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEntityByPassword 通过房间密码添加实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) AddEntityByPassword(entity Entity, password string) error {
|
||||||
|
if slf.options.password == nil || *slf.options.password != password {
|
||||||
|
return ErrRoomPasswordNotMatch
|
||||||
|
}
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
|
||||||
|
if slf.options.maxEntityCount != nil && len(slf.entities) > *slf.options.maxEntityCount {
|
||||||
|
return ErrRoomFull
|
||||||
|
}
|
||||||
|
slf.entities[entity.GetId()] = entity
|
||||||
|
|
||||||
|
slf.manager.OnRoomAddEntityEvent(slf, entity)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveEntity 移除实体
|
||||||
|
// - 当实体被移除时如果实体在座位上,将会自动离开座位
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) RemoveEntity(id EntityID) {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
defer slf.entitiesRWMutex.RUnlock()
|
||||||
|
slf.removeEntity(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeEntity 移除实体(无锁)
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) removeEntity(id EntityID) {
|
||||||
|
slf.leaveSeat(id)
|
||||||
|
entity, exist := slf.entities[id]
|
||||||
|
delete(slf.entities, id)
|
||||||
|
if !exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
slf.manager.OnRoomRemoveEntityEvent(slf, entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveAllEntities 移除所有实体
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) RemoveAllEntities() {
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
for id := range slf.entities {
|
||||||
|
slf.removeEntity(id)
|
||||||
|
delete(slf.entities, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroy 销毁房间
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) Destroy() {
|
||||||
|
slf.manager.roomsRWMutex.Lock()
|
||||||
|
defer slf.manager.roomsRWMutex.Unlock()
|
||||||
|
|
||||||
|
delete(slf.manager.rooms, slf.room.GetId())
|
||||||
|
slf.manager.OnRoomDestroyEvent(slf)
|
||||||
|
|
||||||
|
slf.entitiesRWMutex.Lock()
|
||||||
|
defer slf.entitiesRWMutex.Unlock()
|
||||||
|
|
||||||
|
for eid := range slf.entities {
|
||||||
|
slf.removeEntity(eid)
|
||||||
|
delete(slf.entities, eid)
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.entities = make(map[EntityID]Entity)
|
||||||
|
slf.seat = slf.seat[:]
|
||||||
|
slf.vacancy = slf.vacancy[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoomManager 获取房间管理器
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoomManager() *RoomManager[EntityID, RoomID, Entity, Room] {
|
||||||
|
return slf.manager
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoomID 获取房间ID
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoomID() RoomID {
|
||||||
|
return slf.room.GetId()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast 广播消息
|
||||||
|
func (slf *RoomController[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||||
|
slf.entitiesRWMutex.RLock()
|
||||||
|
entities := hash.Copy(slf.entities)
|
||||||
|
slf.entitiesRWMutex.RUnlock()
|
||||||
|
for _, entity := range entities {
|
||||||
|
for _, condition := range conditions {
|
||||||
|
if !condition(entity) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handler(entity)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package space
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrRoomFull 房间已满
|
||||||
|
ErrRoomFull = errors.New("room is full")
|
||||||
|
// ErrSeatNotEmpty 座位上已经有实体
|
||||||
|
ErrSeatNotEmpty = errors.New("seat is not empty")
|
||||||
|
// ErrNotInRoom 实体不在房间中
|
||||||
|
ErrNotInRoom = errors.New("not in room")
|
||||||
|
// ErrRoomPasswordNotMatch 房间密码不匹配
|
||||||
|
ErrRoomPasswordNotMatch = errors.New("room password not match")
|
||||||
|
// ErrPermissionDenied 权限不足
|
||||||
|
ErrPermissionDenied = errors.New("permission denied")
|
||||||
|
)
|
|
@ -0,0 +1,105 @@
|
||||||
|
package space
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewRoomManager 创建房间管理器
|
||||||
|
func NewRoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]]() *RoomManager[EntityID, RoomID, Entity, Room] {
|
||||||
|
return &RoomManager[EntityID, RoomID, Entity, Room]{
|
||||||
|
roomManagerEvents: new(roomManagerEvents[EntityID, RoomID, Entity, Room]),
|
||||||
|
rooms: make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RoomManager 房间管理器
|
||||||
|
type RoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct {
|
||||||
|
*roomManagerEvents[EntityID, RoomID, Entity, Room]
|
||||||
|
roomsRWMutex sync.RWMutex
|
||||||
|
rooms map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssumeControl 将房间控制权交由 RoomManager 接管
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) AssumeControl(room Room, options ...*RoomControllerOptions) *RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
|
controller := newRoomController(slf, room, mergeRoomControllerOptions(options...))
|
||||||
|
slf.OnRoomAssumeControlEvent(controller)
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
// DestroyRoom 销毁房间
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) DestroyRoom(id RoomID) {
|
||||||
|
slf.roomsRWMutex.Lock()
|
||||||
|
room, exist := slf.rooms[id]
|
||||||
|
slf.roomsRWMutex.Unlock()
|
||||||
|
if !exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
room.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoom 获取房间
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRoom(id RoomID) *RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
defer slf.roomsRWMutex.RUnlock()
|
||||||
|
return slf.rooms[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRooms 获取所有房间
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
defer slf.roomsRWMutex.RUnlock()
|
||||||
|
return hash.Copy(slf.rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoomCount 获取房间数量
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomCount() int {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
defer slf.roomsRWMutex.RUnlock()
|
||||||
|
return len(slf.rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRoomIDs 获取所有房间ID
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
defer slf.roomsRWMutex.RUnlock()
|
||||||
|
return hash.KeyToSlice(slf.rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasEntity 判断特定对象是否在任一房间中
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
rooms := hash.Copy(slf.rooms)
|
||||||
|
slf.roomsRWMutex.RUnlock()
|
||||||
|
for _, room := range rooms {
|
||||||
|
if room.HasEntity(entityId) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEntityRooms 获取特定对象所在的房间
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
rooms := hash.Copy(slf.rooms)
|
||||||
|
slf.roomsRWMutex.RUnlock()
|
||||||
|
var result = make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room])
|
||||||
|
for id, room := range rooms {
|
||||||
|
if room.HasEntity(entityId) {
|
||||||
|
result[id] = room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadcast 向所有房间对象广播消息
|
||||||
|
func (slf *RoomManager[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) {
|
||||||
|
slf.roomsRWMutex.RLock()
|
||||||
|
rooms := hash.Copy(slf.rooms)
|
||||||
|
slf.roomsRWMutex.RUnlock()
|
||||||
|
for _, room := range rooms {
|
||||||
|
room.Broadcast(handler, conditions...)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package space
|
||||||
|
|
||||||
|
import "github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
|
||||||
|
type (
|
||||||
|
RoomAssumeControlEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room])
|
||||||
|
RoomDestroyEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room])
|
||||||
|
RoomAddEntityEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity)
|
||||||
|
RoomRemoveEntityEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity)
|
||||||
|
RoomChangePasswordEventHandle[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] func(controller *RoomController[EntityID, RoomID, Entity, Room], oldPassword, password *string)
|
||||||
|
)
|
||||||
|
|
||||||
|
type roomManagerEvents[EntityID comparable, RoomID comparable, Entity generic.IdR[EntityID], Room generic.IdR[RoomID]] struct {
|
||||||
|
roomAssumeControlEventHandles []RoomAssumeControlEventHandle[EntityID, RoomID, Entity, Room]
|
||||||
|
roomDestroyEventHandles []RoomDestroyEventHandle[EntityID, RoomID, Entity, Room]
|
||||||
|
roomAddEntityEventHandles []RoomAddEntityEventHandle[EntityID, RoomID, Entity, Room]
|
||||||
|
roomRemoveEntityEventHandles []RoomRemoveEntityEventHandle[EntityID, RoomID, Entity, Room]
|
||||||
|
roomChangePasswordEventHandles []RoomChangePasswordEventHandle[EntityID, RoomID, Entity, Room]
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegRoomAssumeControlEvent 注册房间接管事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAssumeControlEvent(handle RoomAssumeControlEventHandle[EntityID, RoomID, Entity, Room]) {
|
||||||
|
slf.roomAssumeControlEventHandles = append(slf.roomAssumeControlEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRoomAssumeControlEvent 房间接管事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAssumeControlEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) {
|
||||||
|
for _, handle := range slf.roomAssumeControlEventHandles {
|
||||||
|
handle(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegRoomDestroyEvent 注册房间销毁事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomDestroyEvent(handle RoomDestroyEventHandle[EntityID, RoomID, Entity, Room]) {
|
||||||
|
slf.roomDestroyEventHandles = append(slf.roomDestroyEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRoomDestroyEvent 房间销毁事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomDestroyEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) {
|
||||||
|
for _, handle := range slf.roomDestroyEventHandles {
|
||||||
|
handle(controller)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegRoomAddEntityEvent 注册房间添加对象事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAddEntityEvent(handle RoomAddEntityEventHandle[EntityID, RoomID, Entity, Room]) {
|
||||||
|
slf.roomAddEntityEventHandles = append(slf.roomAddEntityEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRoomAddEntityEvent 房间添加对象事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAddEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) {
|
||||||
|
for _, handle := range slf.roomAddEntityEventHandles {
|
||||||
|
handle(controller, entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegRoomRemoveEntityEvent 注册房间移除对象事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomRemoveEntityEvent(handle RoomRemoveEntityEventHandle[EntityID, RoomID, Entity, Room]) {
|
||||||
|
slf.roomRemoveEntityEventHandles = append(slf.roomRemoveEntityEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRoomRemoveEntityEvent 房间移除对象事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomRemoveEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) {
|
||||||
|
for _, handle := range slf.roomRemoveEntityEventHandles {
|
||||||
|
handle(controller, entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegRoomChangePasswordEvent 注册房间修改密码事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomChangePasswordEvent(handle RoomChangePasswordEventHandle[EntityID, RoomID, Entity, Room]) {
|
||||||
|
slf.roomChangePasswordEventHandles = append(slf.roomChangePasswordEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnRoomChangePasswordEvent 房间修改密码事件
|
||||||
|
func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomChangePasswordEvent(controller *RoomController[EntityID, RoomID, Entity, Room], oldPassword, password *string) {
|
||||||
|
for _, handle := range slf.roomChangePasswordEventHandles {
|
||||||
|
handle(controller, oldPassword, password)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package space
|
||||||
|
|
||||||
|
// NewRoomControllerOptions 创建房间控制器选项
|
||||||
|
func NewRoomControllerOptions() *RoomControllerOptions {
|
||||||
|
return &RoomControllerOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeRoomControllerOptions 合并房间控制器选项
|
||||||
|
func mergeRoomControllerOptions(options ...*RoomControllerOptions) *RoomControllerOptions {
|
||||||
|
result := NewRoomControllerOptions()
|
||||||
|
for _, option := range options {
|
||||||
|
if option.maxEntityCount != nil {
|
||||||
|
result.maxEntityCount = option.maxEntityCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoomControllerOptions struct {
|
||||||
|
maxEntityCount *int // 房间最大实体数量
|
||||||
|
password *string // 房间密码
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithMaxEntityCount 设置房间最大实体数量
|
||||||
|
func (slf *RoomControllerOptions) WithMaxEntityCount(maxEntityCount int) *RoomControllerOptions {
|
||||||
|
if maxEntityCount > 0 {
|
||||||
|
slf.maxEntityCount = &maxEntityCount
|
||||||
|
}
|
||||||
|
return slf
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPassword 设置房间密码
|
||||||
|
func (slf *RoomControllerOptions) WithPassword(password string) *RoomControllerOptions {
|
||||||
|
if password != "" {
|
||||||
|
slf.password = &password
|
||||||
|
}
|
||||||
|
return slf
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package generic
|
||||||
|
|
||||||
|
type IdR[ID comparable] interface {
|
||||||
|
GetId() ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdW[ID comparable] interface {
|
||||||
|
SetId(id ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IdRW[ID comparable] interface {
|
||||||
|
IdR[ID]
|
||||||
|
IdW[ID]
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package super
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewPermission 创建权限
|
||||||
|
func NewPermission[Code generic.Integer, EntityID comparable]() *Permission[Code, EntityID] {
|
||||||
|
return &Permission[Code, EntityID]{
|
||||||
|
permissions: make(map[EntityID]Code),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Permission[Code generic.Integer, EntityID comparable] struct {
|
||||||
|
permissions map[EntityID]Code
|
||||||
|
l sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPermission 是否有权限
|
||||||
|
func (slf *Permission[Code, EntityID]) HasPermission(entityId EntityID, permission Code) bool {
|
||||||
|
slf.l.RLock()
|
||||||
|
c, exist := slf.permissions[entityId]
|
||||||
|
slf.l.RUnlock()
|
||||||
|
if !exist {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return c&permission != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddPermission 添加权限
|
||||||
|
func (slf *Permission[Code, EntityID]) AddPermission(entityId EntityID, permission ...Code) {
|
||||||
|
|
||||||
|
slf.l.Lock()
|
||||||
|
defer slf.l.Unlock()
|
||||||
|
|
||||||
|
userPermission, exist := slf.permissions[entityId]
|
||||||
|
if !exist {
|
||||||
|
userPermission = 0
|
||||||
|
slf.permissions[entityId] = userPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range permission {
|
||||||
|
userPermission |= p
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.permissions[entityId] = userPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemovePermission 移除权限
|
||||||
|
func (slf *Permission[Code, EntityID]) RemovePermission(entityId EntityID, permission ...Code) {
|
||||||
|
slf.l.Lock()
|
||||||
|
defer slf.l.Unlock()
|
||||||
|
|
||||||
|
userPermission, exist := slf.permissions[entityId]
|
||||||
|
if !exist {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range permission {
|
||||||
|
userPermission &= ^p
|
||||||
|
}
|
||||||
|
|
||||||
|
slf.permissions[entityId] = userPermission
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPermission 设置权限
|
||||||
|
func (slf *Permission[Code, EntityID]) SetPermission(entityId EntityID, permission ...Code) {
|
||||||
|
slf.l.Lock()
|
||||||
|
defer slf.l.Unlock()
|
||||||
|
|
||||||
|
var userPermission Code
|
||||||
|
for _, p := range permission {
|
||||||
|
userPermission |= p
|
||||||
|
}
|
||||||
|
slf.permissions[entityId] = userPermission
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package super_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/super"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewPermission(t *testing.T) {
|
||||||
|
const (
|
||||||
|
Read = 1 << iota
|
||||||
|
Write
|
||||||
|
Execute
|
||||||
|
)
|
||||||
|
|
||||||
|
p := super.NewPermission[int, int]()
|
||||||
|
p.AddPermission(1, Read, Write)
|
||||||
|
t.Log(p.HasPermission(1, Read))
|
||||||
|
t.Log(p.HasPermission(1, Write))
|
||||||
|
p.SetPermission(2, Read|Write)
|
||||||
|
t.Log(p.HasPermission(2, Read))
|
||||||
|
t.Log(p.HasPermission(2, Execute))
|
||||||
|
p.SetPermission(2, Execute)
|
||||||
|
t.Log(p.HasPermission(2, Execute))
|
||||||
|
t.Log(p.HasPermission(2, Read))
|
||||||
|
t.Log(p.HasPermission(2, Write))
|
||||||
|
p.RemovePermission(2, Execute)
|
||||||
|
t.Log(p.HasPermission(2, Execute))
|
||||||
|
}
|
Loading…
Reference in New Issue