Last active
March 14, 2022 14:57
-
-
Save timvisee/7875117 to your computer and use it in GitHub Desktop.
MCDragonRealms Minecraft player head avatar class.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* MCDragonRealms Minecraft player head avatar class. | |
* | |
* This class is able to generate player avatars based on any Minecraft player skin. | |
* | |
* Use the `p` GET param when doing direct HTTP requests to this file to set the avatar type, size and username. (p=TYPE/SIZE/USERNAME) | |
* | |
* Current supported types are (as you can see at the bottom of the script): | |
* helm | |
* head | |
* combo | |
* player | |
* | |
* Use the following four methods to print a player's avatar: | |
* Head::printHelm($username = "char", $size = 16); // Player head with helm | |
* Head::printHead($username = "char", $size = 16); // Player head without helm | |
* Head::printCombo($username = "char", $size = 16); // Player head with helm, with the player's skin printed on top of it | |
* Head::printPlayer($username = "char", $size = 16, $border = false); // Players skin | |
*/ | |
// Define the cache directory to cache the player avatars in | |
define("CACHE_DIR", dirname(__FILE__) . '/cache'); | |
/** | |
* Head class. This class is able to generate player avatars based on any Minecraft player skin. | |
* | |
* @author Tim Visee | |
* @namespace core\head | |
* @copyright Copyright (c) MCDragonRealms 2010-2013. All Rights Reserved. | |
*/ | |
class Head { | |
// Some constants | |
const SKIN_DEFAULT = "char"; | |
const SKIN_HOST = "http://s3.amazonaws.com/MinecraftSkins/"; | |
const CACHE_MAX_AGE = 86400; // 24 hours in seconds | |
/** | |
* Check whether a username is valid | |
* @param $username String Username to check | |
* @return boolean True if the username is valid | |
*/ | |
public static function isValidUsername($username) { | |
// Make sure any name is set | |
if(empty($username) || $username == null) | |
return false; | |
// Make sure the username has valid characters | |
if(preg_match('/[^a-zA-Z0-9_]/', $username) != 0) | |
return false; | |
// Make sure the username has a valid length | |
if(strlen($username) < 2 || strlen($username) > 16) | |
return false; | |
// The username seems to be valid | |
return true; | |
} | |
/** | |
* Try to make a valid username out of a username string, returns 'char' as default | |
* @param $username String Username to parse | |
* @return String Parsed username, 'char' as default | |
*/ | |
public static function parseUsername($username) { | |
// Trim the username from unwanted whitespaces | |
$username = trim($username); | |
// Remove strange characters being added because of a Multicraft bug | |
$username = str_replace("[m", "", $username); | |
// Make sure the username is valid, if so, return the parsed username | |
if(self::isValidUsername($username)) | |
return $username; | |
// The username was invalid, return the default username/skin | |
return self::SKIN_DEFAULT; | |
} | |
/** | |
* Get the skin of a Minecraft player | |
* @param $username String Minecraft username to get the skin from (steve skin by default) | |
* @return PHP Image | |
*/ | |
public static function getSkinImage($username = "char") { | |
// Parse the username | |
$username = self::parseUsername($username); | |
// Check inside the cache | |
$path = CACHE_DIR . "/skin/" . $username . ".png"; | |
if(file_exists($path)) { | |
if((time() - filemtime($path)) < self::CACHE_MAX_AGE) { | |
return imagecreatefrompng($path); | |
} | |
} | |
// Retrieve the skin image | |
$img = imagecreatefrompng(self::SKIN_HOST . $username . ".png"); | |
imagealphablending($img, true); | |
imagesavealpha($img, true); | |
// Cache the data | |
if(!file_exists(dirname($path))) | |
mkdir(dirname($path), 0777, true); | |
imagepng($img, $path, 0); | |
// Return the image | |
return $img; | |
} | |
/** | |
* Get the head image of a Minecraft player | |
* @param $username String Minecraft username to get the head from (steve by default) | |
* @param $size Image size (8 by default) | |
* @return PHP Image with the player's head | |
*/ | |
public static function getHeadImage($username = "char", $size = 8) { | |
// Parse the username | |
$username = self::parseUsername($username); | |
// Check inside the cache | |
$path = CACHE_DIR . "/head/" . $size . "/" . $username . ".png"; | |
if(file_exists($path)) { | |
if((time() - filemtime($path)) < self::CACHE_MAX_AGE) { | |
return imagecreatefrompng($path); | |
} | |
} | |
// Get the skin of the player, crop the head of it and return the image | |
$img = imagecreate($size, $size); | |
$skin_img = Head::getSkinImage($username); | |
imagecopyresized($img, $skin_img, 0, 0, 8, 8, $size, $size, 8, 8); | |
// Cache the data | |
if(!file_exists(dirname($path))) | |
mkdir(dirname($path), 0777, true); | |
imagepng($img, $path, 0); | |
// Return the image | |
return $img; | |
} | |
/** | |
* Get the helm image of a Minecraft player | |
* @param $username String Minecraft username to get the head from (steve by default) | |
* @param $size Image size (8 by default) | |
* @return PHP Image with the player's head | |
*/ | |
public static function getHelmImage($username = "char", $size = 8) { | |
// Parse the username | |
$username = self::parseUsername($username); | |
// Check inside the cache | |
$path = CACHE_DIR . "/helm/" . $size . "/" . $username . ".png"; | |
if(file_exists($path)) { | |
if((time() - filemtime($path)) < self::CACHE_MAX_AGE) { | |
return imagecreatefrompng($path); | |
} | |
} | |
// Get the skin of the player, crop the head of it and return the image | |
$img = imagecreate($size, $size); | |
$skin_img = Head::getSkinImage($username); | |
// Generate the head | |
$head_img = imagecreate(8, 8); | |
imagecopymerge($head_img, $skin_img, 0, 0, 8, 8, 8, 8, 100); | |
// Put the helm over the head | |
for($x = 0; $x < 8; $x++) { | |
for($y = 0; $y < 8; $y++) { | |
if(!self::isImagePixelTransparent($skin_img, $x + 40, $y + 8)) { | |
imagecopymerge($head_img, $skin_img, $x, $y, $x + 40, $y + 8, 1, 1, 100); | |
} | |
} | |
} | |
// Resize and finish the image | |
imagecopyresized($img, $head_img, 0, 0, 0, 0, $size, $size, 8, 8); | |
// Destroy the head image | |
imagedestroy($head_img); | |
// Cache the data | |
if(!file_exists(dirname($path))) | |
mkdir(dirname($path), 0777, true); | |
imagepng($img, $path, 0); | |
// Return the image | |
return $img; | |
} | |
public static function getPlayerImage($username = "char", $size = 16, $border = false) { | |
// Parse the username | |
$username = self::parseUsername($username); | |
// Check inside the cache | |
if($border) | |
$path = CACHE_DIR . "/player-border/" . $size . "/" . $username . ".png"; | |
else | |
$path = CACHE_DIR . "/player/" . $size . "/" . $username . ".png"; | |
if(file_exists($path)) { | |
if((time() - filemtime($path)) < self::CACHE_MAX_AGE) { | |
$img = imagecreatefrompng($path); | |
imagealphablending($img, true); | |
imagesavealpha($img, true); | |
return $img; | |
} | |
} | |
// Get the skin of the player, crop the head of it and return the image | |
if($border) | |
$img = imagecreatetruecolor($size + 2, $size * 2 + 2); | |
else | |
$img = imagecreatetruecolor($size, $size * 2); | |
$skin_img = Head::getSkinImage($username); | |
// Create the player image to create the player in | |
$player_img = imagecreatetruecolor(16, 32); | |
// Make the images transparent | |
imagealphablending($player_img, true); | |
imagesavealpha($player_img, true); | |
imagealphablending($img, true); | |
imagesavealpha($img, true); | |
imagefill($player_img, 0, 0, imagecolorallocatealpha($player_img, 255, 255, 255, 127)); | |
imagefill($img, 0, 0, imagecolorallocatealpha($img, 255, 255, 255, 127)); | |
// Put the player parts into the player image | |
imagecopymerge($player_img, self::getHelmImage($username, 8), 4, 0, 0, 0, 8, 8, 100); // Head | |
imagecopymerge($player_img, $skin_img, 4, 8, 20, 20, 8, 12, 100); // Body | |
imagecopymerge($player_img, $skin_img, 0, 8, 44, 20, 4, 12, 100); // Left arm | |
imagecopyresampled($player_img, $skin_img, 12, 8, 47, 20, 4, 12, -4, 12); // Right arm (flipped) | |
imagecopymerge($player_img, $skin_img, 4, 20, 4, 20, 4, 12, 100); // Left leg | |
imagecopyresampled($player_img, $skin_img, 8, 20, 7, 20, 4, 12, -4, 12); // Right leg (flipped) | |
// Resize and finish the image | |
if($border) | |
imagecopyresized($img, $player_img, 1, 1, 0, 0, $size, $size * 2, 16, 32); | |
else | |
imagecopyresized($img, $player_img, 0, 0, 0, 0, $size, $size * 2, 16, 32); | |
// Destroy the player image | |
imagedestroy($player_img); | |
// Put the border around the image | |
if($border) { | |
// Define the line color | |
$color = imagecolorallocate($img, 255, 255, 255); | |
// Draw the borders | |
imageline($img, ($size / 4), 0, ($size / 4) * 3 + 1, 0, $color); // Head - Top | |
imageline($img, ($size / 4), 0, ($size / 4), ($size * 2 / 4), $color); // Head - Left | |
imageline($img, ($size / 4) * 3 + 1, 0, ($size / 4) * 3 + 1, ($size * 2 / 4), $color); // Head - Right | |
imageline($img, 0, ($size * 2 / 4), ($size / 4), ($size * 2 / 4), $color); // Left Arm - Top | |
imageline($img, 0, ($size * 2 / 4), 0, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $color); // Left Arm - Left | |
imageline($img, 0, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, ($size / 4), (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $color); // Left Arm - Bottom | |
imageline($img, $size - ($size / 4) + 1, ($size * 2 / 4), $size + 1, ($size * 2 / 4), $color); // Right Arm - Top | |
imageline($img, $size + 1, ($size * 2 / 4), $size + 1, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $color); // Right Arm - Right | |
imageline($img, $size - ($size / 4) + 1, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $size + 1, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $color); // Right Arm - Bottom | |
imageline($img, ($size / 4), (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, ($size / 4), ($size * 2) + 1, $color); // Left Leg - Left | |
imageline($img, $size - ($size / 4) + 1, (($size * 2 / 4) * 3 / 2) + ($size * 2 / 4) + 1, $size - ($size / 4) + 1, ($size * 2) + 1, $color); // Right Leg - Right | |
imageline($img, ($size / 4), ($size * 2) + 1, $size - ($size / 4) + 1, ($size * 2) + 1, $color); // Legs - Bottom | |
} | |
// Cache the data | |
if(!file_exists(dirname($path))) | |
mkdir(dirname($path), 0777, true); | |
imagepng($img, $path, 0); | |
// Return the image | |
return $img; | |
} | |
public static function getComboImage($username = "char", $size = 32) { | |
// Parse the username | |
$username = self::parseUsername($username); | |
// Check inside the cache | |
$path = CACHE_DIR . "/combo/" . $size . "/" . $username . ".png"; | |
if(file_exists($path)) { | |
if((time() - filemtime($path)) < self::CACHE_MAX_AGE) { | |
return imagecreatefrompng($path); | |
} | |
} | |
// Get the head image | |
$img = self::getHelmImage($username, $size); | |
// If there's room enough, put the player image over the head | |
if($size >= 32) { | |
// Get the player image (with border) | |
$player_img_width = $size / 4 + 2; | |
$player_img_height = $size / 2 + 2; | |
$player_img = self::getPlayerImage($username, $size / 4, true); | |
imagealphablending($img, true); | |
imagesavealpha($img, true); | |
imagealphablending($player_img, true); | |
imagesavealpha($player_img, true); | |
self::imagecopymergetransparent($img, $player_img, $size / 2 + $size / 8, $size / 2 - $size / 8, 0, 0, $player_img_width, $player_img_height, 100); | |
} | |
// Cache the data | |
if(!file_exists(dirname($path))) | |
mkdir(dirname($path), 0777, true); | |
imagepng($img, $path, 0); | |
// Return the image | |
return $img; | |
} | |
public static function printHelm($username = "char", $size = 8) { | |
// Set the header to an image type | |
header("content-type:image/png"); | |
// Print the generated image | |
$head_img = self::getHelmImage($username, $size); | |
imagepng($head_img); | |
imagedestroy($head_img); | |
} | |
public static function printHead($username = "char", $size = 8) { | |
// Set the header to an image type | |
header("content-type:image/png"); | |
// Print the generated image | |
$head_img = self::getHeadImage($username, $size); | |
imagepng($head_img); | |
imagedestroy($head_img); | |
} | |
public static function printCombo($username = "char", $size = 8) { | |
// Set the header to an image type | |
header("content-type:image/png"); | |
// Print the generated image | |
$head_img = self::getComboImage($username, $size); | |
imagepng($head_img); | |
imagedestroy($head_img); | |
} | |
public static function printPlayer($username = "char", $size = 16, $border = false) { | |
// Set the header to an image type | |
header("content-type:image/png"); | |
// Print the generated image | |
$head_img = self::getPlayerImage($username, $size, $border); | |
imagepng($head_img); | |
imagedestroy($head_img); | |
} | |
private static function isImagePixelTransparent($img, $x, $y) { | |
$rgba = imagecolorat($img, $x, $y); | |
$alpha = ($rgba & 0x7F000000) >> 24; | |
return ($alpha >= 127); | |
} | |
private static function imagecopymergetransparent($dest, $src, $dest_x, $dest_y, $src_x, $src_y, $src_width, $src_height, $pct) { | |
for($x = 0; $x < $src_width; $x++) { | |
for($y = 0; $y < $src_height; $y++) { | |
if(!self::isImagePixelTransparent($src, $src_x + $x, $src_y + $y)) { | |
imagecopymerge($dest, $src, $dest_x + $x, $dest_y + $y, $src_x + $x, $src_y + $y, 1, 1, $pct); | |
} | |
} | |
} | |
} | |
} | |
// Retrieve and parse the image parameters | |
$parts = explode("/", $_GET['p']); | |
$type = $parts[0]; | |
$size = $parts[1]; | |
$username = explode(".", $parts[2]); | |
$username = Head::parseUsername($username[0]); | |
// Verify the requested image size | |
if(!ctype_digit($size) || $size < 1) { | |
echo "Invalid image size!"; | |
die(); | |
} | |
if($size > 256) { | |
echo "The maximum allowed size is 256px!"; | |
die(); | |
} | |
// Display the image | |
if($type == "helm") | |
Head::printHelm($username, $size); | |
elseif($type == "head") | |
Head::printHead($username, $size); | |
elseif($type == "combo") | |
Head::printCombo($username, $size); | |
elseif($type == "player") | |
Head::printPlayer($username, $size, false); | |
else { | |
echo "Invalid image type!"; | |
die(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment