helpers.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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. // set file paths
  25. var hash = get_hash(skin_url);
  26. if (details && details.hash == hash) {
  27. // hash hasn't changed
  28. logging.log(uuid + " hash has not changed");
  29. cache.update_timestamp(uuid, hash);
  30. callback(null, hash);
  31. } else {
  32. // hash has changed
  33. logging.log(uuid + " new hash: " + hash);
  34. var facepath = __dirname + "/../" + config.faces_dir + hash + ".png";
  35. var helmpath = __dirname + "/../" + config.helms_dir + hash + ".png";
  36. if (fs.existsSync(facepath)) {
  37. logging.log(uuid + " Avatar already exists, not downloading");
  38. cache.save_hash(uuid, hash);
  39. callback(null, hash);
  40. } else {
  41. // download skin
  42. networking.get_skin(skin_url, function(err, img) {
  43. if (err || !img) {
  44. callback(err, null);
  45. } else {
  46. // extract face / helm
  47. skins.extract_face(img, facepath, function(err) {
  48. if (err) {
  49. callback(err);
  50. } else {
  51. logging.log(uuid + " face extracted");
  52. logging.debug(facepath);
  53. skins.extract_helm(facepath, img, helmpath, function(err) {
  54. logging.log(uuid + " helm extracted");
  55. logging.debug(helmpath);
  56. cache.save_hash(uuid, hash);
  57. callback(err, hash);
  58. });
  59. }
  60. });
  61. }
  62. });
  63. }
  64. }
  65. } else {
  66. // profile found, but has no skin
  67. cache.save_hash(uuid, null);
  68. callback(null, null);
  69. }
  70. }
  71. });
  72. }
  73. var exp = {};
  74. // returns true if the +uuid+ is a valid uuid or username
  75. // the uuid may be not exist, however
  76. exp.uuid_valid = function(uuid) {
  77. return valid_uuid.test(uuid);
  78. };
  79. // decides whether to get an image from disk or to download it
  80. // callback contains error, status, hash
  81. // the status gives information about how the image was received
  82. // -1: "error"
  83. // 0: "none" - cached as null
  84. // 1: "cached" - found on disk
  85. // 2: "downloaded" - profile downloaded, skin downloaded from mojang servers
  86. // 3: "checked" - profile re-downloaded (was too old), but it has either not changed or has no skin
  87. exp.get_image_hash = function(uuid, callback) {
  88. cache.get_details(uuid, function(err, details) {
  89. if (err) {
  90. callback(err, -1, null);
  91. } else {
  92. if (details && details.time + config.local_cache_time * 1000 >= new Date().getTime()) {
  93. // uuid known + recently updated
  94. logging.log(uuid + " uuid cached & recently updated");
  95. callback(null, (details.hash ? 1 : 0), details.hash);
  96. } else {
  97. if (details) {
  98. logging.log(uuid + " uuid cached, but too old");
  99. } else {
  100. logging.log(uuid + " uuid not cached");
  101. }
  102. store_images(uuid, details, function(err, hash) {
  103. if (err) {
  104. callback(err, -1, details && details.hash);
  105. } else {
  106. var oldhash = details && details.hash || "none";
  107. logging.debug(uuid + " old hash: " + oldhash);
  108. logging.log(uuid + " hash: " + hash);
  109. var status = hash == oldhash ? 3 : 2;
  110. callback(null, status, hash);
  111. }
  112. });
  113. }
  114. }
  115. });
  116. };
  117. // handles requests for +uuid+ avatars with +size+
  118. // callback contains error, status, image buffer, hash
  119. // image is the user's face+helm when helm is true, or the face otherwise
  120. // for status, see get_image_hash
  121. exp.get_avatar = function(uuid, helm, size, callback) {
  122. logging.log("\nrequest: " + uuid);
  123. exp.get_image_hash(uuid, function(err, status, hash) {
  124. if (hash) {
  125. var facepath = __dirname + "/../" + config.faces_dir + hash + ".png";
  126. var helmpath = __dirname + "/../" + config.helms_dir + hash + ".png";
  127. var filepath = facepath;
  128. if (helm && fs.existsSync(helmpath)) {
  129. filepath = helmpath;
  130. }
  131. skins.resize_img(filepath, size, function(img_err, result) {
  132. if (img_err) {
  133. callback(img_err, -1, null, hash);
  134. } else {
  135. // we might have a hash although an error occured
  136. // (e.g. Mojang servers not reachable, using outdated hash)
  137. callback(err, (err ? -1 : status), result, hash);
  138. }
  139. });
  140. } else {
  141. // hash is null when uuid has no skin
  142. callback(err, status, null, null);
  143. }
  144. });
  145. };
  146. // handles requests for +uuid+ skins
  147. // callback contains error, hash, image buffer
  148. exp.get_skin = function(uuid, callback) {
  149. logging.log(uuid + " skin request");
  150. exp.get_image_hash(uuid, function(err, status, hash) {
  151. if (hash) {
  152. var skinurl = "http://textures.minecraft.net/texture/" + hash;
  153. networking.get_skin(skinurl, function(err, img) {
  154. if (err) {
  155. logging.error("error while downloading skin");
  156. callback(err, hash, null);
  157. } else {
  158. callback(null, hash, img);
  159. }
  160. });
  161. } else {
  162. callback(err, null, null);
  163. }
  164. });
  165. };
  166. // handles creations of skin renders
  167. // callback contanis error, hash, image buffer
  168. exp.get_render = function(uuid, scale, helm, body, callback) {
  169. logging.log(uuid + " render request");
  170. exp.get_skin(uuid, function(err, hash, img) {
  171. renders.draw_model(uuid, img, scale, helm, body, function(err, img) {
  172. if (err) {
  173. callback(err, -1, hash, null);
  174. } else if (!img) {
  175. callback(null, 0, hash, null);
  176. } else {
  177. callback(null, 2, hash, img);
  178. }
  179. });
  180. });
  181. };
  182. module.exports = exp;