texture-reader.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. // 图片文件读取模块
  2. // 负责处理所有图片文件相关的逻辑
  3. const fs = require('fs');
  4. const path = require('path');
  5. class TextureReader {
  6. constructor(serverDir) {
  7. this.serverDir = serverDir;
  8. this.sequencesPath = path.join(serverDir, 'disk_data');
  9. }
  10. /**
  11. * 获取可用的文件夹列表
  12. * @returns {Promise<string[]>} 文件夹名称数组
  13. */
  14. async getAvailableFolders() {
  15. return new Promise((resolve, reject) => {
  16. // 检查目录是否存在
  17. fs.access(this.sequencesPath, fs.constants.F_OK, (accessErr) => {
  18. if (accessErr) {
  19. // 目录不存在,返回空数组而不是错误
  20. console.log(`[TextureReader] 目录不存在: ${this.sequencesPath},返回空列表`);
  21. resolve([]);
  22. return;
  23. }
  24. fs.readdir(this.sequencesPath, { withFileTypes: true }, (err, files) => {
  25. if (err) {
  26. // 读取失败,返回空数组而不是错误
  27. console.error(`[TextureReader] 读取目录失败: ${this.sequencesPath}`, err);
  28. resolve([]);
  29. return;
  30. }
  31. const folders = files
  32. .filter(dirent => dirent.isDirectory() && dirent.name.startsWith('player_'))
  33. .map(dirent => dirent.name)
  34. .sort();
  35. resolve(folders);
  36. });
  37. });
  38. });
  39. }
  40. /**
  41. * 获取指定文件夹中的帧文件列表
  42. * @param {string} folderName - 文件夹名称
  43. * @returns {Promise<{frames: number[], maxFrame: number}>} 帧文件信息
  44. */
  45. async getFrameFiles(folderName) {
  46. return new Promise((resolve, reject) => {
  47. // 处理URL编码和空格
  48. const decodedFolderName = decodeURIComponent(folderName).replace(/%20/g, ' ');
  49. const folderPath = path.join(this.sequencesPath, decodedFolderName);
  50. console.log(`[TextureReader] 请求帧列表: folderName=${folderName}, decoded=${decodedFolderName}, folderPath=${folderPath}`);
  51. // 检查目录是否存在
  52. fs.access(folderPath, fs.constants.F_OK, (accessErr) => {
  53. if (accessErr) {
  54. console.error(`[TextureReader] 文件夹不存在: ${folderPath}`);
  55. reject(new Error('Folder not found'));
  56. return;
  57. }
  58. fs.readdir(folderPath, (err, files) => {
  59. if (err) {
  60. console.error(`[TextureReader] 读取文件夹失败: ${err.message}, folderPath=${folderPath}`);
  61. reject(err);
  62. return;
  63. }
  64. // 过滤出 PNG 文件,并按数字排序
  65. // 支持两种格式:1) 纯数字.png (如 00.png) 2) 任意名称+数字.png (如 动画00.png)
  66. const pngFiles = files
  67. .filter(file => file.toLowerCase().endsWith('.png'))
  68. .map(file => {
  69. let frameNum = null;
  70. // 先尝试匹配纯数字格式:00.png, 01.png
  71. let match = /^(\d+)\.png$/i.exec(file);
  72. if (match) {
  73. frameNum = parseInt(match[1], 10);
  74. } else {
  75. // 再尝试匹配文件名末尾的数字格式:动画00.png, 生成白色背景长矛刺击动画00.png
  76. match = /(\d+)\.png$/i.exec(file);
  77. if (match) {
  78. frameNum = parseInt(match[1], 10);
  79. }
  80. }
  81. if (frameNum !== null) {
  82. return {
  83. frameNum: frameNum,
  84. fileName: file
  85. };
  86. }
  87. return null;
  88. })
  89. .filter(item => item !== null)
  90. .sort((a, b) => a.frameNum - b.frameNum);
  91. const result = {
  92. frames: pngFiles.map(item => item.frameNum),
  93. fileNames: pngFiles.map(item => item.fileName),
  94. maxFrame: pngFiles.length > 0 ? Math.max(...pngFiles.map(item => item.frameNum)) : 0
  95. };
  96. resolve(result);
  97. });
  98. });
  99. });
  100. }
  101. /**
  102. * 处理获取文件夹列表的 API 请求
  103. * @param {http.ServerResponse} res - HTTP 响应对象
  104. */
  105. handleGetFolders(res) {
  106. this.getAvailableFolders()
  107. .then(folders => {
  108. res.writeHead(200, { 'Content-Type': 'application/json' });
  109. res.end(JSON.stringify(folders));
  110. })
  111. .catch(err => {
  112. res.writeHead(500, { 'Content-Type': 'application/json' });
  113. res.end(JSON.stringify({ error: 'Failed to read sequences directory', details: err.message }));
  114. });
  115. }
  116. /**
  117. * 处理获取帧文件列表的 API 请求
  118. * @param {string} folderName - 文件夹名称
  119. * @param {http.ServerResponse} res - HTTP 响应对象
  120. */
  121. handleGetFrames(folderName, res) {
  122. this.getFrameFiles(folderName)
  123. .then(result => {
  124. // 检查是否有图片
  125. if (!result.frames || result.frames.length === 0) {
  126. res.writeHead(400, { 'Content-Type': 'application/json' });
  127. res.end(JSON.stringify({
  128. error: '该文件夹中没有图片',
  129. frames: []
  130. }));
  131. return;
  132. }
  133. res.writeHead(200, { 'Content-Type': 'application/json' });
  134. res.end(JSON.stringify(result));
  135. })
  136. .catch(err => {
  137. res.writeHead(404, { 'Content-Type': 'application/json' });
  138. res.end(JSON.stringify({ error: 'Folder not found', details: err.message }));
  139. });
  140. }
  141. }
  142. module.exports = TextureReader;