您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

adb_proto.js 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. const fs = require('fs');
  2. const spawn = require('child_process').spawn;
  3. const spawnSync = require('child_process').spawnSync;
  4. const path = require('path');
  5. const scanf = require('scanf');
  6. const HTMLParser = require('node-html-parser');
  7. const hw = require(path.join(__dirname, 'GTBOT.node'));
  8. /**
  9. * option: {
  10. * nox_path: E:/nox/Nox,
  11. * open_count: 2,
  12. * close_time: 1000(ms)
  13. * }
  14. */
  15. class GT_Nox_Command{
  16. /**
  17. * @param {String} noxName 夜神內部Id e.g. Nox_1,Nox_2
  18. * @param {String} noxPath 夜神的安裝位置, 預設為 C:/Program Files (x86)/Nox
  19. */
  20. constructor(titleTable,option){
  21. this.titleToNoxIdTable = titleTable;
  22. if(typeof option !== "object") option = {};
  23. this.noxPath = option.nox_path?option.nox_path:"C:/Program Files (x86)/Nox";
  24. this.openCount = option.open_count?option.open_count:1;
  25. this.closeTime = option.close_time?option.close_time:1000;
  26. this.nox_command_file = path.join(this.noxPath,"bin","Nox.exe");
  27. this.errorExec = [];
  28. }
  29. nox_command(titleName,option){
  30. let argv_arr = ["-clone:" + this.titleToNoxIdTable[titleName] ];
  31. if(typeof option === "object"){
  32. for(let i in option){
  33. let temp = "-" + i + ":" + option[i];
  34. argv_arr[argv_arr.length] = temp;
  35. }
  36. }
  37. else if(typeof option === "string"){
  38. argv_arr[argv_arr.length] = option;
  39. }
  40. let cmd = spawn(this.nox_command_file, argv_arr, {
  41. detached: true
  42. });
  43. cmd.stdout.on('data', (data) => {
  44. // console.log(`stdout: \n${data}`);
  45. });
  46. cmd.stderr.on('data', (data) => {
  47. // console.log(`stderr: ${data}`);
  48. });
  49. cmd.on('close', (code) => {
  50. // console.log(`child process exited with code $[code]`);
  51. });
  52. }
  53. static checkOpen(title,cb){
  54. let count = 0;
  55. let intervalTemp = setInterval(function(){
  56. let mainW = hw.findWindowEx(0, 0, "Qt5QWindowIcon" , title);
  57. if(mainW){
  58. // init 時會有一個 title 是""的東西
  59. let check1 = hw.findWindowEx(mainW, 0, "Qt5QWindowIcon" , '');
  60. let check2 = hw.findWindowEx(mainW, 0, "Qt5QWindowIcon" , 'default_title_barWindow');
  61. if(check1 === check2){
  62. if(++count > 3){
  63. clearInterval(intervalTemp);
  64. cb();
  65. }
  66. }
  67. }
  68. },1000);
  69. }
  70. open_process(titleName){
  71. if(this.titleToNoxIdTable[titleName] === undefined){
  72. this.errorExec[this.errorExec.length] = titleName;
  73. return;
  74. }
  75. this.nox_command(titleName);
  76. GT_Nox_Command.checkOpen(titleName,function(){
  77. this.finish_open();
  78. }.bind(this));
  79. }
  80. finish_open(){
  81. if(this.open_arr.length <= 0){
  82. let msg = {};
  83. if(this.errorExec.length > 0){
  84. msg.err = this.errorExec.slice(0);;
  85. }
  86. this.finish_open_cb(msg);
  87. this.errorExec = [];
  88. return;
  89. }
  90. else{
  91. let nextOpen = this.open_arr.shift();
  92. this.open_process(nextOpen);
  93. }
  94. }
  95. /**
  96. * 開啟模擬器
  97. * @param {Array || String} titleNameArr 要開啟哪些APK
  98. * @param {Function} cb 開完了,你要做啥
  99. * option{
  100. * cpu:1,
  101. * memory: 960(or 1024),
  102. * resolution: '320x240',
  103. * dpi: 120,
  104. * root:false
  105. * }
  106. */
  107. openNox(titleNameArr,cb){
  108. let titleArr;
  109. if( Array.isArray(titleNameArr) ){
  110. titleArr = titleNameArr;
  111. }
  112. else if(typeof titleNameArr === "string"){
  113. titleArr = [titleNameArr]
  114. }
  115. this.open_arr = titleArr;
  116. this.finish_open_cb = cb;
  117. for(let i=0;i < this.openCount;i++){
  118. let nextOpen = this.open_arr.shift();
  119. this.open_process(nextOpen,i);
  120. if(this.open_arr.length === 0){
  121. break;
  122. }
  123. }
  124. }
  125. openNox_one(titleName){
  126. return new Promise( (resolve, reject) => {
  127. if(this.titleToNoxIdTable[titleName] === undefined){
  128. resolve({
  129. err: "noTitle"
  130. });
  131. }
  132. this.nox_command(titleName);
  133. GT_Nox_Command.checkOpen(titleName,function(){
  134. console.log("YOOOOOOOOOO");
  135. resolve({
  136. err: undefined,
  137. data: "ok"
  138. });
  139. }.bind(this));
  140. });
  141. }
  142. colse_process(closeTitle){
  143. if(this.titleToNoxIdTable[closeTitle] === undefined){
  144. this.errorExec[this.errorExec.length] = closeTitle;
  145. return;
  146. }
  147. this.nox_command(closeTitle,"-quit");
  148. }
  149. /**
  150. * 關閉模擬器
  151. * @param {Array || String} titleNameArr 要關哪些APK
  152. * @param {Function} cb 關完了,你要做啥
  153. */
  154. closeNox(titleNameArr,cb){
  155. let titleArr;
  156. if( Array.isArray(titleNameArr) ){
  157. titleArr = titleNameArr;
  158. }
  159. else if(typeof titleNameArr === "string"){
  160. titleArr = [titleNameArr]
  161. }
  162. // console.log(cb);
  163. this.activeFlage = true;
  164. let closeCount = 1;
  165. this.colse_process(titleArr[0]);
  166. let closeItl = setInterval(function(){
  167. this.colse_process(titleArr[closeCount]);
  168. closeCount++;
  169. if(closeCount >= titleArr.length){
  170. clearInterval(closeItl);
  171. let msg = {};
  172. if(this.errorExec.length > 0){
  173. msg.err = this.errorExec.slice(0);;
  174. }
  175. this.errorExec = [];
  176. if(typeof cb === 'function'){
  177. cb(msg);
  178. }
  179. }
  180. }.bind(this),this.closeTime);
  181. }
  182. restartNox(titleNameArr,cb){
  183. let titleArr;
  184. if( Array.isArray(titleNameArr) ){
  185. titleArr = titleNameArr;
  186. }
  187. else if(typeof titleNameArr === "string"){
  188. titleArr = [titleNameArr]
  189. }
  190. this.closeNox(titleArr,function(){
  191. setTimeout(()=>{
  192. this.openNox(titleArr,cb);
  193. },5000);
  194. }.bind(this));
  195. }
  196. /**
  197. * 安裝APK
  198. * @param {String} apk_path apk 的絕對位置
  199. */
  200. install_apk(apk_path){
  201. if(typeof apk_path !== "string"){
  202. console.log("你的apk path呢!");
  203. return;
  204. }
  205. this.nox_command("'-apk:" + apk_path + "'");
  206. }
  207. /**
  208. * 執行某個App,預設開托蘭
  209. * @param {string} appId app的唯一Id
  210. */
  211. run_app(title,appId){
  212. if(appId === undefined){
  213. appId = "com.asobimo.toramonline";
  214. }
  215. this.nox_command(title,{package:appId});
  216. }
  217. }
  218. class adb_ctrl{
  219. constructor(option){
  220. this.nox_path = option.nox_path?option.nox_path:"C:/Program Files (x86)/Nox";
  221. this.adb_file = path.join(this.nox_path,"bin","nox_adb.exe");
  222. this.setMultiNoxList.call(this);
  223. // title 對應 Nox_id
  224. /**
  225. * {
  226. * atk01: Nox_1
  227. * }
  228. */
  229. // this.nox_table = {};
  230. // port 對應 title
  231. /**
  232. * {
  233. * 61025: Nox_1
  234. * }
  235. */
  236. // this.port_table = {};
  237. this.nox_cmd = new GT_Nox_Command(this.nox_table,option);
  238. // this.connect();
  239. }
  240. openNox(titleArr,cb){
  241. return new Promise( async function(resolve, reject){
  242. let status = await this.nox_cmd.openNox_one.call(this.nox_cmd,titleArr);
  243. resolve(status);
  244. }.bind(this));
  245. }
  246. closeNox(titleArr,cb){
  247. this.nox_cmd.closeNox.call(this.nox_cmd,titleArr,cb);
  248. }
  249. restartNox(titleArr,cb){
  250. this.nox_cmd.restartNox.call(this.nox_cmd,titleArr,cb);
  251. }
  252. exec_adb_cb(argv,cb){
  253. const status = spawn(this.adb_file, argv);
  254. status.stdout.setEncoding('utf8');
  255. status.stderr.setEncoding('utf8');
  256. let stdout = "";
  257. let err = false;
  258. let errInfo = "";
  259. status.stdout.on('data', (data) => {
  260. stdout += (data + "||");
  261. });
  262. status.stderr.on('data', (data) => {
  263. errInfo += (data + "||");
  264. });
  265. status.on('close', (code) => {
  266. cb({
  267. err: err,
  268. errInfo: errInfo,
  269. data: stdout
  270. });
  271. });
  272. }
  273. exec_adb(argv){
  274. return new Promise( (resolve, reject) => {
  275. const status = spawn(this.adb_file, argv);
  276. status.stdout.setEncoding('utf8');
  277. status.stderr.setEncoding('utf8');
  278. let stdout;
  279. status.stdout.on('data', (data) => {
  280. stdout = data;
  281. // console.log(data);
  282. });
  283. status.stderr.on('data', (data) => {
  284. // console.log("err",data);
  285. resolve({
  286. err: true,
  287. data: data
  288. });
  289. });
  290. status.on('close', (code) => {
  291. resolve({
  292. err: false,
  293. data:stdout
  294. });
  295. });
  296. });
  297. }
  298. delay(time){
  299. return new Promise( (resolve, reject) => {
  300. setTimeout(function(){
  301. resolve();
  302. },time);
  303. });
  304. }
  305. /**
  306. * 找multiNox 路徑
  307. */
  308. setMultiNoxList(){
  309. let multi_list;
  310. let multi_file = path.join(process.env.LOCALAPPDATA,'MultiPlayerManager','multiplayer.xml');
  311. try{
  312. multi_list = fs.readFileSync(multi_file,{encoding:"utf-8"});
  313. }
  314. catch(e){
  315. console.log(e);
  316. console.log("找不到夜神多工器啦 冠霆要你滾");
  317. scanf("%d");
  318. }
  319. if(multi_list === undefined){
  320. return;
  321. }
  322. let document = HTMLParser.parse(multi_list);
  323. let Instance = document.querySelectorAll("Instance");
  324. this.nox_table = {};
  325. this.port_table = {};
  326. this.title_to_device_table = {};
  327. for(let i=0;i < Instance.length;i++){
  328. let temp = Instance[i].attributes;
  329. if(temp.id === "Nox_0"){
  330. temp.id = "nox";
  331. }
  332. this.nox_table[temp.name] = temp.id;
  333. let yee = fs.readFileSync(this.nox_path + '/bin/BignoxVMS/' + temp.id + '/' + temp.id + '.vbox',{encoding:"utf-8"});
  334. let port = yee.split('<Forwarding name="port2" proto="1" hostip="127.0.0.1" hostport="')[1].slice(0,5);
  335. this.port_table[port] = temp.name;
  336. this.title_to_device_table[temp.name] = port;
  337. }
  338. // console.log(this.nox_table);
  339. // console.log(this.port_table);
  340. // console.log(this.title_to_device_table);
  341. }
  342. /**
  343. * adb 初始化, adb 操作時,必做喔
  344. */
  345. async adb_init(){
  346. // var killServer = spawnSync(this.adb_file, ["kill-server"] ,{encoding: "utf-8"});
  347. let killServer = await this.exec_adb(["kill-server"]);
  348. if(killServer.err){
  349. if(killServer.data.indexOf("server not running") === -1){
  350. console.log("kill出錯啦~");
  351. console.log(killServer);
  352. scanf("%d");
  353. return "err";
  354. }
  355. }
  356. let startServer = await this.exec_adb( ["start-server"] );
  357. if(startServer.err){
  358. console.log("start出錯啦~");
  359. console.log(startServer);
  360. scanf("%d");
  361. return "err";
  362. }
  363. console.log("初始化完成~ 天佑冠霆啦!!");
  364. }
  365. scanNox(){
  366. return new Promise( (resolve, reject) => {
  367. let count = 0;
  368. let max = Object.keys(this.port_table).length;
  369. // console.log("scan囉");
  370. // console.log(Object.keys(this.port_table).length);
  371. function afterScan(data){
  372. count++;
  373. // console.log(count);
  374. // console.log(data);
  375. if(max === count){
  376. resolve();
  377. }
  378. }
  379. for(let i in this.port_table){
  380. this.exec_adb_cb( ["connect","127.0.0.1:" + i] ,afterScan);
  381. }
  382. });
  383. }
  384. /**
  385. * 連結目前所有已開的夜神模擬器
  386. */
  387. // async connect_one(titleName){
  388. // return new Promise( async function(resolve, reject){
  389. // let status = await this.exec_adb( ["connect","127.0.0.1:" + this.title_to_device_table[titleName]] );
  390. // resolve(status);
  391. // }.bind(this));
  392. // }
  393. /**
  394. * 連結目前所有已開的夜神模擬器
  395. */
  396. async connect(){
  397. await this.adb_init();
  398. if(this.port_table === undefined){
  399. return "err";
  400. }
  401. await this.scanNox();
  402. var devices = await this.exec_adb( ["devices"] );
  403. // console.log(devices);
  404. if(devices.err){
  405. console.log("devices出錯啦~");
  406. console.log(devices);
  407. scanf("%d");
  408. return "err";
  409. }
  410. let all_devices = devices.data.split("List of devices attached\r\n")[1];
  411. let devices_list = all_devices.split("\tdevice\r\n");
  412. devices_list = devices_list.reverse().slice(1);
  413. this.devices_list = devices_list;
  414. this.devices_count = devices_list.length;
  415. this.installed_count = 0;
  416. // console.log(devices_list);
  417. // console.log("你要更新的裝置數量:" + this.devices_count);
  418. }
  419. async disconnect(){
  420. let killServer = await this.exec_adb(["kill-server"]);
  421. if(killServer.err){
  422. if(killServer.data.indexOf("server not running") === -1){
  423. console.log("kill出錯啦~");
  424. console.log(killServer);
  425. scanf("%d");
  426. return "err";
  427. }
  428. }
  429. }
  430. /**
  431. * 停止toram跟cloneApp 的App
  432. */
  433. async close_toram(titleName){
  434. let device = this.title_to_device_table[titleName];
  435. if(device === undefined) return {err: "noThis"};
  436. // for(let i=0; i < this.devices_count;i++){
  437. // let status = await this.exec_adb( ["-s", this.devices_list[i] ,"shell","dumpsys activity | grep top-activity"] );
  438. let status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.asobimo.toramonline"] );
  439. status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.zhuowang.cloneapp"] );
  440. return {};
  441. // }
  442. // this.nox_cmd.run_app("com.zhuowang.cloneapp:platform.gameplugin.P00");
  443. // this.nox_cmd.run_app();
  444. }
  445. async open_toram(titleName){
  446. this.nox_cmd.run_app(titleName);
  447. }
  448. }
  449. module.exports = adb_ctrl;
  450. // let aa = new adb_ctrl({
  451. // // nox_path: "F:/Nox",
  452. // // open_count: 1,
  453. // });
  454. // let atkArr = [];
  455. // for(let i=1;i< 3;i++){
  456. // atkArr[atkArr.length] = "atk0" + i;
  457. // }
  458. // async function yee(gtadb){
  459. // let status = await gtadb.openNox("yee");
  460. // console.log("幹");
  461. // console.log(status);
  462. // // await gtadb.adb_init();
  463. // // let status = await gtadb.connect_one("yee");
  464. // // status = await gtadb.connect_one("yee");
  465. // // console.log(status);
  466. // // await gtadb.open_toram("yee");
  467. // // await gtadb.disconnect();
  468. // }
  469. // yee(aa);