From 6e6f33899b791c585f70c1b8bcca5c8e619f0cea Mon Sep 17 00:00:00 2001 From: kercylan98 Date: Mon, 15 Jan 2024 11:46:08 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E4=BC=98=E5=8C=96=E6=B3=9B=E5=9E=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E4=BD=93=E5=87=BD=E6=95=B0=E7=9A=84=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/activity/README.md | 26 + game/fight/README.md | 85 +++ game/space/README.md | 184 +++++++ server/internal/dispatcher/README.md | 654 +++++++++++++++++++++++ server/internal/dispatcher/dispatcher.go | 4 +- server/lockstep/README.md | 53 ++ utils/aoi/README.md | 12 + utils/arrangement/README.md | 120 +++++ utils/collection/mappings/README.md | 52 ++ utils/fsm/README.md | 27 + utils/generator/astgo/name.go | 11 +- utils/geometry/README.md | 5 + utils/leaderboard/README.md | 101 ++++ utils/moving/README.md | 118 ++++ utils/super/README.md | 18 + 15 files changed, 1464 insertions(+), 6 deletions(-) diff --git a/game/activity/README.md b/game/activity/README.md index 0147d77..1727523 100644 --- a/game/activity/README.md +++ b/game/activity/README.md @@ -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] +*** ### BasicActivityController `INTERFACE` diff --git a/game/fight/README.md b/game/fight/README.md index 14ceab1..fa9683d 100644 --- a/game/fight/README.md +++ b/game/fight/README.md @@ -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() +> 运行 +
+查看 / 收起单元测试 + + +```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() +} + +``` + + +
+ + +*** ### 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 零值 +*** ### TurnBasedEntitySwitchEventHandler `STRUCT` diff --git a/game/space/README.md b/game/space/README.md index cb4d152..e93b2f7 100644 --- a/game/space/README.md +++ b/game/space/README.md @@ -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 中进行处理 +*** ### 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 方法 +*** ### 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] +> 设置房间密码 +*** diff --git a/server/internal/dispatcher/README.md b/server/internal/dispatcher/README.md index ec7fb12..4325cdf 100644 --- a/server/internal/dispatcher/README.md +++ b/server/internal/dispatcher/README.md @@ -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() +> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭 +*** ### 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 中 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) SetClosedHandler(handler func (dispatcher *Action[P, M])) *Dispatcher[P, M] +> 设置消息分发器关闭时的回调函数 +
+查看 / 收起单元测试 + + +```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()) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) Name() string +> 获取消息分发器名称 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) Unique(name string) bool +> 设置唯一消息键,返回是否已存在 +*** +#### func (*Dispatcher) AntiUnique(name string) +> 取消唯一消息键 +*** +#### func (*Dispatcher) Expel() +> 设置该消息分发器即将被驱逐,当消息分发器中没有任何消息时,会自动关闭 +
+查看 / 收起单元测试 + + +```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()) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) UnExpel() +> 取消特定生产者的驱逐计划 +
+查看 / 收起单元测试 + + +```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()) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) IncrCount(producer P, i int64) +> 主动增量设置特定生产者的消息计数,这在等待异步消息完成后再关闭消息分发器时非常有用 +> - 如果 i 为负数,则会减少消息计数 +*** +#### func (*Dispatcher) Put(message M) +> 将消息放入分发器 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) Start() *Dispatcher[P, M] +> 以非阻塞的方式开始进行消息分发,当消息分发器中没有任何消息并且处于驱逐计划 Expel 时,将会自动关闭 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Dispatcher) Closed() bool +> 判断消息分发器是否已关闭 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** ### 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] +> 设置消息分发器关闭时的回调函数 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) SetDispatcherCreatedHandler(handler func (name string)) *Manager[P, M] +> 设置消息分发器创建时的回调函数 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) HasDispatcher(name string) bool +> 检查是否存在指定名称的消息分发器 +
+查看 / 收起单元测试 + + +```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) + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) GetDispatcherNum() int +> 获取当前正在工作的消息分发器数量 +
+查看 / 收起单元测试 + + +```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) + } + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) GetSystemDispatcher() *Dispatcher[P, M] +> 获取系统消息分发器 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) GetDispatcher(p P) *Dispatcher[P, M] +> 获取生产者正在使用的消息分发器,如果生产者没有绑定消息分发器,则会返回系统消息分发器 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) BindProducer(p P, name string) +> 绑定生产者使用特定的消息分发器,如果生产者已经绑定了消息分发器,则会先解绑 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** +#### func (*Manager) UnBindProducer(p P) +> 解绑生产者使用特定的消息分发器 +
+查看 / 收起单元测试 + + +```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") + } + }) + } +} + +``` + + +
+ + +*** ### Message `INTERFACE` diff --git a/server/internal/dispatcher/dispatcher.go b/server/internal/dispatcher/dispatcher.go index 7d696cd..b88dcd2 100644 --- a/server/internal/dispatcher/dispatcher.go +++ b/server/internal/dispatcher/dispatcher.go @@ -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 { diff --git a/server/lockstep/README.md b/server/lockstep/README.md index e0a6a48..3452107 100644 --- a/server/lockstep/README.md +++ b/server/lockstep/README.md @@ -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() +*** ### Option `STRUCT` diff --git a/utils/aoi/README.md b/utils/aoi/README.md index ba1f75c..b710b34 100644 --- a/utils/aoi/README.md +++ b/utils/aoi/README.md @@ -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) +*** ### TwoDimensionalEntity `INTERFACE` 基于2D定义的AOI对象功能接口 diff --git a/utils/arrangement/README.md b/utils/arrangement/README.md index e5feb02..e6fb97d 100644 --- a/utils/arrangement/README.md +++ b/utils/arrangement/README.md @@ -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 中进行评估 +*** ### 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]) +> 编排 +
+查看 / 收起单元测试 + + +```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) +} + +``` + + +
+ + +*** ### 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 +*** ### Item `INTERFACE` 编排成员 diff --git a/utils/collection/mappings/README.md b/utils/collection/mappings/README.md index b91ebf6..4084040 100644 --- a/utils/collection/mappings/README.md +++ b/utils/collection/mappings/README.md @@ -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 +*** diff --git a/utils/fsm/README.md b/utils/fsm/README.md index a8c0f02..ab18674 100644 --- a/utils/fsm/README.md +++ b/utils/fsm/README.md @@ -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 +> 检查状态机上一个状态是否无状态 +*** ### Option `STRUCT` diff --git a/utils/generator/astgo/name.go b/utils/generator/astgo/name.go index 67abeae..89b8d73 100644 --- a/utils/generator/astgo/name.go +++ b/utils/generator/astgo/name.go @@ -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() } diff --git a/utils/geometry/README.md b/utils/geometry/README.md index 9dc4d81..ad08194 100644 --- a/utils/geometry/README.md +++ b/utils/geometry/README.md @@ -947,6 +947,8 @@ type LineSegmentCap[V generic.SignedNumber, Data any] struct { Data Data } ``` +#### func (*LineSegmentCap) GetData() Data +*** ### Point `STRUCT` 表示了一个由 x、y 坐标组成的点 @@ -1010,6 +1012,9 @@ type PointCap[V generic.SignedNumber, D any] struct { Data D } ``` +#### func (PointCap) GetData() D +> 获取数据 +*** ### Shape `STRUCT` 通过多个点表示了一个形状 diff --git a/utils/leaderboard/README.md b/utils/leaderboard/README.md index d42bd9a..2d8b992 100644 --- a/utils/leaderboard/README.md +++ b/utils/leaderboard/README.md @@ -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() +*** ### BinarySearchRankChangeEventHandle `STRUCT` diff --git a/utils/moving/README.md b/utils/moving/README.md index 61111df..6606faf 100644 --- a/utils/moving/README.md +++ b/utils/moving/README.md @@ -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() +} + +``` + +
+查看 / 收起单元测试 + + +```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() +} + +``` + + +
+ + +*** +#### 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() +> 释放对象移动对象所占用的资源 +*** ### TwoDimensionalEntity `INTERFACE` 2D移动对象接口定义 diff --git a/utils/super/README.md b/utils/super/README.md index 6797a26..90754cc 100644 --- a/utils/super/README.md +++ b/utils/super/README.md @@ -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 +> 默认 +*** ### 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) +> 设置权限 +*** ### StackGo `STRUCT` 用于获取上一个协程调用的堆栈信息