Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for any ADB connection #389

Open
trigger337 opened this issue Mar 14, 2024 · 10 comments
Open

Add support for any ADB connection #389

trigger337 opened this issue Mar 14, 2024 · 10 comments
Labels
enhancement New feature or request

Comments

@trigger337
Copy link

Is your feature request related to a problem? Please describe.
I don't have a separate Windows machine, and I don't have an additional GPU to give to a VM

Describe the solution you'd like
It would be useful even for Windows to add support for running on an actual device. I'm guessing most things are done through ADB(and if not already, it can be implemented). So on a rooted device, you can force a resolution and run the game.

Describe alternatives you've considered
Tried a VM without hardware acceleration. Didn't go well. Android VM under a Windows VM even sounds scary

@martinmiglio
Copy link
Member

This would be a desired feature to add at some point, but right now the bot is bound pretty tight to MEmu as it configures a lot of settings on the emulator for the user, things which cant be controlled through ADB.

The core functionality is exclusively ADB once the bot is running though, so it might be worth while to make a mode to unbind it from MEmu and use any given ADB connection. But that would require the user to ensure their emulator has those correct settings.

@martinmiglio martinmiglio added the enhancement New feature or request label Mar 15, 2024
@trigger337
Copy link
Author

Would you mind pointing me to the exact settings/configuring mechanism? If it's not a lot, I might even give it a try, like writing a shell script to run on the device to force absolutely necessary things.

@trigger337
Copy link
Author

I looked through the code under /src/memu and it looks pretty much possible to remake that into anything. If I have enough free time, I might even try it, or, at least, make a script that will do some configurations on the device.

@martinmiglio
Copy link
Member

All MEmu interaction happens through memuc.exe which is controlled by the PyMemuc module. If you can replace every call to that module with a native ADB call, then that should work fine.

The only issue is we would need to skip the emulator creation step and leave that to the user.

For this to get implemented into this project it would need to be toggleable from the GUI with a path input to an ADB executable.

Keep me updated on your progress, thanks for investigating this.

@trigger337
Copy link
Author

Started to do some things. Would like to know if I'm doing anything wrong so far.
https://github.com/trigger337/py-clash-bot/tree/non-emulator
Also, how many parts of the script call PyMemuc? I really don't want to make a bigass separate controller, so it would be great to keep it all inside of adb.py. But if it's absolutely needed, I will see what I can do.

@martinmiglio
Copy link
Member

This looks good so far, you might benefit from using an existing ADB library such as adb-shell or pure-python-adb to avoid managing subprocess calls yourself.

Also, it might be worthwhile to create an abstraction for an emulator controller class which has all the necessary definitions for the bot, which can then be extended into a MEmu controller or pure ADB controller. I think I will investigate this and keep you updated.

@martinmiglio
Copy link
Member

This is what I'm thinking for the abstraction:

"""
This module contains the base class for emulator controllers.
"""

import numpy as np


class BaseEmulatorController:
    """
    Base class for emulator controllers.
    This class is used to define the interface for all emulator controllers.
    """

    def __init__(self):
        raise NotImplementedError

    def __del__(self):
        raise NotImplementedError

    def create(self):
        """
        This method is used to create the emulator.
        """
        raise NotImplementedError

    def configure(self):
        """
        This method is used to configure the emulator.
        """
        raise NotImplementedError

    def start(self):
        """
        This method is used to start the emulator.
        """
        raise NotImplementedError

    def stop(self):
        """
        This method is used to stop the emulator.
        """
        raise NotImplementedError

    def click(self, position: tuple[int, int]):
        """
        This method is used to click on the emulator screen.
        """
        raise NotImplementedError

    def swipe(
        self,
        start_position: tuple[int, int],
        end_position: tuple[int, int],
        duration: float,
    ):
        """
        This method is used to swipe on the emulator screen.
        """
        raise NotImplementedError

    def screenshot(self) -> np.ndarray:
        """
        This method is used to take a screenshot of the emulator screen.
        """
        raise NotImplementedError

    def install_apk(self, apk_path: str):
        """
        This method is used to install an APK on the emulator.
        """
        raise NotImplementedError

    def start_app(self, package_name: str):
        """
        This method is used to start an app on the emulator.
        """
        raise NotImplementedError

@trigger337
Copy link
Author

Seems good. Then how would I make it work? I'm really not an advanced coder and haven't touched modular projects. I would accept any guidance. Also should we use additional libraries? I was making the raw commands so there aren't any new dependencies, but if it's alright, and existing libraries are better, I can make it do.

@martinmiglio
Copy link
Member

For this case, I would prefer us to use a library to make calls to ADB, just to bring us back a layer of abstraction and not have to worry about parsing ADB commands and output.

For implementing base class, here's how I'm starting with the Memu implementation:

from pyclashbot.emulator.base import BaseEmulatorController
from pyclashbot.emulator.memu.screenshot import screen_shotter
from pyclashbot.emulator.memu.client import send_click, send_swipe
from pyclashbot.emulator.memu.configure import configure_vm


class MemuEmulatorController(BaseEmulatorController):
    """
    Class for controlling a MEmu emulator.
    """

    def __init__(self, vm_index: int):
        self.vm_index = vm_index
        super().__init__()

    def create(self):
        """
        This method is used to create the emulator.
        """
        raise NotImplementedError

    def configure(self):
        """
        This method is used to configure the emulator.
        """
        configure_vm(self.vm_index)

    def start(self):
        """
        This method is used to start the emulator.
        """
        raise NotImplementedError

    def stop(self):
        """
        This method is used to stop the emulator.
        """
        raise NotImplementedError

    def click(self, position: tuple[int, int]):
        """
        This method is used to click on the emulator screen.
        """
        send_click(self.vm_index, position[0], position[1])

    def swipe(
        self,
        start_position: tuple[int, int],
        end_position: tuple[int, int],
    ):
        """
        This method is used to swipe on the emulator screen.
        """
        send_swipe(
            self.vm_index,
            start_position[0],
            start_position[1],
            end_position[0],
            end_position[1],
        )

    def screenshot(self):
        """
        This method is used to take a screenshot of the emulator screen.
        """
        return screen_shotter[self.vm_index]

I will need to rework how the bot logic calls these functions, but the plan with this is when the bot is initialized from the main thread, a specific controller class will be selected to handle the interaction with the emulator.

I'm starting this work in the controller branch, you might want to branch of from there so we can work with the same base controller interface.

@martinmiglio
Copy link
Member

If you have specific questions about implementing this join the contributing discussion in the discord

@martinmiglio martinmiglio changed the title Add support for Linux Add support for any ADB connection Apr 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants