docs: 优化泛型结构体函数的文档展示

This commit is contained in:
kercylan98 2024-01-15 11:46:08 +08:00
parent 5ea3202732
commit 6e6f33899b
15 changed files with 1464 additions and 6 deletions

View File

@ -201,6 +201,32 @@ type Controller[Type generic.Basic, ID generic.Basic, Data any, EntityID generic
mutex sync.RWMutex
}
```
#### func (*Controller) GetGlobalData(activityId ID) Data
> 获取特定活动全局数据
***
#### func (*Controller) GetEntityData(activityId ID, entityId EntityID) EntityData
> 获取特定活动实体数据
***
#### func (*Controller) IsOpen(activityId ID) bool
> 活动是否开启
***
#### func (*Controller) IsShow(activityId ID) bool
> 活动是否展示
***
#### func (*Controller) IsOpenOrShow(activityId ID) bool
> 活动是否开启或展示
***
#### func (*Controller) Refresh(activityId ID)
> 刷新活动
***
#### func (*Controller) InitializeNoneData(handler func (activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData]
***
#### func (*Controller) InitializeGlobalData(handler func (activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData]
***
#### func (*Controller) InitializeEntityData(handler func (activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData]
***
#### func (*Controller) InitializeGlobalAndEntityData(handler func (activityId ID, data *DataMeta[Data]), entityHandler func (activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData]
***
<span id="struct_BasicActivityController"></span>
### BasicActivityController `INTERFACE`

View File

@ -65,6 +65,63 @@ type TurnBased[CampID comparable, EntityID comparable, Camp generic.IdR[CampID],
closed bool
}
```
#### func (*TurnBased) Close()
> 关闭回合制
***
#### func (*TurnBased) AddCamp(camp Camp, entity Entity, entities ...Entity)
> 添加阵营
***
#### func (*TurnBased) SetActionTimeout(actionTimeoutHandler func ( Camp, Entity) time.Duration)
> 设置行动超时时间处理函数
> - 默认情况下行动超时时间函数将始终返回 0
***
#### func (*TurnBased) Run()
> 运行
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestTurnBased_Run(t *testing.T) {
tbi := fight.NewTurnBased[string, string, *Camp, *Entity](func(camp *Camp, entity *Entity) time.Duration {
return time.Duration(float64(time.Second) / entity.speed)
})
tbi.SetActionTimeout(func(camp *Camp, entity *Entity) time.Duration {
return time.Second * 5
})
tbi.RegTurnBasedEntityActionTimeoutEvent(func(controller fight.TurnBasedControllerInfo[string, string, *Camp, *Entity]) {
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "阵营", controller.GetCamp().GetId(), "实体", controller.GetEntity().GetId(), "超时")
})
tbi.RegTurnBasedRoundChangeEvent(func(controller fight.TurnBasedControllerInfo[string, string, *Camp, *Entity]) {
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "回合切换")
})
tbi.RegTurnBasedEntitySwitchEvent(func(controller fight.TurnBasedControllerAction[string, string, *Camp, *Entity]) {
switch controller.GetEntity().GetId() {
case "1":
go func() {
time.Sleep(time.Second * 2)
controller.Finish()
}()
case "2":
controller.Refresh(time.Second)
case "4":
controller.Stop()
}
t.Log("时间", time.Now().Unix(), "回合", controller.GetRound(), "阵营", controller.GetCamp().GetId(), "实体", controller.GetEntity().GetId(), "开始行动")
})
tbi.AddCamp(&Camp{id: "1"}, &Entity{id: "1", speed: 1}, &Entity{id: "2", speed: 1})
tbi.AddCamp(&Camp{id: "2"}, &Entity{id: "3", speed: 1}, &Entity{id: "4", speed: 1})
tbi.Run()
}
```
</details>
***
<span id="struct_TurnBasedControllerInfo"></span>
### TurnBasedControllerInfo `INTERFACE`
@ -97,6 +154,34 @@ type TurnBasedController[CampID comparable, EntityID comparable, Camp generic.Id
tb *TurnBased[CampID, EntityID, Camp, Entity]
}
```
#### func (*TurnBasedController) GetRound() int
> 获取当前回合数
***
#### func (*TurnBasedController) GetCamp() Camp
> 获取当前操作阵营
***
#### func (*TurnBasedController) GetEntity() Entity
> 获取当前操作实体
***
#### func (*TurnBasedController) GetActionTimeoutDuration() time.Duration
> 获取当前行动超时时长
***
#### func (*TurnBasedController) GetActionStartTime() time.Time
> 获取当前行动开始时间
***
#### func (*TurnBasedController) GetActionEndTime() time.Time
> 获取当前行动结束时间
***
#### func (*TurnBasedController) Finish()
> 结束当前操作,将立即切换到下一个操作实体
***
#### func (*TurnBasedController) Stop()
> 在当前回合执行完毕后停止回合进程
***
#### func (*TurnBasedController) Refresh(duration time.Duration) time.Time
> 刷新当前操作实体的行动超时时间
> - 当不在行动阶段时,将返回 time.Time 零值
***
<span id="struct_TurnBasedEntitySwitchEventHandler"></span>
### TurnBasedEntitySwitchEventHandler `STRUCT`

View File

@ -70,6 +70,132 @@ type RoomController[EntityID comparable, RoomID comparable, Entity generic.IdR[E
owner *EntityID
}
```
#### func (*RoomController) HasOwner() bool
> 判断是否有房主
***
#### func (*RoomController) IsOwner(entityId EntityID) bool
> 判断是否为房主
***
#### func (*RoomController) GetOwner() Entity
> 获取房主
***
#### func (*RoomController) GetOwnerID() EntityID
> 获取房主 ID
***
#### func (*RoomController) GetOwnerExist() ( Entity, bool)
> 获取房间,并返回房主是否存在的状态
***
#### func (*RoomController) SetOwner(entityId EntityID)
> 设置房主
***
#### func (*RoomController) DelOwner()
> 删除房主,将房间设置为无主的状态
***
#### func (*RoomController) JoinSeat(entityId EntityID, seat ...int) error
> 设置特定对象加入座位,当具体的座位不存在的时候,将会自动分配座位
> - 当目标座位存在玩家或未添加到房间中的时候,将会返回错误
***
#### func (*RoomController) LeaveSeat(entityId EntityID)
> 离开座位
***
#### func (*RoomController) GetSeat(entityId EntityID) int
> 获取座位
***
#### func (*RoomController) GetFirstNotEmptySeat() int
> 获取第一个非空座位号,如果没有非空座位,将返回 UnknownSeat
***
#### func (*RoomController) GetFirstEmptySeatEntity() (entity Entity)
> 获取第一个空座位上的实体,如果没有空座位,将返回空实体
***
#### func (*RoomController) GetRandomEntity() (entity Entity)
> 获取随机实体,如果房间中没有实体,将返回空实体
***
#### func (*RoomController) GetNotEmptySeat() []int
> 获取非空座位
***
#### func (*RoomController) GetEmptySeat() []int
> 获取空座位
> - 空座位需要在有对象离开座位后才可能出现
***
#### func (*RoomController) HasSeat(entityId EntityID) bool
> 判断是否有座位
***
#### func (*RoomController) GetSeatEntityCount() int
> 获取座位上的实体数量
***
#### func (*RoomController) GetSeatEntities() map[EntityID]Entity
> 获取座位上的实体
***
#### func (*RoomController) GetSeatEntitiesByOrdered() []Entity
> 有序的获取座位上的实体
***
#### func (*RoomController) GetSeatEntitiesByOrderedAndContainsEmpty() []Entity
> 获取有序的座位上的实体,包含空座位
***
#### func (*RoomController) GetSeatEntity(seat int) (entity Entity)
> 获取座位上的实体
***
#### func (*RoomController) ContainEntity(id EntityID) bool
> 房间内是否包含实体
***
#### func (*RoomController) GetRoom() Room
> 获取原始房间实例,该实例为被接管的房间的原始实例
***
#### func (*RoomController) GetEntities() map[EntityID]Entity
> 获取所有实体
***
#### func (*RoomController) HasEntity(id EntityID) bool
> 判断是否有实体
***
#### func (*RoomController) GetEntity(id EntityID) Entity
> 获取实体
***
#### func (*RoomController) GetEntityExist(id EntityID) ( Entity, bool)
> 获取实体,并返回实体是否存在的状态
***
#### func (*RoomController) GetEntityIDs() []EntityID
> 获取所有实体ID
***
#### func (*RoomController) GetEntityCount() int
> 获取实体数量
***
#### func (*RoomController) ChangePassword(password *string)
> 修改房间密码
> - 当房间密码为 nil 时,将会取消密码
***
#### func (*RoomController) AddEntity(entity Entity) error
> 添加实体,如果房间存在密码,应使用 AddEntityByPassword 函数进行添加,否则将始终返回 ErrRoomPasswordNotMatch 错误
> - 当房间已满时,将会返回 ErrRoomFull 错误
***
#### func (*RoomController) AddEntityByPassword(entity Entity, password string) error
> 通过房间密码添加实体到该房间中
> - 当未设置房间密码时password 参数将会被忽略
> - 当房间密码不匹配时,将会返回 ErrRoomPasswordNotMatch 错误
> - 当房间已满时,将会返回 ErrRoomFull 错误
***
#### func (*RoomController) RemoveEntity(id EntityID)
> 移除实体
> - 当实体被移除时如果实体在座位上,将会自动离开座位
> - 如果实体为房主,将会根据 RoomControllerOptions.WithOwnerInherit 函数的设置进行继承
***
#### func (*RoomController) RemoveAllEntities()
> 移除该房间中的所有实体
> - 当实体被移除时如果实体在座位上,将会自动离开座位
> - 如果实体为房主,将会根据 RoomControllerOptions.WithOwnerInherit 函数的设置进行继承
***
#### func (*RoomController) Destroy()
> 销毁房间,房间会从 RoomManager 中移除,同时所有房间的实体、座位等数据都会被清空
> - 该函数与 RoomManager.DestroyRoom 相同RoomManager.DestroyRoom 函数为该函数的快捷方式
***
#### func (*RoomController) GetRoomManager() *RoomManager[EntityID, RoomID, Entity, Room]
> 获取该房间控制器所属的房间管理器
***
#### func (*RoomController) GetRoomID() RoomID
> 获取房间 ID
***
#### func (*RoomController) Broadcast(handler func ( Entity), conditions ...func ( Entity) bool)
> 广播,该函数会将所有房间中满足 conditions 的对象传入 handler 中进行处理
***
<span id="struct_RoomManager"></span>
### RoomManager `STRUCT`
房间管理器是用于对房间进行管理的基本单元,通过该实例可以对房间进行增删改查等操作
@ -81,6 +207,53 @@ type RoomManager[EntityID comparable, RoomID comparable, Entity generic.IdR[Enti
rooms map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
}
```
#### func (*RoomManager) AssumeControl(room Room, options ...*RoomControllerOptions[EntityID, RoomID, Entity, Room]) *RoomController[EntityID, RoomID, Entity, Room]
> 将房间控制权交由 RoomManager 接管,返回 RoomController 实例
> - 当任何房间需要被 RoomManager 管理时,都应该调用该方法获取到 RoomController 实例后进行操作
> - 房间被接管后需要在释放房间控制权时调用 RoomController.Destroy 方法,否则将会导致 RoomManager 一直持有房间资源
**示例代码:**
```go
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())
}
```
***
#### func (*RoomManager) DestroyRoom(id RoomID)
> 销毁房间,该函数为 RoomController.Destroy 的快捷方式
***
#### func (*RoomManager) GetRoom(id RoomID) *RoomController[EntityID, RoomID, Entity, Room]
> 通过房间 ID 获取对应房间的控制器 RoomController当房间不存在时将返回 nil
***
#### func (*RoomManager) GetRooms() map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
> 获取包含所有房间 ID 到对应控制器 RoomController 的映射
> - 返回值的 map 为拷贝对象,可安全的对其进行增删等操作
***
#### func (*RoomManager) GetRoomCount() int
> 获取房间管理器接管的房间数量
***
#### func (*RoomManager) GetRoomIDs() []RoomID
> 获取房间管理器接管的所有房间 ID
***
#### func (*RoomManager) HasEntity(entityId EntityID) bool
> 判断特定对象是否在任一房间中,当对象不在任一房间中时将返回 false
***
#### func (*RoomManager) GetEntityRooms(entityId EntityID) map[RoomID]*RoomController[EntityID, RoomID, Entity, Room]
> 获取特定对象所在的房间,返回值为房间 ID 到对应控制器 RoomController 的映射
> - 由于一个对象可能在多个房间中,因此返回值为 map 类型
***
#### func (*RoomManager) Broadcast(handler func ( Entity), conditions ...func ( Entity) bool)
> 向所有房间对象广播消息,该方法将会遍历所有房间控制器并调用 RoomController.Broadcast 方法
***
<span id="struct_RoomAssumeControlEventHandle"></span>
### RoomAssumeControlEventHandle `STRUCT`
@ -98,3 +271,14 @@ type RoomControllerOptions[EntityID comparable, RoomID comparable, Entity generi
ownerInheritHandler func(controller *RoomController[EntityID, RoomID, Entity, Room]) *EntityID
}
```
#### func (*RoomControllerOptions) WithOwnerInherit(inherit bool, inheritHandler ...func (controller *RoomController[EntityID, RoomID, Entity, Room]) *EntityID) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间所有者是否继承,默认为 false
> - inherit: 是否继承,当未设置 inheritHandler 且 inherit 为 true 时,将会按照随机或根据座位号顺序继承房间所有者
> - inheritHandler: 继承处理函数,当 inherit 为 true 时,该函数将会被调用,传入当前房间中的所有实体,返回值为新的房间所有者
***
#### func (*RoomControllerOptions) WithMaxEntityCount(maxEntityCount int) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间最大实体数量
***
#### func (*RoomControllerOptions) WithPassword(password string) *RoomControllerOptions[EntityID, RoomID, Entity, Room]
> 设置房间密码
***

View File

@ -176,6 +176,15 @@ type Action[P Producer, M Message[P]] struct {
d *Dispatcher[P, M]
}
```
#### func (*Action) Name() string
> 获取消息分发器名称
***
#### func (*Action) UnExpel()
> 取消特定生产者的驱逐计划
***
#### func (*Action) Expel()
> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
***
<span id="struct_Handler"></span>
### Handler `STRUCT`
消息处理器
@ -212,6 +221,362 @@ type Dispatcher[P Producer, M Message[P]] struct {
abort chan struct{}
}
```
#### func (*Dispatcher) SetProducerDoneHandler(p P, handler func (p P, dispatcher *Action[P, M])) *Dispatcher[P, M]
> 设置特定生产者所有消息处理完成时的回调函数
> - 如果 handler 为 nil则会删除该生产者的回调函数
>
> 需要注意的是,该 handler 中
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_SetProducerDoneHandler(t *testing.T) {
var cases = []struct {
name string
producer string
messageFinish *atomic.Bool
cancel bool
}{{name: "TestDispatcher_SetProducerDoneHandlerNotCancel", producer: "producer", cancel: false}, {name: "TestDispatcher_SetProducerDoneHandlerCancel", producer: "producer", cancel: true}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageFinish = &atomic.Bool{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
w.Done()
})
d.Put(&TestMessage{producer: c.producer})
d.SetProducerDoneHandler(c.producer, func(p string, dispatcher *dispatcher.Action[string, *TestMessage]) {
c.messageFinish.Store(true)
})
if c.cancel {
d.SetProducerDoneHandler(c.producer, nil)
}
w.Add(1)
d.Start()
w.Wait()
if c.cancel && c.messageFinish.Load() {
t.Errorf("%s should cancel, but not", c.name)
}
})
}
}
```
</details>
***
#### func (*Dispatcher) SetClosedHandler(handler func (dispatcher *Action[P, M])) *Dispatcher[P, M]
> 设置消息分发器关闭时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_SetClosedHandler(t *testing.T) {
var cases = []struct {
name string
handlerFinishMsgCount *atomic.Int64
msgTime time.Duration
msgCount int
}{{name: "TestDispatcher_SetClosedHandler_Normal", msgTime: 0, msgCount: 1}, {name: "TestDispatcher_SetClosedHandler_MessageCount1024", msgTime: 0, msgCount: 1024}, {name: "TestDispatcher_SetClosedHandler_MessageTime1sMessageCount3", msgTime: 1 * time.Second, msgCount: 3}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.handlerFinishMsgCount = &atomic.Int64{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
time.Sleep(c.msgTime)
c.handlerFinishMsgCount.Add(1)
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
for i := 0; i < c.msgCount; i++ {
d.Put(&TestMessage{producer: "producer"})
}
w.Add(1)
d.Start()
d.Expel()
w.Wait()
if c.handlerFinishMsgCount.Load() != int64(c.msgCount) {
t.Errorf("%s should finish %d messages, but finish %d", c.name, c.msgCount, c.handlerFinishMsgCount.Load())
}
})
}
}
```
</details>
***
#### func (*Dispatcher) Name() string
> 获取消息分发器名称
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Name(t *testing.T) {
var cases = []struct{ name string }{{name: "TestDispatcher_Name_Normal"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if d.Name() != c.name {
t.Errorf("%s should equal %s, but not", c.name, c.name)
}
})
}
}
```
</details>
***
#### func (*Dispatcher) Unique(name string) bool
> 设置唯一消息键,返回是否已存在
***
#### func (*Dispatcher) AntiUnique(name string)
> 取消唯一消息键
***
#### func (*Dispatcher) Expel()
> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Expel(t *testing.T) {
var cases = []struct {
name string
handlerFinishMsgCount *atomic.Int64
msgTime time.Duration
msgCount int
}{{name: "TestDispatcher_Expel_Normal", msgTime: 0, msgCount: 1}, {name: "TestDispatcher_Expel_MessageCount1024", msgTime: 0, msgCount: 1024}, {name: "TestDispatcher_Expel_MessageTime1sMessageCount3", msgTime: 1 * time.Second, msgCount: 3}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.handlerFinishMsgCount = &atomic.Int64{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
time.Sleep(c.msgTime)
c.handlerFinishMsgCount.Add(1)
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
for i := 0; i < c.msgCount; i++ {
d.Put(&TestMessage{producer: "producer"})
}
w.Add(1)
d.Start()
d.Expel()
w.Wait()
if c.handlerFinishMsgCount.Load() != int64(c.msgCount) {
t.Errorf("%s should finish %d messages, but finish %d", c.name, c.msgCount, c.handlerFinishMsgCount.Load())
}
})
}
}
```
</details>
***
#### func (*Dispatcher) UnExpel()
> 取消特定生产者的驱逐计划
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_UnExpel(t *testing.T) {
var cases = []struct {
name string
closed *atomic.Bool
isUnExpel bool
expect bool
}{{name: "TestDispatcher_UnExpel_Normal", isUnExpel: true, expect: false}, {name: "TestDispatcher_UnExpel_NotExpel", isUnExpel: false, expect: true}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.closed = &atomic.Bool{}
w := new(sync.WaitGroup)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
w.Done()
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
c.closed.Store(true)
})
d.Put(&TestMessage{producer: "producer"})
w.Add(1)
if c.isUnExpel {
d.Expel()
d.UnExpel()
} else {
d.Expel()
}
d.Start()
w.Wait()
if c.closed.Load() != c.expect {
t.Errorf("%s should %v, but %v", c.name, c.expect, c.closed.Load())
}
})
}
}
```
</details>
***
#### func (*Dispatcher) IncrCount(producer P, i int64)
> 主动增量设置特定生产者的消息计数,这在等待异步消息完成后再关闭消息分发器时非常有用
> - 如果 i 为负数,则会减少消息计数
***
#### func (*Dispatcher) Put(message M)
> 将消息放入分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Put(t *testing.T) {
var cases = []struct {
name string
producer string
messageDone *atomic.Bool
}{{name: "TestDispatcher_Put_Normal", producer: "producer"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageDone = &atomic.Bool{}
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
c.messageDone.Store(true)
w.Done()
})
d.Start()
d.Put(&TestMessage{producer: c.producer})
d.Expel()
w.Wait()
if !c.messageDone.Load() {
t.Errorf("%s should done, but not", c.name)
}
})
}
}
```
</details>
***
#### func (*Dispatcher) Start() *Dispatcher[P, M]
> 以非阻塞的方式开始进行消息分发,当消息分发器中没有任何消息并且处于驱逐计划 Expel 时,将会自动关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Start(t *testing.T) {
var cases = []struct {
name string
producer string
messageDone *atomic.Bool
}{{name: "TestDispatcher_Start_Normal", producer: "producer"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
c.messageDone = &atomic.Bool{}
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
c.messageDone.Store(true)
w.Done()
})
d.Start()
d.Put(&TestMessage{producer: c.producer})
d.Expel()
w.Wait()
if !c.messageDone.Load() {
t.Errorf("%s should done, but not", c.name)
}
})
}
}
```
</details>
***
#### func (*Dispatcher) Closed() bool
> 判断消息分发器是否已关闭
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestDispatcher_Closed(t *testing.T) {
var cases = []struct{ name string }{{name: "TestDispatcher_Closed_Normal"}}
for _, c := range cases {
c := c
t.Run(c.name, func(t *testing.T) {
w := new(sync.WaitGroup)
w.Add(1)
d := dispatcher.NewDispatcher(1024, c.name, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
d.SetClosedHandler(func(dispatcher *dispatcher.Action[string, *TestMessage]) {
w.Done()
})
d.Start()
d.Expel()
w.Wait()
if !d.Closed() {
t.Errorf("%s should closed, but not", c.name)
}
})
}
}
```
</details>
***
<span id="struct_Manager"></span>
### Manager `STRUCT`
消息分发器管理器
@ -229,6 +594,295 @@ type Manager[P Producer, M Message[P]] struct {
createdHandler func(name string)
}
```
#### func (*Manager) Wait()
> 等待所有消息分发器关闭
***
#### func (*Manager) SetDispatcherClosedHandler(handler func (name string)) *Manager[P, M]
> 设置消息分发器关闭时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_SetDispatcherClosedHandler(t *testing.T) {
var cases = []struct {
name string
setCloseHandler bool
}{{name: "TestManager_SetDispatcherClosedHandler_Set", setCloseHandler: true}, {name: "TestManager_SetDispatcherClosedHandler_NotSet", setCloseHandler: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var closed atomic.Bool
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if c.setCloseHandler {
m.SetDispatcherClosedHandler(func(name string) {
closed.Store(true)
})
}
m.BindProducer(c.name, c.name)
m.UnBindProducer(c.name)
m.Wait()
if c.setCloseHandler && !closed.Load() {
t.Errorf("SetDispatcherClosedHandler() should be called")
}
})
}
}
```
</details>
***
#### func (*Manager) SetDispatcherCreatedHandler(handler func (name string)) *Manager[P, M]
> 设置消息分发器创建时的回调函数
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_SetDispatcherCreatedHandler(t *testing.T) {
var cases = []struct {
name string
setCreatedHandler bool
}{{name: "TestManager_SetDispatcherCreatedHandler_Set", setCreatedHandler: true}, {name: "TestManager_SetDispatcherCreatedHandler_NotSet", setCreatedHandler: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var created atomic.Bool
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if c.setCreatedHandler {
m.SetDispatcherCreatedHandler(func(name string) {
created.Store(true)
})
}
m.BindProducer(c.name, c.name)
m.UnBindProducer(c.name)
m.Wait()
if c.setCreatedHandler && !created.Load() {
t.Errorf("SetDispatcherCreatedHandler() should be called")
}
})
}
}
```
</details>
***
#### func (*Manager) HasDispatcher(name string) bool
> 检查是否存在指定名称的消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_HasDispatcher(t *testing.T) {
var cases = []struct {
name string
bindName string
has bool
}{{name: "TestManager_HasDispatcher_Has", bindName: "TestManager_HasDispatcher_Has", has: true}, {name: "TestManager_HasDispatcher_NotHas", bindName: "TestManager_HasDispatcher_NotHas", has: false}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
var cond string
if c.has {
cond = c.bindName
}
if m.HasDispatcher(cond) != c.has {
t.Errorf("HasDispatcher() should return %v", c.has)
}
})
}
}
```
</details>
***
#### func (*Manager) GetDispatcherNum() int
> 获取当前正在工作的消息分发器数量
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetDispatcherNum(t *testing.T) {
var cases = []struct {
name string
num int
}{{name: "TestManager_GetDispatcherNum_N1", num: -1}, {name: "TestManager_GetDispatcherNum_0", num: 0}, {name: "TestManager_GetDispatcherNum_1", num: 1}, {name: "TestManager_GetDispatcherNum_2", num: 2}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
switch {
case c.num <= 0:
return
case c.num == 1:
if m.GetDispatcherNum() != 1 {
t.Errorf("GetDispatcherNum() should return 1")
}
return
default:
for i := 0; i < c.num-1; i++ {
m.BindProducer(fmt.Sprintf("%s_%d", c.name, i), fmt.Sprintf("%s_%d", c.name, i))
}
if m.GetDispatcherNum() != c.num {
t.Errorf("GetDispatcherNum() should return %v", c.num)
}
}
})
}
}
```
</details>
***
#### func (*Manager) GetSystemDispatcher() *Dispatcher[P, M]
> 获取系统消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetSystemDispatcher(t *testing.T) {
var cases = []struct{ name string }{{name: "TestManager_GetSystemDispatcher"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
if m.GetSystemDispatcher() == nil {
t.Errorf("GetSystemDispatcher() should not return nil")
}
})
}
}
```
</details>
***
#### func (*Manager) GetDispatcher(p P) *Dispatcher[P, M]
> 获取生产者正在使用的消息分发器,如果生产者没有绑定消息分发器,则会返回系统消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_GetDispatcher(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_GetDispatcher", bindName: "TestManager_GetDispatcher"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
if m.GetDispatcher(c.bindName) == nil {
t.Errorf("GetDispatcher() should not return nil")
}
})
}
}
```
</details>
***
#### func (*Manager) BindProducer(p P, name string)
> 绑定生产者使用特定的消息分发器,如果生产者已经绑定了消息分发器,则会先解绑
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_BindProducer(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_BindProducer", bindName: "TestManager_BindProducer"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
if m.GetDispatcher(c.bindName) == nil {
t.Errorf("GetDispatcher() should not return nil")
}
})
}
}
```
</details>
***
#### func (*Manager) UnBindProducer(p P)
> 解绑生产者使用特定的消息分发器
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestManager_UnBindProducer(t *testing.T) {
var cases = []struct {
name string
bindName string
}{{name: "TestManager_UnBindProducer", bindName: "TestManager_UnBindProducer"}}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
m := dispatcher.NewManager[string, *TestMessage](1024, func(dispatcher *dispatcher.Dispatcher[string, *TestMessage], message *TestMessage) {
})
m.BindProducer(c.bindName, c.bindName)
m.UnBindProducer(c.bindName)
if m.GetDispatcher(c.bindName) != m.GetSystemDispatcher() {
t.Errorf("GetDispatcher() should return SystemDispatcher")
}
})
}
}
```
</details>
***
<span id="struct_Message"></span>
### Message `INTERFACE`

View File

@ -59,10 +59,8 @@ type Dispatcher[P Producer, M Message[P]] struct {
abort chan struct{}
}
// SetProducerDoneHandler 设置特定生产者所有消息处理完成时的回调函数
// SetProducerDoneHandler 设置特定生产者所有消息处理完成时的回调函数
// - 如果 handler 为 nil则会删除该生产者的回调函数
//
// 需要注意的是,该 handler 中
func (d *Dispatcher[P, M]) SetProducerDoneHandler(p P, handler func(p P, dispatcher *Action[P, M])) *Dispatcher[P, M] {
d.lock.Lock()
if handler == nil {

View File

@ -152,6 +152,59 @@ type Lockstep[ClientID comparable, Command any] struct {
lockstepStoppedEventHandles []StoppedEventHandle[ClientID, Command]
}
```
#### func (*Lockstep) JoinClient(client Client[ClientID])
> 将客户端加入到广播队列中,通常在开始广播前使用
> - 如果客户端在开始广播后加入,将丢失之前的帧数据,如要从特定帧开始追帧请使用 JoinClientWithFrame
***
#### func (*Lockstep) JoinClientWithFrame(client Client[ClientID], frameIndex int64)
> 加入客户端到广播队列中,并从特定帧开始追帧
> - 可用于重连及状态同步、帧同步混用的情况
> - 混用:服务端记录指令时同时做一次状态计算,新客户端加入时直接同步当前状态,之后从特定帧开始广播
***
#### func (*Lockstep) GetClientCount() int
> 获取客户端数量
***
#### func (*Lockstep) DropCache(handler func (frame int64) bool)
> 丢弃特定帧的缓存,当 handler 返回 true 时将丢弃缓存
***
#### func (*Lockstep) LeaveClient(clientId ClientID)
> 将客户端从广播队列中移除
***
#### func (*Lockstep) StartBroadcast()
> 开始广播
> - 在开始广播后将持续按照设定的帧率进行帧数推进,并在每一帧推进时向客户端进行同步,需提前将客户端加入广播队列 JoinClient
> - 广播过程中使用 AddCommand 将该帧数据追加到当前帧中
***
#### func (*Lockstep) StopBroadcast()
> 停止广播
***
#### func (*Lockstep) IsRunning() bool
> 是否正在广播
***
#### func (*Lockstep) AddCommand(command Command)
> 添加命令到当前帧
***
#### func (*Lockstep) AddCommands(commands []Command)
> 添加命令到当前帧
***
#### func (*Lockstep) GetCurrentFrame() int64
> 获取当前帧
***
#### func (*Lockstep) GetClientCurrentFrame(clientId ClientID) int64
> 获取客户端当前帧
***
#### func (*Lockstep) GetFrameLimit() int64
> 获取帧上限
> - 未设置时将返回0
***
#### func (*Lockstep) GetCurrentCommands() []Command
> 获取当前帧还未结束时的所有指令
***
#### func (*Lockstep) RegLockstepStoppedEvent(handle StoppedEventHandle[ClientID, Command])
> 当广播停止时将触发被注册的事件处理函数
***
#### func (*Lockstep) OnLockstepStoppedEvent()
***
<span id="struct_Option"></span>
### Option `STRUCT`

View File

@ -83,6 +83,18 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber, E TwoDimens
repartitionQueue []func()
}
```
#### func (*TwoDimensional) AddEntity(entity E)
***
#### func (*TwoDimensional) DeleteEntity(entity E)
***
#### func (*TwoDimensional) Refresh(entity E)
***
#### func (*TwoDimensional) GetFocus(id EID) map[EID]E
***
#### func (*TwoDimensional) SetSize(width int, height int)
***
#### func (*TwoDimensional) SetAreaSize(width int, height int)
***
<span id="struct_TwoDimensionalEntity"></span>
### TwoDimensionalEntity `INTERFACE`
基于2D定义的AOI对象功能接口

View File

@ -127,6 +127,25 @@ type Area[ID comparable, AreaInfo any] struct {
evaluate AreaEvaluateHandle[ID, AreaInfo]
}
```
#### func (*Area) GetAreaInfo() AreaInfo
> 获取编排区域的信息
***
#### func (*Area) GetItems() map[ID]Item[ID]
> 获取编排区域中的所有成员
***
#### func (*Area) IsAllow(item Item[ID]) (constraintErr error, conflictItems map[ID]Item[ID], allow bool)
> 检测一个成员是否可以被添加到该编排区域中
***
#### func (*Area) IsConflict(item Item[ID]) bool
> 检测一个成员是否会造成冲突
***
#### func (*Area) GetConflictItems(item Item[ID]) map[ID]Item[ID]
> 获取与一个成员产生冲突的所有其他成员
***
#### func (*Area) GetScore(extra ...Item[ID]) float64
> 获取该编排区域的评估分数
> - 当 extra 不为空时,将会将 extra 中的内容添加到 items 中进行评估
***
<span id="struct_AreaOption"></span>
### AreaOption `STRUCT`
编排区域选项
@ -157,6 +176,65 @@ type Arrangement[ID comparable, AreaInfo any] struct {
conflictHandles []ConflictHandle[ID, AreaInfo]
}
```
#### func (*Arrangement) AddArea(areaInfo AreaInfo, options ...AreaOption[ID, AreaInfo])
> 添加一个编排区域
***
#### func (*Arrangement) AddItem(item Item[ID])
> 添加一个成员
***
#### func (*Arrangement) Arrange() (areas []*Area[ID, AreaInfo], noSolution map[ID]Item[ID])
> 编排
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestArrangement_Arrange(t *testing.T) {
var a = arrangement.NewArrangement[int, *Team]()
a.AddArea(&Team{ID: 1}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 2 {
return errors.New("too many")
}
return nil
}))
a.AddArea(&Team{ID: 2}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 1 {
return errors.New("too many")
}
return nil
}))
a.AddArea(&Team{ID: 3}, arrangement.WithAreaConstraint[int, *Team](func(area *arrangement.Area[int, *Team], item arrangement.Item[int]) error {
if len(area.GetItems()) >= 2 {
return errors.New("too many")
}
return nil
}))
for i := 0; i < 10; i++ {
a.AddItem(&Player{ID: i + 1})
}
res, no := a.Arrange()
for _, area := range res {
var str = fmt.Sprintf("area %d: ", area.GetAreaInfo().ID)
for id := range area.GetItems() {
str += fmt.Sprintf("%d ", id)
}
fmt.Println(str)
}
var noStr = "no: "
for _, i := range no {
noStr += fmt.Sprintf("%d ", i.GetID())
}
fmt.Println(noStr)
}
```
</details>
***
<span id="struct_Editor"></span>
### Editor `STRUCT`
提供了大量辅助函数的编辑器
@ -169,6 +247,48 @@ type Editor[ID comparable, AreaInfo any] struct {
retryCount int
}
```
#### func (*Editor) GetPendingCount() int
> 获取待编排的成员数量
***
#### func (*Editor) RemoveAreaItem(area *Area[ID, AreaInfo], item Item[ID])
> 从编排区域中移除一个成员到待编排队列中,如果该成员不存在于编排区域中,则不进行任何操作
***
#### func (*Editor) AddAreaItem(area *Area[ID, AreaInfo], item Item[ID])
> 将一个成员添加到编排区域中,如果该成员已经存在于编排区域中,则不进行任何操作
***
#### func (*Editor) GetAreas() []*Area[ID, AreaInfo]
> 获取所有的编排区域
***
#### func (*Editor) GetAreasWithScoreAsc(extra ...Item[ID]) []*Area[ID, AreaInfo]
> 获取所有的编排区域,并按照分数升序排序
***
#### func (*Editor) GetAreasWithScoreDesc(extra ...Item[ID]) []*Area[ID, AreaInfo]
> 获取所有的编排区域,并按照分数降序排序
***
#### func (*Editor) GetRetryCount() int
> 获取重试次数
***
#### func (*Editor) GetThresholdProgressRate() float64
> 获取重试次数阈值进度
***
#### func (*Editor) GetAllowAreas(item Item[ID]) []*Area[ID, AreaInfo]
> 获取允许的编排区域
***
#### func (*Editor) GetNoAllowAreas(item Item[ID]) []*Area[ID, AreaInfo]
> 获取不允许的编排区域
***
#### func (*Editor) GetBestAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最佳的允许的编排区域,如果不存在,则返回 nil
***
#### func (*Editor) GetBestNoAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最佳的不允许的编排区域,如果不存在,则返回 nil
***
#### func (*Editor) GetWorstAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最差的允许的编排区域,如果不存在,则返回 nil
***
#### func (*Editor) GetWorstNoAllowArea(item Item[ID]) *Area[ID, AreaInfo]
> 获取最差的不允许的编排区域,如果不存在,则返回 nil
***
<span id="struct_Item"></span>
### Item `INTERFACE`
编排成员

View File

@ -46,3 +46,55 @@ type SyncMap[K comparable, V any] struct {
atom bool
}
```
#### func (*SyncMap) Set(key K, value V)
> 设置一个值
***
#### func (*SyncMap) Get(key K) V
> 获取一个值
***
#### func (*SyncMap) Atom(handle func (m map[K]V))
> 原子操作
***
#### func (*SyncMap) Exist(key K) bool
> 判断是否存在
***
#### func (*SyncMap) GetExist(key K) ( V, bool)
> 获取一个值并判断是否存在
***
#### func (*SyncMap) Delete(key K)
> 删除一个值
***
#### func (*SyncMap) DeleteGet(key K) V
> 删除一个值并返回
***
#### func (*SyncMap) DeleteGetExist(key K) ( V, bool)
> 删除一个值并返回是否存在
***
#### func (*SyncMap) DeleteExist(key K) bool
> 删除一个值并返回是否存在
***
#### func (*SyncMap) Clear()
> 清空
***
#### func (*SyncMap) ClearHandle(handle func (key K, value V))
> 清空并处理
***
#### func (*SyncMap) Range(handle func (key K, value V) bool)
> 遍历所有值,如果 handle 返回 true 则停止遍历
***
#### func (*SyncMap) Keys() []K
> 获取所有的键
***
#### func (*SyncMap) Slice() []V
> 获取所有的值
***
#### func (*SyncMap) Map() map[K]V
> 转换为普通 map
***
#### func (*SyncMap) Size() int
> 获取数量
***
#### func (*SyncMap) MarshalJSON() ( []byte, error)
***
#### func (*SyncMap) UnmarshalJSON(bytes []byte) error
***

View File

@ -85,6 +85,33 @@ type FSM[State comparable, Data any] struct {
exitAfterEventHandles map[State][]func(state *FSM[State, Data])
}
```
#### func (*FSM) Update()
> 触发当前状态
***
#### func (*FSM) Register(state State, options ...Option[State, Data])
> 注册状态
***
#### func (*FSM) Unregister(state State)
> 反注册状态
***
#### func (*FSM) HasState(state State) bool
> 检查状态机是否存在特定状态
***
#### func (*FSM) Change(state State)
> 改变状态机状态到新的状态
***
#### func (*FSM) Current() (state State)
> 获取当前状态
***
#### func (*FSM) GetData() Data
> 获取状态机数据
***
#### func (*FSM) IsZero() bool
> 检查状态机是否无状态
***
#### func (*FSM) PrevIsZero() bool
> 检查状态机上一个状态是否无状态
***
<span id="struct_Option"></span>
### Option `STRUCT`

View File

@ -14,7 +14,8 @@ func newName(expr ast.Expr) string {
//case *ast.FuncType:
//case *ast.InterfaceType:
//case *ast.MapType:
//case *ast.ChanType:
case *ast.ChanType:
str.WriteString(newName(e.Value))
case *ast.Ident:
str.WriteString(e.Name)
case *ast.Ellipsis:
@ -28,13 +29,17 @@ func newName(expr ast.Expr) string {
case *ast.IndexExpr:
str.WriteString(newName(e.X))
case *ast.IndexListExpr:
str.WriteString(newName(e.X))
case *ast.SliceExpr:
case *ast.TypeAssertExpr:
case *ast.CallExpr:
str.WriteString(newName(e.X))
//case *ast.TypeAssertExpr:
//case *ast.CallExpr:
case *ast.StarExpr:
str.WriteString(newName(e.X))
case *ast.UnaryExpr:
str.WriteString(newName(e.X))
case *ast.BinaryExpr:
str.WriteString(newName(e.X))
}
return str.String()
}

View File

@ -947,6 +947,8 @@ type LineSegmentCap[V generic.SignedNumber, Data any] struct {
Data Data
}
```
#### func (*LineSegmentCap) GetData() Data
***
<span id="struct_Point"></span>
### Point `STRUCT`
表示了一个由 x、y 坐标组成的点
@ -1010,6 +1012,9 @@ type PointCap[V generic.SignedNumber, D any] struct {
Data D
}
```
#### func (PointCap) GetData() D
> 获取数据
***
<span id="struct_Shape"></span>
### Shape `STRUCT`
通过多个点表示了一个形状

View File

@ -76,6 +76,107 @@ type BinarySearch[CompetitorID comparable, Score generic.Ordered] struct {
rankClearBeforeEventHandles []BinarySearchRankClearBeforeEventHandle[CompetitorID, Score]
}
```
#### func (*BinarySearch) Competitor(competitorId CompetitorID, score Score)
> 声明排行榜竞争者
> - 如果竞争者存在的情况下,会更新已有成绩,否则新增竞争者
**示例代码:**
```go
func ExampleBinarySearch_Competitor() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
for rank, competitor := range bs.GetAllCompetitor() {
fmt.Println(rank, competitor)
}
}
```
***
#### func (*BinarySearch) RemoveCompetitor(competitorId CompetitorID)
> 删除特定竞争者
**示例代码:**
```go
func ExampleBinarySearch_RemoveCompetitor() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
bs.RemoveCompetitor("competitor_ 1")
for rank, competitor := range bs.GetAllCompetitor() {
fmt.Println(rank, competitor)
}
}
```
***
#### func (*BinarySearch) Size() int
> 获取竞争者数量
***
#### func (*BinarySearch) GetRankDefault(competitorId CompetitorID, defaultValue int) int
> 获取竞争者排名,如果竞争者不存在则返回默认值
> - 排名从 0 开始
***
#### func (*BinarySearch) GetRank(competitorId CompetitorID) ( int, error)
> 获取竞争者排名
> - 排名从 0 开始
**示例代码:**
```go
func ExampleBinarySearch_GetRank() {
bs := leaderboard2.NewBinarySearch[string, int](leaderboard2.WithBinarySearchCount[string, int](10))
scores := []int{6131, 132, 5133, 134, 135, 136, 137, 138, 139, 140, 222, 333, 444, 555, 666}
for i := 1; i <= 15; i++ {
bs.Competitor(fmt.Sprintf("competitor_%2d", i), scores[i-1])
}
fmt.Println(bs.GetRank("competitor_ 1"))
}
```
***
#### func (*BinarySearch) GetCompetitor(rank int) (competitorId CompetitorID, err error)
> 获取特定排名的竞争者
***
#### func (*BinarySearch) GetCompetitorWithRange(start int, end int) ( []CompetitorID, error)
> 获取第start名到第end名竞争者
***
#### func (*BinarySearch) GetScore(competitorId CompetitorID) (score Score, err error)
> 获取竞争者成绩
***
#### func (*BinarySearch) GetScoreDefault(competitorId CompetitorID, defaultValue Score) Score
> 获取竞争者成绩,不存在时返回默认值
***
#### func (*BinarySearch) GetAllCompetitor() []CompetitorID
> 获取所有竞争者ID
> - 结果为名次有序的
***
#### func (*BinarySearch) Clear()
> 清空排行榜
***
#### func (*BinarySearch) Cmp(s1 Score, s2 Score) int
***
#### func (*BinarySearch) UnmarshalJSON(bytes []byte) error
***
#### func (*BinarySearch) MarshalJSON() ( []byte, error)
***
#### func (*BinarySearch) RegRankChangeEvent(handle BinarySearchRankChangeEventHandle[CompetitorID, Score])
***
#### func (*BinarySearch) OnRankChangeEvent(competitorId CompetitorID, oldRank int, newRank int, oldScore Score, newScore Score)
***
#### func (*BinarySearch) RegRankClearBeforeEvent(handle BinarySearchRankClearBeforeEventHandle[CompetitorID, Score])
***
#### func (*BinarySearch) OnRankClearBeforeEvent()
***
<span id="struct_BinarySearchRankChangeEventHandle"></span>
### BinarySearchRankChangeEventHandle `STRUCT`

View File

@ -115,6 +115,124 @@ type TwoDimensional[EID generic.Basic, PosType generic.SignedNumber] struct {
position2DStopMoveEventHandles []Position2DStopMoveEventHandle[EID, PosType]
}
```
#### func (*TwoDimensional) MoveTo(entity TwoDimensionalEntity[EID, PosType], x PosType, y PosType)
> 设置对象移动到特定位置
**示例代码:**
```go
func ExampleTwoDimensional_MoveTo() {
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
var wait sync.WaitGroup
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done")
wait.Done()
})
wait.Add(1)
entity := NewEntity(1, 100)
m.MoveTo(entity, 50, 30)
wait.Wait()
}
```
***
#### func (*TwoDimensional) StopMove(id EID)
> 停止特定对象的移动
**示例代码:**
```go
func ExampleTwoDimensional_StopMove() {
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
var wait sync.WaitGroup
m.RegPosition2DChangeEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
fmt.Println("move")
})
m.RegPosition2DStopMoveEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("stop")
wait.Done()
})
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println("done")
wait.Done()
})
wait.Add(1)
entity := NewEntity(1, 100)
m.MoveTo(entity, 50, 300)
m.StopMove(1)
wait.Wait()
}
```
<details>
<summary>查看 / 收起单元测试</summary>
```go
func TestTwoDimensional_StopMove(t *testing.T) {
var wait sync.WaitGroup
m := moving2.NewTwoDimensional(moving2.WithTwoDimensionalTimeUnit[int64, float64](time.Second))
defer func() {
m.Release()
}()
m.RegPosition2DChangeEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64], oldX, oldY float64) {
x, y := entity.GetPosition().GetXY()
fmt.Println(fmt.Sprintf("%d : %d | %f, %f > %f, %f", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli(), oldX, oldY, x, y))
})
m.RegPosition2DDestinationEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | destination", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done()
})
m.RegPosition2DStopMoveEvent(func(moving *moving2.TwoDimensional[int64, float64], entity moving2.TwoDimensionalEntity[int64, float64]) {
fmt.Println(fmt.Sprintf("%d : %d | stop", entity.GetTwoDimensionalEntityID(), time.Now().UnixMilli()))
wait.Done()
})
for i := 0; i < 10; i++ {
wait.Add(1)
entity := NewEntity(int64(i)+1, float64(10+i))
m.MoveTo(entity, 50, 30)
}
time.Sleep(time.Second * 1)
for i := 0; i < 10; i++ {
m.StopMove(int64(i) + 1)
}
wait.Wait()
}
```
</details>
***
#### func (*TwoDimensional) RegPosition2DChangeEvent(handle Position2DChangeEventHandle[EID, PosType])
> 在对象位置改变时将执行注册的事件处理函数
***
#### func (*TwoDimensional) OnPosition2DChangeEvent(entity TwoDimensionalEntity[EID, PosType], oldX PosType, oldY PosType)
***
#### func (*TwoDimensional) RegPosition2DDestinationEvent(handle Position2DDestinationEventHandle[EID, PosType])
> 在对象到达终点时将执行被注册的事件处理函数
***
#### func (*TwoDimensional) OnPosition2DDestinationEvent(entity TwoDimensionalEntity[EID, PosType])
***
#### func (*TwoDimensional) RegPosition2DStopMoveEvent(handle Position2DStopMoveEventHandle[EID, PosType])
> 在对象停止移动时将执行被注册的事件处理函数
***
#### func (*TwoDimensional) OnPosition2DStopMoveEvent(entity TwoDimensionalEntity[EID, PosType])
***
#### func (*TwoDimensional) Release()
> 释放对象移动对象所占用的资源
***
<span id="struct_TwoDimensionalEntity"></span>
### TwoDimensionalEntity `INTERFACE`
2D移动对象接口定义

View File

@ -940,6 +940,12 @@ type Matcher[Value any, Result any] struct {
d bool
}
```
#### func (*Matcher) Case(value Value, result Result) *Matcher[Value, Result]
> 匹配
***
#### func (*Matcher) Default(value Result) Result
> 默认
***
<span id="struct_Permission"></span>
### Permission `STRUCT`
@ -949,6 +955,18 @@ type Permission[Code generic.Integer, EntityID comparable] struct {
l sync.RWMutex
}
```
#### func (*Permission) HasPermission(entityId EntityID, permission Code) bool
> 是否有权限
***
#### func (*Permission) AddPermission(entityId EntityID, permission ...Code)
> 添加权限
***
#### func (*Permission) RemovePermission(entityId EntityID, permission ...Code)
> 移除权限
***
#### func (*Permission) SetPermission(entityId EntityID, permission ...Code)
> 设置权限
***
<span id="struct_StackGo"></span>
### StackGo `STRUCT`
用于获取上一个协程调用的堆栈信息