Merge branch 'renders-new'

This commit is contained in:
jomo 2016-01-20 01:27:42 +01:00
commit e8877c427a
12 changed files with 533 additions and 487 deletions

View File

@ -70,6 +70,15 @@ exp.info = function(callback) {
}); });
}; };
// set model type to value of *slim*
exp.set_slim = function(rid, userId, slim, callback) {
logging.debug(rid, "setting slim for ", userId, "to " + slim);
// store userId in lower case if not null
userId = userId && userId.toLowerCase();
redis.hmset(userId, ["a", Number(slim)], callback);
};
// sets the timestamp for +userId+ // sets the timestamp for +userId+
// if +temp+ is true, the timestamp is set so that the record will be outdated after 60 seconds // if +temp+ is true, the timestamp is set so that the record will be outdated after 60 seconds
// these 60 seconds match the duration of Mojang's rate limit ban // these 60 seconds match the duration of Mojang's rate limit ban
@ -85,31 +94,34 @@ exp.update_timestamp = function(rid, userId, temp, callback) {
}); });
}; };
// create the key +userId+, store +skin_hash+, +cape_hash+ and time // create the key +userId+, store +skin_hash+, +cape_hash+, +slim+ and current time
// if either +skin_hash+ or +cape_hash+ are undefined, they will not be stored // if +skin_hash+ or +cape_hash+ are undefined, they aren't stored
// this feature can be used to write both cape and skin at separate times // this is useful to store cape and skin at separate times, without overwriting the other
// +slim+ can be true (alex) or false (steve)
// +callback+ contans error // +callback+ contans error
exp.save_hash = function(rid, userId, skin_hash, cape_hash, callback) { exp.save_hash = function(rid, userId, skin_hash, cape_hash, slim, callback) {
logging.debug(rid, "caching skin:" + skin_hash + " cape:" + cape_hash); logging.debug(rid, "caching skin:" + skin_hash + " cape:" + cape_hash + " slim:" + slim);
var time = Date.now(); // store shorter null value instead of "null" string
// store shorter null byte instead of "null"
skin_hash = skin_hash === null ? "" : skin_hash; skin_hash = skin_hash === null ? "" : skin_hash;
cape_hash = cape_hash === null ? "" : cape_hash; cape_hash = cape_hash === null ? "" : cape_hash;
// store userId in lower case if not null // store userId in lower case if not null
userId = userId && userId.toLowerCase(); userId = userId && userId.toLowerCase();
if (skin_hash === undefined) {
redis.hmset(userId, "c", cape_hash, "t", time, function(err) { var args = [];
callback(err); if (cape_hash !== undefined) {
}); args.push("c", cape_hash);
} else if (cape_hash === undefined) {
redis.hmset(userId, "s", skin_hash, "t", time, function(err) {
callback(err);
});
} else {
redis.hmset(userId, "s", skin_hash, "c", cape_hash, "t", time, function(err) {
callback(err);
});
} }
if (skin_hash !== undefined) {
args.push("s", skin_hash);
}
if (slim !== undefined) {
args.push("a", Number(!!slim));
}
args.push("t", Date.now());
redis.hmset(userId, args, function(err) {
callback(err);
});
}; };
// removes the hash for +userId+ from the cache // removes the hash for +userId+ from the cache
@ -131,6 +143,7 @@ exp.get_details = function(userId, callback) {
details = { details = {
skin: data.s === "" ? null : data.s, skin: data.s === "" ? null : data.s,
cape: data.c === "" ? null : data.c, cape: data.c === "" ? null : data.c,
slim: data.a === "1",
time: Number(data.t) time: Number(data.t)
}; };
} }

View File

@ -19,14 +19,19 @@ function get_hash(url) {
// gets the skin for +userId+ with +profile+ // gets the skin for +userId+ with +profile+
// uses +cache_details+ to determine if the skin needs to be downloaded or can be taken from cache // uses +cache_details+ to determine if the skin needs to be downloaded or can be taken from cache
// face and face+helm images are extracted and stored to files // face and face+helm images are extracted and stored to files
// callback: error, skin hash // callback: error, skin hash, callback
function store_skin(rid, userId, profile, cache_details, callback) { function store_skin(rid, userId, profile, cache_details, callback) {
networking.get_skin_url(rid, userId, profile, function(err, url) { networking.get_skin_info(rid, userId, profile, function(err, url, slim) {
if (!err && userId.length > 16) {
// updating username with model info from uuid details
cache.set_slim(rid, profile.name, slim);
}
if (!err && url) { if (!err && url) {
var skin_hash = get_hash(url); var skin_hash = get_hash(url);
if (cache_details && cache_details.skin === skin_hash) { if (cache_details && cache_details.skin === skin_hash) {
cache.update_timestamp(rid, userId, false, function(cache_err) { cache.update_timestamp(rid, userId, false, function(cache_err) {
callback(cache_err, skin_hash); callback(cache_err, skin_hash, slim);
}); });
} else { } else {
logging.debug(rid, "new skin hash:", skin_hash); logging.debug(rid, "new skin hash:", skin_hash);
@ -36,25 +41,25 @@ function store_skin(rid, userId, profile, cache_details, callback) {
fs.exists(facepath, function(exists) { fs.exists(facepath, function(exists) {
if (exists) { if (exists) {
logging.debug(rid, "skin already exists, not downloading"); logging.debug(rid, "skin already exists, not downloading");
callback(null, skin_hash); callback(null, skin_hash, slim);
} else { } else {
networking.get_from(rid, url, function(img, response, err1) { networking.get_from(rid, url, function(img, response, err1) {
if (err1 || !img) { if (err1 || !img) {
callback(err1, null); callback(err1, null, slim);
} else { } else {
skins.save_image(img, skinpath, function(skin_err, skin_img) { skins.save_image(img, skinpath, function(skin_err, skin_img) {
if (skin_err) { if (skin_err) {
callback(skin_err, null); callback(skin_err, null, slim);
} else { } else {
skins.extract_face(img, facepath, function(err2) { skins.extract_face(img, facepath, function(err2) {
if (err2) { if (err2) {
callback(err2, null); callback(err2, null, slim);
} else { } else {
logging.debug(rid, "face extracted"); logging.debug(rid, "face extracted");
skins.extract_helm(rid, facepath, img, helmpath, function(err3) { skins.extract_helm(rid, facepath, img, helmpath, function(err3) {
logging.debug(rid, "helm extracted"); logging.debug(rid, "helm extracted");
logging.debug(rid, helmpath); logging.debug(rid, helmpath);
callback(err3, skin_hash); callback(err3, skin_hash, slim);
}); });
} }
}); });
@ -128,7 +133,7 @@ function push_request(userId, type, fun) {
} }
// calls back all queued requests that match userId and type // calls back all queued requests that match userId and type
function resume(userId, type, err, hash) { function resume(userId, type, err, hash, slim) {
var userId_safe = "!" + userId; var userId_safe = "!" + userId;
var callbacks = requests[type][userId_safe]; var callbacks = requests[type][userId_safe];
if (callbacks) { if (callbacks) {
@ -138,7 +143,7 @@ function resume(userId, type, err, hash) {
for (var i = 0; i < callbacks.length; i++) { for (var i = 0; i < callbacks.length; i++) {
// continue the request // continue the request
callbacks[i](err, hash); callbacks[i](err, hash, slim);
// remove from array // remove from array
callbacks.splice(i, 1); callbacks.splice(i, 1);
i--; i--;
@ -152,7 +157,7 @@ function resume(userId, type, err, hash) {
// downloads the images for +userId+ while checking the cache // downloads the images for +userId+ while checking the cache
// status based on +cache_details+. +type+ specifies which // status based on +cache_details+. +type+ specifies which
// image type should be called back on // image type should be called back on
// callback: error, image hash // callback: error, image hash, slim
function store_images(rid, userId, cache_details, type, callback) { function store_images(rid, userId, cache_details, type, callback) {
var is_uuid = userId.length > 16; var is_uuid = userId.length > 16;
if (requests[type]["!" + userId]) { if (requests[type]["!" + userId]) {
@ -167,34 +172,34 @@ function store_images(rid, userId, cache_details, type, callback) {
// error or uuid without profile // error or uuid without profile
if (!err && !profile) { if (!err && !profile) {
// no error, but uuid without profile // no error, but uuid without profile
cache.save_hash(rid, userId, null, null, function(cache_err) { cache.save_hash(rid, userId, null, null, undefined, function(cache_err) {
// we have no profile, so we have neither skin nor cape // we have no profile, so we have neither skin nor cape
resume(userId, "skin", cache_err, null); resume(userId, "skin", cache_err, null, false);
resume(userId, "cape", cache_err, null); resume(userId, "cape", cache_err, null, false);
}); });
} else { } else {
// an error occured, not caching. we can try in 60 seconds // an error occured, not caching. we can try in 60 seconds
resume(userId, type, err, null); resume(userId, type, err, null, false);
} }
} else { } else {
// no error and we have a profile (if it's a uuid) // no error and we have a profile (if it's a uuid)
store_skin(rid, userId, profile, cache_details, function(store_err, skin_hash) { store_skin(rid, userId, profile, cache_details, function(store_err, skin_hash, slim) {
if (store_err && !skin_hash) { if (store_err && !skin_hash) {
// an error occured, not caching. we can try in 60 seconds // an error occured, not caching. we can try in 60 seconds
resume(userId, "skin", store_err, null); resume(userId, "skin", store_err, null, slim);
} else { } else {
cache.save_hash(rid, userId, skin_hash, undefined, function(cache_err) { cache.save_hash(rid, userId, skin_hash, undefined, slim, function(cache_err) {
resume(userId, "skin", (store_err || cache_err), skin_hash); resume(userId, "skin", (store_err || cache_err), skin_hash, slim);
}); });
} }
}); });
store_cape(rid, userId, profile, cache_details, function(store_err, cape_hash) { store_cape(rid, userId, profile, cache_details, function(store_err, cape_hash) {
if (store_err && !cape_hash) { if (store_err && !cape_hash) {
// an error occured, not caching. we can try in 60 seconds // an error occured, not caching. we can try in 60 seconds
resume(userId, "cape", (store_err), cape_hash); resume(userId, "cape", (store_err), cape_hash, false);
} else { } else {
cache.save_hash(rid, userId, undefined, cape_hash, function(cache_err) { cache.save_hash(rid, userId, undefined, cape_hash, undefined, function(cache_err) {
resume(userId, "cape", (store_err || cache_err), cape_hash); resume(userId, "cape", (store_err || cache_err), cape_hash, false);
}); });
} }
}); });
@ -212,7 +217,7 @@ exp.id_valid = function(userId) {
}; };
// decides whether to get a +type+ image for +userId+ from disk or to download it // decides whether to get a +type+ image for +userId+ from disk or to download it
// callback: error, status, hash // callback: error, status, hash, slim
// for status, see response.js // for status, see response.js
exp.get_image_hash = function(rid, userId, type, callback) { exp.get_image_hash = function(rid, userId, type, callback) {
cache.get_details(userId, function(err, cache_details) { cache.get_details(userId, function(err, cache_details) {
@ -221,31 +226,32 @@ exp.get_image_hash = function(rid, userId, type, callback) {
cached_hash = type === "skin" ? cache_details.skin : cache_details.cape; cached_hash = type === "skin" ? cache_details.skin : cache_details.cape;
} }
if (err) { if (err) {
callback(err, -1, null); callback(err, -1, null, false);
} else { } else {
if (cache_details && cache_details[type] !== undefined && cache_details.time + config.caching.local * 1000 >= Date.now()) { if (cache_details && cache_details[type] !== undefined && cache_details.time + config.caching.local * 1000 >= Date.now()) {
// use cached image // use cached image
logging.debug(rid, "userId cached & recently updated"); logging.debug(rid, "userId cached & recently updated");
callback(null, (cached_hash ? 1 : 0), cached_hash); callback(null, (cached_hash ? 1 : 0), cached_hash, cache_details.slim);
} else { } else {
// download image // download image
if (cache_details) { if (cache_details && cache_details[type] !== undefined) {
logging.debug(rid, "userId cached, but too old"); logging.debug(rid, "userId cached, but too old");
logging.debug(rid, JSON.stringify(cache_details));
} else { } else {
logging.debug(rid, "userId not cached"); logging.debug(rid, "userId not cached");
} }
store_images(rid, userId, cache_details, type, function(store_err, new_hash) { store_images(rid, userId, cache_details, type, function(store_err, new_hash, slim) {
if (store_err) { if (store_err) {
// we might have a cached hash although an error occured // we might have a cached hash although an error occured
// (e.g. Mojang servers not reachable, using outdated hash) // (e.g. Mojang servers not reachable, using outdated hash)
cache.update_timestamp(rid, userId, true, function(err2) { cache.update_timestamp(rid, userId, true, function(err2) {
callback(err2 || store_err, -1, cache_details && cached_hash); callback(err2 || store_err, -1, cache_details && cached_hash, slim);
}); });
} else { } else {
var status = cache_details && (cached_hash === new_hash) ? 3 : 2; var status = cache_details && (cached_hash === new_hash) ? 3 : 2;
logging.debug(rid, "cached hash:", (cache_details && cached_hash)); logging.debug(rid, "cached hash:", (cache_details && cached_hash));
logging.debug(rid, "new hash:", new_hash); logging.debug(rid, "new hash:", new_hash);
callback(null, status, new_hash); callback(null, status, new_hash, slim);
} }
}); });
} }
@ -259,7 +265,7 @@ exp.get_image_hash = function(rid, userId, type, callback) {
// image is the user's face+overlay when overlay is true, or the face otherwise // image is the user's face+overlay when overlay is true, or the face otherwise
// for status, see get_image_hash // for status, see get_image_hash
exp.get_avatar = function(rid, userId, overlay, size, callback) { exp.get_avatar = function(rid, userId, overlay, size, callback) {
exp.get_image_hash(rid, userId, "skin", function(err, status, skin_hash) { exp.get_image_hash(rid, userId, "skin", function(err, status, skin_hash, slim) {
if (skin_hash) { if (skin_hash) {
var facepath = path.join(config.directories.faces, skin_hash + ".png"); var facepath = path.join(config.directories.faces, skin_hash + ".png");
var helmpath = path.join(config.directories.helms, skin_hash + ".png"); var helmpath = path.join(config.directories.helms, skin_hash + ".png");
@ -284,25 +290,25 @@ exp.get_avatar = function(rid, userId, overlay, size, callback) {
}; };
// handles requests for +userId+ skins // handles requests for +userId+ skins
// callback: error, skin hash, status, image buffer // callback: error, skin hash, status, image buffer, slim
exp.get_skin = function(rid, userId, callback) { exp.get_skin = function(rid, userId, callback) {
exp.get_image_hash(rid, userId, "skin", function(err, status, skin_hash) { exp.get_image_hash(rid, userId, "skin", function(err, status, skin_hash, slim) {
if (skin_hash) { if (skin_hash) {
var skinpath = path.join(config.directories.skins, skin_hash + ".png"); var skinpath = path.join(config.directories.skins, skin_hash + ".png");
fs.exists(skinpath, function(exists) { fs.exists(skinpath, function(exists) {
if (exists) { if (exists) {
logging.debug(rid, "skin already exists, not downloading"); logging.debug(rid, "skin already exists, not downloading");
skins.open_skin(rid, skinpath, function(skin_err, img) { skins.open_skin(rid, skinpath, function(skin_err, img) {
callback(skin_err || err, skin_hash, status, img); callback(skin_err || err, skin_hash, status, img, slim);
}); });
} else { } else {
networking.save_texture(rid, skin_hash, skinpath, function(net_err, response, img) { networking.save_texture(rid, skin_hash, skinpath, function(net_err, response, img) {
callback(net_err || err, skin_hash, status, img); callback(net_err || err, skin_hash, status, img, slim);
}); });
} }
}); });
} else { } else {
callback(err, null, status, null); callback(err, null, status, null, slim);
} }
}); });
}; };
@ -318,12 +324,12 @@ function get_type(overlay, body) {
// handles creations of 3D renders // handles creations of 3D renders
// callback: error, skin hash, image buffer // callback: error, skin hash, image buffer
exp.get_render = function(rid, userId, scale, overlay, body, callback) { exp.get_render = function(rid, userId, scale, overlay, body, callback) {
exp.get_skin(rid, userId, function(err, skin_hash, status, img) { exp.get_skin(rid, userId, function(err, skin_hash, status, img, slim) {
if (!skin_hash) { if (!skin_hash) {
callback(err, status, skin_hash, null); callback(err, status, skin_hash, null);
return; return;
} }
var renderpath = path.join(config.directories.renders, [skin_hash, scale, get_type(overlay, body)].join("-") + ".png"); var renderpath = path.join(config.directories.renders, [skin_hash, scale, get_type(overlay, body), slim ? "s" : "t"].join("-") + ".png");
fs.exists(renderpath, function(exists) { fs.exists(renderpath, function(exists) {
if (exists) { if (exists) {
renders.open_render(rid, renderpath, function(render_err, rendered_img) { renders.open_render(rid, renderpath, function(render_err, rendered_img) {
@ -335,7 +341,7 @@ exp.get_render = function(rid, userId, scale, overlay, body, callback) {
callback(err, 0, skin_hash, null); callback(err, 0, skin_hash, null);
return; return;
} }
renders.draw_model(rid, img, scale, overlay, body, function(draw_err, drawn_img) { renders.draw_model(rid, img, scale, overlay, body, slim || userId.toLowerCase() === "mhf_alex", function(draw_err, drawn_img) {
if (draw_err) { if (draw_err) {
callback(draw_err, -1, skin_hash, null); callback(draw_err, -1, skin_hash, null);
} else if (!drawn_img) { } else if (!drawn_img) {
@ -354,7 +360,7 @@ exp.get_render = function(rid, userId, scale, overlay, body, callback) {
// handles requests for +userId+ capes // handles requests for +userId+ capes
// callback: error, cape hash, status, image buffer // callback: error, cape hash, status, image buffer
exp.get_cape = function(rid, userId, callback) { exp.get_cape = function(rid, userId, callback) {
exp.get_image_hash(rid, userId, "cape", function(err, status, cape_hash) { exp.get_image_hash(rid, userId, "cape", function(err, status, cape_hash, slim) {
if (!cape_hash) { if (!cape_hash) {
callback(err, null, status, null); callback(err, null, status, null);
return; return;

View File

@ -3,6 +3,7 @@ var logging = require("./logging");
var request = require("request"); var request = require("request");
var config = require("../config"); var config = require("../config");
var skins = require("./skins"); var skins = require("./skins");
require("./object-patch");
var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/"; var session_url = "https://sessionserver.mojang.com/session/minecraft/profile/";
var skins_url = "https://skins.minecraft.net/MinecraftSkins/"; var skins_url = "https://skins.minecraft.net/MinecraftSkins/";
@ -12,50 +13,22 @@ var mojang_urls = [skins_url, capes_url];
var exp = {}; var exp = {};
// extracts the +type+ [SKIN|CAPE] URL // helper method that calls `get_username_url` or `get_uuid_info` based on the +usedId+
// from the nested & encoded +profile+ object
// returns the URL or null if not present
function extract_url(profile, type) {
var url = null;
if (profile && profile.properties) {
profile.properties.forEach(function(prop) {
if (prop.name === "textures") {
var json = new Buffer(prop.value, "base64").toString();
var props = JSON.parse(json);
url = props && props.textures && props.textures[type] && props.textures[type].url || null;
}
});
}
return url;
}
// helper method that calls `get_username_url` or `get_uuid_url` based on the +usedId+
// +userId+ is used for usernames, while +profile+ is used for UUIDs // +userId+ is used for usernames, while +profile+ is used for UUIDs
function get_url(rid, userId, profile, type, callback) { // callback: error, url, slim
function get_info(rid, userId, profile, type, callback) {
if (userId.length <= 16) { if (userId.length <= 16) {
// username // username
exp.get_username_url(rid, userId, type, function(err, url) { exp.get_username_url(rid, userId, type, function(err, url) {
callback(err, url || null); callback(err, url || null, false);
}); });
} else { } else {
exp.get_uuid_url(profile, type, function(url) { exp.get_uuid_info(profile, type, function(url, slim) {
callback(null, url || null); callback(null, url || null, slim);
}); });
} }
} }
// exracts the skin URL of a +profile+ object
// returns null when no URL found (user has no skin)
exp.extract_skin_url = function(profile) {
return extract_url(profile, "SKIN");
};
// exracts the cape URL of a +profile+ object
// returns null when no URL found (user has no cape)
exp.extract_cape_url = function(profile) {
return extract_url(profile, "CAPE");
};
// performs a GET request to the +url+ // performs a GET request to the +url+
// +options+ object includes these options: // +options+ object includes these options:
// encoding (string), default is to return a buffer // encoding (string), default is to return a buffer
@ -133,6 +106,7 @@ exp.get_from = function(rid, url, callback) {
// the skin url is taken from the HTTP redirect // the skin url is taken from the HTTP redirect
// type reference is above // type reference is above
exp.get_username_url = function(rid, name, type, callback) { exp.get_username_url = function(rid, name, type, callback) {
type = Number(type === "CAPE");
exp.get_from(rid, mojang_urls[type] + name + ".png", function(body, response, err) { exp.get_from(rid, mojang_urls[type] + name + ".png", function(body, response, err) {
if (!err) { if (!err) {
if (response) { if (response) {
@ -147,15 +121,24 @@ exp.get_username_url = function(rid, name, type, callback) {
}; };
// gets the URL for a skin/cape from the profile // gets the URL for a skin/cape from the profile
// +type+ specifies which to retrieve // +type+ "SKIN" or "CAPE", specifies which to retrieve
exp.get_uuid_url = function(profile, type, callback) { // callback: url, slim
var url = null; exp.get_uuid_info = function(profile, type, callback) {
if (type === 0) { var properties = Object.get(profile, "properties") || [];
url = exp.extract_skin_url(profile); properties.forEach(function(prop) {
} else if (type === 1) { if (prop.name === "textures") {
url = exp.extract_cape_url(profile); var json = new Buffer(prop.value, "base64").toString();
profile = JSON.parse(json);
}
});
var url = Object.get(profile, "textures." + type + ".url");
var slim;
if (type === "SKIN") {
slim = Object.get(profile, "textures.SKIN.metadata.model") === "slim";
} }
callback(url || null);
callback(url || null, !!slim);
}; };
// make a request to sessionserver for +uuid+ // make a request to sessionserver for +uuid+
@ -181,20 +164,17 @@ exp.get_profile = function(rid, uuid, callback) {
} }
}; };
// get the skin URL for +userId+ // get the skin URL and type for +userId+
// +profile+ is used if +userId+ is a uuid // +profile+ is used if +userId+ is a uuid
exp.get_skin_url = function(rid, userId, profile, callback) { // callback: error, url, slim
get_url(rid, userId, profile, 0, function(err, url) { exp.get_skin_info = function(rid, userId, profile, callback) {
callback(err, url); get_info(rid, userId, profile, "SKIN", callback);
});
}; };
// get the cape URL for +userId+ // get the cape URL for +userId+
// +profile+ is used if +userId+ is a uuid // +profile+ is used if +userId+ is a uuid
exp.get_cape_url = function(rid, userId, profile, callback) { exp.get_cape_url = function(rid, userId, profile, callback) {
get_url(rid, userId, profile, 1, function(err, url) { get_info(rid, userId, profile, "CAPE", callback);
callback(err, url);
});
}; };
// download the +tex_hash+ image from the texture server // download the +tex_hash+ image from the texture server

22
lib/object-patch.js Normal file
View File

@ -0,0 +1,22 @@
// Adds Object.get function
// +pathstr+ is a string of dot-separated nested properties on +ojb+
// returns undefined if any of the properties do not exist
// returns the value of the last property otherwise
//
// Object.get({"foo": {"bar": 123}}, "foo.bar"); // 123
// Object.get({"foo": {"bar": 123}}, "bar.foo"); // undefined
Object.get = function(obj, pathstr) {
var path = pathstr.split(".");
var result = obj;
for (var i = 0; i < path.length; i++) {
var key = path[i];
if (!result || !result.hasOwnProperty(key)) {
return undefined;
} else {
result = result[key];
}
}
return result;
};

View File

@ -8,200 +8,246 @@ var Canvas = require("canvas");
var Image = Canvas.Image; var Image = Canvas.Image;
var exp = {}; var exp = {};
// scales an image from the +imagedata+ onto the +context+ // set alpha values to 255
// scaled by a factor of +scale+ with options +d_x+ and +d_y+ function removeTransparency(canvas) {
function scale_image(imageData, context, d_x, d_y, scale) { var ctx = canvas.getContext("2d");
var width = imageData.width; var imagedata = ctx.getImageData(0, 0, canvas.width, canvas.height);
var height = imageData.height;
context.clearRect(0, 0, width, height); // Clear the spot where it originated from
for (var y = 0; y < height; y++) { // height original
for (var x = 0; x < width; x++) { // width original
// Gets original colour, then makes a scaled square of the same colour
var index = (x + y * width) * 4;
context.fillStyle = "rgba(" + imageData.data[index + 0] + ", " + imageData.data[index + 1] + ", " + imageData.data[index + 2] + ", " + imageData.data[index + 3] + ")";
context.fillRect(d_x + x * scale, d_y + y * scale, scale, scale);
}
}
}
// makes images less worse by using binary transparency
function enhance(context) {
var imagedata = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
var data = imagedata.data; var data = imagedata.data;
// data is [r,g,b,a, r,g,b,a, *] // data is [r,g,b,a, r,g,b,a, *]
for (var i = 3; i < data.length; i += 4) { for (var i = 0; i < data.length; i += 4) {
// round to 0 or 255 // usually we would have to check for alpha = 0
data[i] = Math.round(data[i] / 255) * 255; // and set color to black here
// but node-canvas already does that for us
// remove transparency
data[i + 3] = 255;
} }
context.putImageData(imagedata, 0, 0); ctx.putImageData(imagedata, 0, 0);
return canvas;
} }
// draws the helmet on to the +skin_canvas+ function hasTransparency(canvas) {
// using the skin from the +model_ctx+ at the +scale+ var ctx = canvas.getContext("2d");
exp.draw_helmet = function(skin_canvas, model_ctx, scale) { var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
// Helmet - Front for (var i = 3; i < imageData.length; i += 4) {
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0); if (imageData[i] < 255) {
model_ctx.drawImage(skin_canvas, 40 * scale, 8 * scale, 8 * scale, 8 * scale, 10 * scale, 13 / 1.2 * scale, 8 * scale, 8 * scale); // found pixel with translucent alpha value
// Helmet - Right return true;
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0); }
model_ctx.drawImage(skin_canvas, 32 * scale, 8 * scale, 8 * scale, 8 * scale, 2 * scale, 3 / 1.2 * scale, 8 * scale, 8 * scale);
// Helmet - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 40 * scale, 0, 8 * scale, 8 * scale, -3 * scale, 5 * scale, 8 * scale, 8 * scale);
};
// draws the head on to the +skin_canvas+
// using the skin from the +model_ctx+ at the +scale+
exp.draw_head = function(skin_canvas, model_ctx, scale) {
// Head - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 8 * scale, 8 * scale, 8 * scale, 8 * scale, 10 * scale, 13 / 1.2 * scale, 8 * scale, 8 * scale);
// Head - Right
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 0, 8 * scale, 8 * scale, 8 * scale, 2 * scale, 3 / 1.2 * scale, 8 * scale, 8 * scale);
// Head - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 8 * scale, 0, 8 * scale, 8 * scale, -3 * scale, 5 * scale, 8 * scale, 8 * scale);
};
// draws the body on to the +skin_canvas+
// using the skin from the +model_ctx+ at the +scale+
// parts are labeled as if drawn from the skin's POV
exp.draw_body = function(rid, skin_canvas, model_ctx, scale) {
if (skin_canvas.height === 32 * scale) {
logging.debug(rid, "uses old skin format");
// Left Leg
// Left Leg - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 4 * scale, 20 * scale, 4 * scale, 12 * scale, -16 * scale, 34.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Right Leg
// Right Leg - Right
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 0 * scale, 20 * scale, 4 * scale, 12 * scale, 4 * scale, 26.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Right Leg - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 4 * scale, 20 * scale, 4 * scale, 12 * scale, 8 * scale, 34.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Left
// Arm Left - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 44 * scale, 20 * scale, 4 * scale, 12 * scale, -20 * scale, 20 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Left - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.drawImage(skin_canvas, 44 * scale, 16 * scale, 4 * scale, 4 * scale, 0, 16 * scale, 4 * scale, 4 * scale);
// Body
// Body - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 20 * scale, 20 * scale, 8 * scale, 12 * scale, 8 * scale, 20 / 1.2 * scale, 8 * scale, 12 * scale);
// Arm Right
// Arm Right - Right
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 40 * scale, 20 * scale, 4 * scale, 12 * scale, 0, 16 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Right - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 44 * scale, 20 * scale, 4 * scale, 12 * scale, 4 * scale, 20 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Right - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 44 * scale, 16 * scale, 4 * scale, 4 * scale, -16 * scale, 16 * scale, 4 * scale, 4 * scale);
} else {
logging.debug(rid, "uses new skin format");
// Left Leg
// Left Leg - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 20 * scale, 52 * scale, 4 * scale, 12 * scale, 12 * scale, 34.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Right Leg
// Right Leg - Right
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 0, 20 * scale, 4 * scale, 12 * scale, 4 * scale, 26.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Right Leg - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 4 * scale, 20 * scale, 4 * scale, 12 * scale, 8 * scale, 34.4 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Left
// Arm Left - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 36 * scale, 52 * scale, 4 * scale, 12 * scale, 16 * scale, 20 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Left - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.drawImage(skin_canvas, 36 * scale, 48 * scale, 4 * scale, 4 * scale, 0, 16 * scale, 4 * scale, 4 * scale);
// Body
// Body - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 20 * scale, 20 * scale, 8 * scale, 12 * scale, 8 * scale, 20 / 1.2 * scale, 8 * scale, 12 * scale);
// Arm Right
// Arm Right - Right
model_ctx.setTransform(1, 0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 40 * scale, 20 * scale, 4 * scale, 12 * scale, 0, 16 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Right - Front
model_ctx.setTransform(1, -0.5, 0, 1.2, 0, 0);
model_ctx.drawImage(skin_canvas, 44 * scale, 20 * scale, 4 * scale, 12 * scale, 4 * scale, 20 / 1.2 * scale, 4 * scale, 12 * scale);
// Arm Right - Top
model_ctx.setTransform(-1, 0.5, 1, 0.5, 0, 0);
model_ctx.scale(-1, 1);
model_ctx.drawImage(skin_canvas, 44 * scale, 16 * scale, 4 * scale, 4 * scale, -16 * scale, 16 * scale, 4 * scale, 4 * scale);
} }
}; return false;
}
// sets up the necessary components to draw the skin model function resize(src, scale) {
// uses the +img+ skin with options of drawing var dst = new Canvas();
// the +overlay+ and the +body+ dst.width = scale * src.width;
// callback: error, image buffer dst.height = scale * src.height;
exp.draw_model = function(rid, img, scale, overlay, body, callback) { var context = dst.getContext("2d");
var image = new Image();
image.onerror = function(err) { // don't blur on resize
callback(err, null); context.patternQuality = "fast";
};
image.onload = function() { context.drawImage(src, 0, 0, src.width * scale, src.height * scale);
var width = 64 * scale; return dst;
var original_height = image.height === 32 ? 32 : 64; }
var height = original_height * scale;
var model_canvas = new Canvas(20 * scale, (body ? 44.8 : 17.6) * scale); function getPart(src, x, y, width, height, scale) {
var skin_canvas = new Canvas(width, height); var dst = new Canvas();
var model_ctx = model_canvas.getContext("2d"); dst.width = scale * width;
var skin_ctx = skin_canvas.getContext("2d"); dst.height = scale * height;
var context = dst.getContext("2d");
// don't blur on resize
context.patternQuality = "fast";
context.drawImage(src, x, y, width, height, 0, 0, width * scale, height * scale);
return dst;
}
function flip(src) {
var dst = new Canvas();
dst.width = src.width;
dst.height = src.height;
var context = dst.getContext("2d");
context.scale(-1, 1);
context.drawImage(src, -src.width, 0);
return dst;
}
// skew for isometric perspective
var skew_a = 26 / 45; // 0.57777777
var skew_b = skew_a * 2; // 1.15555555
exp.draw_model = function(rid, img, scale, overlay, is_body, slim, callback) {
var canvas = new Canvas();
canvas.width = scale * 20;
canvas.height = scale * (is_body ? 45.1 : 18.5);
var ctx = canvas.getContext("2d");
var skin = new Image();
skin.onload = function() {
var old_skin = skin.height === 32;
var arm_width = slim ? 3 : 4;
/* eslint-disable no-multi-spaces */
var head_top = resize(removeTransparency(getPart(skin, 8, 0, 8, 8, 1)), scale);
var head_front = resize(removeTransparency(getPart(skin, 8, 8, 8, 8, 1)), scale);
var head_right = resize(removeTransparency(getPart(skin, 0, 8, 8, 8, 1)), scale);
var arm_right_top = resize(removeTransparency(getPart(skin, 44, 16, arm_width, 4, 1)), scale);
var arm_right_front = resize(removeTransparency(getPart(skin, 44, 20, arm_width, 12, 1)), scale);
var arm_right_side = resize(removeTransparency(getPart(skin, 40, 20, 4, 12, 1)), scale);
var arm_left_top = old_skin ? flip(arm_right_top) : resize(removeTransparency(getPart(skin, 36, 48, arm_width, 4, 1)), scale);
var arm_left_front = old_skin ? flip(arm_right_front) : resize(removeTransparency(getPart(skin, 36, 52, arm_width, 12, 1)), scale);
var leg_right_front = resize(removeTransparency(getPart(skin, 4, 20, 4, 12, 1)), scale);
var leg_right_side = resize(removeTransparency(getPart(skin, 0, 20, 4, 12, 1)), scale);
var leg_left_front = old_skin ? flip(leg_right_front) : resize(removeTransparency(getPart(skin, 20, 52, 4, 12, 1)), scale);
var body_front = resize(removeTransparency(getPart(skin, 20, 20, 8, 12, 1)), scale);
/* eslint-enable no-multi-spaces */
skin_ctx.drawImage(image, 0, 0, 64, original_height);
// Scale it
scale_image(skin_ctx.getImageData(0, 0, 64, original_height), skin_ctx, 0, 0, scale);
if (body) {
exp.draw_body(rid, skin_canvas, model_ctx, scale);
}
exp.draw_head(skin_canvas, model_ctx, scale);
if (overlay) { if (overlay) {
exp.draw_helmet(skin_canvas, model_ctx, scale); if (hasTransparency(getPart(skin, 32, 0, 32, 32, 1))) {
// render head overlay
head_top.getContext("2d").drawImage(getPart(skin, 40, 0, 8, 8, scale), 0, 0);
head_front.getContext("2d").drawImage(getPart(skin, 40, 8, 8, 8, scale), 0, 0);
head_right.getContext("2d").drawImage(getPart(skin, 32, 8, 8, 8, scale), 0, 0);
}
if (!old_skin) {
// See #117
// if MC-89760 gets fixed, we can (probably) simply check the whole skin for transparency
/* eslint-disable no-multi-spaces */
var body_region = getPart(skin, 16, 32, 32, 16, 1);
var right_arm_region = getPart(skin, 48, 48, 16, 16, 1);
var left_arm_region = getPart(skin, 40, 32, 16, 16, 1);
var right_leg_region = getPart(skin, 0, 32, 16, 16, 1);
var left_leg_region = getPart(skin, 0, 48, 16, 16, 1);
/* eslint-enable no-multi-spaces */
if (hasTransparency(body_region)) {
// render body overlay
body_front.getContext("2d").drawImage(getPart(skin, 20, 36, 8, 12, scale), 0, 0);
}
if (hasTransparency(right_arm_region)) {
// render right arm overlay
arm_right_top.getContext("2d").drawImage(getPart(skin, 44, 32, arm_width, 4, scale), 0, 0);
arm_right_front.getContext("2d").drawImage(getPart(skin, 44, 36, arm_width, 12, scale), 0, 0);
arm_right_side.getContext("2d").drawImage(getPart(skin, 40, 36, 4, 12, scale), 0, 0);
}
if (hasTransparency(left_arm_region)) {
// render left arm overlay
arm_left_top.getContext("2d").drawImage(getPart(skin, 36 + 16, 48, arm_width, 4, scale), 0, 0);
arm_left_front.getContext("2d").drawImage(getPart(skin, 36 + 16, 52, arm_width, 12, scale), 0, 0);
}
if (hasTransparency(right_leg_region)) {
// render right leg overlay
leg_right_front.getContext("2d").drawImage(getPart(skin, 4, 36, 4, 12, scale), 0, 0);
leg_right_side.getContext("2d").drawImage(getPart(skin, 0, 36, 4, 12, scale), 0, 0);
}
if (hasTransparency(left_leg_region)) {
// render left leg overlay
leg_left_front.getContext("2d").drawImage(getPart(skin, 4, 52, 4, 12, scale), 0, 0);
}
}
} }
// FIXME: This is a temporary fix for #32 var x = 0;
enhance(model_ctx); var y = 0;
var z = 0;
model_canvas.toBuffer(function(err, buf) { var z_offset = scale * 3;
var x_offset = scale * 2;
if (is_body) {
// pre-render front onto separate canvas
var front = new Canvas();
front.width = scale * 16;
front.height = scale * 24;
var frontc = front.getContext("2d");
frontc.patternQuality = "fast";
frontc.drawImage(arm_right_front, (4 - arm_width) * scale, 0 * scale, arm_width * scale, 12 * scale);
frontc.drawImage(arm_left_front, 12 * scale, 0 * scale, arm_width * scale, 12 * scale);
frontc.drawImage(body_front, 4 * scale, 0 * scale, 8 * scale, 12 * scale);
frontc.drawImage(leg_right_front, 4 * scale, 12 * scale, 4 * scale, 12 * scale);
frontc.drawImage(leg_left_front, 8 * scale, 12 * scale, 4 * scale, 12 * scale);
// top
x = x_offset + scale * 2;
y = scale * -arm_width;
z = z_offset + scale * 8;
ctx.setTransform(1, -skew_a, 1, skew_a, 0, 0);
ctx.drawImage(arm_right_top, y - z - 0.5, x + z, arm_right_top.width + 1, arm_right_top.height + 1);
y = scale * 8;
ctx.drawImage(arm_left_top, y - z, x + z, arm_left_top.width, arm_left_top.height + 1);
// right side
ctx.setTransform(1, skew_a, 0, skew_b, 0, 0);
x = x_offset + scale * 2;
y = 0;
z = z_offset + scale * 20;
ctx.drawImage(leg_right_side, x + y, z - y, leg_right_side.width, leg_right_side.height);
x = x_offset + scale * 2;
y = scale * -arm_width;
z = z_offset + scale * 8;
ctx.drawImage(arm_right_side, x + y, z - y - 0.5, arm_right_side.width, arm_right_side.height + 1);
// front
z = z_offset + scale * 12;
y = 0;
ctx.setTransform(1, -skew_a, 0, skew_b, 0, skew_a);
ctx.drawImage(front, y + x, x + z - 0.5, front.width, front.height);
}
// head top
x = x_offset;
y = -0.5;
z = z_offset;
ctx.setTransform(1, -skew_a, 1, skew_a, 0, 0);
ctx.drawImage(head_top, y - z, x + z, head_top.width, head_top.height + 1);
// head front
x = x_offset + 8 * scale;
y = 0;
z = z_offset - 0.5;
ctx.setTransform(1, -skew_a, 0, skew_b, 0, skew_a);
ctx.drawImage(head_front, y + x, x + z, head_front.width, head_front.height);
// head right
x = x_offset;
y = 0;
z = z_offset;
ctx.setTransform(1, skew_a, 0, skew_b, 0, 0);
ctx.drawImage(head_right, x + y, z - y - 0.5, head_right.width, head_right.height + 1);
canvas.toBuffer(function(err, buf) {
if (err) {
logging.error(rid, "error creating buffer:", err);
}
callback(err, buf); callback(err, buf);
}); });
}; };
image.src = img; skin.src = img;
}; };
// helper method to open a render from +renderpath+ // helper method to open a render from +renderpath+
// callback: error, image buffer // callback: error, image buffer
exp.open_render = function(rid, renderpath, callback) { exp.open_render = function(rid, renderpath, callback) {
fs.readFile(renderpath, function(err, buf) { fs.readFile(renderpath, callback);
callback(err, buf);
});
}; };
module.exports = exp; module.exports = exp;

View File

@ -63,19 +63,15 @@ module.exports = function(request, response, result) {
headers["X-Storage-Type"] = human_status[result.status]; headers["X-Storage-Type"] = human_status[result.status];
} }
if (result.body) { // use crc32 as a hash function for Etag
// use Mojang's image hash if available var etag = "\"" + crc(result.body || "") + "\"";
// use crc32 as a hash function otherwise
var etag = result.hash && result.hash.substr(0, 10) || crc(result.body);
headers.Etag = "\"" + etag + "\"";
// handle etag caching // handle etag caching
var incoming_etag = request.headers["if-none-match"]; var incoming_etag = request.headers["if-none-match"];
if (incoming_etag && incoming_etag === headers.Etag) { if (incoming_etag && incoming_etag === etag) {
response.writeHead(304, headers); response.writeHead(304, headers);
response.end(); response.end();
return; return;
}
} }
if (result.redirect) { if (result.redirect) {
@ -87,12 +83,16 @@ module.exports = function(request, response, result) {
if (result.status === -2) { if (result.status === -2) {
response.writeHead(result.code || 422, headers); response.writeHead(result.code || 422, headers);
response.end(result.body);
} else if (result.status === -1) { } else if (result.status === -1) {
response.writeHead(500, headers); response.writeHead(500, headers);
response.end(result.body);
} else { } else {
response.writeHead(result.body ? 200 : 404, headers); if (result.body) {
response.end(result.body); headers.Etag = etag;
response.writeHead(200, headers);
} else {
response.writeHead(404, headers);
}
} }
response.end(result.body);
}; };

View File

@ -7,7 +7,8 @@ var url = require("url");
function handle_default(img_status, userId, size, def, req, err, callback) { function handle_default(img_status, userId, size, def, req, err, callback) {
def = def || skins.default_skin(userId); def = def || skins.default_skin(userId);
if (def !== "steve" && def !== "mhf_steve" && def !== "alex" && def !== "mhf_alex") { var defname = def.toLowerCase();
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
if (helpers.id_valid(def)) { if (helpers.id_valid(def)) {
// clean up the old URL to match new image // clean up the old URL to match new image
var parsed = req.url; var parsed = req.url;
@ -29,7 +30,7 @@ function handle_default(img_status, userId, size, def, req, err, callback) {
} }
} else { } else {
// handle steve and alex // handle steve and alex
def = def.toLowerCase(); def = defname;
if (def.substr(0, 4) !== "mhf_") { if (def.substr(0, 4) !== "mhf_") {
def = "mhf_" + def; def = "mhf_" + def;
} }

View File

@ -12,7 +12,8 @@ var fs = require("fs");
// overlay is query param // overlay is query param
function handle_default(rid, scale, overlay, body, img_status, userId, size, def, req, err, callback) { function handle_default(rid, scale, overlay, body, img_status, userId, size, def, req, err, callback) {
def = def || skins.default_skin(userId); def = def || skins.default_skin(userId);
if (def !== "steve" && def !== "mhf_steve" && def !== "alex" && def !== "mhf_alex") { var defname = def.toLowerCase();
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
if (helpers.id_valid(def)) { if (helpers.id_valid(def)) {
// clean up the old URL to match new image // clean up the old URL to match new image
var parsed = req.url; var parsed = req.url;
@ -34,13 +35,13 @@ function handle_default(rid, scale, overlay, body, img_status, userId, size, def
} }
} else { } else {
// handle steve and alex // handle steve and alex
def = def.toLowerCase(); def = defname;
if (def.substr(0, 4) !== "mhf_") { if (def.substr(0, 4) !== "mhf_") {
def = "mhf_" + def; def = "mhf_" + def;
} }
fs.readFile(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function (fs_err, buf) { fs.readFile(path.join(__dirname, "..", "public", "images", def + "_skin.png"), function(fs_err, buf) {
// we render the default skins, but not custom images // we render the default skins, but not custom images
renders.draw_model(rid, buf, scale, overlay, body, function(render_err, def_img) { renders.draw_model(rid, buf, scale, overlay, body, def === "mhf_alex", function(render_err, def_img) {
callback({ callback({
status: img_status, status: img_status,
body: def_img, body: def_img,
@ -55,7 +56,7 @@ function handle_default(rid, scale, overlay, body, img_status, userId, size, def
// GET render request // GET render request
module.exports = function(req, callback) { module.exports = function(req, callback) {
var raw_type = (req.url.path_list[1] || ""); var raw_type = req.url.path_list[1] || "";
var rid = req.id; var rid = req.id;
var body = raw_type === "body"; var body = raw_type === "body";
var userId = (req.url.path_list[2] || "").split(".")[0]; var userId = (req.url.path_list[2] || "").split(".")[0];

View File

@ -8,7 +8,8 @@ var url = require("url");
function handle_default(img_status, userId, def, req, err, callback) { function handle_default(img_status, userId, def, req, err, callback) {
def = def || skins.default_skin(userId); def = def || skins.default_skin(userId);
if (def !== "steve" && def !== "mhf_steve" && def !== "alex" && def !== "mhf_alex") { var defname = def.toLowerCase();
if (defname !== "steve" && defname !== "mhf_steve" && defname !== "alex" && defname !== "mhf_alex") {
if (helpers.id_valid(def)) { if (helpers.id_valid(def)) {
// clean up the old URL to match new image // clean up the old URL to match new image
var parsed = req.url; var parsed = req.url;
@ -30,7 +31,7 @@ function handle_default(img_status, userId, def, req, err, callback) {
} }
} else { } else {
// handle steve and alex // handle steve and alex
def = def.toLowerCase(); def = defname;
if (def.substr(0, 4) !== "mhf_") { if (def.substr(0, 4) !== "mhf_") {
def = "mhf_" + def; def = "mhf_" + def;
} }
@ -83,7 +84,7 @@ module.exports = function(req, callback) {
userId = userId.replace(/-/g, ""); userId = userId.replace(/-/g, "");
try { try {
helpers.get_skin(rid, userId, function(err, hash, status, image) { helpers.get_skin(rid, userId, function(err, hash, status, image, slim) {
if (err) { if (err) {
if (err.code === "ENOENT") { if (err.code === "ENOENT") {
// no such file // no such file

View File

@ -104,8 +104,12 @@ exp.resize_img = function(inname, size, callback) {
// returns "mhf_alex" or "mhf_steve" calculated by the +uuid+ // returns "mhf_alex" or "mhf_steve" calculated by the +uuid+
exp.default_skin = function(uuid) { exp.default_skin = function(uuid) {
if (uuid.length <= 16) { if (uuid.length <= 16) {
// we can't get the skin type by username if (uuid.toLowerCase() === "mhf_alex") {
return "mhf_steve"; return uuid;
} else {
// we can't get the skin type by username
return "mhf_steve";
}
} else { } else {
// great thanks to Minecrell for research into Minecraft and Java's UUID hashing! // great thanks to Minecrell for research into Minecraft and Java's UUID hashing!
// https://git.io/xJpV // https://git.io/xJpV

View File

@ -34,7 +34,7 @@
"iojs": "2.0.x" "iojs": "2.0.x"
}, },
"dependencies": { "dependencies": {
"canvas": "^1.2.9", "canvas": "^1.3.4",
"crc": "~3.3.0", "crc": "~3.3.0",
"ejs": "^2.3.4", "ejs": "^2.3.4",
"lwip": "~0.0.7", "lwip": "~0.0.7",

View File

@ -17,7 +17,7 @@ var fs = require("fs");
config.server.http_timeout *= 3; config.server.http_timeout *= 3;
// no spam // no spam
if (process.env.VERBOSE_TEST !== "true") { if (process.env.VERBOSE_TEST !== "true" && process.env.TRAVIS !== "true") {
logging.log = logging.debug = logging.warn = logging.error = function() {}; logging.log = logging.debug = logging.warn = logging.error = function() {};
} }
@ -52,7 +52,10 @@ var alex_ids = [
"fffffff1" + "fffffff1" + "fffffff1" + "fffffff0", "fffffff1" + "fffffff1" + "fffffff1" + "fffffff0",
]; ];
var rid = "TestReqID: "; // generates a 12 character random string
function rid() {
return Math.random().toString(36).substring(2, 14);
}
function getRandomInt(min, max) { function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min; return Math.floor(Math.random() * (max - min + 1)) + min;
@ -120,14 +123,14 @@ describe("Crafatar", function() {
}); });
it("should not exist (uuid)", function(done) { it("should not exist (uuid)", function(done) {
var number = getRandomInt(0, 9).toString(); var number = getRandomInt(0, 9).toString();
networking.get_profile(rid, Array(33).join(number), function(err, profile) { networking.get_profile(rid(), Array(33).join(number), function(err, profile) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(profile, null); assert.strictEqual(profile, null);
done(); done();
}); });
}); });
it("should not exist (username)", function(done) { it("should not exist (username)", function(done) {
networking.get_username_url(rid, "Steve", 0, function(err, profile) { networking.get_username_url(rid(), "Steve", 0, function(err, profile) {
assert.ifError(err); assert.ifError(err);
done(); done();
}); });
@ -136,10 +139,10 @@ describe("Crafatar", function() {
describe("Avatar", function() { describe("Avatar", function() {
it("uuid's account should exist, but skin should not", function(done) { it("uuid's account should exist, but skin should not", function(done) {
// profile "Alex" - hoping it'll never have a skin // profile "Alex" - hoping it'll never have a skin
networking.get_profile(rid, "ec561538f3fd461daff5086b22154bce", function(err, profile) { networking.get_profile(rid(), "ec561538f3fd461daff5086b22154bce", function(err, profile) {
assert.ifError(err); assert.ifError(err);
assert.notStrictEqual(profile, null); assert.notStrictEqual(profile, null);
networking.get_uuid_url(profile, 1, function(url) { networking.get_uuid_info(profile, "CAPE", function(url) {
assert.strictEqual(url, null); assert.strictEqual(url, null);
done(); done();
}); });
@ -172,7 +175,7 @@ describe("Crafatar", function() {
it("should time out on uuid info download", function(done) { it("should time out on uuid info download", function(done) {
var original_timeout = config.server.http_timeout; var original_timeout = config.server.http_timeout;
config.server.http_timeout = 1; config.server.http_timeout = 1;
networking.get_profile(rid, "069a79f444e94726a5befca90e38aaf5", function(err, profile) { networking.get_profile(rid(), "069a79f444e94726a5befca90e38aaf5", function(err, profile) {
assert.strictEqual(err.code, "ETIMEDOUT"); assert.strictEqual(err.code, "ETIMEDOUT");
config.server.http_timeout = original_timeout; config.server.http_timeout = original_timeout;
done(); done();
@ -181,7 +184,7 @@ describe("Crafatar", function() {
it("should time out on username info download", function(done) { it("should time out on username info download", function(done) {
var original_timeout = config.server.http_timeout; var original_timeout = config.server.http_timeout;
config.server.http_timeout = 1; config.server.http_timeout = 1;
networking.get_username_url(rid, "jomo", 0, function(err, url) { networking.get_username_url(rid(), "jomo", 0, function(err, url) {
assert.strictEqual(err.code, "ETIMEDOUT"); assert.strictEqual(err.code, "ETIMEDOUT");
config.server.http_timeout = original_timeout; config.server.http_timeout = original_timeout;
done(); done();
@ -190,7 +193,7 @@ describe("Crafatar", function() {
it("should time out on skin download", function(done) { it("should time out on skin download", function(done) {
var original_timeout = config.http_timeout; var original_timeout = config.http_timeout;
config.server.http_timeout = 1; config.server.http_timeout = 1;
networking.get_from(rid, "http://textures.minecraft.net/texture/477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) { networking.get_from(rid(), "http://textures.minecraft.net/texture/477be35554684c28bdeee4cf11c591d3c88afb77e0b98da893fd7bc318c65184", function(body, res, error) {
assert.strictEqual(error.code, "ETIMEDOUT"); assert.strictEqual(error.code, "ETIMEDOUT");
config.server.http_timeout = original_timeout; config.server.http_timeout = original_timeout;
done(); done();
@ -198,14 +201,14 @@ describe("Crafatar", function() {
}); });
it("should not find the skin", function(done) { it("should not find the skin", function(done) {
assert.doesNotThrow(function() { assert.doesNotThrow(function() {
networking.get_from(rid, "http://textures.minecraft.net/texture/this-does-not-exist", function(img, response, err) { networking.get_from(rid(), "http://textures.minecraft.net/texture/this-does-not-exist", function(img, response, err) {
assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions assert.strictEqual(err, null); // no error here, but it shouldn't throw exceptions
done(); done();
}); });
}); });
}); });
it("should not find the file", function(done) { it("should not find the file", function(done) {
skins.open_skin(rid, "non/existent/path", function(err, img) { skins.open_skin(rid(), "non/existent/path", function(err, img) {
assert(err); assert(err);
done(); done();
}); });
@ -233,7 +236,6 @@ describe("Crafatar", function() {
assert.ifError(error); assert.ifError(error);
assert.ifError(body); assert.ifError(body);
assert.equal(res.statusCode, 304); assert.equal(res.statusCode, 304);
assert(res.headers.etag);
assert_headers(res); assert_headers(res);
callback(); callback();
}); });
@ -338,440 +340,396 @@ describe("Crafatar", function() {
var server_tests = { var server_tests = {
"avatar with existing username": { "avatar with existing username": {
url: "http://localhost:3000/avatars/jeb_?size=16", url: "http://localhost:3000/avatars/jeb_?size=16",
etag: '"a846b82963"', crc32: [1623808067]
crc32: 1623808067
}, },
"avatar with non-existent username": { "avatar with non-existent username": {
url: "http://localhost:3000/avatars/0?size=16", url: "http://localhost:3000/avatars/0?size=16",
etag: '"mhf_steve"',
crc32: [2416827277, 1243826040] crc32: [2416827277, 1243826040]
}, },
"avatar with non-existent username defaulting to alex": { "avatar with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/avatars/0?size=16&default=mhf_alex", url: "http://localhost:3000/avatars/0?size=16&default=mhf_alex",
etag: '"mhf_alex"',
crc32: [862751081, 809395677] crc32: [862751081, 809395677]
}, },
"avatar with non-existent username defaulting to username": { "avatar with non-existent username defaulting to username": {
url: "http://localhost:3000/avatars/0?size=16&default=jeb_", url: "http://localhost:3000/avatars/0?size=16&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/avatars/jeb_?size=16" redirect: "/avatars/jeb_?size=16"
}, },
"avatar with non-existent username defaulting to uuid": { "avatar with non-existent username defaulting to uuid": {
url: "http://localhost:3000/avatars/0?size=16&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/avatars/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16" redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
}, },
"avatar with non-existent username defaulting to url": { "avatar with non-existent username defaulting to url": {
url: "http://localhost:3000/avatars/0?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/avatars/0?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay avatar with existing username": { "overlay avatar with existing username": {
url: "http://localhost:3000/avatars/jeb_?size=16&overlay", url: "http://localhost:3000/avatars/jeb_?size=16&overlay",
etag: '"a846b82963"', crc32: [646871998]
crc32: 646871998
}, },
"overlay avatar with non-existent username": { "overlay avatar with non-existent username": {
url: "http://localhost:3000/avatars/0?size=16&overlay", url: "http://localhost:3000/avatars/0?size=16&overlay",
etag: '"mhf_steve"',
crc32: [2416827277, 1243826040] crc32: [2416827277, 1243826040]
}, },
"overlay avatar with non-existent username defaulting to alex": { "overlay avatar with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/avatars/0?size=16&overlay&default=mhf_alex", url: "http://localhost:3000/avatars/0?size=16&overlay&default=mhf_alex",
etag: '"mhf_alex"',
crc32: [862751081, 809395677] crc32: [862751081, 809395677]
}, },
"overlay avatar with non-existent username defaulting to username": { "overlay avatar with non-existent username defaulting to username": {
url: "http://localhost:3000/avatars/0?size=16&overlay&default=jeb_", url: "http://localhost:3000/avatars/0?size=16&overlay&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/avatars/jeb_?size=16&overlay=" redirect: "/avatars/jeb_?size=16&overlay="
}, },
"overlay avatar with non-existent username defaulting to uuid": { "overlay avatar with non-existent username defaulting to uuid": {
url: "http://localhost:3000/avatars/0?size=16&overlay&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/avatars/0?size=16&overlay&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay=" redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay="
}, },
"overlay avatar with non-existent username defaulting to url": { "overlay avatar with non-existent username defaulting to url": {
url: "http://localhost:3000/avatars/0?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/avatars/0?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"avatar with existing uuid": { "avatar with existing uuid": {
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16", url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16",
etag: '"a846b82963"', crc32: [1623808067]
crc32: 1623808067
}, },
"avatar with non-existent uuid": { "avatar with non-existent uuid": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16",
etag: '"mhf_steve"',
crc32: [2416827277, 1243826040] crc32: [2416827277, 1243826040]
}, },
"avatar with non-existent uuid defaulting to alex": { "avatar with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=mhf_alex",
etag: '"mhf_alex"',
crc32: [862751081, 809395677] crc32: [862751081, 809395677]
}, },
"avatar with non-existent uuid defaulting to username": { "avatar with non-existent uuid defaulting to username": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/avatars/jeb_?size=16" redirect: "/avatars/jeb_?size=16"
}, },
"avatar with non-existent uuid defaulting to uuid": { "avatar with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16" redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
}, },
"avatar with non-existent uuid defaulting to url": { "avatar with non-existent uuid defaulting to url": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay avatar with existing uuid": { "overlay avatar with existing uuid": {
url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay", url: "http://localhost:3000/avatars/853c80ef3c3749fdaa49938b674adae6?size=16&overlay",
etag: '"a846b82963"', crc32: [646871998]
crc32: 646871998
}, },
"overlay avatar with non-existent uuid": { "overlay avatar with non-existent uuid": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay",
etag: '"mhf_steve"',
crc32: [2416827277, 1243826040] crc32: [2416827277, 1243826040]
}, },
"overlay avatar with non-existent uuid defaulting to alex": { "overlay avatar with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=mhf_alex",
etag: '"mhf_alex"',
crc32: [862751081, 809395677] crc32: [862751081, 809395677]
}, },
"overlay avatar with non-existent uuid defaulting to username": { "overlay avatar with non-existent uuid defaulting to username": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/avatars/jeb_?size=16" redirect: "/avatars/jeb_?size=16"
}, },
"overlay avatar with non-existent uuid defaulting to uuid": { "overlay avatar with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16" redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?size=16"
}, },
"overlay avatar with non-existent uuid defaulting to url": { "overlay avatar with non-existent uuid defaulting to url": {
url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/avatars/00000000000000000000000000000000?size=16&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"cape with existing username": { "cape with existing username": {
url: "http://localhost:3000/capes/jeb_", url: "http://localhost:3000/capes/jeb_",
etag: '"3f688e0e69"',
crc32: [989800403, 1901140141] crc32: [989800403, 1901140141]
}, },
"cape with non-existent username": { "cape with non-existent username": {
url: "http://localhost:3000/capes/0", url: "http://localhost:3000/capes/0",
crc32: 0 crc32: [0]
}, },
"cape with non-existent username defaulting to url": { "cape with non-existent username defaulting to url": {
url: "http://localhost:3000/capes/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/capes/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"cape with existing uuid": { "cape with existing uuid": {
url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/capes/853c80ef3c3749fdaa49938b674adae6",
etag: '"3f688e0e69"',
crc32: [989800403, 1901140141] crc32: [989800403, 1901140141]
}, },
"cape with non-existent uuid": { "cape with non-existent uuid": {
url: "http://localhost:3000/capes/00000000000000000000000000000000", url: "http://localhost:3000/capes/00000000000000000000000000000000",
crc32: 0 crc32: [0]
}, },
"cape with non-existent uuid defaulting to url": { "cape with non-existent uuid defaulting to url": {
url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/capes/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"skin with existing username": { "skin with existing username": {
url: "http://localhost:3000/skins/jeb_", url: "http://localhost:3000/skins/jeb_",
etag: '"a846b82963"', crc32: [26500336]
crc32: 26500336
}, },
"skin with non-existent username": { "skin with non-existent username": {
url: "http://localhost:3000/skins/0", url: "http://localhost:3000/skins/0",
etag: '"mhf_steve"', crc32: [981937087]
crc32: 981937087
}, },
"skin with non-existent username defaulting to alex": { "skin with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/skins/0?default=mhf_alex", url: "http://localhost:3000/skins/0?default=mhf_alex",
etag: '"mhf_alex"', crc32: [2298915739]
crc32: 2298915739
}, },
"skin with non-existent username defaulting to username": { "skin with non-existent username defaulting to username": {
url: "http://localhost:3000/skins/0?size=16&default=jeb_", url: "http://localhost:3000/skins/0?size=16&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/skins/jeb_?size=16" redirect: "/skins/jeb_?size=16"
}, },
"skin with non-existent username defaulting to uuid": { "skin with non-existent username defaulting to uuid": {
url: "http://localhost:3000/skins/0?size=16&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/skins/0?size=16&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16" redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
}, },
"skin with non-existent username defaulting to url": { "skin with non-existent username defaulting to url": {
url: "http://localhost:3000/skins/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/skins/0?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"skin with existing uuid": { "skin with existing uuid": {
url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/skins/853c80ef3c3749fdaa49938b674adae6",
etag: '"a846b82963"', crc32: [26500336]
crc32: 26500336
}, },
"skin with non-existent uuid": { "skin with non-existent uuid": {
url: "http://localhost:3000/skins/00000000000000000000000000000000", url: "http://localhost:3000/skins/00000000000000000000000000000000",
etag: '"mhf_steve"', crc32: [981937087]
crc32: 981937087
}, },
"skin with non-existent uuid defaulting to alex": { "skin with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex", url: "http://localhost:3000/skins/00000000000000000000000000000000?default=mhf_alex",
etag: '"mhf_alex"', crc32: [2298915739]
crc32: 2298915739
}, },
"skin with non-existent uuid defaulting to username": { "skin with non-existent uuid defaulting to username": {
url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=jeb_", url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/skins/jeb_?size=16" redirect: "/skins/jeb_?size=16"
}, },
"skin with non-existent uuid defaulting to uuid": { "skin with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/skins/00000000000000000000000000000000?size=16&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16" redirect: "/skins/853c80ef3c3749fdaa49938b674adae6?size=16"
}, },
"skin with non-existent uuid defaulting to url": { "skin with non-existent uuid defaulting to url": {
url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/skins/00000000000000000000000000000000?default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"head render with existing username": { "head render with existing username": {
url: "http://localhost:3000/renders/head/jeb_?scale=2", url: "http://localhost:3000/renders/head/jeb_?scale=2",
etag: '"a846b82963"', crc32: [3487896679, 3001090792]
crc32: [1743362302, 208074514, 2506366593]
}, },
"head render with non-existent username": { "head render with non-existent username": {
url: "http://localhost:3000/renders/head/0?scale=2", url: "http://localhost:3000/renders/head/0?scale=2",
etag: '"mhf_steve"', crc32: [3257141069, 214248305]
crc32: [897270661, 1026982335, 1726107733]
}, },
"head render with non-existent username defaulting to alex": { "head render with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/renders/head/0?scale=2&default=mhf_alex", url: "http://localhost:3000/renders/head/0?scale=2&default=mhf_alex",
etag: '"mhf_alex"', crc32: [263450586, 3116770561]
crc32: [2357619670, 3172866498, 2214491831]
}, },
"head render with non-existent username defaulting to username": { "head render with non-existent username defaulting to username": {
url: "http://localhost:3000/avatars/0?scale=2&default=jeb_", url: "http://localhost:3000/avatars/0?scale=2&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/avatars/jeb_?scale=2" redirect: "/avatars/jeb_?scale=2"
}, },
"head render with non-existent username defaulting to uuid": { "head render with non-existent username defaulting to uuid": {
url: "http://localhost:3000/avatars/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/avatars/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?scale=2" redirect: "/avatars/853c80ef3c3749fdaa49938b674adae6?scale=2"
}, },
"head render with non-existent username defaulting to url": { "head render with non-existent username defaulting to url": {
url: "http://localhost:3000/renders/head/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/head/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay head render with existing username": { "overlay head render with existing username": {
url: "http://localhost:3000/renders/head/jeb_?scale=2&overlay", url: "http://localhost:3000/renders/head/jeb_?scale=2&overlay",
etag: '"a846b82963"', crc32: [762377383, 1726474987]
crc32: [4178514320, 2340078566, 3980890516]
}, },
"overlay head render with non-existent username": { "overlay head render with non-existent username": {
url: "http://localhost:3000/renders/head/0?scale=2&overlay", url: "http://localhost:3000/renders/head/0?scale=2&overlay",
etag: '"mhf_steve"', crc32: [3257141069, 214248305]
crc32: [507497693, 3868868707, 7372195]
}, },
"overlay head render with non-existent username defaulting to alex": { "overlay head render with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=mhf_alex", url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=mhf_alex",
etag: '"mhf_alex"', crc32: [263450586, 3116770561]
crc32: [891113664, 1785326216, 622500655]
}, },
"overlay head render with non-existent username defaulting to username": { "overlay head render with non-existent username defaulting to username": {
url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=jeb_", url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/head/jeb_?scale=2&overlay=" redirect: "/renders/head/jeb_?scale=2&overlay="
}, },
"overlay head render with non-existent username defaulting to uuid": { "overlay head render with non-existent username defaulting to uuid": {
url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=" redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
}, },
"overlay head render with non-existent username defaulting to url": { "overlay head render with non-existent username defaulting to url": {
url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/head/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"head render with existing uuid": { "head render with existing uuid": {
url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2", url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2",
etag: '"a846b82963"', crc32: [3487896679, 3001090792]
crc32: [1743362302, 208074514, 2506366593]
}, },
"head render with non-existent uuid": { "head render with non-existent uuid": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2",
etag: '"mhf_steve"', crc32: [3257141069, 214248305]
crc32: [897270661, 1026982335, 1726107733]
}, },
"head render with non-existent uuid defaulting to alex": { "head render with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=mhf_alex", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=mhf_alex",
etag: '"mhf_alex"', crc32: [263450586, 3116770561]
crc32: [2357619670, 3172866498, 2214491831]
}, },
"head render with non-existent uuid defaulting to username": { "head render with non-existent uuid defaulting to username": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=jeb_", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/head/jeb_?scale=2" redirect: "/renders/head/jeb_?scale=2"
}, },
"head render with non-existent uuid defaulting to uuid": { "head render with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2" redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2"
}, },
"head render with non-existent uuid defaulting to url": { "head render with non-existent uuid defaulting to url": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay head render with existing uuid": { "overlay head render with existing uuid": {
url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay", url: "http://localhost:3000/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
etag: '"a846b82963"', crc32: [762377383, 1726474987]
crc32: [4178514320, 2340078566, 3980890516]
}, },
"overlay head render with non-existent uuid": { "overlay head render with non-existent uuid": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay",
etag: '"mhf_steve"', crc32: [3257141069, 214248305]
crc32: [507497693, 3868868707, 7372195]
}, },
"overlay head render with non-existent uuid defaulting to alex": { "overlay head render with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
etag: '"mhf_alex"', crc32: [263450586, 3116770561]
crc32: [891113664, 1785326216, 622500655]
}, },
"overlay head with non-existent uuid defaulting to username": { "overlay head with non-existent uuid defaulting to username": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=jeb_", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/head/jeb_?scale=2&overlay=" redirect: "/renders/head/jeb_?scale=2&overlay="
}, },
"overlay head with non-existent uuid defaulting to uuid": { "overlay head with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=" redirect: "/renders/head/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
}, },
"overlay head render with non-existent uuid defaulting to url": { "overlay head render with non-existent uuid defaulting to url": {
url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/head/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"body render with existing username": { "body render with existing username": {
url: "http://localhost:3000/renders/body/jeb_?scale=2", url: "http://localhost:3000/renders/body/jeb_?scale=2",
etag: '"a846b82963"', crc32: [3127075871, 2595192206]
crc32: [1023392610, 4127764743, 3884408742]
}, },
"body render with non-existent username": { "body render with non-existent username": {
url: "http://localhost:3000/renders/body/0?scale=2", url: "http://localhost:3000/renders/body/0?scale=2",
etag: '"mhf_steve"', crc32: [1046655221, 1620063267]
crc32: [3559591930, 3663447404, 1521463481]
}, },
"body render with non-existent username defaulting to alex": { "body render with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/renders/body/0?scale=2&default=mhf_alex", url: "http://localhost:3000/renders/body/0?scale=2&default=mhf_alex",
etag: '"mhf_alex"', crc32: [549240598, 3952648540]
crc32: [470529151, 1823026927, 2079926997]
}, },
"body render with non-existent username defaulting to username": { "body render with non-existent username defaulting to username": {
url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_", url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/body/jeb_?scale=2" redirect: "/renders/body/jeb_?scale=2"
}, },
"body render with non-existent username defaulting to uuid": { "body render with non-existent username defaulting to uuid": {
url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2" redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
}, },
"body render with non-existent username defaulting to url": { "body render with non-existent username defaulting to url": {
url: "http://localhost:3000/renders/body/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/body/0?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay body render with existing username": { "overlay body render with existing username": {
url: "http://localhost:3000/renders/body/jeb_?scale=2&overlay", url: "http://localhost:3000/renders/body/jeb_?scale=2&overlay",
etag: '"a846b82963"', crc32: [699892097, 2732138694]
crc32: [3476579592, 97705180, 3086172613]
}, },
"overlay body render with non-existent username": { "overlay body render with non-existent username": {
url: "http://localhost:3000/renders/body/0?scale=2&overlay", url: "http://localhost:3000/renders/body/0?scale=2&overlay",
etag: '"mhf_steve"', crc32: [1046655221, 1620063267]
crc32: [3992841063, 1025743887, 1906839968]
}, },
"overlay body render with non-existent username defaulting to alex": { "overlay body render with non-existent username defaulting to mhf_alex": {
url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=mhf_alex", url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=mhf_alex",
etag: '"mhf_alex"', crc32: [549240598, 3952648540]
crc32: [3317518715, 3621585514, 294661951]
}, },
"overlay body render with non-existent username defaulting to username": { "overlay body render with non-existent username defaulting to username": {
url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=jeb_", url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/body/jeb_?scale=2&overlay=" redirect: "/renders/body/jeb_?scale=2&overlay="
}, },
"overlay body render with non-existent username defaulting to uuid": { "overlay body render with non-existent username defaulting to uuid": {
url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay=" redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay="
}, },
"overlay body render with non-existent username defaulting to url": { "overlay body render with non-existent username defaulting to url": {
url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/body/0?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"body render with existing uuid": { "body render with existing uuid": {
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2", url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2",
etag: '"a846b82963"', crc32: [3127075871, 2595192206]
crc32: [1023392610, 4127764743, 3884408742]
}, },
"body render with non-existent uuid": { "body render with non-existent uuid": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2",
etag: '"mhf_steve"', crc32: [1046655221, 1620063267]
crc32: [3559591930, 3663447404, 1521463481]
}, },
"body render with non-existent uuid defaulting to alex": { "body render with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=mhf_alex",
etag: '"mhf_alex"', crc32: [549240598, 3952648540]
crc32: [470529151, 1823026927, 2079926997]
}, },
"body render with non-existent uuid defaulting to username": { "body render with non-existent uuid defaulting to username": {
url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_", url: "http://localhost:3000/renders/body/0?scale=2&default=jeb_",
crc32: 0, crc32: [0],
redirect: "/renders/body/jeb_?scale=2" redirect: "/renders/body/jeb_?scale=2"
}, },
"body render with non-existent uuid defaulting to uuid": { "body render with non-existent uuid defaulting to uuid": {
url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6", url: "http://localhost:3000/renders/body/0?scale=2&default=853c80ef3c3749fdaa49938b674adae6",
crc32: 0, crc32: [0],
redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2" redirect: "/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2"
}, },
"body render with non-existent uuid defaulting to url": { "body render with non-existent uuid defaulting to url": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
"overlay body render with existing uuid": { "overlay body render with existing uuid": {
url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay", url: "http://localhost:3000/renders/body/853c80ef3c3749fdaa49938b674adae6?scale=2&overlay",
etag: '"a846b82963"', crc32: [699892097, 2732138694]
crc32: [3476579592, 97705180, 3086172613]
}, },
"overlay body render with non-existent uuid": { "overlay body render with non-existent uuid": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay",
etag: '"mhf_steve"', crc32: [1046655221, 1620063267]
crc32: [3992841063, 1025743887, 1906839968]
}, },
"overlay body render with non-existent uuid defaulting to alex": { "overlay body render with non-existent uuid defaulting to mhf_alex": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=mhf_alex",
etag: '"mhf_alex"', crc32: [549240598, 3952648540]
crc32: [3317518715, 3621585514, 294661951]
}, },
"overlay body render with non-existent uuid defaulting to url": { "overlay body render with non-existent uuid defaulting to url": {
url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive", url: "http://localhost:3000/renders/body/00000000000000000000000000000000?scale=2&overlay&default=http%3A%2F%2Fexample.com%2FCaseSensitive",
crc32: 0, crc32: [0],
redirect: "http://example.com/CaseSensitive" redirect: "http://example.com/CaseSensitive"
}, },
}; };
@ -784,32 +742,30 @@ describe("Crafatar", function() {
assert.ifError(error); assert.ifError(error);
assert_headers(res); assert_headers(res);
assert(res.headers["x-storage-type"]); assert(res.headers["x-storage-type"]);
assert.strictEqual(res.headers.etag, location.etag); var hash = crc(body);
var matches = false; var matches = false;
if (location.crc32 instanceof Array) { for (var c = 0; c < location.crc32.length; c++) {
for (var i = 0; i < location.crc32.length; i++) { if (location.crc32[c] === hash) {
if (location.crc32[i] === crc(body)) { matches = true;
matches = true; break;
break;
}
} }
} else {
matches = location.crc32 === crc(body);
} }
try { try {
assert.ok(matches); assert(matches);
} catch(e) { } catch(e) {
throw new Error(crc(body) + " != " + location.crc32 + " | " + body.toString("base64")); throw new Error(hash + " != " + location.crc32 + " | " + body.toString("base64"));
} }
assert.strictEqual(res.headers.location, location.redirect); assert.strictEqual(res.headers.location, location.redirect);
if (location.etag === undefined) { if (location.crc32[0] === 0) {
assert.strictEqual(res.statusCode, location.redirect ? 307 : 404); assert.strictEqual(res.statusCode, location.redirect ? 307 : 404);
assert.ifError(res.headers.etag); // etag must not be present on non-200
assert.strictEqual(res.headers["content-type"], "text/plain"); assert.strictEqual(res.headers["content-type"], "text/plain");
done(); done();
} else { } else {
assert(res.headers.etag);
assert.strictEqual(res.headers["content-type"], "image/png"); assert.strictEqual(res.headers["content-type"], "image/png");
assert.strictEqual(res.statusCode, 200); assert.strictEqual(res.statusCode, 200);
assert(res.headers.etag);
assert.strictEqual(res.headers.etag, '"' + hash + '"');
assert_cache(location.url, res.headers.etag, function() { assert_cache(location.url, res.headers.etag, function() {
done(); done();
}); });
@ -819,6 +775,22 @@ describe("Crafatar", function() {
}(loc)); }(loc));
} }
it("should update the username skin type on uuid request", function(done) {
/*eslint-disable handle-callback-err */
request.get("http://localhost:3000/renders/body/mhf_alex", function(error, res, body) {
cache.get_details("mhf_alex", function(err, details) {
assert.strictEqual(details.slim, false);
request.get("http://localhost:3000/renders/body/6ab4317889fd490597f60f67d9d76fd9", function(uerror, ures, ubody) {
cache.get_details("mhf_alex", function(cerr, cdetails) {
/*eslint-enable handle-callback-err */
assert.strictEqual(cdetails.slim, true);
done();
});
});
});
});
});
it("should return a 422 (invalid size)", function(done) { it("should return a 422 (invalid size)", function(done) {
var size = config.avatars.max_size + 1; var size = config.avatars.max_size + 1;
request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) { request.get("http://localhost:3000/avatars/Jake_0?size=" + size, function(error, res, body) {
@ -878,13 +850,13 @@ describe("Crafatar", function() {
// we have to make sure that we test both a 32x64 and 64x64 skin // we have to make sure that we test both a 32x64 and 64x64 skin
describe("Networking: Render", function() { describe("Networking: Render", function() {
it("should not fail (username, 32x64 skin)", function(done) { it("should not fail (username, 32x64 skin)", function(done) {
helpers.get_render(rid, "md_5", 6, true, true, function(err, hash, img) { helpers.get_render(rid(), "md_5", 6, true, true, function(err, hash, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
}); });
it("should not fail (username, 64x64 skin)", function(done) { it("should not fail (username, 64x64 skin)", function(done) {
helpers.get_render(rid, "Jake_0", 6, true, true, function(err, hash, img) { helpers.get_render(rid(), "Jake_0", 6, true, true, function(err, hash, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
@ -893,7 +865,7 @@ describe("Crafatar", function() {
describe("Networking: Cape", function() { describe("Networking: Cape", function() {
it("should not fail (guaranteed cape)", function(done) { it("should not fail (guaranteed cape)", function(done) {
helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) { helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
@ -902,13 +874,13 @@ describe("Crafatar", function() {
before(function() { before(function() {
cache.get_redis().flushall(); cache.get_redis().flushall();
}); });
helpers.get_cape(rid, "Dinnerbone", function(err, hash, status, img) { helpers.get_cape(rid(), "Dinnerbone", function(err, hash, status, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
}); });
it("should not be found", function(done) { it("should not be found", function(done) {
helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(img, null); assert.strictEqual(img, null);
done(); done();
@ -918,7 +890,7 @@ describe("Crafatar", function() {
describe("Networking: Skin", function() { describe("Networking: Skin", function() {
it("should not fail", function(done) { it("should not fail", function(done) {
helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
@ -927,7 +899,7 @@ describe("Crafatar", function() {
before(function() { before(function() {
cache.get_redis().flushall(); cache.get_redis().flushall();
}); });
helpers.get_cape(rid, "Jake_0", function(err, hash, status, img) { helpers.get_cape(rid(), "Jake_0", function(err, hash, status, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
@ -948,14 +920,14 @@ describe("Crafatar", function() {
}); });
it("should be downloaded", function(done) { it("should be downloaded", function(done) {
helpers.get_avatar(rid, id, false, 160, function(err, status, image) { helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(status, 2); assert.strictEqual(status, 2);
done(); done();
}); });
}); });
it("should be cached", function(done) { it("should be cached", function(done) {
helpers.get_avatar(rid, id, false, 160, function(err, status, image) { helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(status === 0 || status === 1, true); assert.strictEqual(status === 0 || status === 1, true);
done(); done();
@ -967,7 +939,7 @@ describe("Crafatar", function() {
it("should be checked", function(done) { it("should be checked", function(done) {
var original_cache_time = config.caching.local; var original_cache_time = config.caching.local;
config.caching.local = 0; config.caching.local = 0;
helpers.get_avatar(rid, id, false, 160, function(err, status, image) { helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
assert.ifError(err); assert.ifError(err);
assert.strictEqual(status, 3); assert.strictEqual(status, 3);
config.caching.local = original_cache_time; config.caching.local = original_cache_time;
@ -979,7 +951,7 @@ describe("Crafatar", function() {
describe("Networking: Skin", function() { describe("Networking: Skin", function() {
it("should not fail (uuid)", function(done) { it("should not fail (uuid)", function(done) {
helpers.get_skin(rid, id, function(err, hash, status, img) { helpers.get_skin(rid(), id, function(err, hash, status, img) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });
@ -988,13 +960,13 @@ describe("Crafatar", function() {
describe("Networking: Render", function() { describe("Networking: Render", function() {
it("should not fail (full body)", function(done) { it("should not fail (full body)", function(done) {
helpers.get_render(rid, id, 6, true, true, function(err, hash, img) { helpers.get_render(rid(), id, 6, true, true, function(err, hash, img) {
assert.ifError(err); assert.ifError(err);
done(); done();
}); });
}); });
it("should not fail (only head)", function(done) { it("should not fail (only head)", function(done) {
helpers.get_render(rid, id, 6, true, false, function(err, hash, img) { helpers.get_render(rid(), id, 6, true, false, function(err, hash, img) {
assert.ifError(err); assert.ifError(err);
done(); done();
}); });
@ -1003,7 +975,7 @@ describe("Crafatar", function() {
describe("Networking: Cape", function() { describe("Networking: Cape", function() {
it("should not fail (possible cape)", function(done) { it("should not fail (possible cape)", function(done) {
helpers.get_cape(rid, id, function(err, hash, status, img) { helpers.get_cape(rid(), id, function(err, hash, status, img) {
assert.ifError(err); assert.ifError(err);
done(); done();
}); });
@ -1018,8 +990,8 @@ describe("Crafatar", function() {
if (id_type === "uuid") { if (id_type === "uuid") {
it("uuid should be rate limited", function(done) { it("uuid should be rate limited", function(done) {
networking.get_profile(rid, id, function() { networking.get_profile(rid(), id, function() {
networking.get_profile(rid, id, function(err, profile) { networking.get_profile(rid(), id, function(err, profile) {
assert.strictEqual(err.toString(), "HTTP: 429"); assert.strictEqual(err.toString(), "HTTP: 429");
assert.strictEqual(profile, null); assert.strictEqual(profile, null);
done(); done();
@ -1028,8 +1000,8 @@ describe("Crafatar", function() {
}); });
} else { } else {
it("username should NOT be rate limited (username)", function(done) { it("username should NOT be rate limited (username)", function(done) {
helpers.get_avatar(rid, id, false, 160, function() { helpers.get_avatar(rid(), id, false, 160, function() {
helpers.get_avatar(rid, id, false, 160, function(err, status, image) { helpers.get_avatar(rid(), id, false, 160, function(err, status, image) {
assert.strictEqual(err, null); assert.strictEqual(err, null);
done(); done();
}); });