diff --git a/src/MaaDebugger/maafw/__init__.py b/src/MaaDebugger/maafw/__init__.py index 77e0cec..1ad4cd7 100644 --- a/src/MaaDebugger/maafw/__init__.py +++ b/src/MaaDebugger/maafw/__init__.py @@ -1,10 +1,11 @@ import re +import io from pathlib import Path from typing import Callable, List, Optional, Tuple, Union from asyncify import asyncify from PIL import Image -from maa.controller import AdbController, Win32Controller +from maa.controller import AdbController, Win32Controller, CustomController from maa.context import Context, ContextEventSink from maa.tasker import Tasker, RecognitionDetail from maa.resource import Resource, ResourceEventSink @@ -12,14 +13,32 @@ from maa.agent_client import AgentClient from maa.library import Library from maa.event_sink import NotificationType +import numpy as np -from ..utils import cvmat_to_image +from ..utils.img_tools import cvmat_to_image, rgb_to_bgr + + +class MyCustomController(CustomController): + def __init__(self, img_bytes: bytes): + super().__init__() + + img = Image.open(io.BytesIO(img_bytes)) + self.ndarray = rgb_to_bgr(np.array(img)) + + def connect(self) -> bool: + return True + + def request_uuid(self) -> str: + return "0" + + def screencap(self) -> np.ndarray: + return self.ndarray class MaaFW: resource: Optional[Resource] - controller: Union[AdbController, Win32Controller, None] + controller: Union[AdbController, Win32Controller, CustomController, None] tasker: Optional[Tasker] agent: Optional[AgentClient] context_event_sink: Optional[ContextEventSink] @@ -89,6 +108,12 @@ def connect_win32hwnd( return True, None + def connect_custom_controller(self, img_bytes) -> Tuple[bool, Optional[str]]: + self.controller = MyCustomController(img_bytes) + self.controller.post_connection().wait() + + return True, None + @asyncify def load_resource(self, dir: List[Path]) -> Tuple[bool, Optional[str]]: if not self.resource: @@ -149,7 +174,20 @@ def run_task( if not AgentClient().register_sink(self.resource, self.controller, self.tasker): return False, "Failed to register Agent sink." - return self.tasker.post_task(entry, pipeline_override).wait().succeeded, None + if isinstance(self.controller, CustomController): + # disable action + pipeline_override.update( + {entry: {"action": {"type": "DoNothing"}, "next": []}} + ) + return ( + self.tasker.post_task(entry, pipeline_override).wait().succeeded, + None, + ) + else: + return ( + self.tasker.post_task(entry, pipeline_override).wait().succeeded, + None, + ) @asyncify def stop_task(self) -> None: @@ -176,6 +214,9 @@ def click(self, x, y) -> bool: if not self.controller: return False + if isinstance(self.controller, CustomController): + return False + return self.controller.post_click(x, y).wait().succeeded @asyncify diff --git a/src/MaaDebugger/utils/__init__.py b/src/MaaDebugger/utils/img_tools.py similarity index 52% rename from src/MaaDebugger/utils/__init__.py rename to src/MaaDebugger/utils/img_tools.py index 4ff55bf..5d237dd 100644 --- a/src/MaaDebugger/utils/__init__.py +++ b/src/MaaDebugger/utils/img_tools.py @@ -6,3 +6,11 @@ def cvmat_to_image(cvmat: ndarray) -> Image.Image: pil = Image.fromarray(cvmat) b, g, r = pil.split() return Image.merge("RGB", (r, g, b)) + + +def rgb_to_bgr(arr: ndarray) -> ndarray: + """RGB -> BGR 转换""" + if arr.ndim == 3 and arr.shape[2] >= 3: + return arr[:, :, ::-1].copy() + else: + return arr diff --git a/src/MaaDebugger/webpage/index_page/master_control.py b/src/MaaDebugger/webpage/index_page/master_control.py index 0dcfb3f..ce7ef15 100644 --- a/src/MaaDebugger/webpage/index_page/master_control.py +++ b/src/MaaDebugger/webpage/index_page/master_control.py @@ -39,6 +39,7 @@ def connect_control(): with ui.tabs() as tabs: adb = ui.tab("Adb") win32 = ui.tab("Win32") + custom = ui.tab("Custom") tab_panels = ui.tab_panels(tabs, value="Adb").bind_value(STORAGE, "controller_type") with tab_panels: @@ -49,6 +50,10 @@ def connect_control(): with ui.row(align_items="center").classes("w-full"): connect_win32_control() + with ui.tab_panel(custom): + with ui.row(align_items="center").classes("w-full"): + connect_custom_control() + os_type = system.get_os_type() if os_type != system.OSTypeEnum.Windows: win32.disable() @@ -301,6 +306,24 @@ def on_change_hwnd_select(value: Optional[str]): hwnd_input.value = value +def connect_custom_control(): + def on_upload(e): + GlobalStatus.ctrl_connecting = Status.RUNNING + try: + maafw.connect_custom_controller(e.content.read()) + except Exception as e: + GlobalStatus.ctrl_connecting = Status.FAILED + ui.notify( + f"Failed to load image. {e}", position="bottom-right", type="negative" + ) + return + + GlobalStatus.ctrl_connecting = Status.SUCCEEDED + + StatusIndicator(GlobalStatus, "ctrl_connecting") + ui.upload(auto_upload=True, on_upload=lambda e: on_upload(e)) + + def screenshot_control(): with ( ui.row() diff --git a/src/MaaDebugger/webpage/reco_page/__init__.py b/src/MaaDebugger/webpage/reco_page/__init__.py index 3645be8..4c58875 100644 --- a/src/MaaDebugger/webpage/reco_page/__init__.py +++ b/src/MaaDebugger/webpage/reco_page/__init__.py @@ -2,7 +2,7 @@ from nicegui import ui -from ...utils import cvmat_to_image +from ...utils.img_tools import cvmat_to_image from ...maafw import maafw, RecognitionDetail