Skip to content

Commit

Permalink
Allow fetching by texture IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
TibiNonEst committed Mar 28, 2022
1 parent 5f98fd7 commit 06e788f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 27 deletions.
5 changes: 4 additions & 1 deletion worker/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export enum RequestedKind {
// IdentityKind determines if the request is for a UUID or a username.
export enum IdentityKind {
Uuid,
Username
Username,
TextureID
}

export enum TextureKind {
Expand Down Expand Up @@ -95,6 +96,8 @@ export function interpretRequest(request: Request): CraftheadRequest | null {
} else if (identity.length === 36) {
identity = identity.replace(/-/g, '')
identityType = IdentityKind.Uuid
} else if (identity.length === 64) {
identityType = IdentityKind.TextureID
} else {
return null
}
Expand Down
78 changes: 52 additions & 26 deletions worker/services/mojang/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export default class MojangRequestService {
* @param gatherer any promise gatherer
*/
async normalizeRequest(request: CraftheadRequest, gatherer: PromiseGatherer): Promise<CraftheadRequest> {
if (request.identityType === IdentityKind.Uuid) {
if (request.identityType === IdentityKind.Uuid || request.identityType == IdentityKind.TextureID) {
return request;
}

Expand All @@ -61,22 +61,18 @@ export default class MojangRequestService {
* Fetches a texture directly from the Mojang servers. Assumes the request has been normalized already.
*/
private async retrieveTextureDirect(request: CraftheadRequest, gatherer: PromiseGatherer, kind: TextureKind): Promise<Response> {
if (request.identityType == IdentityKind.TextureID) {
const textureResponse = await MojangRequestService.fetchTextureFromId(request.identity);
return MojangRequestService.constructTextureResponse(textureResponse);
}

const rawUuid = fromHex(request.identity);
if (uuidVersion(rawUuid) === 4) {
const lookup = await this.mojangApi.fetchProfile(request.identity, gatherer);
if (lookup.result) {
let textureResponse = await MojangRequestService.fetchTextureFromProfile(lookup.result, kind);
if (textureResponse) {
const buff = await textureResponse.texture.arrayBuffer();
if (buff && buff.byteLength > 0) {
return new Response(buff, {
status: 200,
headers: {
'X-Crafthead-Profile-Cache-Hit': lookup.source,
'X-Crafthead-Skin-Model': request.model || textureResponse.model || 'default'
}
});
}
return MojangRequestService.constructTextureResponse(textureResponse, lookup.source)
}
return new Response(STEVE_SKIN, {
status: 404,
Expand Down Expand Up @@ -104,6 +100,27 @@ export default class MojangRequestService {
});
}

private static async constructTextureResponse(textureResponse: TextureResponse, source?: string): Promise<Response> {
const buff = await textureResponse.texture.arrayBuffer();
if (buff && buff.byteLength > 0) {
return new Response(buff, {
status: 200,
headers: {
'X-Crafthead-Profile-Cache-Hit': source || 'miss',
'X-Crafthead-Skin-Model': textureResponse.model || 'default'
}
});
} else {
return new Response(STEVE_SKIN, {
status: 404,
headers: {
'X-Crafthead-Profile-Cache-Hit': 'not-found',
'X-Crafthead-Skin-Model': 'default'
}
});
}
}

async retrieveSkin(request: CraftheadRequest, gatherer: PromiseGatherer): Promise<Response> {
if (request.identity === 'char' || request.identity === 'MHF_Steve') {
// These are special-cased by Minotar.
Expand Down Expand Up @@ -146,28 +163,37 @@ export default class MojangRequestService {
const textureUrl = type === TextureKind.CAPE ? texturesData?.CAPE?.url : texturesData?.SKIN.url;

if (textureUrl) {
const textureResponse = await fetch(textureUrl, {
cf: {
cacheEverything: true,
cacheTtl: 86400
},
headers: {
'User-Agent': 'Crafthead (+https://crafthead.net)'
}
});
if (!textureResponse.ok) {
throw new Error(`Unable to retrieve texture from Mojang, http status ${textureResponse.status}`);
}

console.log("Successfully retrieved texture");
return { texture: textureResponse, model: texturesData?.SKIN?.metadata?.model };
return this.fetchTextureFromUrl(textureUrl);
}
}

console.log("Invalid properties found! Falling back to a default texture.")
return undefined;
}

private static async fetchTextureFromId(id: string): Promise<TextureResponse> {
const url = `https://textures.minecraft.net/texture/${id}`
return this.fetchTextureFromUrl(url);
}

private static async fetchTextureFromUrl(textureUrl: string): Promise<TextureResponse> {
const textureResponse = await fetch(textureUrl, {
cf: {
cacheEverything: true,
cacheTtl: 86400
},
headers: {
'User-Agent': 'Crafthead (+https://crafthead.net)'
}
});
if (!textureResponse.ok) {
throw new Error(`Unable to retrieve texture from Mojang, http status ${textureResponse.status}`);
}

console.log("Successfully retrieved texture");
return { texture: textureResponse, model: "default" };
}

async fetchProfile(request: CraftheadRequest, gatherer: PromiseGatherer): Promise<CacheComputeResult<MojangProfile | null>> {
const normalized = await this.normalizeRequest(request, gatherer);
if (!normalized.identity || uuidVersion(fromHex(normalized.identity)) === 3) {
Expand Down

0 comments on commit 06e788f

Please sign in to comment.