A modded EditSaber for making beat saber maps.
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.

729 lines
16 KiB

  1. "use strict";
  2. const fs = require("fs");
  3. var clients = [];
  4. var net = require('net');
  5. try{
  6. var request = require('request');
  7. }
  8. catch(err){
  9. console.log(err);
  10. console.log('Go read the damn readme. Run "npm install" or install the request module in some other way.');
  11. process.exit()
  12. }
  13. var https = require('https');
  14. var config = {};
  15. var diff;
  16. var fileSize;
  17. var diffIndex;
  18. var charIndex;
  19. var diffFound = false;
  20. var charFound = false;
  21. var changesMade = false;
  22. var downloadURL = "";
  23. console.log("\n\n\n");
  24. // Loading up json files and such that you need
  25. try{
  26. config = require('./config.json');
  27. }
  28. catch(err){
  29. }
  30. process.argv.shift();
  31. process.argv.shift();
  32. var type = "";
  33. while(process.argv.length > 0)
  34. {
  35. var input = process.argv.shift()
  36. if (input.startsWith("--"))
  37. {
  38. type = input.replace('--','');
  39. }
  40. else
  41. {
  42. if (type == "")
  43. {
  44. console.log('you fucked up');
  45. process.exit()
  46. }
  47. else
  48. {
  49. config[type] = input;
  50. }
  51. }
  52. }
  53. // Check and display relevant info
  54. try{
  55. if(config.folder !== "" && config.folder !== undefined){
  56. console.log(' Song folder: ' + config.folder);
  57. }
  58. else{
  59. console.log('\nError: Fill in folder field');
  60. process.exit()
  61. }
  62. if(config.difficulty !== "" && config.characteristic !== undefined){
  63. console.log(' Song characteristic: ' + config.characteristic);
  64. }
  65. else{
  66. console.log(' Song characteristic: Standard');
  67. }
  68. if(config.difficulty !== "" && config.difficulty !== undefined){
  69. console.log(' Song difficulty: ' + config.difficulty);
  70. }
  71. else{
  72. console.log('\nError: Fill in difficulty field');
  73. process.exit()
  74. }
  75. if(config.port !== "" && config.port !== undefined){
  76. console.log(' Port: ' + config.port);
  77. }
  78. else{
  79. console.log('\nError: Fill in port field');
  80. process.exit()
  81. }
  82. if(config.password != "" && config.password !== undefined){
  83. console.log(' Password: ' + config.password);
  84. }
  85. else{
  86. config.password = "";
  87. console.log(' Password: no password being used');
  88. }
  89. if(config.download !== "" && config.download !== undefined){
  90. console.log(' Song download: ' + config.download);
  91. downloadURL = config.download;
  92. }
  93. }
  94. catch(err){
  95. console.log('config file is not formatted correctly');
  96. process.exit()
  97. }
  98. var data = fs.readFileSync('./Songs/' + config.folder + '/info.dat');
  99. // Loading stuff
  100. try{
  101. var info = loadJSON('./Songs/' + config.folder + '/info.dat');
  102. }
  103. catch(err){
  104. console.log('Could not find file: Songs/' + config.folder + '/info.dat');
  105. process.exit()
  106. }
  107. if (!config.characteristic)
  108. {
  109. config.characteristic = "Standard";
  110. }
  111. for (var i = 0; i < info._difficultyBeatmapSets.length; i++)
  112. {
  113. if (info._difficultyBeatmapSets[i]._beatmapCharacteristicName == config.characteristic)
  114. {
  115. for(var j = 0; j < info._difficultyBeatmapSets[i]._difficultyBeatmaps.length; j++)
  116. {
  117. if (info._difficultyBeatmapSets[i]._difficultyBeatmaps[j]._difficulty == config.difficulty)
  118. {
  119. diff = loadJSON('./Songs/' + config.folder + '/' + info._difficultyBeatmapSets[i]._difficultyBeatmaps[j]._beatmapFilename);
  120. try{
  121. fileSize = fs.statSync('./Songs/' + config.folder + '/' + info._songFilename);
  122. }catch(err)
  123. {
  124. //console.log('\nFailed to find '+info.difficultyLevels[i].audioPath+'. now using fileSize argument');
  125. if(!config.hasOwnProperty('fileSize'))
  126. {
  127. //console.log('could not use fileSize argument, finding from download URL');
  128. request({
  129. url: downloadURL,
  130. method: "HEAD"
  131. }, function(err, response, body) {
  132. var downloadResponse = response.headers;
  133. if (downloadResponse.hasOwnProperty('content-length'))
  134. {
  135. fileSize = downloadResponse['content-length'];
  136. }
  137. else
  138. {
  139. console.log('Unable to get file size. Please specify fileSize in config.json or as an argument');
  140. process.exit();
  141. }
  142. });
  143. }
  144. fileSize = config['content-length'];
  145. }
  146. diffIndex = j;
  147. charIndex = i;
  148. diffFound = true;
  149. break;
  150. }
  151. }
  152. }
  153. }
  154. if (diff === null)
  155. {
  156. console.log('No proper difficulty found.');
  157. process.exit();
  158. }
  159. if (diffFound == false)
  160. {
  161. console.log('Difficulty/Characteristic not found');
  162. process.exit();
  163. }
  164. setInterval(saveFile, 300000);//save every 5 minutes
  165. // if(config.download === "")
  166. // {
  167. // config.download = 'none';
  168. // }
  169. var server = net.createServer(function(socket){
  170. socket.name = "wait";
  171. socket.send = false;
  172. //add new client to the list
  173. clients.push(socket);
  174. // Handle incoming messages from clients.
  175. socket.on('data', function (data) {
  176. if(socket.name == "wait")
  177. {
  178. if (config.password !== "" && data.toString().includes(";|;"))
  179. {
  180. console.log("attempted connection: " + data.toString().split(";|;")[1] + "\nPassword: " +data.toString().split(";|;")[0]);
  181. if (config.password == data.toString().split(";|;")[0])
  182. {
  183. for (var i = 0; i < clients.length; i++)
  184. {
  185. if (clients[i].name == data.toString().split(";|;")[1] || "wait" == data.toString().split(";|;")[1])
  186. {
  187. if (socket.address == clients[i].address)
  188. {
  189. console.log('duplicate from same client. Destroying old client.')
  190. clients[i].destroy();
  191. clients.splice(i, 1);
  192. }
  193. else
  194. {
  195. socket.write('dc:duplicate name');
  196. clients.splice(clients.indexOf(socket), 1);
  197. socket.destroy();
  198. return;
  199. }
  200. }
  201. }
  202. socket.name = data.toString().split(";|;")[1];
  203. // Send a nice welcome message and announce
  204. socket.write(config.folder.split('/').pop()+"::"+diffIndex*100+ charIndex+";;;"); //0
  205. socket.write(JSON.stringify(info)+";;;"); //1
  206. socket.write(info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename+";;;"); //2
  207. socket.write(JSON.stringify(diff)+";;;");//3
  208. socket.write(info._songFilename+";;;"+fileSize+";;;");//4 & 5
  209. socket.write(downloadURL +";;;" );//6
  210. socket.send = true;
  211. }
  212. else
  213. {
  214. socket.write('dc:wrong password');
  215. clients.splice(clients.indexOf(socket), 1);
  216. socket.destroy();
  217. return;
  218. }
  219. }
  220. else if (config.password === "")
  221. {
  222. if(data.toString().includes(";|;"))
  223. {
  224. data = data.toString().split(";|;")[1];
  225. }
  226. console.log("attempted connection: " + data);
  227. for (var i = 0; i < clients.length; i++)
  228. {
  229. if (clients[i].name == data || "wait" == data)
  230. {
  231. socket.write('dc:duplicate name');
  232. clients.splice(clients.indexOf(socket), 1);
  233. socket.destroy();
  234. return;
  235. }
  236. }
  237. socket.name = data.toString();
  238. // Send a nice welcome message and announce
  239. socket.write(config.folder.split('/').pop()+"::"+diffIndex*100+ charIndex+";;;"); //0
  240. socket.write(JSON.stringify(info)+";;;"); //1
  241. socket.write(info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename+";;;"); //2
  242. socket.write(JSON.stringify(diff)+";;;");//3
  243. socket.write(info._songFilename+";;;"+fileSize+";;;");//4 & 5
  244. socket.write(downloadURL +";;;" );//6
  245. socket.send = true;
  246. }
  247. else if (config.password !== "" && !data.toString().includes(";|;"))
  248. {
  249. socket.write('dc:password required');
  250. clients.splice(clients.indexOf(socket), 1);
  251. socket.destroy();
  252. return;
  253. }
  254. else
  255. {
  256. socket.write('dc:uhhh something else');
  257. clients.splice(clients.indexOf(socket), 1);
  258. socket.destroy();
  259. return;
  260. }
  261. console.log(socket.name + " joined the session.");
  262. broadcast("System:,:sy:,:"+ socket.name + " joined the session.;:;");
  263. }
  264. else
  265. {
  266. broadcast(data.toString(), socket);
  267. var commands = data.toString().split(";:;");
  268. for (var i = 0; i < commands.length; i++)
  269. {
  270. readMessage(commands[i]);
  271. }
  272. }
  273. });
  274. // Remove the client from the list when it leaves
  275. socket.on('end', function () {
  276. try
  277. {
  278. clients.splice(clients.indexOf(socket), 1);
  279. console.log(socket.name + " left the session.");
  280. broadcast("System:,:sy:,:"+ socket.name + " left the session.;:;");
  281. broadcast(socket.name + ":,:rc;:;");
  282. }
  283. catch(err)
  284. {
  285. console.log(err);
  286. }
  287. });
  288. socket.on('error',function () {
  289. try
  290. {
  291. clients.splice(clients.indexOf(socket), 1);
  292. broadcast("System:,:sy:,:"+ socket.name + " left the session.;:;");
  293. broadcast(socket.name + ":,:rc;:;");
  294. }
  295. catch(err)
  296. {
  297. console.log(err);
  298. }
  299. });
  300. // Send a message to all clients
  301. function broadcast(message, sender) {
  302. clients.forEach(function (client) {
  303. // Don't want to send it to sender
  304. if (client === sender)
  305. {
  306. return;
  307. }
  308. if (client.send == false)
  309. {
  310. return;
  311. }
  312. client.write(message);
  313. });
  314. }
  315. });
  316. //Get IP address
  317. console.log('\n\n\n');
  318. if (downloadURL === undefined || downloadURL === "")
  319. {
  320. var req = request.post('https://catbox.moe/user/api.php', function (err, resp, body) {
  321. if (err) {
  322. console.log(err);
  323. } else {
  324. // var downloadInfo = JSON.parse(body)
  325. downloadURL = body;
  326. console.log('Download URL generated: ' + body);
  327. https.get('https://ifconfig.co/ip', function(res){
  328. res.setEncoding('utf8');
  329. res.on('data', function(chunk){
  330. server.listen(config.port, '0.0.0.0');
  331. process.stdout.write("Server Started on IP address: ")
  332. console.log(chunk);
  333. });
  334. res.on('error', function(err) {
  335. server.listen(config.port, '0.0.0.0');
  336. process.stdout.write("Server Started");
  337. });
  338. });
  339. }
  340. });
  341. var form = req.form();
  342. form.append('reqtype', 'fileupload');
  343. form.append('fileToUpload', fs.createReadStream('./Songs/'+config.folder+'/'+info._songFilename));
  344. }else
  345. {
  346. https.get('https://ifconfig.co/ip', function(res){
  347. res.setEncoding('utf8');
  348. res.on('data', function(chunk){
  349. server.listen(config.port, '0.0.0.0');
  350. process.stdout.write("Server Started on IP address: ")
  351. console.log(chunk);
  352. });
  353. res.on('error', function(err) {
  354. server.listen(config.port, '0.0.0.0');
  355. process.stdout.write("Server Started");
  356. });
  357. });
  358. }
  359. function readMessage(message)
  360. {
  361. changesMade = true;
  362. var type = message.split(":,:")[1];
  363. switch(type)
  364. {
  365. case 'an':
  366. try{
  367. addNote(message.split(':,:')[2].split(" "));
  368. }
  369. catch(err){
  370. }
  371. break;
  372. case 'rn':
  373. try{
  374. removeNote(message.split(':,:')[2].split(" "));
  375. }
  376. catch(err){
  377. }
  378. break;
  379. case 'ae':
  380. try{
  381. addEvent(message.split(':,:')[2].split(" "));
  382. }
  383. catch(err){
  384. }
  385. break;
  386. case 're':
  387. try{
  388. removeEvent(message.split(':,:')[2].split(" "));
  389. }
  390. catch(err){
  391. }
  392. break;
  393. case 'aw':
  394. try{
  395. addWall(message.split(':,:')[2].split(" "));
  396. }
  397. catch(err){
  398. }
  399. break;
  400. case 'rw':
  401. try{
  402. removeWall(message.split(':,:')[2].split(" "));
  403. }
  404. catch(err){
  405. }
  406. break;
  407. case 'ab':
  408. try{
  409. addBookmark(message.split(':,:')[2].split("|||"));
  410. }
  411. catch(err){
  412. }
  413. break;
  414. case 'rb':
  415. try{
  416. removeBookmark(message.split(':,:')[2].split(" "));
  417. }
  418. catch(err){
  419. }
  420. break;
  421. default:
  422. //console.log(message);
  423. break;
  424. }
  425. }
  426. function addNote(data)
  427. {
  428. var note = stringToNote(data)
  429. if (!hasNull(note))
  430. {
  431. diff._notes.push(note);
  432. }
  433. }
  434. function removeNote(data)
  435. {
  436. for (var j = diff._notes.length -1 ; j >= 0; j--)
  437. {
  438. if (sameNote(diff._notes[j],stringToNote(data)))
  439. {
  440. diff._notes.splice(j,1);
  441. break;
  442. }
  443. }
  444. }
  445. function stringToNote(data)
  446. {
  447. return {_time: parseFloat(data[0]),
  448. _lineIndex: parseInt(data[1]),
  449. _lineLayer: parseInt(data[2]),
  450. _type: parseInt(data[3]),
  451. _cutDirection: parseInt(data[4])};
  452. }
  453. function sameNote(note1,note2)
  454. {
  455. if (Math.abs(note1._time - note2._time) > 0.001)
  456. return false;
  457. if (note1._lineIndex != note2._lineIndex)
  458. return false;
  459. if (note1._lineLayer != note2._lineLayer)
  460. return false;
  461. if (note1._type != note2._type)
  462. return false;
  463. if (note1._cutDirection != note2._cutDirection)
  464. return false;
  465. return true;
  466. }
  467. function addEvent(data)
  468. {
  469. if (data.length == 3)
  470. {
  471. var event = stringToEvent(data)
  472. if (!hasNull(event))
  473. {
  474. diff._events.push(event);
  475. }
  476. }
  477. else
  478. {
  479. var event = stringToEvent(data)
  480. if (!hasNull(event))
  481. {
  482. diff._BPMChanges.push(stringToBPM(data));
  483. }
  484. }
  485. }
  486. function removeEvent(data)
  487. {
  488. if (data.length == 3)
  489. {
  490. for (var j = diff._events.length -1 ; j >= 0; j--)
  491. {
  492. if (sameEvent(diff._events[j],stringToEvent(data)))
  493. {
  494. diff._events.splice(j,1);
  495. break;
  496. }
  497. }
  498. }
  499. else
  500. {
  501. for (var j = diff._BPMChanges.length -1 ; j >= 0; j--)
  502. {
  503. if (sameBPM(stringToBPM(data), diff._BPMChanges[j]))
  504. {
  505. diff._BPMChanges.splice(j,1);
  506. }
  507. }
  508. }
  509. }
  510. function sameEvent(event1,event2)
  511. {
  512. if (Math.abs(event1._time - event2._time) > 0.001)
  513. return false;
  514. if (event1._type != event2._type)
  515. return false;
  516. if (event1._value != event2._value)
  517. return false;
  518. return true;
  519. }
  520. function sameBPM(event1,event2)
  521. {
  522. if (Math.abs(event1._BPM - event2._BPM) > 0.02)
  523. return false;
  524. if (Math.abs(event1._time - event2._time) > 0.001)
  525. return false;
  526. return true;
  527. }
  528. function stringToEvent(data)
  529. {
  530. return {
  531. _time: parseFloat(data[0]),
  532. _type: parseInt(data[1]),
  533. _value: parseInt(data[2])
  534. };
  535. }
  536. function stringToBPM(data)
  537. {
  538. return {
  539. _BPM:parseFloat(data[0]),
  540. _time:parseFloat(data[1]),
  541. _beatsPerBar:parseFloat(data[2]),
  542. _metronomeOffset:parseFloat(data[3])}
  543. }
  544. function addWall(data)
  545. {
  546. var wall = stringToWall(data)
  547. if (!hasNull(wall))
  548. {
  549. diff._obstacles.push(wall);
  550. }
  551. }
  552. function removeWall(data)
  553. {
  554. for (var j = diff._obstacles.length -1 ; j >= 0; j--)
  555. {
  556. if (sameWall(diff._obstacles[j],stringToWall(data)))
  557. {
  558. diff._obstacles.splice(j,1);
  559. break;
  560. }
  561. }
  562. }
  563. function stringToWall(data)
  564. {
  565. return {_time: parseFloat(data[0]),
  566. _lineIndex: parseInt(data[1]),
  567. _type: parseInt(data[2]),
  568. _duration: parseFloat(data[3]),
  569. _width: parseInt(data[4])
  570. };
  571. }
  572. function sameWall(wall1,wall2)
  573. {
  574. if (Math.abs(wall1._time - wall2._time) > 0.001)
  575. return false;
  576. if (wall1._lineIndex != wall2._lineIndex)
  577. return false;
  578. if (wall1._type != wall2._type)
  579. return false;
  580. if (Math.abs(wall1._duration - wall2._duration) > 0.001)
  581. return false;
  582. if (wall1._width != wall2._width)
  583. return false;
  584. return true;
  585. }
  586. function addBookmark(data)
  587. {
  588. diff._bookmarks.push(stringToBookmark(data));
  589. }
  590. function removeBookmark(data)
  591. {
  592. for (var j = diff._bookmarks.length - 1; j >= 0; j--)
  593. {
  594. if (Math.abs(diff._bookmarks[j]._time - stringToBookmark(data)._time) < 0.001)
  595. {
  596. diff._bookmarks.splice(j,1);
  597. break;
  598. }
  599. }
  600. }
  601. function stringToBookmark(data)
  602. {
  603. return {
  604. _time: parseFloat(data[0]),
  605. _name: data[1]
  606. };
  607. }
  608. function saveFile()
  609. {
  610. if (changesMade)
  611. {
  612. fs.writeFile('./Songs/' + config.folder + '/' + info._difficultyBeatmapSets[charIndex]._difficultyBeatmaps[diffIndex]._beatmapFilename, JSON.stringify(diff), function(err) {
  613. if (err)
  614. console.log(err);
  615. else
  616. {
  617. console.log ("file successfully saved.\nNote Count: "+ diff._notes.length+"\nEvent Count: "+diff._events.length);
  618. }
  619. });
  620. }
  621. changesMade = false;
  622. }
  623. // server.getConnections(function(err,count){
  624. // console.log(count);
  625. // });
  626. function disconnectSoon(socket)
  627. {
  628. if(socket.name == "wait" || socket.name.length > 30)
  629. {
  630. clients.splice(clients.indexOf(socket), 1);
  631. socket.destroy();
  632. return;
  633. }
  634. }
  635. function hasNull(object)
  636. {
  637. for(var key in object) {
  638. if (object[key] === null)
  639. {
  640. try
  641. {
  642. console.log("null object detected at :" + object._time);
  643. }
  644. catch(err)
  645. {
  646. console.log("null object detected")
  647. }
  648. return true;
  649. }
  650. }
  651. }
  652. function loadJSON(file) {
  653. var data = fs.readFileSync(file);
  654. return JSON.parse(data);
  655. }