PocketMine-MP 5.14.2 git-50e2c469a547a16a23b2dc691e70a51d34e29395
Skin.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\entity;
25
26use Ahc\Json\Comment as CommentedJsonDecoder;
28use function implode;
29use function in_array;
30use function json_encode;
31use function strlen;
32use const JSON_THROW_ON_ERROR;
33
34final class Skin{
35 public const ACCEPTED_SKIN_SIZES = [
36 64 * 32 * 4,
37 64 * 64 * 4,
38 128 * 128 * 4
39 ];
40
41 private string $skinId;
42 private string $skinData;
43 private string $capeData;
44 private string $geometryName;
45 private string $geometryData;
46
47 private static function checkLength(string $string, string $name, int $maxLength) : void{
48 if(strlen($string) > $maxLength){
49 throw new InvalidSkinException("$name must be at most $maxLength bytes, but have " . strlen($string) . " bytes");
50 }
51 }
52
53 public function __construct(string $skinId, string $skinData, string $capeData = "", string $geometryName = "", string $geometryData = ""){
54 self::checkLength($skinId, "Skin ID", Limits::INT16_MAX);
55 self::checkLength($geometryName, "Geometry name", Limits::INT16_MAX);
56 self::checkLength($geometryData, "Geometry data", Limits::INT32_MAX);
57
58 if($skinId === ""){
59 throw new InvalidSkinException("Skin ID must not be empty");
60 }
61 $len = strlen($skinData);
62 if(!in_array($len, self::ACCEPTED_SKIN_SIZES, true)){
63 throw new InvalidSkinException("Invalid skin data size $len bytes (allowed sizes: " . implode(", ", self::ACCEPTED_SKIN_SIZES) . ")");
64 }
65 if($capeData !== "" && strlen($capeData) !== 8192){
66 throw new InvalidSkinException("Invalid cape data size " . strlen($capeData) . " bytes (must be exactly 8192 bytes)");
67 }
68
69 if($geometryData !== ""){
70 try{
71 $decodedGeometry = (new CommentedJsonDecoder())->decode($geometryData);
72 }catch(\RuntimeException $e){
73 throw new InvalidSkinException("Invalid geometry data: " . $e->getMessage(), 0, $e);
74 }
75
76 /*
77 * Hack to cut down on network overhead due to skins, by un-pretty-printing geometry JSON.
78 *
79 * Mojang, some stupid reason, send every single model for every single skin in the selected skin-pack.
80 * Not only that, they are pretty-printed.
81 * TODO: find out what model crap can be safely dropped from the packet (unless it gets fixed first)
82 */
83 $geometryData = json_encode($decodedGeometry, JSON_THROW_ON_ERROR);
84 }
85
86 $this->skinId = $skinId;
87 $this->skinData = $skinData;
88 $this->capeData = $capeData;
89 $this->geometryName = $geometryName;
90 $this->geometryData = $geometryData;
91 }
92
93 public function getSkinId() : string{
94 return $this->skinId;
95 }
96
97 public function getSkinData() : string{
98 return $this->skinData;
99 }
100
101 public function getCapeData() : string{
102 return $this->capeData;
103 }
104
105 public function getGeometryName() : string{
106 return $this->geometryName;
107 }
108
109 public function getGeometryData() : string{
110 return $this->geometryData;
111 }
112}