helper.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. import ctypes
  2. import os
  3. import threading
  4. import time
  5. from dataclasses import dataclass
  6. from model.custom_struct import GameConfig, AccountInfo
  7. from model.global_manager import GM
  8. from scripts.script import Script
  9. from tools.dm_operate import Dm
  10. from tools.emulator.emulator import Emulator
  11. from tools.emulator.ld_operate import LD
  12. from tools.emulator.ys_operate import YS
  13. from tools.upload_log import LogInfo
  14. from tools.utils import Utils
  15. from tools.log import logger
  16. @dataclass
  17. class ScriptHelper:
  18. dm: Dm = None # 大漠对象
  19. bind_handle: int = None # 绑定句柄
  20. top_handle: int = None # 顶层句柄
  21. emulator_index: int = None # 模拟器下标
  22. timeout: int = None # 超时时间
  23. game_id: str = None # 游戏id
  24. game_type: str = None # 游戏类型
  25. account: str = None # 账号
  26. password: str = None # 密码
  27. retained: int = None # 新增留存
  28. log_uuid: str = None # 日志uuid
  29. log_port: int = None # 日志端口
  30. emu: Emulator = None # 模拟器对象
  31. class MyHelper:
  32. """模拟器任务助手"""
  33. _lock = threading.Lock()
  34. def __init__(self, window_id: int, game_config: GameConfig, account_info: AccountInfo):
  35. self.window_id = window_id
  36. self.game_config = game_config
  37. self.account_info = account_info
  38. self.emulator_type = self.game_config.emulator_type
  39. self.emu = self._initialize_emulator_instance()
  40. self.emulator_index = self._get_emulator()
  41. self.dm = Dm()
  42. self.emulator_info = None
  43. self.wy_ip = GM.wy_ip
  44. self.log_port = GM.script_log_port
  45. self.script = Script(self.game_config.script)
  46. self.log_uuid = f'{time.time()}_{self.account_info.account}'
  47. self.upload_log = LogInfo(self.log_uuid)
  48. self.upload_account_log(GM.device_info.pc_name, GM.device_info.officer, GM.device_mac)
  49. def _initialize_emulator_instance(self):
  50. emulator_mapping = {
  51. '雷电模拟器4-32位': lambda: LD(4),
  52. '雷电模拟器4-64位': lambda: LD(64),
  53. '雷电模拟器9': lambda: LD(9),
  54. '夜神模拟器7-32位': lambda: YS(7),
  55. '夜神模拟器7-64位': lambda: YS(8),
  56. '夜神模拟器9': lambda: YS(9)
  57. }
  58. # 仅当查找到类型时才会实例化,否则返回默认的 LD(9)
  59. return emulator_mapping.get(self.emulator_type, lambda: LD(9))()
  60. def _get_emulator(self):
  61. with MyHelper._lock:
  62. index = self.window_id
  63. while GM.get_global_control():
  64. if self.emu.is_exists(index):
  65. if GM.get_emulator_status(f'{self.emulator_type}-{index}') == 0:
  66. GM.set_emulator_status(f'{self.emulator_type}-{index}', 1)
  67. return index
  68. raise Exception(f'模拟器-{index}状态异常')
  69. else:
  70. # 模拟器不存在,添加新的模拟器
  71. self.emu.add(f'new-{index}')
  72. time.sleep(3)
  73. def get_emulator_index(self):
  74. return self.emulator_index
  75. def set_emulator_index(self, index):
  76. self.emulator_index = index
  77. def restore_emulator(self):
  78. try:
  79. self.emu.restore(self.emulator_index, os.path.join(GM.image_path, self.game_config.image))
  80. time.sleep(1)
  81. self.emu.rename(self.emulator_index, f'{self.game_config.task_id}-{self.emulator_index}')
  82. return True, '还原模拟器成功'
  83. except Exception as e:
  84. return False, str(e)
  85. def modify_emulator(self):
  86. try:
  87. self.emu.modify(self.emulator_index,
  88. cpu=self.game_config.cpu,
  89. memory=self.game_config.memory,
  90. resolution=self.game_config.resolution,
  91. manufacturer=self.account_info.manufacturer,
  92. model=self.account_info.model,
  93. pnumber=self.account_info.pnumber,
  94. imei=self.account_info.imei,
  95. imsi=self.account_info.imsi,
  96. simserial=self.account_info.simserial,
  97. androidid=self.account_info.androidid,
  98. mac=self.account_info.mac if self.emu.emulator_type == 'ld' else self.account_info.mac_colon
  99. )
  100. return True, '修改模拟器信息成功'
  101. except Exception as e:
  102. return False, str(e)
  103. def switch_emulator_area(self):
  104. if self.wy_ip is None:
  105. return False, "未初始化无忧IP"
  106. self.wy_ip.set_wy_token()
  107. if len(self.account_info.number_operatorTypes.split('_')) < 1:
  108. return False, "number_operatorTypes参数错误"
  109. return self.wy_ip.switch_emulator_area(self.emulator_index,
  110. self.account_info.number_operatorTypes.split('_')[1],
  111. self.account_info.number_operatorTypes.split('_')[0])
  112. def confirm_emulator_area(self):
  113. result, message = self.wy_ip.queryProcessProxyRegion(self.emulator_info.vbox_pid)
  114. if not result:
  115. return result, message
  116. if message != self.account_info.number_operatorTypes.split('_')[0]:
  117. return False, f'当前模拟器切换地区失败,目标地区:{self.account_info.number_operatorTypes.split("_")[0]},当前地区:{message}'
  118. return True, '当前模拟器切换地区成功'
  119. def start_emulator_and_set_position(self):
  120. """启动模拟器并设置窗口位置"""
  121. try:
  122. # 获取共享路径
  123. share_path = GM.get_share_path()
  124. emulator_share_dir = os.path.join(share_path, str(self.window_id))
  125. # 如果目录不存在则创建
  126. if not os.path.exists(emulator_share_dir):
  127. os.makedirs(emulator_share_dir)
  128. # 设置共享目录
  129. self.emu.set_share_dir(self.emulator_index, emulator_share_dir)
  130. # 启动模拟器并确认启动状态
  131. result, message = self.emu.start_and_confirm(self.emulator_index)
  132. # if result:
  133. # self.emulator_info = message
  134. # # 设置模拟器窗口位置
  135. # self.emu.set_emulator_position(self.emulator_info.top_handle, self.window_id)
  136. # time.sleep(1)
  137. # return True, f"模拟器编号:{self.emulator_index},启动成功"
  138. # return False, f"模拟器编号:{self.emulator_index},,启动失败,错误信息:{message}"
  139. except Exception as e:
  140. return False, f"模拟器编号:{self.emulator_index},启动失败,错误信息:{str(e)}"
  141. def close_emulator_and_confirm(self):
  142. """关闭模拟器并确保所有相关进程被终止。"""
  143. # 检查模拟器是否正在运行
  144. if self.emu.is_running(self.emulator_index):
  145. self.emu.close(self.emulator_index)
  146. time.sleep(3) # 等待模拟器关闭的缓冲时间
  147. # 获取模拟器信息
  148. emu_info = self.emu.get_info(self.emulator_index)
  149. if emu_info:
  150. # 确认主进程 pid 有效
  151. if emu_info.pid > 0:
  152. Utils.kill_process(emu_info.pid)
  153. # 确认 VBox 进程 pid 有效
  154. if emu_info.vbox_pid > 0:
  155. Utils.kill_process(emu_info.vbox_pid)
  156. def bind_emulator(self):
  157. try:
  158. if self.game_config.script.endswith('.dll'):
  159. result = self.script.execute('dm_bind',
  160. self.emulator_index,
  161. self.emulator_info.bind_handle,
  162. timeout=10
  163. )
  164. return True, result
  165. elif self.game_config.script.endswith('.py'):
  166. script_helper = ScriptHelper(dm=self.dm,
  167. emu=self.emu,
  168. emulator_index=self.emulator_index,
  169. top_handle=self.emulator_info.top_handle,
  170. bind_handle=self.emulator_info.bind_handle,
  171. game_type=self.game_config.game_type,
  172. game_id=self.game_config.task_id,
  173. account=self.account_info.account,
  174. password=self.account_info.password,
  175. retained=self.account_info.retained,
  176. log_uuid=self.log_uuid,
  177. log_port=self.log_port,
  178. timeout=self.game_config.timeout * 60
  179. )
  180. result = self.script.execute('dm_bind', script_helper, timeout=10)
  181. return True, result
  182. else:
  183. return False, '脚本类型错误'
  184. except Exception as e:
  185. return False, str(e)
  186. def start_game(self):
  187. """启动游戏并处理可能的异常。
  188. Returns:
  189. tuple: 成功与否的标志和结果或错误信息。
  190. """
  191. logger.info(f"进游戏了1",100)
  192. try:
  193. if self.game_config.script.endswith('.dll'):
  194. logger.info(f"进游戏了3", 100)
  195. # 获取字符串的字节数据
  196. task_id_bytes = self.game_config.task_id.encode('gbk')
  197. account_bytes = self.account_info.account.encode('gbk')
  198. script_path_bytes = self.script.script_path.encode('gbk')
  199. log_uuid_bytes = self.log_uuid.encode('gbk')
  200. # 创建 c_char_p 指针
  201. task_id_ptr = ctypes.c_char_p(task_id_bytes)
  202. account_ptr = ctypes.c_char_p(account_bytes)
  203. script_path_ptr = ctypes.c_char_p(script_path_bytes)
  204. log_uuid_ptr = ctypes.c_char_p(log_uuid_bytes)
  205. result = self.script.execute(
  206. 'script_start_game',
  207. self.emulator_index,
  208. task_id_ptr,
  209. self.emulator_info.bind_handle,
  210. account_ptr,
  211. self.game_config.timeout * 60 * 1000,
  212. script_path_ptr,
  213. GM.script_log_port,
  214. log_uuid_ptr,
  215. timeout=30 * 60
  216. )
  217. return True, result
  218. elif self.game_config.script.endswith('.py'):
  219. logger.info(f"进游戏了2", 100)
  220. script_helper = ScriptHelper(dm=self.dm,
  221. emu=self.emu,
  222. emulator_index=self.emulator_index,
  223. top_handle=self.emulator_info.top_handle,
  224. bind_handle=self.emulator_info.bind_handle,
  225. game_id=self.game_config.task_id,
  226. game_type=self.game_config.game_type,
  227. account=self.account_info.account,
  228. password=self.account_info.password,
  229. retained=self.account_info.retained,
  230. log_uuid=self.log_uuid,
  231. log_port=self.log_port,
  232. timeout=self.game_config.timeout * 60
  233. )
  234. result = self.script.execute('script_start_game',script_helper, timeout=30*60)
  235. logger.info(f"进游戏了4", 100)
  236. return True, result
  237. else:
  238. return False, "脚本文件格式错误"
  239. except Exception as e:
  240. return False, f"脚本_启动游戏,错误: {e}"
  241. def login_game(self):
  242. """登录游戏并处理可能的异常。
  243. Returns:
  244. tuple: 成功与否的标志和结果或错误信息。
  245. """
  246. try:
  247. if self.game_config.script.endswith('.dll'):
  248. # 获取字符串的字节数据
  249. account_bytes = self.account_info.account.encode('gbk')
  250. password_bytes = self.account_info.password.encode('gbk')
  251. wechat_order_id_bytes = self.account_info.wechat_order_id.encode('gbk')
  252. task_id_bytes = self.game_config.task_id.encode('gbk')
  253. script_path_bytes = self.script.script_path.encode('gbk')
  254. channel_id_bytes = self.game_config.channel_id.encode('gbk')
  255. # 创建 c_char_p 指针
  256. account_ptr = ctypes.c_char_p(account_bytes)
  257. password_ptr = ctypes.c_char_p(password_bytes)
  258. wechat_order_id_ptr = ctypes.c_char_p(wechat_order_id_bytes)
  259. task_id_ptr = ctypes.c_char_p(task_id_bytes)
  260. script_path_ptr = ctypes.c_char_p(script_path_bytes)
  261. channel_id_ptr = ctypes.c_char_p(channel_id_bytes)
  262. result = self.script.execute(
  263. 'script_login_game',
  264. self.emulator_index, # 窗口下标
  265. self.game_config.timeout * 60 * 1000, # 超时时间
  266. self.emulator_info.bind_handle,
  267. account_ptr,
  268. password_ptr,
  269. self.account_info.retained,
  270. wechat_order_id_ptr,
  271. 1,
  272. task_id_ptr,
  273. script_path_ptr,
  274. GM.script_log_port,
  275. channel_id_ptr,
  276. timeout=30 * 60
  277. )
  278. return True, result
  279. elif self.game_config.script.endswith('.py'):
  280. script_helper = ScriptHelper(dm=self.dm,
  281. emu=self.emu,
  282. emulator_index=self.emulator_index,
  283. top_handle=self.emulator_info.top_handle,
  284. bind_handle=self.emulator_info.bind_handle,
  285. game_id=self.game_config.task_id,
  286. game_type=self.game_config.game_type,
  287. account=self.account_info.account,
  288. password=self.account_info.password,
  289. retained=self.account_info.retained,
  290. log_uuid=self.log_uuid,
  291. log_port=self.log_port,
  292. timeout=self.game_config.timeout * 60
  293. )
  294. result = self.script.execute('script_login_game',script_helper, timeout=30 * 60)
  295. return True, result
  296. else:
  297. return False, "脚本文件格式错误"
  298. except Exception as e:
  299. return False, f'脚本_登陆游戏,错误: {e}'
  300. def main_task(self):
  301. """执行主线任务并处理可能的异常。
  302. Returns:
  303. tuple: 成功与否的标志和结果或错误信息。
  304. """
  305. try:
  306. if self.game_config.script.endswith('.dll'):
  307. # 获取字符串的字节数据
  308. account_bytes = self.account_info.account.encode('gbk')
  309. task_id_bytes = self.game_config.task_id.encode('gbk')
  310. script_path_bytes = self.script.script_path.encode('gbk')
  311. # 创建 c_char_p 指针
  312. account_ptr = ctypes.c_char_p(account_bytes)
  313. task_id_ptr = ctypes.c_char_p(task_id_bytes)
  314. script_path_ptr = ctypes.c_char_p(script_path_bytes)
  315. result = self.script.execute(
  316. 'script_main_task',
  317. self.emulator_index,
  318. self.game_config.timeout * 60 * 1000,
  319. account_ptr, # 直接传递数据地址
  320. 0,
  321. self.emulator_info.bind_handle,
  322. task_id_ptr, # 直接传递数据地址
  323. script_path_ptr, # 直接传递数据地址
  324. GM.script_log_port,
  325. timeout=60 * 60
  326. )
  327. return True, result
  328. elif self.game_config.script.endswith('.py'):
  329. script_helper = ScriptHelper(dm=self.dm,
  330. emu=self.emu,
  331. emulator_index=self.emulator_index,
  332. top_handle=self.emulator_info.top_handle,
  333. bind_handle=self.emulator_info.bind_handle,
  334. game_id=self.game_config.task_id,
  335. game_type=self.game_config.game_type,
  336. account=self.account_info.account,
  337. password=self.account_info.password,
  338. retained=self.account_info.retained,
  339. log_uuid=self.log_uuid,
  340. log_port=self.log_port,
  341. timeout=self.game_config.timeout * 60
  342. )
  343. result = self.script.execute('script_main_task', script_helper, timeout=60 * 60)
  344. return True, result
  345. else:
  346. return False, "脚本文件格式错误"
  347. except Exception as e:
  348. return False, f'脚本_教程主线,错误: {e}'
  349. def upload_account_log(self, pc_code: str, operator: str, pc_mac: str):
  350. self.upload_log.set_account_info(game_id=int(self.game_config.task_id),
  351. account_type=self.account_info.game_type,
  352. pwd=self.account_info.password,
  353. account=self.account_info.account,
  354. task_type=self.account_info.retained)
  355. self.upload_log.set_pc_info(pc_code=pc_code, operator=operator, pc_mac=pc_mac, pc_ip='')
  356. self.upload_log.upload_pull_account_log(1)
  357. def upload_device_log(self, status: int):
  358. try:
  359. self.upload_log.set_device_info(device_id=self.emu.get_android_id(self.emulator_index),
  360. device_manufacturer=self.emu.get_manufacturer(self.emulator_index),
  361. device_model=self.emu.get_model(self.emulator_index),
  362. device_imei=self.emu.get_IMEI(self.emulator_index),
  363. device_sdk=self.emu.get_android_version(self.emulator_index),
  364. device_mac=self.emu.get_mac(self.emulator_index),
  365. device_number=self.emu.get_phone_number(self.emulator_index),
  366. script_device_id='')
  367. self.upload_log.set_simulator_info(simulator_code=str(self.emulator_index),
  368. simulator_mac='',
  369. simulator_ip_city='',
  370. simulator_ip=self.emu.get_net_ip(self.emulator_index)
  371. )
  372. ret = self.upload_log.upload_start_simulator_log(status=status)
  373. return ret
  374. except Exception as e:
  375. return False, f'脚本_设备信息,错误: {e}'