This repository has been archived by the owner on Nov 9, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
361 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
name: S3DItemToolS | ||
main: skh6075\s3ditemtools\S3DItemToolS | ||
author: AvasKr | ||
version: 1.0.0 | ||
api: 3.10.0 | ||
website: https://github.com/GodVas |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools; | ||
|
||
use pocketmine\plugin\PluginBase; | ||
use skh6075\s3ditemtools\skin\SkinFactory; | ||
use function is_dir; | ||
use function mkdir; | ||
use const DIRECTORY_SEPARATOR; | ||
|
||
class S3DItemToolS extends PluginBase{ | ||
|
||
/** @var ?S3DItemToolS */ | ||
private static $instance = null; | ||
/** @var string[] */ | ||
protected $directories = [ | ||
"models", | ||
"images", | ||
"skins" | ||
]; | ||
|
||
|
||
public static function getInstance(): ?S3DItemToolS{ | ||
return self::$instance; | ||
} | ||
|
||
public function onLoad(): void{ | ||
if (self::$instance === null) { | ||
self::$instance = $this; | ||
} | ||
} | ||
|
||
public function onEnable(): void{ | ||
foreach ($this->directories as $directory) { | ||
if (is_dir($this->getDataFolder() . $directory . DIRECTORY_SEPARATOR)) { | ||
continue; | ||
} | ||
mkdir($this->getDataFolder() . $directory . DIRECTORY_SEPARATOR); | ||
} | ||
SkinFactory::getInstance()->init(); | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools\listener; | ||
|
||
use pocketmine\event\Listener; | ||
use pocketmine\event\player\PlayerItemHeldEvent; | ||
use pocketmine\event\player\PlayerJoinEvent; | ||
use pocketmine\event\player\PlayerQuitEvent; | ||
use skh6075\s3ditemtools\skin\PlayerSkin; | ||
use skh6075\s3ditemtools\skin\SkinFactory; | ||
use skh6075\s3ditemtools\skin\SkinReflection; | ||
|
||
class EventListener implements Listener{ | ||
|
||
/** | ||
* @param PlayerJoinEvent $event | ||
* @priority HIGHEST | ||
*/ | ||
public function onPlayerJoin(PlayerJoinEvent $event): void{ | ||
$player = $event->getPlayer(); | ||
PlayerSkin::getInstance()->makeSkinImage($player); | ||
PlayerSkin::getInstance()->setPlayerSkin($player); | ||
} | ||
|
||
/** | ||
* @param PlayerQuitEvent $event | ||
* @priority HIGHEST | ||
*/ | ||
public function onPlayerQuit(PlayerQuitEvent $event): void{ | ||
PlayerSkin::getInstance()->deleteSkinImage($event->getPlayer()); | ||
} | ||
|
||
public function onPlayerItemdHeld(PlayerItemHeldEvent $event): void{ | ||
$player = $event->getPlayer(); | ||
$item = $event->getItem(); | ||
|
||
if (!is_null($item->getNamedTagEntry("3d_model"))) { | ||
if (($reflection = SkinFactory::getInstance()->getSkinReflection($item->getNamedTagEntry("3d_model")->getValue())) instanceof SkinReflection) { | ||
$reflection->sendSkinImage($player); | ||
} else { | ||
PlayerSkin::getInstance()->callbackSkin($player); | ||
} | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools\skin; | ||
|
||
use pocketmine\entity\Skin; | ||
use pocketmine\Player; | ||
use pocketmine\Server; | ||
use skh6075\s3ditemtools\S3DItemToolS; | ||
use function file_exists; | ||
use function file_get_contents; | ||
use function unlink; | ||
use function intdiv; | ||
use function ord; | ||
use function chr; | ||
use function imagecreatetruecolor; | ||
use function imagefill; | ||
use function imagecolorallocatealpha; | ||
use function imagesetpixel; | ||
use function imagesavealpha; | ||
use function imagecreatefrompng; | ||
use function imagecopymerge; | ||
use function imagedestroy; | ||
use function getimagesize; | ||
|
||
final class PlayerSkin{ | ||
|
||
/** @var ?PlayerSkin */ | ||
private static $instance = null; | ||
/** @var S3DItemToolS */ | ||
protected $plugin; | ||
/** @var Skin[] */ | ||
private static $skins = []; | ||
|
||
|
||
public static function getInstance(): ?PlayerSkin{ | ||
if (self::$instance === null) { | ||
self::$instance = new self(); | ||
} | ||
return self::$instance; | ||
} | ||
|
||
public function __construct() { | ||
$this->plugin = S3DItemToolS::getInstance(); | ||
} | ||
|
||
public function getPlayerSkin(Player $player): ?Skin{ | ||
return self::$skins[$player->getLowerCaseName()] ?? null; | ||
} | ||
|
||
public function setPlayerSkin(Player $player): void{ | ||
self::$skins[$player->getLowerCaseName()] = $player->getSkin(); | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
*/ | ||
public function makeSkinImage(Player $player): void{ | ||
$skinPath = $this->plugin->getDataFolder() . "skins" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"; | ||
$image = $this->convertSkinImage($player->getSkin()->getSkinData()); | ||
$background = imagecolorallocate($image, 255, 255, 255); | ||
imagecolortransparent($image, $background); | ||
imagepng($image, $skinPath); | ||
imagedestroy($image); | ||
} | ||
|
||
/** | ||
* @param string $skinData | ||
* @return false|resource | ||
*/ | ||
private function convertSkinImage(string $skinData) { | ||
$size = strlen($skinData); | ||
$width = SkinMap::SKIN_WIDTH_SIZE[$size]; | ||
$height = SkinMap::SKIN_HEIGHT_SIZE[$size]; | ||
$pos = 0; | ||
$image = imagecreatetruecolor($width, $height); | ||
|
||
imagefill($image, 0, 0, imagecolorallocatealpha($image, 0, 0, 0, 127)); | ||
for ($y = 0; $y < $height; $y ++) { | ||
for ($x = 0; $x < $width; $x ++) { | ||
$r = ord($skinData[$pos]); | ||
$pos ++; | ||
$g = ord($skinData[$pos]); | ||
$pos ++; | ||
$b = ord($skinData[$pos]); | ||
$pos ++; | ||
$a = 127 - intdiv(ord($skinData[$pos]), 2); | ||
$pos ++; | ||
$color = imagecolorallocatealpha($image, $r, $g, $b, $a); | ||
imagesetpixel($image, $x, $y, $color); | ||
} | ||
} | ||
imagesavealpha($image, true); | ||
return $image; | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
* @param string $resource | ||
*/ | ||
public function convertImagemerge(Player $player, string $resource): void{ | ||
$playerImage = imagecreatefrompng($this->plugin->getDataFolder() . "skins" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"); | ||
$modelImage = imagecreatefrompng($this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $resource . ".png"); | ||
[$width, $height] = getimagesize($this->plugin->getDataFolder() . "skins" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"); | ||
imagecopymerge($playerImage, $modelImage, 56, 16, 0, 0, $width, $height, 100); | ||
imagesavealpha($playerImage, true); | ||
imagepng($playerImage, $this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"); | ||
|
||
imagedestroy($playerImage); | ||
imagedestroy($modelImage); | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
* @param string $resource | ||
*/ | ||
public function sendImageSkin(Player $player, string $resource): void{ | ||
$path = $this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"; | ||
$image = imagecreatefrompng($path); | ||
$skinBytes = ""; | ||
$size = (int) getimagesize($path) [1]; | ||
|
||
for ($y = 0; $y < $size; $y ++) { | ||
for ($x = 0; $x < 64; $x ++) { | ||
$colorat = imagecolorat($image, $x, $y); | ||
$a = ((~((int) ($colorat >> 24))) << 1) & 0xff; | ||
$r = ($colorat >> 16) & 0xff; | ||
$g = ($colorat >> 8) & 0xff; | ||
$b = $colorat & 0xff; | ||
$skinBytes .= chr($r) . chr($g) . chr($b) . chr($a); | ||
} | ||
} | ||
imagedestroy($image); | ||
$skin = new Skin($player->getSkin()->getSkinId(), $skinBytes, "", "geometry." . $resource, file_get_contents($this->plugin->getDataFolder() . "models" . DIRECTORY_SEPARATOR . $resource . ".json")); | ||
$this->broadcastChangeSkin($player, $skin); | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
* @param Skin $skin | ||
*/ | ||
public function broadcastChangeSkin(Player $player, Skin $skin): void{ | ||
$player->setSkin($skin); | ||
foreach (Server::getInstance()->getOnlinePlayers() as $players) { | ||
$player->sendSkin([$players, $player]); | ||
} | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
*/ | ||
public function deleteSkinImage(Player $player): void{ | ||
if (file_exists($this->plugin->getDataFolder() . "skins" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png")) { | ||
unlink($this->plugin->getDataFolder() . "skins" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"); | ||
} | ||
if (file_exists($this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png")) { | ||
unlink($this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $player->getLowerCaseName() . ".png"); | ||
} | ||
if (isset(self::$skins[$player->getLowerCaseName()])) { | ||
unset(self::$skins[$player->getLowerCaseName()]); | ||
} | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
*/ | ||
public function callbackSkin(Player $player): void{ | ||
if (($skin = $this->getPlayerSkin($player)) instanceof Skin) { | ||
$this->broadcastChangeSkin($player, $skin); | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools\skin; | ||
|
||
use skh6075\s3ditemtools\S3DItemToolS; | ||
use function array_diff; | ||
use function explode; | ||
use function file_exists; | ||
use const DIRECTORY_SEPARATOR; | ||
|
||
final class SkinFactory{ | ||
|
||
/** @var ?SkinFactory */ | ||
private static $instance = null; | ||
/** @var S3DItemToolS */ | ||
protected $plugin; | ||
/** @var SkinReflection[] */ | ||
private static $reflections = []; | ||
|
||
|
||
public static function getInstance(): ?SkinFactory{ | ||
if (self::$instance === null) { | ||
self::$instance = new self(); | ||
} | ||
return self::$instance; | ||
} | ||
|
||
public function __construct() { | ||
$this->plugin = S3DItemToolS::getInstance(); | ||
} | ||
|
||
public function init(): void{ | ||
foreach (array_diff(scandir($this->plugin->getDataFolder() . "models" . DIRECTORY_SEPARATOR), ['.', '..']) as $value) { | ||
if (!isset(explode(".", $value)[0]) || explode(".", $value)[1] !== "json" || !file_exists($this->plugin->getDataFolder() . "images" . DIRECTORY_SEPARATOR . $value . ".png")) { | ||
continue; | ||
} | ||
self::$reflections[explode(".", $value) [0]] = new SkinReflection(explode(".", $value) [1]); | ||
} | ||
} | ||
|
||
/** | ||
* @param string $name | ||
* | ||
* @return ?SkinReflection | ||
*/ | ||
public function getSkinReflection(string $name): ?SkinReflection{ | ||
return self::$reflections[$name] ?? null; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools\skin; | ||
|
||
final class SkinMap{ | ||
|
||
/** @var int[] */ | ||
public const SKIN_WIDTH_SIZE = [ | ||
64 * 32 * 4 => 64, | ||
64 * 64 * 4 => 64, | ||
128 * 128 * 4 => 128 | ||
]; | ||
|
||
/** @var int[] */ | ||
public const SKIN_HEIGHT_SIZE = [ | ||
64 * 32 * 4 => 32, | ||
64 * 64 * 4 => 64, | ||
128 * 128 * 4 => 128 | ||
]; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
namespace skh6075\s3ditemtools\skin; | ||
|
||
use pocketmine\Player; | ||
|
||
class SkinReflection{ | ||
|
||
/** @var string */ | ||
protected $name; | ||
|
||
|
||
public function __construct(string $name) { | ||
$this->name = $name; | ||
} | ||
|
||
public function getName(): string{ | ||
return $this->name; | ||
} | ||
|
||
/** | ||
* @param Player $player | ||
*/ | ||
public function sendSkinImage(Player $player): void{ | ||
PlayerSkin::getInstance()->convertImagemerge($player, $this->name); | ||
PlayerSkin::getInstance()->sendImageSkin($player, $this->name); | ||
} | ||
} |