Skip to content

Instantly share code, notes, and snippets.

@timvisee
Last active March 14, 2022 14:57
Show Gist options
  • Save timvisee/7875117 to your computer and use it in GitHub Desktop.
Save timvisee/7875117 to your computer and use it in GitHub Desktop.
MCDragonRealms Minecraft player head avatar class.
<?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("", "", $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