Skip to content

Commit

Permalink
add additional configuration logging (#424)
Browse files Browse the repository at this point in the history
  • Loading branch information
martinmiglio committed Aug 23, 2024
1 parent e6eb4a4 commit 2c13c1a
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 42 deletions.
19 changes: 16 additions & 3 deletions src/pyclashbot/memu/configure.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""A module for configuring Memu VMs.
"""
"""A module for configuring Memu VMs."""

import logging
import time

from pymemuc import ConfigKeys
from pymemuc import ConfigKeys, PyMemucError, PyMemucException

from pyclashbot.memu.pmc import pmc

ANDROID_VERSION = "96" # android 9, 64 bit
EMULATOR_NAME = f"pyclashbot-{ANDROID_VERSION}"

# see https://pymemuc.readthedocs.io/pymemuc.html#the-vm-configuration-keys-table
MEMU_CONFIGURATION: dict[ConfigKeys, str | int | float] = {
"start_window_mode": 1, # remember window position
Expand Down Expand Up @@ -53,5 +55,16 @@ def configure_vm(vm_index):
logging.info("Configured VM %s", vm_index)


def get_vm_configuration(vm_index: int) -> dict[str, str]:
current_configuration = {}
for key in MEMU_CONFIGURATION:
try:
current_value = pmc.get_configuration_vm(key, vm_index=vm_index)
current_configuration[key] = current_value
except PyMemucError as e:
logging.exception("Failed to get configuration for key %s: %s", key, e)
return current_configuration


if __name__ == "__main__":
pass
43 changes: 8 additions & 35 deletions src/pyclashbot/memu/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@

import psutil
import PySimpleGUI as sg
from pymemuc import PyMemucError, VMInfo

from pyclashbot.bot.nav import check_if_in_battle_at_start, check_if_on_clash_main_menu
from pyclashbot.memu.client import click, screenshot
from pyclashbot.memu.configure import configure_vm
from pyclashbot.memu.pmc import pmc
from pyclashbot.memu.configure import EMULATOR_NAME, configure_vm
from pyclashbot.memu.pmc import get_vm_index, pmc
from pyclashbot.utils.logger import Logger
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pymemuc import VMInfo


ANDROID_VERSION = "96" # android 9, 64 bit
EMULATOR_NAME = f"pyclashbot-{ANDROID_VERSION}"
APK_BASE_NAME = "com.supercell.clashroyale"


Expand Down Expand Up @@ -169,7 +171,7 @@ def check_for_vm(logger: Logger) -> int:
find_vm_tries = 0
while time.time() - find_vm_start_time < find_vm_timeout:
find_vm_tries += 1
vm_index = get_vm_index(logger, EMULATOR_NAME)
vm_index = get_vm_index(EMULATOR_NAME)

if vm_index != -1:
logger.change_status(
Expand Down Expand Up @@ -243,35 +245,6 @@ def rename_vm(


# emulator interaction methods


def get_vm_index(logger: Logger, name: str) -> int:
"""Get the index of the vm with the given name"""
# get list of vms on machine
vms: list[VMInfo] = pmc.list_vm_info()

# sorted by index, lowest to highest
vms.sort(key=lambda x: x["index"])

# get the indecies of all vms named pyclashbot
vm_indices: list[int] = [vm["index"] for vm in vms if vm["title"] == name]

# delete all vms except the lowest index, keep looping until there is only one
while len(vm_indices) > 1:
# as long as no exception is raised, this while loop should exit on first iteration
for vm_index in vm_indices[1:]:
try:
pmc.delete_vm(vm_index)
vm_indices.remove(vm_index)
except PyMemucError as err:
logger.error(str(err))
# don't raise error, just continue to loop until its deleted
# raise err # if program hangs on deleting vm then uncomment this line

# return the index. if no vms named pyclashbot exist, return -1
return vm_indices[0] if vm_indices else -1


def home_button_press(vm_index, clicks=4):
"""Method for skipping the memu ads that popip up when you start memu"""
for _ in range(clicks):
Expand Down
31 changes: 28 additions & 3 deletions src/pyclashbot/memu/pmc.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""This module provides a PyMemuc singleton instance.
"""
"""This module provides a PyMemuc singleton instance."""

import logging
import sys
from os.path import join

from pymemuc import PyMemuc
from pymemuc import PyMemuc, PyMemucError, VMInfo

FROZEN = getattr(sys, "frozen", False)

Expand All @@ -15,3 +15,28 @@
pmc._get_memu_top_level(),
"adb.exe",
)


def get_vm_index(name: str) -> int:
"""Get the index of the vm with the given name"""
# get list of vms on machine
vms: list[VMInfo] = pmc.list_vm_info()

# sorted by index, lowest to highest
vms.sort(key=lambda x: x["index"])

# get the indecies of all vms named pyclashbot
vm_indices: list[int] = [vm["index"] for vm in vms if vm["title"] == name]

# delete all vms except the lowest index, keep looping until there is only one
while len(vm_indices) > 1:
# as long as no exception is raised, this while loop should exit on first iteration
for vm_index in vm_indices[1:]:
try:
pmc.delete_vm(vm_index)
vm_indices.remove(vm_index)
except PyMemucError as err:
logging.exception(err)

# return the index. if no vms named pyclashbot exist, return -1
return vm_indices[0] if vm_indices else -1
12 changes: 11 additions & 1 deletion src/pyclashbot/utils/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from os import listdir, makedirs, remove
from os.path import basename, exists, expandvars, getmtime, join

from pyclashbot.memu.configure import EMULATOR_NAME, get_vm_configuration
from pyclashbot.memu.pmc import get_vm_index
from pyclashbot.utils.machine_info import MACHINE_INFO
from pyclashbot.utils.pastebin import upload_pastebin
from pyclashbot.utils.versioning import __version__
Expand Down Expand Up @@ -59,7 +61,15 @@ def initalize_pylogging() -> None:
""",
)
logging.info(
"Machine Info: \n%s", pprint.pformat(MACHINE_INFO, sort_dicts=False, indent=4),
"Machine Info: \n%s",
pprint.pformat(MACHINE_INFO, sort_dicts=False, indent=4),
)

vm_index = get_vm_index(EMULATOR_NAME)

logging.info(
"VM Configuration: \n%s",
pprint.pformat(get_vm_configuration(vm_index), indent=4),
)
compress_logs()

Expand Down
21 changes: 21 additions & 0 deletions src/pyclashbot/utils/machine_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

import configparser
import ctypes
import logging
import platform
from os.path import join
import subprocess

import psutil

from pyclashbot.memu.pmc import pmc
from pyclashbot.utils.subprocess import run

user32 = ctypes.windll.user32

Expand All @@ -18,6 +21,23 @@
)


def check_hyper_v_enabled() -> bool:
"""Check if Hyper-V is enabled on the system."""
try:
_, result = run(
[
"powershell",
'"Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V"',
],
)

# Check if Hyper-V is enabled based on the output
return "State : Enabled" in result
except subprocess.CalledProcessError as e:
logging.exception("Error executing command: %s", e)
return False


MACHINE_INFO: dict[str, str | int | float] = {
"os": platform.system(),
"os_version": platform.version(),
Expand All @@ -30,6 +50,7 @@
"cpu_count": int(psutil.cpu_count(logical=False)),
"cpu_freq": float(psutil.cpu_freq().current),
"memu_version": memu_config.get("reginfo", "version", fallback="unknown"),
"hyper-v_enabled": check_hyper_v_enabled(),
}

if __name__ == "__main__":
Expand Down
66 changes: 66 additions & 0 deletions src/pyclashbot/utils/subprocess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from os import name
from subprocess import PIPE, Popen, TimeoutExpired
from typing import Tuple

# check if running on windows
WIN32 = name == "nt"
ST_INFO = None
if WIN32:
import ctypes
from subprocess import (
CREATE_NO_WINDOW,
REALTIME_PRIORITY_CLASS,
STARTF_USESHOWWINDOW,
STARTF_USESTDHANDLES,
STARTUPINFO,
SW_HIDE,
)

ST_INFO = STARTUPINFO() # pyright: ignore [reportConstantRedefinition]
ST_INFO.dwFlags |= (
STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES | REALTIME_PRIORITY_CLASS
)
ST_INFO.wShowWindow = SW_HIDE
CR_FLAGS = CREATE_NO_WINDOW
subprocess_flags = {
"startupinfo": ST_INFO,
"creationflags": CR_FLAGS,
"start_new_session": True,
}
else:
subprocess_flags = {}


def _terminate_process( # pyright: ignore [reportUnusedFunction]
process: Popen[str],
) -> None:
"""Terminate a process forcefully on Windows."""
handle = ctypes.windll.kernel32.OpenProcess(1, False, process.pid)
ctypes.windll.kernel32.TerminateProcess(handle, -1)
ctypes.windll.kernel32.CloseHandle(handle)


def run(
args: list[str],
) -> Tuple[int, str]:
with Popen(
args,
shell=False,
bufsize=-1,
stdout=PIPE,
stderr=PIPE,
close_fds=True,
universal_newlines=True,
**subprocess_flags,
) as process:
try:
result, _ = process.communicate(timeout=5)
except TimeoutExpired:
if WIN32:
# pylint: disable=protected-access
_terminate_process(process)
process.kill()
result, _ = process.communicate()
raise

return (process.returncode, result)

0 comments on commit 2c13c1a

Please sign in to comment.