helpers.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. var networking = require("./networking");
  2. var logging = require("./logging");
  3. var config = require("./config");
  4. var cache = require("./cache");
  5. var skins = require("./skins");
  6. var renders = require("./renders");
  7. var fs = require("fs");
  8. // 0098cb60-fa8e-427c-b299-793cbd302c9a
  9. var valid_uuid = /^([0-9a-f-A-F-]{32,36}|[a-zA-Z0-9_]{1,16})$/; // uuid|username
  10. var hash_pattern = /[0-9a-f]+$/;
  11. function get_hash(url) {
  12. return hash_pattern.exec(url)[0].toLowerCase();
  13. }
  14. // requests skin for +uuid+ and extracts face/helm if image hash in +details+ changed
  15. // callback contains error, image hash
  16. function store_images(uuid, details, callback) {
  17. // get skin_url for +uuid+
  18. networking.get_skin_url(uuid, function(err, skin_url) {
  19. if (err) {
  20. callback(err, null);
  21. } else {
  22. if (skin_url) {
  23. logging.log(uuid + " " + skin_url);
  24. var hash = get_hash(skin_url);
  25. // set file paths
  26. var facepath = __dirname + "/../" + config.faces_dir + hash + ".png";
  27. var helmpath = __dirname + "/../" + config.helms_dir + hash + ".png";
  28. // make sure the file is still there
  29. fs.exists(facepath, function(err, face_exists) {
  30. if (err) {
  31. logging.error(uuid + "error: " + err);
  32. }
  33. var exists = !err && face_exists;
  34. if (exists && details && details.hash == hash) {
  35. logging.log(uuid + " hash has not changed");
  36. cache.update_timestamp(uuid, hash);
  37. callback(null, hash);
  38. } else {
  39. if (!exists) {
  40. logging.warn(uuid + " File was deleted! Downloading again.");
  41. } else {
  42. // hash has changed
  43. logging.log(uuid + " new hash: " + hash);
  44. }
  45. if (exists) {
  46. logging.log(uuid + " Avatar already exists, not downloading");
  47. cache.save_hash(uuid, hash);
  48. callback(null, hash);
  49. } else {
  50. // download skin
  51. networking.get_skin(skin_url, function(err, img) {
  52. if (err || !img) {
  53. callback(err, null);
  54. } else {
  55. // extract face / helm
  56. skins.extract_face(img, facepath, function(err) {
  57. if (err) {
  58. callback(err);
  59. } else {
  60. logging.log(uuid + " face extracted");
  61. logging.debug(facepath);
  62. skins.extract_helm(facepath, img, helmpath, function(err) {
  63. logging.log(uuid + " helm extracted");
  64. logging.debug(helmpath);
  65. cache.save_hash(uuid, hash);
  66. callback(err, hash);
  67. });
  68. }
  69. });
  70. }
  71. });
  72. }
  73. }
  74. });
  75. } else {
  76. // profile found, but has no skin
  77. cache.save_hash(uuid, null);
  78. callback(null, null);
  79. }
  80. }
  81. });
  82. }
  83. var exp = {};
  84. // returns true if the +uuid+ is a valid uuid or username
  85. // the uuid may be not exist, however
  86. exp.uuid_valid = function(uuid) {
  87. return valid_uuid.test(uuid);
  88. };
  89. // decides whether to get an image from disk or to download it
  90. // callback contains error, status, hash
  91. // the status gives information about how the image was received
  92. // -1: "error"
  93. // 0: "none" - cached as null
  94. // 1: "cached" - found on disk
  95. // 2: "downloaded" - profile downloaded, skin downloaded from mojang servers
  96. // 3: "checked" - profile re-downloaded (was too old), but it has either not changed or has no skin
  97. exp.get_image_hash = function(uuid, callback) {
  98. cache.get_details(uuid, function(err, details) {
  99. if (err) {
  100. callback(err, -1, null);
  101. } else {
  102. if (details && details.time + config.local_cache_time * 1000 >= new Date().getTime()) {
  103. // uuid known + recently updated
  104. logging.log(uuid + " uuid cached & recently updated");
  105. callback(null, (details.hash ? 1 : 0), details.hash);
  106. } else {
  107. if (details) {
  108. logging.log(uuid + " uuid cached, but too old");
  109. } else {
  110. logging.log(uuid + " uuid not cached");
  111. }
  112. store_images(uuid, details, function(err, hash) {
  113. if (err) {
  114. callback(err, -1, details && details.hash);
  115. } else {
  116. // skin is only checked (3) when uuid known AND hash didn't change
  117. // in all other cases the skin is downloaded (2)
  118. var status = details && (details.hash == hash) ? 3 : 2;
  119. logging.debug(uuid + " old hash: " + (details && details.hash));
  120. logging.log(uuid + " hash: " + hash);
  121. callback(null, status, hash);
  122. }
  123. });
  124. }
  125. }
  126. });
  127. };
  128. // handles requests for +uuid+ avatars with +size+
  129. // callback contains error, status, image buffer, hash
  130. // image is the user's face+helm when helm is true, or the face otherwise
  131. // for status, see get_image_hash
  132. exp.get_avatar = function(uuid, helm, size, callback) {
  133. logging.log("request: " + uuid);
  134. exp.get_image_hash(uuid, function(err, status, hash) {
  135. if (hash) {
  136. var facepath = __dirname + "/../" + config.faces_dir + hash + ".png";
  137. var helmpath = __dirname + "/../" + config.helms_dir + hash + ".png";
  138. var filepath = facepath;
  139. if (helm && fs.existsSync(helmpath)) {
  140. filepath = helmpath;
  141. }
  142. skins.resize_img(filepath, size, function(img_err, result) {
  143. if (img_err) {
  144. callback(img_err, -1, null, hash);
  145. } else {
  146. // we might have a hash although an error occured
  147. // (e.g. Mojang servers not reachable, using outdated hash)
  148. callback(err, (err ? -1 : status), result, hash);
  149. }
  150. });
  151. } else {
  152. // hash is null when uuid has no skin
  153. callback(err, status, null, null);
  154. }
  155. });
  156. };
  157. // handles requests for +uuid+ skins
  158. // callback contains error, hash, image buffer
  159. exp.get_skin = function(uuid, callback) {
  160. logging.log(uuid + " skin request");
  161. exp.get_image_hash(uuid, function(err, status, hash) {
  162. var skinpath = __dirname + "/../" + config.skins_dir + hash + ".png";
  163. if (fs.existsSync(skinpath)) {
  164. logging.log("skin already exists, not downloading");
  165. skins.open_skin(skinpath, function(err, img) {
  166. callback(err, hash, img);
  167. });
  168. return;
  169. }
  170. networking.save_skin(uuid, hash, skinpath, function(err, img) {
  171. callback(err, hash, img);
  172. });
  173. });
  174. };
  175. function get_type(helm, body) {
  176. var text = body ? "body" : "head";
  177. return helm ? text+"helm" : text;
  178. }
  179. // handles creations of skin renders
  180. // callback contanis error, hash, image buffer
  181. exp.get_render = function(uuid, scale, helm, body, callback) {
  182. logging.log(uuid + " render request");
  183. exp.get_image_hash(uuid, function(err, status, hash) {
  184. exp.get_skin(uuid, function(err, hash, img) {
  185. if (!hash) {
  186. callback(err, -1, hash, null);
  187. return;
  188. }
  189. var renderpath = __dirname + "/../" + config.renders_dir + hash + "-" + scale + "-" + get_type(helm, body) + ".png";
  190. if (fs.existsSync(renderpath)) {
  191. renders.open_render(renderpath, function(err, img) {
  192. callback(err, 1, hash, img);
  193. });
  194. return;
  195. }
  196. if (!img) {
  197. callback(err, 0, hash, null);
  198. return;
  199. }
  200. renders.draw_model(uuid, img, scale, helm, body, function(err, img) {
  201. if (err) {
  202. callback(err, -1, hash, null);
  203. } else if (!img) {
  204. callback(null, 0, hash, null);
  205. } else {
  206. fs.writeFile(renderpath, img, "binary", function(err){
  207. if (err) {
  208. logging.log(err);
  209. }
  210. callback(null, 2, hash, img);
  211. });
  212. }
  213. });
  214. });
  215. });
  216. };
  217. module.exports = exp;