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

Opt: Add start/stop LDPlayer,NoxPlayer,BlueStack4,MEmuPlayer support. #3867

Merged
merged 8 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions module/device/platform/emulator_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ def MuMuPlayer12_id(self):

return None

@cached_property
def LDPlayer_id(self):
"""
Convert LDPlayer instance name to instance id.
Example names:
leidian0
leidian1

Returns:
int: Instance ID, or None if this is not a LDPlayer instance
"""
res = re.search(r'leidian(\d+)', self.name)
if res:
return int(res.group(1))

return None

class EmulatorBase:
# Values here must match those in argument.yaml EmulatorInfo.Emulator.option
Expand Down
30 changes: 26 additions & 4 deletions module/device/platform/emulator_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ def path_to_type(cls, path: str) -> str:
return cls.NoxPlayer64
else:
return cls.NoxPlayer
if exe == 'bluestacks.exe':
if dir1 in ['bluestacks', 'bluestacks_cn']:
if exe in ['bluestacks.exe', 'bluestacksgp.exe']:
if dir1 in ['bluestacks', 'bluestacks_cn', 'bluestackscn']:
return cls.BlueStacks4
elif dir1 in ['bluestacks_nxt', 'bluestacks_nxt_cn']:
return cls.BlueStacks5
Expand Down Expand Up @@ -127,7 +127,7 @@ def path_to_type(cls, path: str) -> str:
return ''

@staticmethod
def multi_to_single(exe):
def multi_to_single(exe: str):
"""
Convert a string that might be a multi-instance manager to its single instance executable.

Expand Down Expand Up @@ -155,6 +155,28 @@ def multi_to_single(exe):
else:
yield exe

@staticmethod
def single_to_console(exe: str):
"""
Convert a string that might be a single instance executable to its console.

Args:
exe (str): Path to emulator executable

Returns:
str: Path to emulator console
"""
if 'MuMuPlayer.exe' in exe:
return exe.replace('MuMuPlayer.exe', 'MuMuManager.exe')
elif 'LDPlayer.exe' in exe:
return exe.replace('LDPlayer.exe', 'dnconsole.exe')
elif 'Bluestacks.exe' in exe:
return exe.replace('Bluestacks.exe', 'bsconsole.exe')
elif 'MEmu.exe' in exe:
return exe.replace('MEmu.exe', 'memuc.exe')
else:
return exe

@staticmethod
def vbox_file_to_serial(file: str) -> str:
"""
Expand Down Expand Up @@ -224,7 +246,7 @@ def iter_instances(self):
elif self == Emulator.BlueStacks4:
# ../Engine/Android
regex = re.compile(r'^Android')
for folder in self.list_folder('../Engine', is_dir=True):
for folder in self.list_folder('./Engine/ProgramData/Engine', is_dir=True):
folder = os.path.basename(folder)
res = regex.match(folder)
if not res:
Expand Down
62 changes: 44 additions & 18 deletions module/device/platform/platform_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def _emulator_start(self, instance: EmulatorInstance):
"""
Start a emulator without error handling
"""
exe = instance.emulator.path
exe: str = instance.emulator.path
if instance == Emulator.MuMuPlayer:
# NemuPlayer.exe
self.execute(exe)
Expand All @@ -94,24 +94,29 @@ def _emulator_start(self, instance: EmulatorInstance):
if instance.MuMuPlayer12_id is None:
logger.warning(f'Cannot get MuMu instance index from name {instance.name}')
self.execute(f'"{exe}" -v {instance.MuMuPlayer12_id}')
elif instance == Emulator.LDPlayerFamily:
# LDPlayer.exe index=0
self.execute(f'"{exe}" index={instance.LDPlayer_id}')
elif instance == Emulator.NoxPlayerFamily:
# Nox.exe -clone:Nox_1
self.execute(f'"{exe}" -clone:{instance.name}')
elif instance == Emulator.BlueStacks5:
# HD-Player.exe -instance Pie64
self.execute(f'"{exe}" -instance {instance.name}')
elif instance == Emulator.BlueStacks4:
# BlueStacks\Client\Bluestacks.exe -vmname Android_1
# Bluestacks.exe -vmname Android_1
self.execute(f'"{exe}" -vmname {instance.name}')
elif instance == Emulator.MEmuPlayer:
# MEmu.exe MEmu_0
self.execute(f'"{exe}" {instance.name}')
else:
raise EmulatorUnknown(f'Cannot start an unknown emulator instance: {instance}')

def _emulator_stop(self, instance: EmulatorInstance):
"""
Stop a emulator without error handling
"""
logger.hr('Emulator stop', level=2)
exe = instance.emulator.path
exe: str = instance.emulator.path
if instance == Emulator.MuMuPlayer:
# MuMu6 does not have multi instance, kill one means kill all
# Has 4 processes
Expand Down Expand Up @@ -141,26 +146,35 @@ def _emulator_stop(self, instance: EmulatorInstance):
rf')'
)
elif instance == Emulator.MuMuPlayer12:
# MuMu 12 has 2 processes:
# E:\ProgramFiles\Netease\MuMuPlayer-12.0\shell\MuMuPlayer.exe -v 0
# "C:\Program Files\MuMuVMMVbox\Hypervisor\MuMuVMMHeadless.exe" --comment MuMuPlayer-12.0-0 --startvm xxx
# E:\Program Files\Netease\MuMu Player 12\shell\MuMuManager.exe api -v 1 shutdown_player
if instance.MuMuPlayer12_id is None:
logger.warning(f'Cannot get MuMu instance index from name {instance.name}')
self.execute(f'"{Emulator.single_to_console(exe)}" api -v {instance.MuMuPlayer12_id} shutdown_player')
elif instance == Emulator.LDPlayerFamily:
# E:\Program Files\leidian\LDPlayer9\dnconsole.exe quit --index 0
self.execute(f'"{Emulator.single_to_console(exe)}" quit --index {instance.LDPlayer_id}')
elif instance == Emulator.NoxPlayerFamily:
# Nox.exe -clone:Nox_1 -quit
self.execute(f'"{exe}" -clone:{instance.name} -quit')
elif instance == Emulator.BlueStacks5:
# BlueStack has 2 processes
# C:\Program Files\BlueStacks_nxt_cn\HD-Player.exe --instance Pie64
# C:\Program Files\BlueStacks_nxt_cn\BstkSVC.exe -Embedding
self.kill_process_by_regex(
rf'('
rf'MuMuVMMHeadless.exe.*--comment {instance.name}'
rf'|MuMuPlayer.exe.*-v {instance.MuMuPlayer12_id}'
rf'HD-Player.exe.*"--instance" "{instance.name}"'
rf')'
)
# There is also a shared service, no need to kill it
# "C:\Program Files\MuMuVMMVbox\Hypervisor\MuMuVMMSVC.exe" --Embedding
elif instance == Emulator.NoxPlayerFamily:
# Nox.exe -clone:Nox_1 -quit
self.execute(f'"{exe}" -clone:{instance.name} -quit')
elif instance == Emulator.BlueStacks4:
# E:\Program Files (x86)\BluestacksCN\bsconsole.exe quit --name Android
self.execute(f'"{Emulator.single_to_console(exe)}" quit --name {instance.name}')
elif instance == Emulator.MEmuPlayer:
# F:\Program Files\Microvirt\MEmu\memuc.exe stop -n MEmu_0
self.execute(f'"{Emulator.single_to_console(exe)}" stop -n {instance.name}')
else:
raise EmulatorUnknown(f'Cannot stop an unknown emulator instance: {instance}')

def _emulator_function_wrapper(self, func):
def _emulator_function_wrapper(self, func: callable):
"""
Args:
func (callable): _emulator_start or _emulator_stop
Expand Down Expand Up @@ -312,10 +326,22 @@ def emulator_start(self):

def emulator_stop(self):
logger.hr('Emulator stop', level=1)
return self._emulator_function_wrapper(self._emulator_stop)

for _ in range(3):
# Stop
if self._emulator_function_wrapper(self._emulator_stop):
# Success
return True
else:
# Failed to stop, start and stop again
if self._emulator_function_wrapper(self._emulator_start):
continue
else:
return False

logger.error('Failed to stop emulator 3 times, stopped')
return False

if __name__ == '__main__':
self = PlatformWindows('alas')
d = self.emulator_instance
print(d)
print(d)