diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index f851bf6..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: release - -on: - push: - tags: - - "v*" - - workflow_dispatch: - -jobs: - meta: - uses: ./.github/workflows/meta.yml - - release: - if: ${{ needs.meta.outputs.is_release == 'true' }} - needs: meta - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Release - uses: softprops/action-gh-release@v2 - with: - tag_name: ${{ needs.meta.outputs.tag }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 797e604..57d39a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,8 @@ name: test on: push: + tags: + - "v*" branches: - "**" @@ -9,7 +11,12 @@ on: branches: - "**" + workflow_dispatch: + jobs: + meta: + uses: ./.github/workflows/meta.yml + windows: runs-on: windows-latest strategy: @@ -173,4 +180,15 @@ jobs: if: always() with: name: MAA-macos-${{ matrix.arch }}-full_log - path: "test/debug" \ No newline at end of file + path: "test/debug" + + release: + if: ${{ needs.meta.outputs.is_release == 'true' }} + needs: [meta, windows, ubuntu, macos] + runs-on: ubuntu-latest + + steps: + - name: Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ needs.meta.outputs.tag }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index e4d45a8..665b08e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /.idea -/test/debug \ No newline at end of file +/test/debug +/test/config \ No newline at end of file diff --git a/README.md b/README.md index 2bc0e95..93f8503 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ go reference - - maa framework + + maa framework

@@ -97,7 +97,7 @@ Replace `[path to maafw include directory]` with the actual path to the MaaFrame ## Examples - [Quirk Start](#quirk-start) -- [Custom Recognizer](#custom-recognizer) +- [Custom Recognition](#custom-recognition) - [Custom Action](#custom-action) - [PI CLI](#pi-cli) @@ -140,7 +140,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } @@ -151,11 +151,11 @@ func main() { ``` -### Custom Recognizer +### Custom Recognition -See [custom-recognizer](examples/custom-recognizer) for details. +See [custom-recognition](examples/custom-recognition) for details. -Here is a basic example to implement your custom recognizer: +Here is a basic example to implement your custom recognition: ```go package main @@ -190,12 +190,12 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } - res.RegisterCustomRecognizer("MyRec", &MyRec{}) + res.RegisterCustomRecognition("MyRec", &MyRec{}) detail := tasker.PostPipeline("Startup").Wait().GetDetail() fmt.Println(detail) @@ -203,7 +203,7 @@ func main() { type MyRec struct{} -func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomRecognizerResult, bool) { +func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognitionArg) (maa.CustomRecognitionResult, bool) { ctx.RunRecognition("MyCustomOCR", arg.Img, maa.J{ "MyCustomOCR": maa.J{ "roi": []int{100, 100, 200, 300}, @@ -229,7 +229,7 @@ func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomR ctx.OverrideNext(arg.CurrentTaskName, []string{"TaskA", "TaskB"}) - return maa.CustomRecognizerResult{ + return maa.CustomRecognitionResult{ Box: maa.Rect{0, 0, 100, 100}, Detail: "Hello World!", }, true @@ -276,7 +276,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } @@ -289,7 +289,7 @@ func main() { type MyAct struct{} -func (a *MyAct) Run(_ *maa.Context, arg *maa.CustomActionArg) bool { +func (a *MyAct) Run(_ *maa.Context, _ *maa.CustomActionArg) bool { return true } diff --git a/README_zh.md b/README_zh.md index cdc4cf7..e4450ea 100644 --- a/README_zh.md +++ b/README_zh.md @@ -11,8 +11,8 @@ go reference - - maa framework + + maa framework

@@ -139,7 +139,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } @@ -152,7 +152,7 @@ func main() { ### 自定义识别器 -有关详细信息,请参阅 [custom-recognizer](examples/custom-recognizer)。 +有关详细信息,请参阅 [custom-recognition](examples/custom-recognition)。 以下是一个实现自定义识别器的基本示例: @@ -189,12 +189,12 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } - res.RegisterCustomRecognizer("MyRec", &MyRec{}) + res.RegisterCustomRecognition("MyRec", &MyRec{}) detail := tasker.PostPipeline("Startup").Wait().GetDetail() fmt.Println(detail) @@ -202,7 +202,7 @@ func main() { type MyRec struct{} -func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomRecognizerResult, bool) { +func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognitionArg) (maa.CustomRecognitionResult, bool) { ctx.RunRecognition("MyCustomOCR", arg.Img, maa.J{ "MyCustomOCR": maa.J{ "roi": []int{100, 100, 200, 300}, @@ -228,7 +228,7 @@ func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomR ctx.OverrideNext(arg.CurrentTaskName, []string{"TaskA", "TaskB"}) - return maa.CustomRecognizerResult{ + return maa.CustomRecognitionResult{ Box: maa.Rect{0, 0, 100, 100}, Detail: "Hello World!", }, true @@ -275,7 +275,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } @@ -288,7 +288,7 @@ func main() { type MyAct struct{} -func (a *MyAct) Run(_ *maa.Context, arg *maa.CustomActionArg) bool { +func (a *MyAct) Run(_ *maa.Context, _ *maa.CustomActionArg) bool { return true } diff --git a/context.go b/context.go index 8a123b4..97007b5 100644 --- a/context.go +++ b/context.go @@ -54,7 +54,7 @@ func (ctx *Context) runRecognition(entry, override string, img image.Image) *Rec cOverride := C.CString(override) defer C.free(unsafe.Pointer(cOverride)) imgBuf := buffer.NewImageBuffer() - imgBuf.SetRawData(img) + imgBuf.Set(img) defer imgBuf.Destroy() recId := int64(C.MaaContextRunRecognition(ctx.handle, cEntry, cOverride, (*C.MaaImageBuffer)(imgBuf.Handle()))) diff --git a/context_test.go b/context_test.go index aa97f94..2664b85 100644 --- a/context_test.go +++ b/context_test.go @@ -21,15 +21,15 @@ func (t *testContextRunPipelineAct) Run(ctx *Context, _ *CustomActionArg) bool { } func TestContext_RunPipeline(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -63,15 +63,15 @@ func (t *testContextRunRecognitionAct) Run(ctx *Context, _ *CustomActionArg) boo } func TestContext_RunRecognition(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -103,15 +103,15 @@ func (a testContextRunActionAct) Run(ctx *Context, arg *CustomActionArg) bool { } func TestContext_RunAction(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -153,15 +153,15 @@ func (t *testContextOverriderPipelineAct) Run(ctx *Context, _ *CustomActionArg) } func TestContext_OverridePipeline(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -200,15 +200,15 @@ func (t *testContextOverrideNextAct) Run(ctx *Context, _ *CustomActionArg) bool } func TestContext_OverrideNext(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -235,15 +235,15 @@ func (t *testContextGetTaskJobAct) Run(ctx *Context, _ *CustomActionArg) bool { } func TestContext_GetTaskJob(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -270,15 +270,15 @@ func (t testContextGetTaskerAct) Run(ctx *Context, _ *CustomActionArg) bool { } func TestContext_GetTasker(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) @@ -303,15 +303,15 @@ func (t testContextCloneAct) Run(ctx *Context, _ *CustomActionArg) bool { } func TestContext_Clone(t *testing.T) { - ctrl := createDbgController(t) + ctrl := createDbgController(t, nil) defer ctrl.Destroy() isConnected := ctrl.PostConnect().Wait().Success() require.True(t, isConnected) - res := createResource(t) + res := createResource(t, nil) defer res.Destroy() - tasker := createTasker(t) + tasker := createTasker(t, nil) defer tasker.Destroy() taskerBind(t, tasker, ctrl, res) diff --git a/controller.go b/controller.go index 7e4752d..c1a3cfe 100644 --- a/controller.go +++ b/controller.go @@ -9,9 +9,9 @@ extern void _MaaNotificationCallbackAgent(const char* message, const char* detai import "C" import ( "github.com/MaaXYZ/maa-framework-go/internal/buffer" - "github.com/MaaXYZ/maa-framework-go/internal/notification" "github.com/MaaXYZ/maa-framework-go/internal/store" "image" + "time" "unsafe" ) @@ -26,7 +26,7 @@ type Controller interface { PostConnect() *Job PostClick(x, y int32) *Job - PostSwipe(x1, y1, x2, y2, duration int32) *Job + PostSwipe(x1, y1, x2, y2 int32, duration time.Duration) *Job PostPressKey(keycode int32) *Job PostInputText(text string) *Job PostStartApp(intent string) *Job @@ -99,7 +99,7 @@ func NewAdbController( screencapMethod AdbScreencapMethod, inputMethod AdbInputMethod, config, agentPath string, - callback func(msg, detailsJson string), + notify Notification, ) Controller { cAdbPath := C.CString(adbPath) cAddress := C.CString(address) @@ -112,7 +112,7 @@ func NewAdbController( C.free(unsafe.Pointer(cAgentPath)) }() - id := notification.RegisterCallback(callback) + id := registerNotificationCallback(notify) handle := C.MaaAdbControllerCreate( cAdbPath, cAddress, @@ -164,9 +164,9 @@ func NewWin32Controller( hWnd unsafe.Pointer, screencapMethod Win32ScreencapMethod, inputMethod Win32InputMethod, - callback func(msg, detailsJson string), + notify Notification, ) Controller { - id := notification.RegisterCallback(callback) + id := registerNotificationCallback(notify) handle := C.MaaWin32ControllerCreate( hWnd, C.uint64_t(screencapMethod), @@ -202,7 +202,7 @@ func NewDbgController( readPath, writePath string, dbgCtrlType DbgControllerType, config string, - callback func(msg, detailsJson string), + notify Notification, ) Controller { cReadPath := C.CString(readPath) cWritePath := C.CString(writePath) @@ -213,7 +213,7 @@ func NewDbgController( C.free(unsafe.Pointer(cConfig)) }() - id := notification.RegisterCallback(callback) + id := registerNotificationCallback(notify) handle := C.MaaDbgControllerCreate( cReadPath, cWritePath, @@ -236,10 +236,10 @@ func NewDbgController( // NewCustomController creates a custom controller instance. func NewCustomController( ctrl CustomController, - callback func(msg, detailsJson string), + notify Notification, ) Controller { ctrlID := registerCustomControllerCallbacks(ctrl) - cbID := notification.RegisterCallback(callback) + notifyID := registerNotificationCallback(notify) handle := C.MaaCustomControllerCreate( (*C.MaaCustomControllerCallbacks)(ctrl.Handle()), // Here, we are simply passing the uint64 value as a pointer @@ -248,13 +248,13 @@ func NewCustomController( C.MaaNotificationCallback(C._MaaNotificationCallbackAgent), // Here, we are simply passing the uint64 value as a pointer // and will not actually dereference this pointer. - unsafe.Pointer(uintptr(cbID)), + unsafe.Pointer(uintptr(notifyID)), ) if handle == nil { return nil } controllerStore.Set(unsafe.Pointer(handle), controllerStoreValue{ - NotificationCallbackID: cbID, + NotificationCallbackID: notifyID, CustomControllerCallbacksID: ctrlID, }) return &controller{handle: handle} @@ -263,7 +263,7 @@ func NewCustomController( // Destroy frees the controller instance. func (c *controller) Destroy() { value := controllerStore.Get(c.Handle()) - notification.UnregisterCallback(value.NotificationCallbackID) + unregisterNotificationCallback(value.NotificationCallbackID) unregisterCustomControllerCallbacks(value.CustomControllerCallbacksID) controllerStore.Del(c.Handle()) C.MaaControllerDestroy(c.handle) @@ -353,8 +353,15 @@ func (c *controller) PostClick(x, y int32) *Job { } // PostSwipe posts a swipe. -func (c *controller) PostSwipe(x1, y1, x2, y2, duration int32) *Job { - id := int64(C.MaaControllerPostSwipe(c.handle, C.int32_t(x1), C.int32_t(y1), C.int32_t(x2), C.int32_t(y2), C.int32_t(duration))) +func (c *controller) PostSwipe(x1, y1, x2, y2 int32, duration time.Duration) *Job { + id := int64(C.MaaControllerPostSwipe( + c.handle, + C.int32_t(x1), + C.int32_t(y1), + C.int32_t(x2), + C.int32_t(y2), + C.int32_t(duration.Milliseconds()), + )) return NewJob(id, c.status, c.wait) } @@ -439,7 +446,7 @@ func (c *controller) CacheImage() image.Image { return nil } - img := imgBuffer.GetByRawData() + img := imgBuffer.Get() return img } diff --git a/controller_test.go b/controller_test.go index 49de9db..44d05ca 100644 --- a/controller_test.go +++ b/controller_test.go @@ -3,13 +3,180 @@ package maa import ( "github.com/stretchr/testify/require" "testing" + "time" ) -func createDbgController(t *testing.T) Controller { +func createDbgController(t *testing.T, notify Notification) Controller { testingPath := "./test/data_set/PipelineSmoking/Screenshot" resultPath := "./test/data_set/debug" - ctrl := NewDbgController(testingPath, resultPath, DbgControllerTypeCarouselImage, "{}", nil) + ctrl := NewDbgController(testingPath, resultPath, DbgControllerTypeCarouselImage, "{}", notify) require.NotNil(t, ctrl) return ctrl } + +func TestNewDbgController(t *testing.T) { + ctrl := createDbgController(t, nil) + ctrl.Destroy() +} + +func TestController_Handle(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + handle := ctrl.Handle() + require.NotNil(t, handle) +} + +func TestController_SetScreenshotTargetLongSide(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + got := ctrl.SetScreenshotTargetLongSide(1280) + require.True(t, got) +} + +func TestController_SetScreenshotTargetShortSide(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + got := ctrl.SetScreenshotTargetShortSide(720) + require.True(t, got) +} + +func TestController_SetRecording(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + got := ctrl.SetRecording(true) + require.True(t, got) +} + +func TestController_PostConnect(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) +} + +func TestController_Connected(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + connected := ctrl.Connected() + require.True(t, connected) +} + +func TestController_PostClick(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + clicked := ctrl.PostClick(100, 200).Wait().Success() + require.True(t, clicked) +} + +func TestController_PostSwipe(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + swiped := ctrl.PostSwipe(100, 200, 400, 300, 2*time.Second).Wait().Success() + require.True(t, swiped) +} + +func TestController_PostPressKey(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + pressed := ctrl.PostPressKey(4).Wait().Success() + require.True(t, pressed) +} + +func TestController_PostInputText(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + inputted := ctrl.PostInputText("Hello World").Wait().Success() + require.True(t, inputted) +} + +func TestController_PostStartApp(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + started := ctrl.PostStartApp("com.android.settings").Wait().Success() + require.True(t, started) +} + +func TestController_PostStopApp(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + stopped := ctrl.PostStopApp("com.android.settings").Wait().Success() + require.True(t, stopped) +} + +func TestController_PostTouchDown(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + downed := ctrl.PostTouchDown(0, 100, 200, 1000).Wait().Success() + require.True(t, downed) +} + +func TestController_PostTouchMove(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + downed := ctrl.PostTouchDown(0, 100, 200, 1000).Wait().Success() + require.True(t, downed) + moved := ctrl.PostTouchMove(0, 200, 300, 1000).Wait().Success() + require.True(t, moved) +} + +func TestController_PostTouchUp(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + downed := ctrl.PostTouchDown(0, 100, 200, 1000).Wait().Success() + require.True(t, downed) + moved := ctrl.PostTouchMove(0, 200, 300, 1000).Wait().Success() + require.True(t, moved) + upped := ctrl.PostTouchUp(0).Wait().Success() + require.True(t, upped) +} + +func TestController_PostScreencap(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + screencaped := ctrl.PostScreencap().Wait().Success() + require.True(t, screencaped) +} + +func TestController_CacheImage(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + screencaped := ctrl.PostScreencap().Wait().Success() + require.True(t, screencaped) + img := ctrl.CacheImage() + require.NotNil(t, img) +} + +func TestController_GetUUID(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + uuid, oK := ctrl.GetUUID() + require.True(t, oK) + require.NotEmpty(t, uuid) +} diff --git a/custom_controller.go b/custom_controller.go index 56bc030..e162614 100644 --- a/custom_controller.go +++ b/custom_controller.go @@ -176,7 +176,7 @@ func _ScreencapAgent(handleArg unsafe.Pointer, imgBuffer *C.MaaImageBuffer) C.ui img, captured := ctrl.Screencap() if captured { imgImgBuffer := buffer.NewImageBufferByHandle(unsafe.Pointer(imgBuffer)) - if ok := imgImgBuffer.SetRawData(img); ok { + if ok := imgImgBuffer.Set(img); ok { return C.uint8_t(1) } } diff --git a/custom_recognizer.go b/custom_recognition.go similarity index 70% rename from custom_recognizer.go rename to custom_recognition.go index 24a3b55..710a22e 100644 --- a/custom_recognizer.go +++ b/custom_recognition.go @@ -5,7 +5,7 @@ package maa #include #include "def.h" -extern uint8_t _MaaCustomRecognizerCallbackAgent( +extern uint8_t _MaaCustomRecognitionCallbackAgent( MaaContext* ctx, int64_t task_id, const char* current_task_name, @@ -26,25 +26,25 @@ import ( ) var ( - customRecognizerCallbackID uint64 - customRecognizerCallbackAgents = make(map[uint64]CustomRecognizer) + customRecognitionCallbackID uint64 + customRecognitionCallbackAgents = make(map[uint64]CustomRecognition) ) -func registerCustomRecognizer(recognizer CustomRecognizer) uint64 { - id := atomic.AddUint64(&customRecognizerCallbackID, 1) - customRecognizerCallbackAgents[id] = recognizer +func registerCustomRecognition(recognizer CustomRecognition) uint64 { + id := atomic.AddUint64(&customRecognitionCallbackID, 1) + customRecognitionCallbackAgents[id] = recognizer return id } -func unregisterCustomRecognizer(id uint64) bool { - if _, ok := customRecognizerCallbackAgents[id]; !ok { +func unregisterCustomRecognition(id uint64) bool { + if _, ok := customRecognitionCallbackAgents[id]; !ok { return false } - delete(customRecognizerCallbackAgents, id) + delete(customRecognitionCallbackAgents, id) return true } -type CustomRecognizerArg struct { +type CustomRecognitionArg struct { TaskDetail *TaskDetail CurrentTaskName string CustomRecognizerName string @@ -53,17 +53,17 @@ type CustomRecognizerArg struct { Roi Rect } -type CustomRecognizer interface { - Run(ctx *Context, arg *CustomRecognizerArg) (CustomRecognizerResult, bool) +type CustomRecognition interface { + Run(ctx *Context, arg *CustomRecognitionArg) (CustomRecognitionResult, bool) } -type CustomRecognizerResult struct { +type CustomRecognitionResult struct { Box Rect Detail string } -//export _MaaCustomRecognizerCallbackAgent -func _MaaCustomRecognizerCallbackAgent( +//export _MaaCustomRecognitionCallbackAgent +func _MaaCustomRecognitionCallbackAgent( ctx *C.MaaContext, taskId C.int64_t, currentTaskName, customRecognizerName, customRecognitionParam C.StringView, @@ -76,16 +76,16 @@ func _MaaCustomRecognizerCallbackAgent( // Here, we are simply passing the uint64 value as a pointer // and will not actually dereference this pointer. id := uint64(uintptr(recognizerArg)) - recognizer := customRecognizerCallbackAgents[id] + recognizer := customRecognitionCallbackAgents[id] context := Context{handle: ctx} tasker := context.GetTasker() taskDetail := tasker.getTaskDetail(int64(taskId)) imgBuffer := buffer.NewImageBufferByHandle(unsafe.Pointer(img)) - imgImg := imgBuffer.GetByRawData() + imgImg := imgBuffer.Get() ret, ok := recognizer.Run( &Context{handle: ctx}, - &CustomRecognizerArg{ + &CustomRecognitionArg{ TaskDetail: taskDetail, CurrentTaskName: C.GoString(currentTaskName), CustomRecognizerName: C.GoString(customRecognizerName), diff --git a/examples/custom-action/main.go b/examples/custom-action/main.go index 301eabb..ebfec11 100644 --- a/examples/custom-action/main.go +++ b/examples/custom-action/main.go @@ -30,7 +30,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } @@ -43,6 +43,6 @@ func main() { type MyAct struct{} -func (a *MyAct) Run(_ *maa.Context, arg *maa.CustomActionArg) bool { +func (a *MyAct) Run(_ *maa.Context, _ *maa.CustomActionArg) bool { return true } diff --git a/examples/custom-recognizer/main.go b/examples/custom-recognition/main.go similarity index 86% rename from examples/custom-recognizer/main.go rename to examples/custom-recognition/main.go index e6989dd..637763a 100644 --- a/examples/custom-recognizer/main.go +++ b/examples/custom-recognition/main.go @@ -30,12 +30,12 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } - res.RegisterCustomRecognizer("MyRec", &MyRec{}) + res.RegisterCustomRecognition("MyRec", &MyRec{}) detail := tasker.PostPipeline("Startup").Wait().GetDetail() fmt.Println(detail) @@ -43,7 +43,7 @@ func main() { type MyRec struct{} -func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomRecognizerResult, bool) { +func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognitionArg) (maa.CustomRecognitionResult, bool) { ctx.RunRecognition("MyCustomOCR", arg.Img, maa.J{ "MyCustomOCR": maa.J{ "roi": []int{100, 100, 200, 300}, @@ -69,7 +69,7 @@ func (r *MyRec) Run(ctx *maa.Context, arg *maa.CustomRecognizerArg) (maa.CustomR ctx.OverrideNext(arg.CurrentTaskName, []string{"TaskA", "TaskB"}) - return maa.CustomRecognizerResult{ + return maa.CustomRecognitionResult{ Box: maa.Rect{0, 0, 100, 100}, Detail: "Hello World!", }, true diff --git a/examples/custom-recognizer/resource/image/.gitkeep b/examples/custom-recognition/resource/image/.gitkeep similarity index 100% rename from examples/custom-recognizer/resource/image/.gitkeep rename to examples/custom-recognition/resource/image/.gitkeep diff --git a/examples/custom-recognizer/resource/model/ocr/.gitkeep b/examples/custom-recognition/resource/model/ocr/.gitkeep similarity index 100% rename from examples/custom-recognizer/resource/model/ocr/.gitkeep rename to examples/custom-recognition/resource/model/ocr/.gitkeep diff --git a/examples/custom-recognizer/resource/pipeline/pipeline.json b/examples/custom-recognition/resource/pipeline/pipeline.json similarity index 100% rename from examples/custom-recognizer/resource/pipeline/pipeline.json rename to examples/custom-recognition/resource/pipeline/pipeline.json diff --git a/examples/quick-start/main.go b/examples/quick-start/main.go index 11d751d..4296bff 100644 --- a/examples/quick-start/main.go +++ b/examples/quick-start/main.go @@ -30,7 +30,7 @@ func main() { defer res.Destroy() res.PostPath("./resource").Wait() tasker.BindResource(res) - if tasker.Inited() { + if tasker.Initialized() { fmt.Println("Failed to init MAA.") os.Exit(1) } diff --git a/internal/buffer/image_buffer.go b/internal/buffer/image_buffer.go index fd382f2..7ed6917 100644 --- a/internal/buffer/image_buffer.go +++ b/internal/buffer/image_buffer.go @@ -6,12 +6,9 @@ package buffer */ import "C" import ( - "bytes" - "errors" "image" "image/color" "image/draw" - "image/png" "unsafe" ) @@ -21,6 +18,9 @@ type ImageBuffer struct { func NewImageBuffer() *ImageBuffer { handle := C.MaaImageBufferCreate() + if handle == nil { + return nil + } return &ImageBuffer{ handle: handle, } @@ -48,8 +48,8 @@ func (i *ImageBuffer) Clear() bool { return C.MaaImageBufferClear(i.handle) != 0 } -// GetByRawData retrieves the image from raw data stored in the buffer. -func (i *ImageBuffer) GetByRawData() image.Image { +// Get retrieves the image from raw data stored in the buffer. +func (i *ImageBuffer) Get() image.Image { rawData := i.getRawData() if rawData == nil { return nil @@ -71,8 +71,8 @@ func (i *ImageBuffer) GetByRawData() image.Image { return img } -// SetRawData converts an image.Image to raw data and sets it in the buffer. -func (i *ImageBuffer) SetRawData(img image.Image) bool { +// Set converts an image.Image to raw data and sets it in the buffer. +func (i *ImageBuffer) Set(img image.Image) bool { width := img.Bounds().Dx() height := img.Bounds().Dy() imageType := int32(16) // CV_8UC3 @@ -139,64 +139,3 @@ func (i *ImageBuffer) setRawData(data unsafe.Pointer, width, height, imageType i C.int32_t(imageType), ) != 0 } - -// GetByEncoded retrieves the decoded image from the buffer. -// It returns the decoded image and an error if the operation was unsuccessful. -func (i *ImageBuffer) GetByEncoded() (image.Image, error) { - encodedData := i.getEncoded() - if encodedData == nil { - return nil, errors.New("failed to get encoded image data") - } - dataSize := i.getEncodedSize() - if dataSize == 0 { - return nil, errors.New("encoded image size is zero") - } - - data := C.GoBytes(encodedData, C.int32_t(dataSize)) - img, err := png.Decode(bytes.NewReader(data)) - if err != nil { - return nil, err - } - return img, nil -} - -// SetEncoded encodes the given image and sets it in the buffer. -// It takes an image.Image as input and returns an error if the operation was unsuccessful. -func (i *ImageBuffer) SetEncoded(img image.Image) error { - var buf bytes.Buffer - if err := png.Encode(&buf, img); err != nil { - return err - } - - data := buf.Bytes() - cData := C.CBytes(data) - defer C.free(cData) - - if !i.setEncoded(cData, uint64(len(data))) { - return errors.New("failed to set encoded image data") - } - return nil -} - -// getEncoded retrieves the encoded image data from the buffer. -// It returns a pointer to the encoded image data. -func (i *ImageBuffer) getEncoded() unsafe.Pointer { - return unsafe.Pointer(C.MaaImageBufferGetEncoded(i.handle)) -} - -// getEncodedSize retrieves the size of the encoded image data in the buffer. -// It returns the size of the encoded image data as an integer. -func (i *ImageBuffer) getEncodedSize() int32 { - return int32(C.MaaImageBufferGetEncodedSize(i.handle)) -} - -// setEncoded sets the encoded image data in the buffer. -// It takes a pointer to the encoded image data and the size of the data. -// It returns true if the operation was successful, otherwise false. -func (i *ImageBuffer) setEncoded(data unsafe.Pointer, size uint64) bool { - return C.MaaImageBufferSetEncoded( - i.handle, - C.MaaImageEncodedData(data), - C.uint64_t(size), - ) != 0 -} diff --git a/internal/buffer/image_buffer_test.go b/internal/buffer/image_buffer_test.go new file mode 100644 index 0000000..b758def --- /dev/null +++ b/internal/buffer/image_buffer_test.go @@ -0,0 +1,59 @@ +package buffer + +import ( + "github.com/stretchr/testify/require" + "image" + "image/color" + "testing" +) + +func createImageBuffer(t *testing.T) *ImageBuffer { + imageBuffer := NewImageBuffer() + require.NotNil(t, imageBuffer) + return imageBuffer +} + +func TestNewImageBuffer(t *testing.T) { + imageBuffer := createImageBuffer(t) + imageBuffer.Destroy() +} + +func TestImageBuffer_Handle(t *testing.T) { + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + handle := imageBuffer.Handle() + require.NotNil(t, handle) +} + +func TestImageBuffer_IsEmpty(t *testing.T) { + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + got := imageBuffer.IsEmpty() + require.True(t, got) +} + +func TestImageBuffer_Clear(t *testing.T) { + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + got := imageBuffer.Clear() + require.True(t, got) +} + +func TestImageBuffer_Set(t *testing.T) { + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + + width, height := 2, 2 + img1 := image.NewNRGBA(image.Rect(0, 0, width, height)) + img1.SetNRGBA(0, 0, color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + img1.SetNRGBA(1, 0, color.NRGBA{R: 0, G: 255, B: 0, A: 255}) + img1.SetNRGBA(0, 1, color.NRGBA{R: 0, G: 0, B: 255, A: 255}) + img1.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) + + got := imageBuffer.Set(img1) + require.True(t, got) + + img2 := imageBuffer.Get() + require.NotNil(t, img2) + require.Equal(t, img1, img2) +} diff --git a/internal/buffer/image_list_buffer.go b/internal/buffer/image_list_buffer.go index 4eb2a62..95c6fea 100644 --- a/internal/buffer/image_list_buffer.go +++ b/internal/buffer/image_list_buffer.go @@ -16,6 +16,9 @@ type ImageListBuffer struct { func NewImageListBuffer() *ImageListBuffer { handle := C.MaaImageListBufferCreate() + if handle == nil { + return nil + } return &ImageListBuffer{ handle: handle, } @@ -52,7 +55,7 @@ func (il *ImageListBuffer) Get(index uint64) image.Image { img := &ImageBuffer{ handle: handle, } - return img.GetByRawData() + return img.Get() } func (il *ImageListBuffer) GetAll() []image.Image { @@ -65,7 +68,7 @@ func (il *ImageListBuffer) GetAll() []image.Image { return images } -func (il *ImageListBuffer) Append(value ImageBuffer) bool { +func (il *ImageListBuffer) Append(value *ImageBuffer) bool { return C.MaaImageListBufferAppend( il.handle, (*C.MaaImageBuffer)(value.Handle()), diff --git a/internal/buffer/image_list_buffer_test.go b/internal/buffer/image_list_buffer_test.go new file mode 100644 index 0000000..580ab8a --- /dev/null +++ b/internal/buffer/image_list_buffer_test.go @@ -0,0 +1,143 @@ +package buffer + +import ( + "github.com/stretchr/testify/require" + "image" + "image/color" + "testing" +) + +func createImageListBuffer(t *testing.T) *ImageListBuffer { + imageListBuffer := NewImageListBuffer() + require.NotNil(t, imageListBuffer) + return imageListBuffer +} + +func TestNewImageListBuffer(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + imageListBuffer.Destroy() +} + +func TestImageListBuffer_Handle(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + handle := imageListBuffer.Handle() + require.NotNil(t, handle) +} + +func TestImageListBuffer_IsEmpty(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + got := imageListBuffer.IsEmpty() + require.True(t, got) +} + +func TestImageListBuffer_Clear(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + got := imageListBuffer.Clear() + require.True(t, got) +} + +func TestImageListBuffer_Append(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + + width, height := 2, 2 + img1 := image.NewNRGBA(image.Rect(0, 0, width, height)) + img1.SetNRGBA(0, 0, color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + img1.SetNRGBA(1, 0, color.NRGBA{R: 0, G: 255, B: 0, A: 255}) + img1.SetNRGBA(0, 1, color.NRGBA{R: 0, G: 0, B: 255, A: 255}) + img1.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) + + got := imageBuffer.Set(img1) + require.True(t, got) + + appended := imageListBuffer.Append(imageBuffer) + require.True(t, appended) + + got2 := imageListBuffer.IsEmpty() + require.False(t, got2) + + img2 := imageListBuffer.Get(0) + require.NotNil(t, img2) + require.Equal(t, img1, img2) +} + +func TestImageListBuffer_Remove(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + + width, height := 2, 2 + img := image.NewNRGBA(image.Rect(0, 0, width, height)) + img.SetNRGBA(0, 0, color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + img.SetNRGBA(1, 0, color.NRGBA{R: 0, G: 255, B: 0, A: 255}) + img.SetNRGBA(0, 1, color.NRGBA{R: 0, G: 0, B: 255, A: 255}) + img.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) + + got := imageBuffer.Set(img) + require.True(t, got) + + appended := imageListBuffer.Append(imageBuffer) + require.True(t, appended) + + removed := imageListBuffer.Remove(0) + require.True(t, removed) + + got2 := imageListBuffer.IsEmpty() + require.True(t, got2) +} + +func TestImageListBuffer_Size(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + + width, height := 2, 2 + img1 := image.NewNRGBA(image.Rect(0, 0, width, height)) + img1.SetNRGBA(0, 0, color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + img1.SetNRGBA(1, 0, color.NRGBA{R: 0, G: 255, B: 0, A: 255}) + img1.SetNRGBA(0, 1, color.NRGBA{R: 0, G: 0, B: 255, A: 255}) + img1.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) + + got := imageBuffer.Set(img1) + require.True(t, got) + + appended := imageListBuffer.Append(imageBuffer) + require.True(t, appended) + + size := imageListBuffer.Size() + require.Equal(t, uint64(1), size) +} + +func TestImageListBuffer_GetAll(t *testing.T) { + imageListBuffer := createImageListBuffer(t) + defer imageListBuffer.Destroy() + + imageBuffer := createImageBuffer(t) + defer imageBuffer.Destroy() + + width, height := 2, 2 + img1 := image.NewNRGBA(image.Rect(0, 0, width, height)) + img1.SetNRGBA(0, 0, color.NRGBA{R: 255, G: 0, B: 0, A: 255}) + img1.SetNRGBA(1, 0, color.NRGBA{R: 0, G: 255, B: 0, A: 255}) + img1.SetNRGBA(0, 1, color.NRGBA{R: 0, G: 0, B: 255, A: 255}) + img1.SetNRGBA(1, 1, color.NRGBA{R: 255, G: 255, B: 255, A: 255}) + + got := imageBuffer.Set(img1) + require.True(t, got) + + appended := imageListBuffer.Append(imageBuffer) + require.True(t, appended) + + list := imageListBuffer.GetAll() + require.Len(t, list, 1) +} diff --git a/internal/buffer/rect_buffer.go b/internal/buffer/rect_buffer.go index aee8fe6..27a71f9 100644 --- a/internal/buffer/rect_buffer.go +++ b/internal/buffer/rect_buffer.go @@ -13,12 +13,19 @@ type Rect struct { X, Y, W, H int32 } +func (r Rect) ToInts() [4]int32 { + return [4]int32{r.X, r.Y, r.W, r.H} +} + type RectBuffer struct { handle *C.MaaRect } func NewRectBuffer() *RectBuffer { handle := C.MaaRectCreate() + if handle == nil { + return nil + } return &RectBuffer{ handle: handle, } diff --git a/internal/buffer/rect_buffer_test.go b/internal/buffer/rect_buffer_test.go new file mode 100644 index 0000000..0b17a64 --- /dev/null +++ b/internal/buffer/rect_buffer_test.go @@ -0,0 +1,44 @@ +package buffer + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func createRectBuffer(t *testing.T) *RectBuffer { + rectBuffer := NewRectBuffer() + require.NotNil(t, rectBuffer) + return rectBuffer +} + +func TestNewRectBuffer(t *testing.T) { + rectBuffer := createRectBuffer(t) + rectBuffer.Destroy() +} + +func TestRectBuffer_Handle(t *testing.T) { + rectBuffer := createRectBuffer(t) + defer rectBuffer.Destroy() + handle := rectBuffer.Handle() + require.NotNil(t, handle) +} + +func TestRectBuffer_Set(t *testing.T) { + rectBuffer := createRectBuffer(t) + defer rectBuffer.Destroy() + + rect1 := Rect{100, 200, 300, 400} + got := rectBuffer.Set(rect1) + require.True(t, got) + + x := rectBuffer.GetX() + require.Equal(t, rect1.X, x) + y := rectBuffer.GetY() + require.Equal(t, rect1.Y, y) + w := rectBuffer.GetW() + require.Equal(t, rect1.W, w) + h := rectBuffer.GetH() + require.Equal(t, rect1.H, h) + rect2 := rectBuffer.Get() + require.Equal(t, rect1, rect2) +} diff --git a/internal/buffer/string_buffer.go b/internal/buffer/string_buffer.go index 12bb159..0493624 100644 --- a/internal/buffer/string_buffer.go +++ b/internal/buffer/string_buffer.go @@ -13,6 +13,9 @@ type StringBuffer struct { func NewStringBuffer() *StringBuffer { handle := C.MaaStringBufferCreate() + if handle == nil { + return nil + } return &StringBuffer{ handle: handle, } diff --git a/internal/buffer/string_buffer_test.go b/internal/buffer/string_buffer_test.go new file mode 100644 index 0000000..d8850dc --- /dev/null +++ b/internal/buffer/string_buffer_test.go @@ -0,0 +1,71 @@ +package buffer + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func createStringBuffer(t *testing.T) *StringBuffer { + stringBuffer := NewStringBuffer() + require.NotNil(t, stringBuffer) + return stringBuffer +} + +func TestNewStringBuffer(t *testing.T) { + stringBuffer := createStringBuffer(t) + stringBuffer.Destroy() +} + +func TestStringBuffer_Handle(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + handle := stringBuffer.Handle() + require.NotNil(t, handle) +} + +func TestStringBuffer_IsEmpty(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + got := stringBuffer.IsEmpty() + require.True(t, got) +} + +func TestStringBuffer_Clear(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + got := stringBuffer.Clear() + require.True(t, got) +} + +func TestStringBuffer_Set(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + str1 := "test" + got := stringBuffer.Set(str1) + require.True(t, got) + + str2 := stringBuffer.Get() + require.Equal(t, str1, str2) +} + +func TestStringBuffer_Size(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + str := "test" + got := stringBuffer.Set(str) + require.True(t, got) + + size := stringBuffer.Size() + require.Equal(t, uint64(len(str)), size) +} + +func TestStringBuffer_SetWithSize(t *testing.T) { + stringBuffer := createStringBuffer(t) + defer stringBuffer.Destroy() + str1 := "test" + got := stringBuffer.SetWithSize(str1, uint64(len(str1))) + require.True(t, got) + + str2 := stringBuffer.Get() + require.Equal(t, str1, str2) +} diff --git a/internal/buffer/string_list_buffer.go b/internal/buffer/string_list_buffer.go index 2aa3386..649482f 100644 --- a/internal/buffer/string_list_buffer.go +++ b/internal/buffer/string_list_buffer.go @@ -13,6 +13,9 @@ type StringListBuffer struct { func NewStringListBuffer() *StringListBuffer { handle := C.MaaStringListBufferCreate() + if handle == nil { + return nil + } return &StringListBuffer{ handle: handle, } diff --git a/internal/buffer/string_list_buffer_test.go b/internal/buffer/string_list_buffer_test.go new file mode 100644 index 0000000..840f19b --- /dev/null +++ b/internal/buffer/string_list_buffer_test.go @@ -0,0 +1,114 @@ +package buffer + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func createStringListBuffer(t *testing.T) *StringListBuffer { + stringListBuffer := NewStringListBuffer() + require.NotNil(t, stringListBuffer) + return stringListBuffer +} + +func TestNewStringListBuffer(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + stringListBuffer.Destroy() +} + +func TestStringListBuffer_Handle(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + handle := stringListBuffer.Handle() + require.NotNil(t, handle) +} + +func TestStringListBuffer_IsEmpty(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + got := stringListBuffer.IsEmpty() + require.True(t, got) +} + +func TestStringListBuffer_Clear(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + got := stringListBuffer.Clear() + require.True(t, got) +} + +func TestStringListBuffer_Append(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + + stringBuffer := createStringBuffer(t) + str1 := "test" + got1 := stringBuffer.Set(str1) + require.True(t, got1) + + got2 := stringListBuffer.Append(stringBuffer) + require.True(t, got2) + + got3 := stringListBuffer.IsEmpty() + require.False(t, got3) + + str2 := stringListBuffer.Get(0) + require.Equal(t, str1, str2) +} + +func TestStringListBuffer_Remove(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + + stringBuffer := createStringBuffer(t) + str1 := "test" + got1 := stringBuffer.Set(str1) + require.True(t, got1) + + got2 := stringListBuffer.Append(stringBuffer) + require.True(t, got2) + + removed := stringListBuffer.Remove(0) + require.True(t, removed) + + got3 := stringListBuffer.IsEmpty() + require.True(t, got3) +} + +func TestStringListBuffer_Size(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + + stringBuffer := createStringBuffer(t) + str1 := "test" + got1 := stringBuffer.Set(str1) + require.True(t, got1) + + got2 := stringListBuffer.Append(stringBuffer) + require.True(t, got2) + + got3 := stringListBuffer.IsEmpty() + require.False(t, got3) + + size := stringListBuffer.Size() + require.Equal(t, uint64(1), size) +} + +func TestStringListBuffer_GetAll(t *testing.T) { + stringListBuffer := createStringListBuffer(t) + defer stringListBuffer.Destroy() + + stringBuffer := createStringBuffer(t) + str1 := "test" + got1 := stringBuffer.Set(str1) + require.True(t, got1) + + got2 := stringListBuffer.Append(stringBuffer) + require.True(t, got2) + + got3 := stringListBuffer.IsEmpty() + require.False(t, got3) + + list := stringListBuffer.GetAll() + require.Len(t, list, 1) +} diff --git a/internal/notification/cgo.go b/internal/notification/cgo.go deleted file mode 100644 index c520cf6..0000000 --- a/internal/notification/cgo.go +++ /dev/null @@ -1,10 +0,0 @@ -//go:build !customenv - -package notification - -/* -#cgo !windows pkg-config: maa -#cgo windows CFLAGS: -IC:/maa/include -#cgo windows LDFLAGS: -LC:/maa/bin -lMaaFramework -*/ -import "C" diff --git a/internal/notification/notification.go b/internal/notification/notification.go deleted file mode 100644 index 56e2411..0000000 --- a/internal/notification/notification.go +++ /dev/null @@ -1,41 +0,0 @@ -package notification - -/* -#include - -typedef const char* StringView; - -extern void _MaaNotificationCallbackAgent(const char* message, const char* details_json, void* callback_arg); -*/ -import "C" -import ( - "sync/atomic" - "unsafe" -) - -var ( - notificationCallbackID uint64 - notificationCallbackAgents = make(map[uint64]func(msg, detailsJson string)) -) - -func RegisterCallback(callback func(msg, detailsJson string)) uint64 { - id := atomic.AddUint64(¬ificationCallbackID, 1) - notificationCallbackAgents[id] = callback - return id -} - -func UnregisterCallback(id uint64) { - delete(notificationCallbackAgents, id) -} - -//export _MaaNotificationCallbackAgent -func _MaaNotificationCallbackAgent(msg, detailsJson C.StringView, callbackArg unsafe.Pointer) { - // Here, we are simply passing the uint64 value as a pointer - // and will not actually dereference this pointer. - id := uint64(uintptr(callbackArg)) - callback := notificationCallbackAgents[id] - if callback == nil { - return - } - callback(C.GoString(msg), C.GoString(detailsJson)) -} diff --git a/json.go b/json.go index 3cee1e0..2a5a54d 100644 --- a/json.go +++ b/json.go @@ -13,3 +13,11 @@ func toJSON(v any) (string, error) { } return string(data), nil } + +func formJSON(data []byte, v any) error { + err := json.Unmarshal(data, v) + if err != nil { + return err + } + return nil +} diff --git a/notification.go b/notification.go new file mode 100644 index 0000000..959835b --- /dev/null +++ b/notification.go @@ -0,0 +1,185 @@ +package maa + +/* +#include + +typedef const char* StringView; + +extern void _MaaNotificationCallbackAgent(const char* message, const char* details_json, void* notify_arg); +*/ +import "C" +import ( + "strings" + "sync/atomic" + "unsafe" +) + +var ( + notificationCallbackID uint64 + notificationCallbackAgents = make(map[uint64]Notification) +) + +func registerNotificationCallback(notify Notification) uint64 { + id := atomic.AddUint64(¬ificationCallbackID, 1) + notificationCallbackAgents[id] = notify + return id +} + +func unregisterNotificationCallback(id uint64) { + delete(notificationCallbackAgents, id) +} + +type NotificationType int + +// NotificationType +const ( + NotificationTypeUnknown NotificationType = iota + NotificationTypeStarting + NotificationTypeSucceeded + NotificationTypeFailed +) + +type ResourceLoadingDetail struct { + ResID uint64 `json:"res_id"` + Hash string `json:"hash"` + Path string `json:"path"` +} + +type ControllerActionDetail struct { + CtrlID uint64 `json:"ctrl_id"` + UUID string `json:"uuid"` + Action string `json:"action"` +} + +type TaskerTaskDetail struct { + TaskID uint64 `json:"task_id"` + Entry string `json:"entry"` + UUID string `json:"uuid"` + Hash string `json:"hash"` +} + +type TaskNextListDetail struct { + TaskID uint64 `json:"task_id"` + Name string `json:"name"` + NextList []string `json:"next_list"` +} + +type TaskRecognitionDetail struct { + TaskID uint64 `json:"task_id"` + RecID uint64 `json:"reco_id"` + Name string `json:"name"` +} + +type TaskActionDetail struct { + TaskID uint64 `json:"task_id"` + NodeID uint64 `json:"node_id"` + Name string `json:"name"` +} + +type Notification interface { + OnResourceLoading(notifyType NotificationType, detail ResourceLoadingDetail) + OnControllerAction(notifyType NotificationType, detail ControllerActionDetail) + OnTaskerTask(notifyType NotificationType, detail TaskerTaskDetail) + OnTaskNextList(notifyType NotificationType, detail TaskNextListDetail) + OnTaskRecognition(notifyType NotificationType, detail TaskRecognitionDetail) + OnTaskAction(notifyType NotificationType, detail TaskActionDetail) + OnRawNotification(msg, detailsJSON string) + OnUnknownNotification(msg, detailsJSON string) +} + +type NotificationHandler struct{} + +func _NewNotificationHandler() Notification { + return &NotificationHandler{} +} + +func (n *NotificationHandler) OnResourceLoading(_ NotificationType, _ ResourceLoadingDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnControllerAction(_ NotificationType, _ ControllerActionDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnTaskerTask(_ NotificationType, _ TaskerTaskDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnTaskNextList(_ NotificationType, _ TaskNextListDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnTaskRecognition(_ NotificationType, _ TaskRecognitionDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnTaskAction(_ NotificationType, _ TaskActionDetail) { + // DO NOTHING +} + +func (n *NotificationHandler) OnRawNotification(msg, detailsJSON string) { + notifyType := n.notificationType(msg) + switch { + case strings.HasPrefix(msg, "Resource.Loading"): + var detail ResourceLoadingDetail + _ = formJSON([]byte(msg), &detail) + n.OnResourceLoading(notifyType, detail) + return + case strings.HasPrefix(msg, "Controller.Action"): + var detail ControllerActionDetail + _ = formJSON([]byte(msg), &detail) + n.OnControllerAction(notifyType, detail) + return + case strings.HasPrefix(msg, "Tasker.Task"): + var detail TaskerTaskDetail + _ = formJSON([]byte(msg), &detail) + n.OnTaskerTask(notifyType, detail) + return + case strings.HasPrefix(msg, "Task.NextList"): + var detail TaskNextListDetail + _ = formJSON([]byte(msg), &detail) + n.OnTaskNextList(notifyType, detail) + return + case strings.HasPrefix(msg, "Task.Recognition"): + var detail TaskRecognitionDetail + _ = formJSON([]byte(msg), &detail) + n.OnTaskRecognition(notifyType, detail) + return + case strings.HasPrefix(msg, "Task.Action"): + var detail TaskActionDetail + _ = formJSON([]byte(msg), &detail) + n.OnTaskAction(notifyType, detail) + return + default: + n.OnUnknownNotification(msg, detailsJSON) + } +} + +func (n *NotificationHandler) OnUnknownNotification(_, _ string) { + // DO NOTHING +} + +func (n *NotificationHandler) notificationType(msg string) NotificationType { + switch { + case strings.HasSuffix(msg, ".Starting"): + return NotificationTypeStarting + case strings.HasSuffix(msg, "Succeeded"): + return NotificationTypeSucceeded + case strings.HasSuffix(msg, "Failed"): + return NotificationTypeFailed + default: + return NotificationTypeUnknown + } +} + +//export _MaaNotificationCallbackAgent +func _MaaNotificationCallbackAgent(msg, detailsJson C.StringView, notifyArg unsafe.Pointer) { + // Here, we are simply passing the uint64 value as a pointer + // and will not actually dereference this pointer. + id := uint64(uintptr(notifyArg)) + notify := notificationCallbackAgents[id] + if notify == nil { + return + } + notify.OnRawNotification(C.GoString(msg), C.GoString(detailsJson)) +} diff --git a/notification_test.go b/notification_test.go new file mode 100644 index 0000000..49b2afc --- /dev/null +++ b/notification_test.go @@ -0,0 +1,38 @@ +package maa + +import ( + "fmt" + "github.com/stretchr/testify/require" + "testing" +) + +type testNotificationHandlerOnRawNotification struct { + *NotificationHandler +} + +func (t *testNotificationHandlerOnRawNotification) OnRawNotification(msg, detailsJson string) { + fmt.Printf("TestNotificationHandler_OnRawNotification, msg: %s, detailsJson: %s\n", msg, detailsJson) + t.NotificationHandler.OnRawNotification(msg, detailsJson) +} + +func TestNotificationHandler_OnRawNotification(t *testing.T) { + ctrl := createDbgController(t, &testNotificationHandlerOnRawNotification{}) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, &testNotificationHandlerOnRawNotification{}) + defer res.Destroy() + + tasker := createTasker(t, &testNotificationHandlerOnRawNotification{}) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got := tasker.PostPipeline("TestNotificationHandler_OnRawNotification", J{ + "TestNotificationHandler_OnRawNotification": J{ + "action": "Click", + "target": []int{100, 200, 100, 100}, + }, + }).Wait().Success() + require.True(t, got) +} diff --git a/option.go b/option.go index c380991..6d019be 100644 --- a/option.go +++ b/option.go @@ -40,14 +40,17 @@ const ( // value: bool, eg: true; val_size: sizeof(bool) GlobalOptionShowHitDraw - // GlobalOptionDebugMessage Whether to callback debug message + // GlobalOptionDebugMode Whether to debug // // value: bool, eg: true; val_size: sizeof(bool) - GlobalOptionDebugMessage + GlobalOptionDebugMode ) // SetLogDir sets the log directory. func SetLogDir(path string) bool { + if path == "" { + return false + } cPath := C.CString(path) defer C.free(unsafe.Pointer(cPath)) return C.MaaSetGlobalOption(C.int32_t(GlobalOptionLogDir), C.MaaOptionValue(cPath), C.uint64_t(len(path))) != 0 @@ -99,11 +102,11 @@ func SetShowHitDraw(enabled bool) bool { return C.MaaSetGlobalOption(C.int32_t(GlobalOptionShowHitDraw), C.MaaOptionValue(unsafe.Pointer(&cEnabled)), C.uint64_t(unsafe.Sizeof(cEnabled))) != 0 } -// SetDebugMessage sets whether to callback debug message. -func SetDebugMessage(enabled bool) bool { +// SetDebugMode sets whether to enable debug mode. +func SetDebugMode(enabled bool) bool { var cEnabled uint8 if enabled { cEnabled = 1 } - return C.MaaSetGlobalOption(C.int32_t(GlobalOptionDebugMessage), C.MaaOptionValue(unsafe.Pointer(&cEnabled)), C.uint64_t(unsafe.Sizeof(cEnabled))) != 0 + return C.MaaSetGlobalOption(C.int32_t(GlobalOptionDebugMode), C.MaaOptionValue(unsafe.Pointer(&cEnabled)), C.uint64_t(unsafe.Sizeof(cEnabled))) != 0 } diff --git a/option_test.go b/option_test.go new file mode 100644 index 0000000..4452a5f --- /dev/null +++ b/option_test.go @@ -0,0 +1,192 @@ +package maa + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestSetLogDir(t *testing.T) { + testCases := []struct { + name string + path string + expected bool + }{ + { + name: "ValidPath", + path: "./test/debug", + expected: true, + }, + { + name: "EmptyPath", + path: "", + expected: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetLogDir(tc.path) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestSetSaveDraw(t *testing.T) { + testCases := []struct { + name string + enabled bool + expected bool + }{ + { + name: "EnableSaveDraw", + enabled: true, + expected: true, + }, + { + name: "DisableSaveDraw", + enabled: false, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetSaveDraw(tc.enabled) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestSetRecording(t *testing.T) { + testCases := []struct { + name string + enabled bool + expected bool + }{ + { + name: "EnableRecording", + enabled: true, + expected: true, + }, + { + name: "DisableRecording", + enabled: false, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetRecording(tc.enabled) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestSetStdoutLevel(t *testing.T) { + testCases := []struct { + name string + level LoggingLevel + expected bool + }{ + { + name: "SetLevelOff", + level: LoggingLevelOff, + expected: true, + }, + { + name: "SetLevelFatal", + level: LoggingLevelFatal, + expected: true, + }, + { + name: "SetLevelError", + level: LoggingLevelError, + expected: true, + }, + { + name: "SetLevelWarn", + level: LoggingLevelWarn, + expected: true, + }, + { + name: "SetLevelInfo", + level: LoggingLevelInfo, + expected: true, + }, + { + name: "SetLevelDebug", + level: LoggingLevelDebug, + expected: true, + }, + { + name: "SetLevelTrace", + level: LoggingLevelTrace, + expected: true, + }, + { + name: "SetLevelAll", + level: LoggingLevelAll, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetStdoutLevel(tc.level) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestSetShowHitDraw(t *testing.T) { + testCases := []struct { + name string + enabled bool + expected bool + }{ + { + name: "EnableShowHitDraw", + enabled: true, + expected: true, + }, + { + name: "DisableShowHitDraw", + enabled: false, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetShowHitDraw(tc.enabled) + require.Equal(t, tc.expected, result) + }) + } +} + +func TestSetDebugMode(t *testing.T) { + testCases := []struct { + name string + enabled bool + expected bool + }{ + { + name: "EnableDebugMode", + enabled: true, + expected: true, + }, + { + name: "DisableDebugMode", + enabled: false, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := SetDebugMode(tc.enabled) + require.Equal(t, tc.expected, result) + }) + } +} diff --git a/resource.go b/resource.go index ca62080..c75b358 100644 --- a/resource.go +++ b/resource.go @@ -6,7 +6,7 @@ package maa extern void _MaaNotificationCallbackAgent(const char* message, const char* details_json, void* callback_arg); -extern uint8_t _MaaCustomRecognizerCallbackAgent( +extern uint8_t _MaaCustomRecognitionCallbackAgent( MaaContext* ctx, int64_t task_id, const char* current_task_name, @@ -31,7 +31,6 @@ extern uint8_t _MaaCustomActionCallbackAgent( import "C" import ( "github.com/MaaXYZ/maa-framework-go/internal/buffer" - "github.com/MaaXYZ/maa-framework-go/internal/notification" "github.com/MaaXYZ/maa-framework-go/internal/store" "unsafe" ) @@ -49,8 +48,8 @@ type Resource struct { } // NewResource creates a new resource. -func NewResource(callback func(msg, detailsJson string)) *Resource { - id := notification.RegisterCallback(callback) +func NewResource(notify Notification) *Resource { + id := registerNotificationCallback(notify) handle := C.MaaResourceCreate( C.MaaNotificationCallback(C._MaaNotificationCallbackAgent), // Here, we are simply passing the uint64 value as a pointer @@ -73,7 +72,7 @@ func NewResource(callback func(msg, detailsJson string)) *Resource { // Destroy frees the resource. func (r *Resource) Destroy() { value := resourceStore.Get(r.Handle()) - notification.UnregisterCallback(value.NotificationCallbackID) + unregisterNotificationCallback(value.NotificationCallbackID) resourceStore.Del(r.Handle()) C.MaaResourceDestroy(r.handle) } @@ -82,12 +81,12 @@ func (r *Resource) Handle() unsafe.Pointer { return unsafe.Pointer(r.handle) } -// RegisterCustomRecognizer registers a custom recognizer to the resource. -func (r *Resource) RegisterCustomRecognizer(name string, recognizer CustomRecognizer) bool { - id := registerCustomRecognizer(recognizer) +// RegisterCustomRecognition registers a custom recognition to the resource. +func (r *Resource) RegisterCustomRecognition(name string, recognition CustomRecognition) bool { + id := registerCustomRecognition(recognition) value := resourceStore.Get(r.Handle()) if oldID, ok := value.CustomRecognizersCallbackID[name]; ok { - unregisterCustomRecognizer(oldID) + unregisterCustomRecognition(oldID) } value.CustomRecognizersCallbackID[name] = id resourceStore.Set(r.Handle(), value) @@ -95,10 +94,10 @@ func (r *Resource) RegisterCustomRecognizer(name string, recognizer CustomRecogn cName := C.CString(name) defer C.free(unsafe.Pointer(cName)) - got := C.MaaResourceRegisterCustomRecognizer( + got := C.MaaResourceRegisterCustomRecognition( r.handle, cName, - C.MaaCustomRecognizerCallback(C._MaaCustomRecognizerCallbackAgent), + C.MaaCustomRecognitionCallback(C._MaaCustomRecognitionCallbackAgent), // Here, we are simply passing the uint64 value as a pointer // and will not actually dereference this pointer. unsafe.Pointer(uintptr(id)), @@ -106,11 +105,11 @@ func (r *Resource) RegisterCustomRecognizer(name string, recognizer CustomRecogn return got != 0 } -// UnregisterCustomRecognizer unregisters a custom recognizer from the resource. -func (r *Resource) UnregisterCustomRecognizer(name string) bool { +// UnregisterCustomRecognition unregisters a custom recognition from the resource. +func (r *Resource) UnregisterCustomRecognition(name string) bool { value := resourceStore.Get(r.Handle()) if id, ok := value.CustomRecognizersCallbackID[name]; ok { - unregisterCustomRecognizer(id) + unregisterCustomRecognition(id) } else { return false } @@ -118,18 +117,18 @@ func (r *Resource) UnregisterCustomRecognizer(name string) bool { cName := C.CString(name) defer C.free(unsafe.Pointer(cName)) - got := C.MaaResourceUnregisterCustomRecognizer(r.handle, cName) + got := C.MaaResourceUnregisterCustomRecognition(r.handle, cName) return got != 0 } -// ClearCustomRecognizer clears all custom recognizers registered from the resource. -func (r *Resource) ClearCustomRecognizer() bool { +// ClearCustomRecognition clears all custom recognitions registered from the resource. +func (r *Resource) ClearCustomRecognition() bool { value := resourceStore.Get(r.Handle()) for _, id := range value.CustomRecognizersCallbackID { - unregisterCustomRecognizer(id) + unregisterCustomRecognition(id) } - got := C.MaaResourceClearCustomRecognizer(r.handle) + got := C.MaaResourceClearCustomRecognition(r.handle) return got != 0 } diff --git a/resource_test.go b/resource_test.go index 1d71570..1183220 100644 --- a/resource_test.go +++ b/resource_test.go @@ -5,8 +5,308 @@ import ( "testing" ) -func createResource(t *testing.T) *Resource { - res := NewResource(nil) +func createResource(t *testing.T, notify Notification) *Resource { + res := NewResource(notify) require.NotNil(t, res) return res } + +func TestNewResource(t *testing.T) { + res := createResource(t, nil) + res.Destroy() +} + +func TestResource_Handle(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + handle := res.Handle() + require.NotNil(t, handle) +} + +type testResourceTestRec struct{} + +func (t *testResourceTestRec) Run(_ *Context, _ *CustomRecognitionArg) (CustomRecognitionResult, bool) { + return CustomRecognitionResult{}, true +} + +func TestResource_RegisterCustomRecognition(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got1 := res.RegisterCustomRecognition("TestRec", &testResourceTestRec{}) + require.True(t, got1) + + got2 := tasker.PostPipeline("TestResource_RegisterCustomRecognition", J{ + "TestResource_RegisterCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec", + }, + }).Wait().Success() + require.True(t, got2) +} + +func TestResource_UnregisterCustomRecognition(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got1 := res.RegisterCustomRecognition("TestRec", &testResourceTestRec{}) + require.True(t, got1) + + got2 := tasker.PostPipeline("TestResource_UnregisterCustomRecognition", J{ + "TestResource_UnregisterCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec", + }, + }).Wait().Success() + require.True(t, got2) + + got3 := res.UnregisterCustomRecognition("TestRec") + require.True(t, got3) + + got4 := tasker.PostPipeline("TestResource_UnregisterCustomRecognition", J{ + "TestResource_UnregisterCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec", + }, + }).Wait().Failure() + require.True(t, got4) +} + +func TestResource_ClearCustomRecognition(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got1 := res.RegisterCustomRecognition("TestRec1", &testResourceTestRec{}) + require.True(t, got1) + got2 := res.RegisterCustomRecognition("TestRec2", &testResourceTestRec{}) + require.True(t, got2) + + got3 := tasker.PostPipeline("TestResource_ClearCustomRecognition", J{ + "TestResource_ClearCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec1", + }, + }).Wait().Success() + require.True(t, got3) + got4 := tasker.PostPipeline("TestResource_ClearCustomRecognition", J{ + "TestResource_ClearCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec2", + }, + }).Wait().Success() + require.True(t, got4) + + got5 := res.ClearCustomRecognition() + require.True(t, got5) + + got6 := tasker.PostPipeline("TestResource_ClearCustomRecognition", J{ + "TestResource_ClearCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec1", + }, + }).Wait().Failure() + require.True(t, got6) + got7 := tasker.PostPipeline("TestResource_ClearCustomRecognition", J{ + "TestResource_ClearCustomRecognition": J{ + "recognition": "custom", + "custom_recognition": "TestRec2", + }, + }).Wait().Failure() + require.True(t, got7) +} + +type testResourceTestAct struct{} + +func (t *testResourceTestAct) Run(_ *Context, _ *CustomActionArg) bool { + return true +} + +func TestResource_RegisterCustomAction(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + registered := res.RegisterCustomAction("TestAct", &testResourceTestAct{}) + require.True(t, registered) + + got := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct", + }, + }).Wait().Success() + require.True(t, got) +} + +func TestResource_UnregisterCustomAction(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + registered := res.RegisterCustomAction("TestAct", &testResourceTestAct{}) + require.True(t, registered) + + got1 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct", + }, + }).Wait().Success() + require.True(t, got1) + + unregistered := res.UnregisterCustomAction("TestAct") + require.True(t, unregistered) + + got2 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct", + }, + }).Wait().Failure() + require.True(t, got2) +} + +func TestResource_ClearCustomAction(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + registered1 := res.RegisterCustomAction("TestAct1", &testResourceTestAct{}) + require.True(t, registered1) + registered2 := res.RegisterCustomAction("TestAct2", &testResourceTestAct{}) + require.True(t, registered2) + + got1 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct1", + }, + }).Wait().Success() + require.True(t, got1) + got2 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct2", + }, + }).Wait().Success() + require.True(t, got2) + + cleared := res.ClearCustomAction() + require.True(t, cleared) + + got3 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct1", + }, + }).Wait().Failure() + require.True(t, got3) + got4 := tasker.PostPipeline("TestResource_RegisterCustomAction", J{ + "TestResource_RegisterCustomAction": J{ + "action": "custom", + "custom_action": "TestAct2", + }, + }).Wait().Failure() + require.True(t, got4) +} + +func TestResource_PostPath(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) +} + +func TestResource_Clear(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) + cleared := res.Clear() + require.True(t, cleared) +} + +func TestResource_Loaded(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) + loaded := res.Loaded() + require.True(t, loaded) +} + +func TestResource_GetHash(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) + hash, ok := res.GetHash() + require.True(t, ok) + require.NotEqual(t, "0", hash) +} + +func TestResource_GetTaskList(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) + taskList, ok := res.GetTaskList() + require.True(t, ok) + require.NotEmpty(t, taskList) +} diff --git a/tasker.go b/tasker.go index 9c28a76..a0ca65d 100644 --- a/tasker.go +++ b/tasker.go @@ -9,10 +9,8 @@ extern void _MaaNotificationCallbackAgent(const char* message, const char* detai import "C" import ( "github.com/MaaXYZ/maa-framework-go/internal/buffer" - "github.com/MaaXYZ/maa-framework-go/internal/notification" "github.com/MaaXYZ/maa-framework-go/internal/store" "image" - "time" "unsafe" ) @@ -23,8 +21,8 @@ type Tasker struct { } // NewTasker creates an new tasker. -func NewTasker(callback func(msg, detailsJson string)) *Tasker { - id := notification.RegisterCallback(callback) +func NewTasker(notify Notification) *Tasker { + id := registerNotificationCallback(notify) handle := C.MaaTaskerCreate( C.MaaNotificationCallback(C._MaaNotificationCallbackAgent), // Here, we are simply passing the uint64 value as a pointer @@ -41,7 +39,7 @@ func NewTasker(callback func(msg, detailsJson string)) *Tasker { // Destroy free the tasker. func (t *Tasker) Destroy() { id := taskerStore.Get(t.Handle()) - notification.UnregisterCallback(id) + unregisterNotificationCallback(id) taskerStore.Del(t.Handle()) C.MaaTaskerDestroy(t.handle) } @@ -61,8 +59,8 @@ func (t *Tasker) BindController(ctrl Controller) bool { return C.MaaTaskerBindController(t.handle, (*C.MaaController)(ctrl.Handle())) != 0 } -// Inited checks if the tasker is initialized. -func (t *Tasker) Inited() bool { +// Initialized checks if the tasker is initialized. +func (t *Tasker) Initialized() bool { return C.MaaTaskerInited(t.handle) != 0 } @@ -108,13 +106,6 @@ func (t *Tasker) wait(id int64) Status { return Status(C.MaaTaskerWait(t.handle, C.int64_t(id))) } -// WaitAll waits for all tasks to complete. -func (t *Tasker) WaitAll() { - for t.Running() { - time.Sleep(time.Millisecond * 10) - } -} - // Running checks if the instance running. func (t *Tasker) Running() bool { return C.MaaTaskerRunning(t.handle) != 0 @@ -182,7 +173,7 @@ func (t *Tasker) getRecognitionDetail(recId int64) *RecognitionDetail { return nil } - rawImg := raw.GetByRawData() + rawImg := raw.Get() DrawImages := draws.GetAll() return &RecognitionDetail{ @@ -200,7 +191,6 @@ type NodeDetail struct { ID int64 Name string Recognition *RecognitionDetail - Times uint64 RunCompleted bool } @@ -209,14 +199,12 @@ func (t *Tasker) getNodeDetail(nodeId int64) *NodeDetail { name := buffer.NewStringBuffer() defer name.Destroy() var recId int64 - var times uint64 var runCompleted uint8 got := C.MaaTaskerGetNodeDetail( t.handle, C.int64_t(nodeId), (*C.MaaStringBuffer)(name.Handle()), (*C.int64_t)(unsafe.Pointer(&recId)), - (*C.uint64_t)(unsafe.Pointer(×)), (*C.uint8_t)(unsafe.Pointer(&runCompleted)), ) if got == 0 { @@ -232,7 +220,6 @@ func (t *Tasker) getNodeDetail(nodeId int64) *NodeDetail { ID: nodeId, Name: name.Get(), Recognition: recognitionDetail, - Times: times, RunCompleted: runCompleted != 0, } } diff --git a/tasker_test.go b/tasker_test.go index f57a868..b2919af 100644 --- a/tasker_test.go +++ b/tasker_test.go @@ -3,10 +3,11 @@ package maa import ( "github.com/stretchr/testify/require" "testing" + "time" ) -func createTasker(t *testing.T) *Tasker { - tasker := NewTasker(nil) +func createTasker(t *testing.T, notify Notification) *Tasker { + tasker := NewTasker(notify) require.NotNil(t, tasker) return tasker } @@ -17,3 +18,177 @@ func taskerBind(t *testing.T, tasker *Tasker, ctrl Controller, res *Resource) { isCtrlBound := tasker.BindController(ctrl) require.True(t, isCtrlBound) } + +func TestNewTasker(t *testing.T) { + tasker := createTasker(t, nil) + tasker.Destroy() +} + +func TestTasker_Handle(t *testing.T) { + tasker := createTasker(t, nil) + defer tasker.Destroy() + handle := tasker.Handle() + require.NotNil(t, handle) +} + +func TestTasker_BindResource(t *testing.T) { + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + bound := tasker.BindResource(res) + require.True(t, bound) +} + +func TestTasker_BindController(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + bound := tasker.BindController(ctrl) + require.True(t, bound) +} + +func TestTasker_Initialized(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + connected := ctrl.PostConnect().Wait().Success() + require.True(t, connected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + initialized := tasker.Initialized() + require.True(t, initialized) +} + +func TestTasker_PostPipeline(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got := tasker.PostPipeline("TestTasker_PostPipeline", J{ + "TestTasker_PostPipeline": J{ + "action": "Click", + "target": []int{100, 200, 100, 100}, + }, + }).Wait().Success() + require.True(t, got) +} + +func TestTasker_Running(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got := tasker.Running() + require.False(t, got) +} + +func TestTasker_PostStop(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got := tasker.PostStop() + require.True(t, got) +} + +func TestTasker_GetResource(t *testing.T) { + res1 := createResource(t, nil) + defer res1.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + bound := tasker.BindResource(res1) + require.True(t, bound) + + res2 := tasker.GetResource() + require.NotNil(t, res2) + require.Equal(t, res1.Handle(), res2.Handle()) +} + +func TestTasker_GetController(t *testing.T) { + ctrl1 := createDbgController(t, nil) + defer ctrl1.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + bound := tasker.BindController(ctrl1) + require.True(t, bound) + + ctrl2 := tasker.GetController() + require.NotNil(t, ctrl2) + require.Equal(t, ctrl1.Handle(), ctrl2.Handle()) +} + +func TestTasker_ClearCache(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + + got := tasker.ClearCache() + require.True(t, got) +} + +func TestTasker_GetLatestNode(t *testing.T) { + ctrl := createDbgController(t, nil) + defer ctrl.Destroy() + isConnected := ctrl.PostConnect().Wait().Success() + require.True(t, isConnected) + + res := createResource(t, nil) + defer res.Destroy() + resDir := "./test/data_set/PipelineSmoking/resource" + isPathSet := res.PostPath(resDir).Wait().Success() + require.True(t, isPathSet) + + tasker := createTasker(t, nil) + defer tasker.Destroy() + taskerBind(t, tasker, ctrl, res) + job := tasker.PostPipeline("Wilderness") + require.NotNil(t, job) + time.Sleep(2 * time.Second) + detail := tasker.GetLatestNode("Wilderness") + t.Log(detail) + got := job.Wait().Success() + require.True(t, got) +} diff --git a/test/pipleline_smoking_test.go b/test/pipleline_smoking_test.go index 2770cb0..5b13736 100644 --- a/test/pipleline_smoking_test.go +++ b/test/pipleline_smoking_test.go @@ -31,7 +31,7 @@ func TestPipelineSmoking(t *testing.T) { isCtrlBound := tasker.BindController(ctrl) require.True(t, isCtrlBound) - isInitialized := tasker.Inited() + isInitialized := tasker.Initialized() require.True(t, isInitialized) got := tasker.PostPipeline("Wilderness").Wait().Success() diff --git a/toolkit.go b/toolkit.go index 693c139..2867cc7 100644 --- a/toolkit.go +++ b/toolkit.go @@ -4,7 +4,7 @@ package maa #include #include -extern uint8_t _MaaCustomRecognizerCallbackAgent( +extern uint8_t _MaaCustomRecognitionCallbackAgent( MaaContext* ctx, int64_t task_id, const char* current_task_name, @@ -30,7 +30,6 @@ extern void _MaaNotificationCallbackAgent(const char* message, const char* detai */ import "C" import ( - "github.com/MaaXYZ/maa-framework-go/internal/notification" "unsafe" ) @@ -138,9 +137,9 @@ type piStoreValue struct { var piStore = make(map[uint64]piStoreValue) -// RegisterPICustomRecognizer registers a custom recognizer. -func (t *Toolkit) RegisterPICustomRecognizer(instId uint64, name string, recognizer CustomRecognizer) { - id := registerCustomRecognizer(recognizer) +// RegisterPICustomRecognition registers a custom recognizer. +func (t *Toolkit) RegisterPICustomRecognition(instId uint64, name string, recognition CustomRecognition) { + id := registerCustomRecognition(recognition) if _, ok := piStore[instId]; !ok { piStore[instId] = piStoreValue{ CustomRecognizersCallbackID: make(map[string]uint64), @@ -155,7 +154,7 @@ func (t *Toolkit) RegisterPICustomRecognizer(instId uint64, name string, recogni C.MaaToolkitProjectInterfaceRegisterCustomRecognition( C.uint64_t(instId), cName, - C.MaaCustomRecognizerCallback(C._MaaCustomRecognizerCallbackAgent), + C.MaaCustomRecognitionCallback(C._MaaCustomRecognitionCallbackAgent), // Here, we are simply passing the uint64 value as a pointer // and will not actually dereference this pointer. unsafe.Pointer(uintptr(id)), @@ -186,11 +185,11 @@ func (t *Toolkit) RegisterPICustomAction(instId uint64, name string, action Cust ) } -// ClearPICustom unregisters all custom recognizers and actions for a given instance. +// ClearPICustom unregisters all custom recognitions and actions for a given instance. func (t *Toolkit) ClearPICustom(instId uint64) { value := piStore[instId] for _, id := range value.CustomRecognizersCallbackID { - unregisterCustomRecognizer(id) + unregisterCustomRecognition(id) } for _, id := range value.CustomActionsCallbackID { unregisterCustomAction(id) @@ -198,7 +197,7 @@ func (t *Toolkit) ClearPICustom(instId uint64) { } // RunCli runs the PI CLI. -func (t *Toolkit) RunCli(instId uint64, resourcePath, userPath string, directly bool, callback func(msg, detailsJson string)) bool { +func (t *Toolkit) RunCli(instId uint64, resourcePath, userPath string, directly bool, notify Notification) bool { cResourcePath := C.CString(resourcePath) defer C.free(unsafe.Pointer(cResourcePath)) cUserPath := C.CString(userPath) @@ -207,7 +206,7 @@ func (t *Toolkit) RunCli(instId uint64, resourcePath, userPath string, directly if directly { cDirectly = 1 } - id := notification.RegisterCallback(callback) + id := registerNotificationCallback(notify) got := C.MaaToolkitProjectInterfaceRunCli( C.uint64_t(instId), cResourcePath, diff --git a/toolkit_test.go b/toolkit_test.go new file mode 100644 index 0000000..6158936 --- /dev/null +++ b/toolkit_test.go @@ -0,0 +1,63 @@ +package maa + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func createToolkit(t *testing.T) *Toolkit { + toolkit := NewToolkit() + require.NotNil(t, toolkit) + return toolkit +} + +func TestNewToolkit(t *testing.T) { + createToolkit(t) +} + +func TestToolkit_ConfigInitOption(t *testing.T) { + toolkit := createToolkit(t) + got := toolkit.ConfigInitOption("./test", "{}") + require.True(t, got) +} + +func TestToolkit_FindAdbDevices(t *testing.T) { + toolkit := createToolkit(t) + adbDevices := toolkit.FindAdbDevices() + require.NotNil(t, adbDevices) +} + +func TestToolkit_FindDesktopWindows(t *testing.T) { + toolkit := createToolkit(t) + desktopWindows := toolkit.FindDesktopWindows() + require.NotNil(t, desktopWindows) +} + +type testToolKitRec struct{} + +func (t *testToolKitRec) Run(_ *Context, _ *CustomRecognitionArg) (CustomRecognitionResult, bool) { + return CustomRecognitionResult{}, true +} + +func TestToolkit_RegisterPICustomRecognition(t *testing.T) { + toolkit := createToolkit(t) + toolkit.RegisterPICustomRecognition(0, "TestRec", &testToolKitRec{}) +} + +type testToolkitAct struct{} + +func (t testToolkitAct) Run(_ *Context, _ *CustomActionArg) bool { + return true +} + +func TestToolkit_RegisterPICustomAction(t *testing.T) { + toolkit := createToolkit(t) + toolkit.RegisterPICustomAction(0, "TestAct", &testToolkitAct{}) +} + +func TestToolkit_ClearPICustom(t *testing.T) { + toolkit := createToolkit(t) + toolkit.RegisterPICustomRecognition(0, "TestRec", &testToolKitRec{}) + toolkit.RegisterPICustomAction(0, "TestAct", &testToolkitAct{}) + toolkit.ClearPICustom(0) +}