diff --git a/game/space/README.md b/game/space/README.md new file mode 100644 index 0000000..aea2cae --- /dev/null +++ b/game/space/README.md @@ -0,0 +1,54 @@ +# Space + +[![Go doc](https://img.shields.io/badge/go.dev-reference-brightgreen?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space) + +计划提供游戏中常见的空间设计,例如房间、地图等。开发者可以使用它来快速构建游戏中的常见空间,例如多人房间、地图等。 + +## Room [`房间`]((https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomManager)) +房间在 `Minotaur` 中仅仅只是一个可以为任意可比较类型的 `ID`,当需要将现有或新设计的房间纳入 [`RoomManager`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomManager) 管理时,需要实现 [`Room`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomManager) 管理时,仅需要实现 [`generic.IdR`](https://pkg.go.dev/github.com/kercylan98/minotaur/utils/generic#IdR) 接口即可。 + +该功能由 +[`RoomManager`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomManager)、 +[`RoomController`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomController) +组成。 + +当创建一个新的房间并纳入 [`RoomManager`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomManager) 管理后,将会得到一个 [`RoomController`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomController)。通过 [`RoomController`](https://pkg.go.dev/github.com/kercylan98/minotaur/game/space#RoomController) 可以对房间进行管理,例如:获取房间信息、加入房间、退出房间等。 + +### 使用示例 +```go +package main + +import ( + "fmt" + "github.com/kercylan98/minotaur/game/space" +) + +type Room struct { + Id int64 +} + +func (r *Room) GetId() int64 { + return r.Id +} + +type Player struct { + Id string +} + +func (p *Player) GetId() string { + return p.Id +} + +func main() { + var rm = space.NewRoomManager[string, int64, *Player, *Room]() + var room = &Room{Id: 1} + var controller = rm.AssumeControl(room) + + if err := controller.AddEntity(&Player{Id: "1"}); err != nil { + // 房间密码不匹配或者房间已满 + panic(err) + } + + fmt.Println(controller.GetEntityCount()) // 1 +} +``` \ No newline at end of file diff --git a/game/space/doc.go b/game/space/doc.go new file mode 100644 index 0000000..4cff87f --- /dev/null +++ b/game/space/doc.go @@ -0,0 +1,2 @@ +// Package space 游戏中常见的空间设计,例如房间、地图等 +package space diff --git a/game/space/room_controller.go b/game/space/room_controller.go index b409352..f67f693 100644 --- a/game/space/room_controller.go +++ b/game/space/room_controller.go @@ -36,59 +36,59 @@ type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[E // 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] +func (rc *RoomController[EntityID, RoomID, Entity, Room]) JoinSeat(entityId EntityID, seat ...int) error { + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() + _, exist := rc.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 { + if targetSeat < len(rc.seat) && rc.seat[targetSeat] != nil { return ErrSeatNotEmpty } } else { - if len(slf.vacancy) > 0 { - targetSeat = slf.vacancy[0] - slf.vacancy = slf.vacancy[1:] + if len(rc.vacancy) > 0 { + targetSeat = rc.vacancy[0] + rc.vacancy = rc.vacancy[1:] } else { - targetSeat = len(slf.seat) + targetSeat = len(rc.seat) } } - if targetSeat >= len(slf.seat) { - slf.seat = append(slf.seat, make([]*EntityID, targetSeat-len(slf.seat)+1)...) + if targetSeat >= len(rc.seat) { + rc.seat = append(rc.seat, make([]*EntityID, targetSeat-len(rc.seat)+1)...) } - slf.seat[targetSeat] = &entityId + rc.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) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) LeaveSeat(entityId EntityID) { + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() + rc.leaveSeat(entityId) } // leaveSeat 离开座位(无锁) -func (slf *RoomController[EntityID, RoomID, Entity, Room]) leaveSeat(entityId EntityID) { - for i, seat := range slf.seat { +func (rc *RoomController[EntityID, RoomID, Entity, Room]) leaveSeat(entityId EntityID) { + for i, seat := range rc.seat { if seat != nil && *seat == entityId { - slf.seat[i] = nil - slf.vacancy = append(slf.vacancy, i) + rc.seat[i] = nil + rc.vacancy = append(rc.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 { +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeat(entityId EntityID) int { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + for i, seat := range rc.seat { if seat != nil && *seat == entityId { return i } @@ -97,11 +97,11 @@ func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeat(entityId Enti } // GetNotEmptySeat 获取非空座位 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []int { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []int { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() var seats []int - for i, player := range slf.seat { + for i, player := range rc.seat { if player != nil { seats = append(seats, i) } @@ -111,21 +111,21 @@ func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetNotEmptySeat() []i // GetEmptySeat 获取空座位 // - 空座位需要在有对象离开座位后才可能出现 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int { - return slice.Copy(slf.vacancy) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEmptySeat() []int { + return slice.Copy(rc.vacancy) } // HasSeat 判断是否有座位 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) HasSeat(entityId EntityID) bool { - return slf.GetSeat(entityId) != -1 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) HasSeat(entityId EntityID) bool { + return rc.GetSeat(entityId) != -1 } // GetSeatEntityCount 获取座位上的实体数量 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntityCount() int { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntityCount() int { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() var count int - for _, seat := range slf.seat { + for _, seat := range rc.seat { if seat != nil { count++ } @@ -134,213 +134,219 @@ func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntityCount() } // GetSeatEntities 获取座位上的实体 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntities() map[EntityID]Entity { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntities() map[EntityID]Entity { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() var entities = make(map[EntityID]Entity) - for _, entityId := range slf.seat { + for _, entityId := range rc.seat { if entityId != nil { - entities[*entityId] = slf.entities[*entityId] + entities[*entityId] = rc.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 { +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntitiesByOrdered() []Entity { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + var entities = make([]Entity, 0, len(rc.seat)) + for _, entityId := range rc.seat { if entityId != nil { - entities = append(entities, slf.entities[*entityId]) + entities = append(entities, rc.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 { +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntitiesByOrderedAndContainsEmpty() []Entity { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + var entities = make([]Entity, len(rc.seat)) + for i, entityId := range rc.seat { if entityId != nil { - entities[i] = slf.entities[*entityId] + entities[i] = rc.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] +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetSeatEntity(seat int) (entity Entity) { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + if seat < len(rc.seat) { + eid := rc.seat[seat] if eid != nil { - return slf.entities[*eid] + return rc.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] +func (rc *RoomController[EntityID, RoomID, Entity, Room]) ContainEntity(id EntityID) bool { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + _, exist := rc.entities[id] return exist } -// GetRoom 获取原始房间实例 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoom() Room { - return slf.room +// GetRoom 获取原始房间实例,该实例为被接管的房间的原始实例 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoom() Room { + return rc.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) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntities() map[EntityID]Entity { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + return hash.Copy(rc.entities) } // HasEntity 判断是否有实体 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) HasEntity(id EntityID) bool { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() - _, exist := slf.entities[id] +func (rc *RoomController[EntityID, RoomID, Entity, Room]) HasEntity(id EntityID) bool { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + _, exist := rc.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] +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntity(id EntityID) Entity { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + return rc.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) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityIDs() []EntityID { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + return hash.KeyToSlice(rc.entities) } // GetEntityCount 获取实体数量 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetEntityCount() int { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() - return len(slf.entities) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetEntityCount() int { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + return len(rc.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) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) ChangePassword(password *string) { + old := rc.options.password + rc.options.password = password + rc.manager.OnRoomChangePasswordEvent(rc, old, rc.options.password) } -// AddEntity 添加实体 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) AddEntity(entity Entity) error { - if slf.options.password != nil { +// AddEntity 添加实体,如果房间存在密码,应使用 AddEntityByPassword 函数进行添加,否则将始终返回 ErrRoomPasswordNotMatch 错误 +// - 当房间已满时,将会返回 ErrRoomFull 错误 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) AddEntity(entity Entity) error { + if rc.options.password != nil { return ErrRoomPasswordNotMatch } - slf.entitiesRWMutex.Lock() - defer slf.entitiesRWMutex.Unlock() + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() - if slf.options.maxEntityCount != nil && len(slf.entities) > *slf.options.maxEntityCount { + if rc.options.maxEntityCount != nil && len(rc.entities) > *rc.options.maxEntityCount { return ErrRoomFull } - slf.entities[entity.GetId()] = entity + rc.entities[entity.GetId()] = entity - slf.manager.OnRoomAddEntityEvent(slf, entity) + rc.manager.OnRoomAddEntityEvent(rc, 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 { +// AddEntityByPassword 通过房间密码添加实体到该房间中 +// - 当未设置房间密码时,password 参数将会被忽略 +// - 当房间密码不匹配时,将会返回 ErrRoomPasswordNotMatch 错误 +// - 当房间已满时,将会返回 ErrRoomFull 错误 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) AddEntityByPassword(entity Entity, password string) error { + if rc.options.password == nil || *rc.options.password != password { return ErrRoomPasswordNotMatch } - slf.entitiesRWMutex.Lock() - defer slf.entitiesRWMutex.Unlock() + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() - if slf.options.maxEntityCount != nil && len(slf.entities) > *slf.options.maxEntityCount { + if rc.options.maxEntityCount != nil && len(rc.entities) > *rc.options.maxEntityCount { return ErrRoomFull } - slf.entities[entity.GetId()] = entity + rc.entities[entity.GetId()] = entity - slf.manager.OnRoomAddEntityEvent(slf, entity) + rc.manager.OnRoomAddEntityEvent(rc, entity) return nil } // RemoveEntity 移除实体 // - 当实体被移除时如果实体在座位上,将会自动离开座位 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) RemoveEntity(id EntityID) { - slf.entitiesRWMutex.RLock() - defer slf.entitiesRWMutex.RUnlock() - slf.removeEntity(id) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) RemoveEntity(id EntityID) { + rc.entitiesRWMutex.RLock() + defer rc.entitiesRWMutex.RUnlock() + rc.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) +func (rc *RoomController[EntityID, RoomID, Entity, Room]) removeEntity(id EntityID) { + rc.leaveSeat(id) + entity, exist := rc.entities[id] + delete(rc.entities, id) if !exist { return } - slf.manager.OnRoomRemoveEntityEvent(slf, entity) + rc.manager.OnRoomRemoveEntityEvent(rc, 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) +// RemoveAllEntities 移除该房间中的所有实体 +// - 当实体被移除时如果实体在座位上,将会自动离开座位 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) RemoveAllEntities() { + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() + for id := range rc.entities { + rc.removeEntity(id) + delete(rc.entities, id) } } -// Destroy 销毁房间 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) Destroy() { - slf.manager.roomsRWMutex.Lock() - defer slf.manager.roomsRWMutex.Unlock() +// Destroy 销毁房间,房间会从 RoomManager 中移除,同时所有房间的实体、座位等数据都会被清空 +// - 该函数与 RoomManager.DestroyRoom 相同,RoomManager.DestroyRoom 函数为该函数的快捷方式 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) Destroy() { + rc.manager.roomsRWMutex.Lock() + defer rc.manager.roomsRWMutex.Unlock() - delete(slf.manager.rooms, slf.room.GetId()) - slf.manager.OnRoomDestroyEvent(slf) + delete(rc.manager.rooms, rc.room.GetId()) + rc.manager.OnRoomDestroyEvent(rc) - slf.entitiesRWMutex.Lock() - defer slf.entitiesRWMutex.Unlock() + rc.entitiesRWMutex.Lock() + defer rc.entitiesRWMutex.Unlock() - for eid := range slf.entities { - slf.removeEntity(eid) - delete(slf.entities, eid) + for eid := range rc.entities { + rc.removeEntity(eid) + delete(rc.entities, eid) } - slf.entities = make(map[EntityID]Entity) - slf.seat = slf.seat[:] - slf.vacancy = slf.vacancy[:] + rc.entities = make(map[EntityID]Entity) + rc.seat = rc.seat[:] + rc.vacancy = rc.vacancy[:] } -// GetRoomManager 获取房间管理器 -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoomManager() *RoomManager[EntityID, RoomID, Entity, Room] { - return slf.manager +// GetRoomManager 获取该房间控制器所属的房间管理器 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoomManager() *RoomManager[EntityID, RoomID, Entity, Room] { + return rc.manager } -// GetRoomID 获取房间ID -func (slf *RoomController[EntityID, RoomID, Entity, Room]) GetRoomID() RoomID { - return slf.room.GetId() +// GetRoomID 获取房间 ID +func (rc *RoomController[EntityID, RoomID, Entity, Room]) GetRoomID() RoomID { + return rc.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() +// Broadcast 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理 +func (rc *RoomController[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) { + rc.entitiesRWMutex.RLock() + entities := hash.Copy(rc.entities) + rc.entitiesRWMutex.RUnlock() for _, entity := range entities { for _, condition := range conditions { if !condition(entity) { diff --git a/game/space/room_manager.go b/game/space/room_manager.go index a142e17..5099c07 100644 --- a/game/space/room_manager.go +++ b/game/space/room_manager.go @@ -6,7 +6,7 @@ import ( "sync" ) -// NewRoomManager 创建房间管理器 +// NewRoomManager 创建房间管理器 RoomManager 的实例 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]), @@ -14,64 +14,68 @@ func NewRoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[E } } -// RoomManager 房间管理器 +// 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) +// AssumeControl 将房间控制权交由 RoomManager 接管,返回 RoomController 实例 +// - 当任何房间需要被 RoomManager 管理时,都应该调用该方法获取到 RoomController 实例后进行操作 +// - 房间被接管后需要在释放房间控制权时调用 RoomController.Destroy 方法,否则将会导致 RoomManager 一直持有房间资源 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) AssumeControl(room Room, options ...*RoomControllerOptions) *RoomController[EntityID, RoomID, Entity, Room] { + controller := newRoomController(rm, room, mergeRoomControllerOptions(options...)) + rm.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() +// DestroyRoom 销毁房间,该函数为 RoomController.Destroy 的快捷方式 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) DestroyRoom(id RoomID) { + rm.roomsRWMutex.Lock() + room, exist := rm.rooms[id] + rm.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] +// GetRoom 通过房间 ID 获取对应房间的控制器 RoomController,当房间不存在时将返回 nil +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoom(id RoomID) *RoomController[EntityID, RoomID, Entity, Room] { + rm.roomsRWMutex.RLock() + defer rm.roomsRWMutex.RUnlock() + return rm.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) +// GetRooms 获取包含所有房间 ID 到对应控制器 RoomController 的映射 +// - 返回值的 map 为拷贝对象,可安全的对其进行增删等操作 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] { + rm.roomsRWMutex.RLock() + defer rm.roomsRWMutex.RUnlock() + return hash.Copy(rm.rooms) } -// GetRoomCount 获取房间数量 -func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomCount() int { - slf.roomsRWMutex.RLock() - defer slf.roomsRWMutex.RUnlock() - return len(slf.rooms) +// GetRoomCount 获取房间管理器接管的房间数量 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomCount() int { + rm.roomsRWMutex.RLock() + defer rm.roomsRWMutex.RUnlock() + return len(rm.rooms) } -// GetRoomIDs 获取所有房间ID -func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID { - slf.roomsRWMutex.RLock() - defer slf.roomsRWMutex.RUnlock() - return hash.KeyToSlice(slf.rooms) +// GetRoomIDs 获取房间管理器接管的所有房间 ID +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetRoomIDs() []RoomID { + rm.roomsRWMutex.RLock() + defer rm.roomsRWMutex.RUnlock() + return hash.KeyToSlice(rm.rooms) } -// HasEntity 判断特定对象是否在任一房间中 -func (slf *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool { - slf.roomsRWMutex.RLock() - rooms := hash.Copy(slf.rooms) - slf.roomsRWMutex.RUnlock() +// HasEntity 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId EntityID) bool { + rm.roomsRWMutex.RLock() + rooms := hash.Copy(rm.rooms) + rm.roomsRWMutex.RUnlock() for _, room := range rooms { if room.HasEntity(entityId) { return true @@ -80,11 +84,12 @@ func (slf *RoomManager[EntityID, RoomID, Entity, Room]) HasEntity(entityId Entit 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() +// GetEntityRooms 获取特定对象所在的房间,返回值为房间 ID 到对应控制器 RoomController 的映射 +// - 由于一个对象可能在多个房间中,因此返回值为 map 类型 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room] { + rm.roomsRWMutex.RLock() + rooms := hash.Copy(rm.rooms) + rm.roomsRWMutex.RUnlock() var result = make(map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]) for id, room := range rooms { if room.HasEntity(entityId) { @@ -94,11 +99,11 @@ func (slf *RoomManager[EntityID, RoomID, Entity, Room]) GetEntityRooms(entityId 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() +// Broadcast 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法 +func (rm *RoomManager[EntityID, RoomID, Entity, Room]) Broadcast(handler func(Entity), conditions ...func(Entity) bool) { + rm.roomsRWMutex.RLock() + rooms := hash.Copy(rm.rooms) + rm.roomsRWMutex.RUnlock() for _, room := range rooms { room.Broadcast(handler, conditions...) } diff --git a/game/space/room_manager_events.go b/game/space/room_manager_events.go index 942594b..cf15f0d 100644 --- a/game/space/room_manager_events.go +++ b/game/space/room_manager_events.go @@ -19,61 +19,61 @@ type roomManagerEvents[EntityID comparable, RoomID comparable, Entity generic.Id } // RegRoomAssumeControlEvent 注册房间接管事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAssumeControlEvent(handle RoomAssumeControlEventHandle[EntityID, RoomID, Entity, Room]) { - slf.roomAssumeControlEventHandles = append(slf.roomAssumeControlEventHandles, handle) +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAssumeControlEvent(handle RoomAssumeControlEventHandle[EntityID, RoomID, Entity, Room]) { + rme.roomAssumeControlEventHandles = append(rme.roomAssumeControlEventHandles, handle) } // OnRoomAssumeControlEvent 房间接管事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAssumeControlEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) { - for _, handle := range slf.roomAssumeControlEventHandles { +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAssumeControlEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) { + for _, handle := range rme.roomAssumeControlEventHandles { handle(controller) } } // RegRoomDestroyEvent 注册房间销毁事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomDestroyEvent(handle RoomDestroyEventHandle[EntityID, RoomID, Entity, Room]) { - slf.roomDestroyEventHandles = append(slf.roomDestroyEventHandles, handle) +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomDestroyEvent(handle RoomDestroyEventHandle[EntityID, RoomID, Entity, Room]) { + rme.roomDestroyEventHandles = append(rme.roomDestroyEventHandles, handle) } // OnRoomDestroyEvent 房间销毁事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomDestroyEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) { - for _, handle := range slf.roomDestroyEventHandles { +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomDestroyEvent(controller *RoomController[EntityID, RoomID, Entity, Room]) { + for _, handle := range rme.roomDestroyEventHandles { handle(controller) } } // RegRoomAddEntityEvent 注册房间添加对象事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAddEntityEvent(handle RoomAddEntityEventHandle[EntityID, RoomID, Entity, Room]) { - slf.roomAddEntityEventHandles = append(slf.roomAddEntityEventHandles, handle) +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomAddEntityEvent(handle RoomAddEntityEventHandle[EntityID, RoomID, Entity, Room]) { + rme.roomAddEntityEventHandles = append(rme.roomAddEntityEventHandles, handle) } // OnRoomAddEntityEvent 房间添加对象事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAddEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) { - for _, handle := range slf.roomAddEntityEventHandles { +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomAddEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) { + for _, handle := range rme.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) +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomRemoveEntityEvent(handle RoomRemoveEntityEventHandle[EntityID, RoomID, Entity, Room]) { + rme.roomRemoveEntityEventHandles = append(rme.roomRemoveEntityEventHandles, handle) } // OnRoomRemoveEntityEvent 房间移除对象事件 -func (slf *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomRemoveEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) { - for _, handle := range slf.roomRemoveEntityEventHandles { +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomRemoveEntityEvent(controller *RoomController[EntityID, RoomID, Entity, Room], entity Entity) { + for _, handle := range rme.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) +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) RegRoomChangePasswordEvent(handle RoomChangePasswordEventHandle[EntityID, RoomID, Entity, Room]) { + rme.roomChangePasswordEventHandles = append(rme.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 { +func (rme *roomManagerEvents[EntityID, RoomID, Entity, Room]) OnRoomChangePasswordEvent(controller *RoomController[EntityID, RoomID, Entity, Room], oldPassword, password *string) { + for _, handle := range rme.roomChangePasswordEventHandles { handle(controller, oldPassword, password) } } diff --git a/game/space/room_manager_example_test.go b/game/space/room_manager_example_test.go new file mode 100644 index 0000000..c97f878 --- /dev/null +++ b/game/space/room_manager_example_test.go @@ -0,0 +1,46 @@ +package space_test + +import ( + "fmt" + "github.com/kercylan98/minotaur/game/space" +) + +type Room struct { + Id int64 +} + +func (r *Room) GetId() int64 { + return r.Id +} + +type Player struct { + Id string +} + +func (p *Player) GetId() string { + return p.Id +} + +func ExampleNewRoomManager() { + var rm = space.NewRoomManager[string, int64, *Player, *Room]() + fmt.Println(rm == nil) + + // Output: + // false +} + +func ExampleRoomManager_AssumeControl() { + var rm = space.NewRoomManager[string, int64, *Player, *Room]() + var room = &Room{Id: 1} + var controller = rm.AssumeControl(room) + + if err := controller.AddEntity(&Player{Id: "1"}); err != nil { + // 房间密码不匹配或者房间已满 + panic(err) + } + + fmt.Println(controller.GetEntityCount()) + + // Output: + // 1 +} diff --git a/game/space/room_options.go b/game/space/room_options.go index f563c5d..33661be 100644 --- a/game/space/room_options.go +++ b/game/space/room_options.go @@ -22,17 +22,17 @@ type RoomControllerOptions struct { } // WithMaxEntityCount 设置房间最大实体数量 -func (slf *RoomControllerOptions) WithMaxEntityCount(maxEntityCount int) *RoomControllerOptions { +func (rco *RoomControllerOptions) WithMaxEntityCount(maxEntityCount int) *RoomControllerOptions { if maxEntityCount > 0 { - slf.maxEntityCount = &maxEntityCount + rco.maxEntityCount = &maxEntityCount } - return slf + return rco } // WithPassword 设置房间密码 -func (slf *RoomControllerOptions) WithPassword(password string) *RoomControllerOptions { +func (rco *RoomControllerOptions) WithPassword(password string) *RoomControllerOptions { if password != "" { - slf.password = &password + rco.password = &password } - return slf + return rco } diff --git a/server/event.go b/server/event.go index 5d5cfdd..d79b9eb 100644 --- a/server/event.go +++ b/server/event.go @@ -15,23 +15,25 @@ import ( "time" ) -type StartBeforeEventHandler func(srv *Server) -type StartFinishEventHandler func(srv *Server) -type StopEventHandler func(srv *Server) -type ConnectionReceivePacketEventHandler func(srv *Server, conn *Conn, packet []byte) -type ConnectionOpenedEventHandler func(srv *Server, conn *Conn) -type ConnectionClosedEventHandler func(srv *Server, conn *Conn, err any) -type MessageErrorEventHandler func(srv *Server, message *Message, err error) -type MessageLowExecEventHandler func(srv *Server, message *Message, cost time.Duration) -type ConsoleCommandEventHandler func(srv *Server, command string, params ConsoleParams) -type ConnectionOpenedAfterEventHandler func(srv *Server, conn *Conn) -type ConnectionWritePacketBeforeEventHandler func(srv *Server, conn *Conn, packet []byte) []byte -type ShuntChannelCreatedEventHandler func(srv *Server, guid int64) -type ShuntChannelClosedEventHandler func(srv *Server, guid int64) -type ConnectionPacketPreprocessEventHandler func(srv *Server, conn *Conn, packet []byte, abort func(), usePacket func(newPacket []byte)) -type MessageExecBeforeEventHandler func(srv *Server, message *Message) bool -type MessageReadyEventHandler func(srv *Server) -type OnDeadlockDetectEventHandler func(srv *Server, message *Message) +type ( + StartBeforeEventHandler func(srv *Server) + StartFinishEventHandler func(srv *Server) + StopEventHandler func(srv *Server) + ConnectionReceivePacketEventHandler func(srv *Server, conn *Conn, packet []byte) + ConnectionOpenedEventHandler func(srv *Server, conn *Conn) + ConnectionClosedEventHandler func(srv *Server, conn *Conn, err any) + MessageErrorEventHandler func(srv *Server, message *Message, err error) + MessageLowExecEventHandler func(srv *Server, message *Message, cost time.Duration) + ConsoleCommandEventHandler func(srv *Server, command string, params ConsoleParams) + ConnectionOpenedAfterEventHandler func(srv *Server, conn *Conn) + ConnectionWritePacketBeforeEventHandler func(srv *Server, conn *Conn, packet []byte) []byte + ShuntChannelCreatedEventHandler func(srv *Server, guid int64) + ShuntChannelClosedEventHandler func(srv *Server, guid int64) + ConnectionPacketPreprocessEventHandler func(srv *Server, conn *Conn, packet []byte, abort func(), usePacket func(newPacket []byte)) + MessageExecBeforeEventHandler func(srv *Server, message *Message) bool + MessageReadyEventHandler func(srv *Server) + OnDeadlockDetectEventHandler func(srv *Server, message *Message) +) func newEvent(srv *Server) *event { return &event{ @@ -51,7 +53,7 @@ func newEvent(srv *Server) *event { connectionPacketPreprocessEventHandlers: slice.NewPriority[ConnectionPacketPreprocessEventHandler](), messageExecBeforeEventHandlers: slice.NewPriority[MessageExecBeforeEventHandler](), messageReadyEventHandlers: slice.NewPriority[MessageReadyEventHandler](), - dedeadlockDetectEventHandlers: slice.NewPriority[OnDeadlockDetectEventHandler](), + deadlockDetectEventHandlers: slice.NewPriority[OnDeadlockDetectEventHandler](), } } @@ -72,7 +74,7 @@ type event struct { connectionPacketPreprocessEventHandlers *slice.Priority[ConnectionPacketPreprocessEventHandler] messageExecBeforeEventHandlers *slice.Priority[MessageExecBeforeEventHandler] messageReadyEventHandlers *slice.Priority[MessageReadyEventHandler] - dedeadlockDetectEventHandlers *slice.Priority[OnDeadlockDetectEventHandler] + deadlockDetectEventHandlers *slice.Priority[OnDeadlockDetectEventHandler] consoleCommandEventHandlers map[string]*slice.Priority[ConsoleCommandEventHandler] consoleCommandEventHandlerInitOnce sync.Once @@ -440,11 +442,11 @@ func (slf *event) OnMessageReadyEvent() { // RegDeadlockDetectEvent 在死锁检测触发时立即执行被注册的事件处理函数 func (slf *event) RegDeadlockDetectEvent(handler OnDeadlockDetectEventHandler, priority ...int) { - slf.dedeadlockDetectEventHandlers.Append(handler, slice.GetValue(priority, 0)) + slf.deadlockDetectEventHandlers.Append(handler, slice.GetValue(priority, 0)) } func (slf *event) OnDeadlockDetectEvent(message *Message) { - if slf.dedeadlockDetectEventHandlers.Len() == 0 { + if slf.deadlockDetectEventHandlers.Len() == 0 { return } defer func() { @@ -453,7 +455,7 @@ func (slf *event) OnDeadlockDetectEvent(message *Message) { debug.PrintStack() } }() - slf.dedeadlockDetectEventHandlers.RangeValue(func(index int, value OnDeadlockDetectEventHandler) bool { + slf.deadlockDetectEventHandlers.RangeValue(func(index int, value OnDeadlockDetectEventHandler) bool { value(slf.Server, message) return true }) diff --git a/utils/super/f.go b/utils/super/function.go similarity index 100% rename from utils/super/f.go rename to utils/super/function.go