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/slice"
|
||||
"github.com/kercylan98/minotaur/utils/timer"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -65,6 +66,8 @@ type Round[Data RoundData] struct {
|
|||
changeEventHandles []RoundChangeEvent[Data] // 游戏回合变更事件
|
||||
actionTimeoutEventHandles []RoundActionTimeoutEvent[Data] // 行动超时事件
|
||||
actionRefreshEventHandles []RoundActionRefreshEvent[Data] // 行动刷新事件
|
||||
|
||||
actionMutex sync.Mutex
|
||||
}
|
||||
|
||||
// GetData 获取游戏数据
|
||||
|
@ -77,6 +80,8 @@ func (slf *Round[Data]) GetData() Data {
|
|||
func (slf *Round[Data]) Start() {
|
||||
slf.currentEntity = -1
|
||||
slf.round = 1
|
||||
slf.actionMutex.Lock()
|
||||
defer slf.actionMutex.Unlock()
|
||||
slf.loop(false)
|
||||
}
|
||||
|
||||
|
@ -157,7 +162,11 @@ func (slf *Round[Data]) SkipCamp() {
|
|||
// ActionRefresh 刷新行动超时时间
|
||||
func (slf *Round[Data]) ActionRefresh() {
|
||||
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 结束行动
|
||||
|
@ -166,6 +175,8 @@ func (slf *Round[Data]) ActionFinish() {
|
|||
if slf.shareAction {
|
||||
slf.currentEntity = -1
|
||||
}
|
||||
slf.actionMutex.Lock()
|
||||
defer slf.actionMutex.Unlock()
|
||||
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{}) {
|
||||
for playerId := range players {
|
||||
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
|
||||
}
|
||||
delete(rooms, roomId)
|
||||
if len(rooms) == 0 {
|
||||
slf.players.Delete(player.GetID())
|
||||
}
|
||||
})
|
||||
slf.rp.Atom(func(m map[int64]map[PID]struct{}) {
|
||||
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 {
|
||||
ID string
|
||||
}
|
||||
|
||||
func (slf *Player) GetID() string {
|
||||
return ""
|
||||
return slf.ID
|
||||
}
|
||||
|
||||
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