diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..fb12325 --- /dev/null +++ b/plugin.yml @@ -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 \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/S3DItemToolS.php b/src/skh6075/s3ditemtools/S3DItemToolS.php new file mode 100644 index 0000000..8a5d4cf --- /dev/null +++ b/src/skh6075/s3ditemtools/S3DItemToolS.php @@ -0,0 +1,42 @@ +directories as $directory) { + if (is_dir($this->getDataFolder() . $directory . DIRECTORY_SEPARATOR)) { + continue; + } + mkdir($this->getDataFolder() . $directory . DIRECTORY_SEPARATOR); + } + SkinFactory::getInstance()->init(); + } +} \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/listener/EventListener.php b/src/skh6075/s3ditemtools/listener/EventListener.php new file mode 100644 index 0000000..f458e24 --- /dev/null +++ b/src/skh6075/s3ditemtools/listener/EventListener.php @@ -0,0 +1,45 @@ +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); + } + } + } +} \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/skin/PlayerSkin.php b/src/skh6075/s3ditemtools/skin/PlayerSkin.php new file mode 100644 index 0000000..6712242 --- /dev/null +++ b/src/skh6075/s3ditemtools/skin/PlayerSkin.php @@ -0,0 +1,171 @@ +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); + } + } +} \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/skin/SkinFactory.php b/src/skh6075/s3ditemtools/skin/SkinFactory.php new file mode 100644 index 0000000..aa51f3c --- /dev/null +++ b/src/skh6075/s3ditemtools/skin/SkinFactory.php @@ -0,0 +1,49 @@ +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; + } +} \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/skin/SkinMap.php b/src/skh6075/s3ditemtools/skin/SkinMap.php new file mode 100644 index 0000000..a04f159 --- /dev/null +++ b/src/skh6075/s3ditemtools/skin/SkinMap.php @@ -0,0 +1,20 @@ + 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 + ]; +} \ No newline at end of file diff --git a/src/skh6075/s3ditemtools/skin/SkinReflection.php b/src/skh6075/s3ditemtools/skin/SkinReflection.php new file mode 100644 index 0000000..ecf63a5 --- /dev/null +++ b/src/skh6075/s3ditemtools/skin/SkinReflection.php @@ -0,0 +1,28 @@ +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); + } +} \ No newline at end of file