task_processor.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { parentPort } from 'node:worker_threads';
  2. import fs from 'node:fs';
  3. import md5 from "./src/misc/md5.js";
  4. import path from "path";
  5. import defaultProcess from "./src/defaultProcess.js";
  6. import npcInit from "./src/npcInit.js";
  7. import skillDefinitions from "./src/skillDefinitions.js";
  8. import wardrobeItems from "./src/wardrobeItems.js";
  9. import wardrobeItemsTypes from "./src/wardrobeItemsTypes.js";
  10. const thisFilesHash = md5(fs.readFileSync(__filename,{encoding: 'utf8'}));
  11. //https://stackoverflow.com/a/25221100/7200161
  12. function baseFileName(fullpath){
  13. return fullpath.split('\\').pop().split('/').pop();
  14. }
  15. function convertFile(task){
  16. const VERSION = task.VERSION;
  17. const filePath = task.filePath;
  18. const outPath = task.outPath;
  19. const generatedFilesPrefix = task.generatedFilesPrefix;
  20. const options = task.options;
  21. try{
  22. const data = fs.readFileSync(filePath,{encoding: 'utf8'});
  23. const startTime = (new Date()).getTime();
  24. const baseFileNameStr = baseFileName(filePath);
  25. const outFilePath = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.tw');
  26. const outFilePathTS = path.join(outPath,generatedFilesPrefix,baseFileNameStr.split('.')[0]+'.ts');
  27. const qsp2twOptions = data.split('\n')?.[1];
  28. if(qsp2twOptions.startsWith("!! SKIP_QSRC2TW")){
  29. if(fs.existsSync(outFilePath))
  30. fs.unlink(outFilePath,(err) => {if (err) throw err;});
  31. if(fs.existsSync(outFilePathTS))
  32. fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
  33. return [0,"SKIP"];
  34. }
  35. //#region Skip File Output if outfile exists, is based on the same codebase (determined by hash) and has used the same compier-version
  36. const codeHash = md5(data);
  37. if (fs.existsSync(outFilePath)) {
  38. try{
  39. const secondLineData = fs.readFileSync(outFilePath, "utf-8").split('\n')[1];
  40. const qsrc2twResultMatch = secondLineData.match(/<!--\s*qsrc2twResult=({.*})\s*-->/);
  41. if(qsrc2twResultMatch){
  42. const qsrc2twResult = JSON.parse(qsrc2twResultMatch[1]);
  43. if((qsrc2twResult.code && codeHash == qsrc2twResult.code) &&
  44. (qsrc2twResult.version && thisFilesHash == qsrc2twResult.version)){
  45. return [0,"EXISTS"];
  46. }
  47. }
  48. }
  49. catch(e){
  50. //Something unexpected happens. No need to handle this, because we'll just run the default file-processing.
  51. }
  52. }
  53. //#endregion
  54. var convertMode = "default";
  55. var convertModeOptions = {};
  56. /*if(qsp2twOptions.startsWith("!! QSRC2TW_module")){
  57. convertMode = qsp2twOptions.trim().split(" ").toReversed()[0];
  58. }*/
  59. const modeLookupRegex = /\s*!!\s*QSRC2TW_module\s+(\w+)(\s+{[^}]+})?/;
  60. let modeLookupResult;
  61. try{
  62. if(modeLookupResult = modeLookupRegex.exec(qsp2twOptions)){
  63. convertMode = modeLookupResult[1];
  64. if(modeLookupResult[2])
  65. convertModeOptions = JSON.parse(modeLookupResult[2]);
  66. }
  67. }
  68. catch(e){
  69. console.log(e.toString());
  70. }
  71. /**
  72. * Return value is Array [TwineCode, TSCode]. TwineCode must not be null.
  73. */
  74. var convertFunction = (code,options)=>[null,null];
  75. switch (convertMode) {
  76. case "default": convertFunction = (code,options) => [defaultProcess(code),null]; break;
  77. case "npcInit": convertFunction = (code,options) => npcInit(code); break;
  78. case "stat_sklattrib_lvlset": convertFunction = (code,options) => skillDefinitions(code); break;
  79. case "wardrobeItems": convertFunction = (code,options) => wardrobeItems(code,options); break;
  80. case "wardrobeItemsTypes": convertFunction = (code,options) => wardrobeItemsTypes(code,options); break;
  81. default:
  82. console.warn("Unreckognized Convert Mode");
  83. break;
  84. }
  85. try{
  86. let twineCode = "";
  87. let twineCodeRaw = undefined;
  88. let tsCodeRaw = undefined;
  89. const defaultConsole = console;
  90. if(!options.verboseErrors)
  91. console = {log:(...args)=>{}, debug: (...args)=>{},warn:(...args)=>{}, error: (...args)=>{}};
  92. try{
  93. [twineCodeRaw,tsCodeRaw] = convertFunction(data,convertModeOptions);
  94. }
  95. catch(e){
  96. throw e;
  97. }
  98. finally{
  99. console = defaultConsole;
  100. }
  101. if(!twineCodeRaw){
  102. console.error("Twine Code must be generated by every convertMode");
  103. return [2,"Invalid convertFunction"];
  104. }else{
  105. twineCode = twineCodeRaw.split('\n')
  106. .toSpliced(1,0,`<!--qsrc2twResult={"version":"${thisFilesHash}","code":"${codeHash}","time":"${(new Date().toISOString())}"}-->`)
  107. .join('\n');
  108. fs.writeFile(outFilePath, twineCode, err => {
  109. if (err) {
  110. console.error(err);
  111. } else {
  112. }
  113. });
  114. }
  115. if(!tsCodeRaw){
  116. if(fs.existsSync(outFilePathTS))
  117. fs.unlink(outFilePathTS,(err) => {if (err) throw err;});
  118. }else{
  119. fs.writeFile(outFilePathTS, tsCodeRaw, err => {
  120. if (err) {
  121. console.error(err);
  122. } else {
  123. }
  124. });
  125. }
  126. const executionTime = (new Date()).getTime() - startTime;
  127. console.log(`${baseFileNameStr.padEnd(30,'.')} ${executionTime} ms`);
  128. return [0,"SUCCESS"];
  129. }catch(e){
  130. //console.error(`Error in "${baseFileNameStr}". No output was generated`);
  131. return [1,`Error in "${baseFileNameStr}". No output was generated`,e];
  132. }
  133. }
  134. catch(e){
  135. //console.error();
  136. return [1,`Critical Error in "${filePath}". No output was generated`, e];
  137. }
  138. }
  139. parentPort.on('message', (task) => {
  140. parentPort.postMessage(convertFile(task));
  141. });