You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  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. colse_process(closeTitle){
  126. if(this.titleToNoxIdTable[closeTitle] === undefined){
  127. this.errorExec[this.errorExec.length] = closeTitle;
  128. return;
  129. }
  130. this.nox_command(closeTitle,"-quit");
  131. }
  132. /**
  133. * 關閉模擬器
  134. * @param {Array || String} titleNameArr 要關哪些APK
  135. * @param {Function} cb 關完了,你要做啥
  136. */
  137. closeNox(titleNameArr,cb){
  138. let titleArr;
  139. if( Array.isArray(titleNameArr) ){
  140. titleArr = titleNameArr;
  141. }
  142. else if(typeof titleNameArr === "string"){
  143. titleArr = [titleNameArr]
  144. }
  145. // console.log(cb);
  146. this.activeFlage = true;
  147. let closeCount = 1;
  148. this.colse_process(titleArr[0]);
  149. let closeItl = setInterval(function(){
  150. this.colse_process(titleArr[closeCount]);
  151. closeCount++;
  152. if(closeCount >= titleArr.length){
  153. clearInterval(closeItl);
  154. let msg = {};
  155. if(this.errorExec.length > 0){
  156. msg.err = this.errorExec.slice(0);;
  157. }
  158. this.errorExec = [];
  159. if(typeof cb === 'function'){
  160. cb(msg);
  161. }
  162. }
  163. }.bind(this),this.closeTime);
  164. }
  165. restartNox(titleNameArr,cb){
  166. let titleArr;
  167. if( Array.isArray(titleNameArr) ){
  168. titleArr = titleNameArr;
  169. }
  170. else if(typeof titleNameArr === "string"){
  171. titleArr = [titleNameArr]
  172. }
  173. this.closeNox(titleArr,function(){
  174. setTimeout(()=>{
  175. this.openNox(titleArr,cb);
  176. },5000);
  177. }.bind(this));
  178. }
  179. /**
  180. * 安裝APK
  181. * @param {String} apk_path apk 的絕對位置
  182. */
  183. install_apk(apk_path){
  184. if(typeof apk_path !== "string"){
  185. console.log("你的apk path呢!");
  186. return;
  187. }
  188. this.nox_command("'-apk:" + apk_path + "'");
  189. }
  190. /**
  191. * 執行某個App,預設開托蘭
  192. * @param {string} appId app的唯一Id
  193. */
  194. run_app(appId){
  195. if(appId === undefined){
  196. appId = "com.asobimo.toramonline";
  197. }
  198. this.nox_command({package:appId});
  199. }
  200. }
  201. class adb_ctrl{
  202. constructor(option){
  203. this.nox_path = option.nox_path?option.nox_path:"C:/Program Files (x86)/Nox";
  204. this.adb_file = path.join(this.nox_path,"bin","nox_adb.exe");
  205. this.setMultiNoxList.call(this);
  206. // title 對應 Nox_id
  207. /**
  208. * {
  209. * atk01: Nox_1
  210. * }
  211. */
  212. // this.nox_table = {};
  213. // port 對應 title
  214. /**
  215. * {
  216. * 61025: Nox_1
  217. * }
  218. */
  219. // this.port_table = {};
  220. this.nox_cmd = new GT_Nox_Command(this.nox_table,option);
  221. // this.connect();
  222. }
  223. openNox(titleArr,cb){
  224. this.nox_cmd.openNox.call(this.nox_cmd,titleArr,cb);
  225. }
  226. closeNox(titleArr,cb){
  227. this.nox_cmd.closeNox.call(this.nox_cmd,titleArr,cb);
  228. }
  229. restartNox(titleArr,cb){
  230. this.nox_cmd.restartNox.call(this.nox_cmd,titleArr,cb);
  231. }
  232. exec_adb_cb(argv,cb){
  233. const status = spawn(this.adb_file, argv);
  234. status.stdout.setEncoding('utf8');
  235. status.stderr.setEncoding('utf8');
  236. let stdout = "";
  237. let err = false;
  238. let errInfo = "";
  239. status.stdout.on('data', (data) => {
  240. stdout += (data + "||");
  241. });
  242. status.stderr.on('data', (data) => {
  243. errInfo += (data + "||");
  244. });
  245. status.on('close', (code) => {
  246. cb({
  247. err: err,
  248. errInfo: errInfo,
  249. data: stdout
  250. });
  251. });
  252. }
  253. exec_adb(argv){
  254. return new Promise( (resolve, reject) => {
  255. const status = spawn(this.adb_file, argv);
  256. status.stdout.setEncoding('utf8');
  257. status.stderr.setEncoding('utf8');
  258. let stdout;
  259. status.stdout.on('data', (data) => {
  260. stdout = data;
  261. // console.log(data);
  262. });
  263. status.stderr.on('data', (data) => {
  264. // console.log("err",data);
  265. resolve({
  266. err: true,
  267. data: data
  268. });
  269. });
  270. status.on('close', (code) => {
  271. resolve({
  272. err: false,
  273. data:stdout
  274. });
  275. });
  276. });
  277. }
  278. delay(time){
  279. return new Promise( (resolve, reject) => {
  280. setTimeout(function(){
  281. resolve();
  282. },time);
  283. });
  284. }
  285. /**
  286. * 找multiNox 路徑
  287. */
  288. setMultiNoxList(){
  289. let multi_list;
  290. let multi_file = path.join(process.env.LOCALAPPDATA,'MultiPlayerManager','multiplayer.xml');
  291. try{
  292. multi_list = fs.readFileSync(multi_file,{encoding:"utf-8"});
  293. }
  294. catch(e){
  295. console.log(e);
  296. console.log("找不到夜神多工器啦 冠霆要你滾");
  297. scanf("%d");
  298. }
  299. if(multi_list === undefined){
  300. return;
  301. }
  302. let document = HTMLParser.parse(multi_list);
  303. let Instance = document.querySelectorAll("Instance");
  304. this.nox_table = {};
  305. this.port_table = {};
  306. this.title_to_device_table = {};
  307. for(let i=0;i < Instance.length;i++){
  308. let temp = Instance[i].attributes;
  309. if(temp.id === "Nox_0"){
  310. temp.id = "nox";
  311. }
  312. this.nox_table[temp.name] = temp.id;
  313. let yee = fs.readFileSync(this.nox_path + '/bin/BignoxVMS/' + temp.id + '/' + temp.id + '.vbox',{encoding:"utf-8"});
  314. let port = yee.split('<Forwarding name="port2" proto="1" hostip="127.0.0.1" hostport="')[1].slice(0,5);
  315. this.port_table[port] = temp.name;
  316. this.title_to_device_table[temp.name] = port;
  317. }
  318. // console.log(this.nox_table);
  319. // console.log(this.port_table);
  320. }
  321. /**
  322. * adb 初始化, adb 操作時,必做喔
  323. */
  324. async adb_init(){
  325. // var killServer = spawnSync(this.adb_file, ["kill-server"] ,{encoding: "utf-8"});
  326. let killServer = await this.exec_adb(["kill-server"]);
  327. if(killServer.err){
  328. if(killServer.data.indexOf("server not running") === -1){
  329. console.log("kill出錯啦~");
  330. console.log(killServer);
  331. scanf("%d");
  332. return "err";
  333. }
  334. }
  335. let startServer = await this.exec_adb( ["start-server"] );
  336. if(startServer.err){
  337. console.log("start出錯啦~");
  338. console.log(startServer);
  339. scanf("%d");
  340. return "err";
  341. }
  342. console.log("初始化完成~ 天佑冠霆啦!!");
  343. }
  344. scanNox(){
  345. return new Promise( (resolve, reject) => {
  346. let count = 0;
  347. let max = Object.keys(this.port_table).length;
  348. // console.log("scan囉");
  349. // console.log(Object.keys(this.port_table).length);
  350. function afterScan(data){
  351. count++;
  352. // console.log(count);
  353. // console.log(data);
  354. if(max === count){
  355. resolve();
  356. }
  357. }
  358. for(let i in this.port_table){
  359. this.exec_adb_cb( ["connect","127.0.0.1:" + i] ,afterScan);
  360. }
  361. });
  362. }
  363. /**
  364. * 連結目前所有已開的夜神模擬器
  365. */
  366. async connect(){
  367. await this.adb_init();
  368. if(this.port_table === undefined){
  369. return "err";
  370. }
  371. await this.scanNox();
  372. var devices = await this.exec_adb( ["devices"] );
  373. // console.log(devices);
  374. if(devices.err){
  375. console.log("devices出錯啦~");
  376. console.log(devices);
  377. scanf("%d");
  378. return "err";
  379. }
  380. let all_devices = devices.data.split("List of devices attached\r\n")[1];
  381. let devices_list = all_devices.split("\tdevice\r\n");
  382. devices_list = devices_list.reverse().slice(1);
  383. this.devices_list = devices_list;
  384. this.devices_count = devices_list.length;
  385. this.installed_count = 0;
  386. // console.log(devices_list);
  387. // console.log("你要更新的裝置數量:" + this.devices_count);
  388. }
  389. async disconnect(){
  390. let killServer = await this.exec_adb(["kill-server"]);
  391. if(killServer.err){
  392. if(killServer.data.indexOf("server not running") === -1){
  393. console.log("kill出錯啦~");
  394. console.log(killServer);
  395. scanf("%d");
  396. return "err";
  397. }
  398. }
  399. }
  400. /**
  401. * 停止toram跟cloneApp 的App
  402. */
  403. async close_toram(titleName){
  404. let device = this.title_to_device_table[titleName];
  405. if(device === undefined) return {err: "noThis"};
  406. // for(let i=0; i < this.devices_count;i++){
  407. // let status = await this.exec_adb( ["-s", this.devices_list[i] ,"shell","dumpsys activity | grep top-activity"] );
  408. let status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.asobimo.toramonline"] );
  409. status = await this.exec_adb( ["-s", "127.0.0.1:" + device ,"shell","am","force-stop","com.zhuowang.cloneapp"] );
  410. return {};
  411. // }
  412. // this.nox_cmd.run_app("com.zhuowang.cloneapp:platform.gameplugin.P00");
  413. // this.nox_cmd.run_app();
  414. }
  415. }
  416. module.exports = adb_ctrl;
  417. // let aa = new adb_ctrl({
  418. // nox_path: "F:/Nox",
  419. // open_count: 1,
  420. // });
  421. // let atkArr = [];
  422. // for(let i=1;i< 3;i++){
  423. // atkArr[atkArr.length] = "atk0" + i;
  424. // }
  425. // async function yee(gtadb){
  426. // // await gtadb.adb_init.call(gtadb);
  427. // await gtadb.connect();
  428. // await gtadb.close_toram("atk01");
  429. // await gtadb.close_toram("atk02");
  430. // }
  431. // yee(aa);