PocketMine-MP 5.21.2 git-a6534ecbbbcf369264567d27e5ed70f7f5be9816
Loading...
Searching...
No Matches
RuntimeDataReader.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\data\runtime;
25
26use pocketmine\block\utils\BrewingStandSlot;
28use pocketmine\block\utils\WallConnectionType;
32use function get_class;
33use function intdiv;
34use function log;
35use function spl_object_id;
36
38 private int $offset = 0;
39
40 public function __construct(
41 private int $maxBits,
42 private int $value
43 ){}
44
45 public function readInt(int $bits) : int{
46 $bitsLeft = $this->maxBits - $this->offset;
47 if($bits > $bitsLeft){
48 throw new \InvalidArgumentException("No bits left in buffer (need $bits, have $bitsLeft");
49 }
50
51 $value = ($this->value >> $this->offset) & ~(~0 << $bits);
52 $this->offset += $bits;
53 return $value;
54 }
55
56 public function int(int $bits, int &$value) : void{
57 $value = $this->readInt($bits);
58 }
59
63 public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
64 $offset = $this->offset;
65 $this->boundedIntAuto($min, $max, $value);
66 $actualBits = $this->offset - $offset;
67 if($this->offset !== $offset + $bits){
68 throw new \InvalidArgumentException("Bits should be $actualBits for the given bounds, but received $bits. Use boundedIntAuto() for automatic bits calculation.");
69 }
70 }
71
72 private function readBoundedIntAuto(int $min, int $max) : int{
73 $bits = ((int) log($max - $min, 2)) + 1;
74 $result = $this->readInt($bits) + $min;
75 if($result < $min || $result > $max){
76 throw new InvalidSerializedRuntimeDataException("Value is outside the range $min - $max");
77 }
78 return $result;
79 }
80
81 public function boundedIntAuto(int $min, int $max, int &$value) : void{
82 $value = $this->readBoundedIntAuto($min, $max);
83 }
84
85 protected function readBool() : bool{
86 return $this->readInt(1) === 1;
87 }
88
89 public function bool(bool &$value) : void{
90 $value = $this->readBool();
91 }
92
93 public function horizontalFacing(int &$facing) : void{
94 $facing = match($this->readInt(2)){
95 0 => Facing::NORTH,
96 1 => Facing::EAST,
97 2 => Facing::SOUTH,
98 3 => Facing::WEST,
99 default => throw new AssumptionFailedError("Unreachable")
100 };
101 }
102
106 public function facingFlags(array &$faces) : void{
107 $result = [];
108 foreach(Facing::ALL as $facing){
109 if($this->readBool()){
110 $result[$facing] = $facing;
111 }
112 }
113
114 $faces = $result;
115 }
116
120 public function horizontalFacingFlags(array &$faces) : void{
121 $result = [];
122 foreach(Facing::HORIZONTAL as $facing){
123 if($this->readBool()){
124 $result[$facing] = $facing;
125 }
126 }
127
128 $faces = $result;
129 }
130
131 public function facing(int &$facing) : void{
132 $facing = match($this->readInt(3)){
133 0 => Facing::DOWN,
134 1 => Facing::UP,
135 2 => Facing::NORTH,
136 3 => Facing::SOUTH,
137 4 => Facing::WEST,
138 5 => Facing::EAST,
139 default => throw new InvalidSerializedRuntimeDataException("Invalid facing value")
140 };
141 }
142
143 public function facingExcept(int &$facing, int $except) : void{
144 $result = 0;
145 $this->facing($result);
146 if($result === $except){
147 throw new InvalidSerializedRuntimeDataException("Illegal facing value");
148 }
149
150 $facing = $result;
151 }
152
153 public function axis(int &$axis) : void{
154 $axis = match($this->readInt(2)){
155 0 => Axis::X,
156 1 => Axis::Z,
157 2 => Axis::Y,
158 default => throw new InvalidSerializedRuntimeDataException("Invalid axis value")
159 };
160 }
161
162 public function horizontalAxis(int &$axis) : void{
163 $axis = match($this->readInt(1)){
164 0 => Axis::X,
165 1 => Axis::Z,
166 default => throw new AssumptionFailedError("Unreachable")
167 };
168 }
169
174 public function wallConnections(array &$connections) : void{
175 $result = [];
176 $offset = 0;
177 $packed = $this->readBoundedIntAuto(0, (3 ** 4) - 1);
178 foreach(Facing::HORIZONTAL as $facing){
179 $type = intdiv($packed, (3 ** $offset)) % 3;
180 if($type !== 0){
181 $result[$facing] = match($type){
182 1 => WallConnectionType::SHORT,
183 2 => WallConnectionType::TALL,
184 default => throw new AssumptionFailedError("Unreachable")
185 };
186 }
187 $offset++;
188 }
189
190 $connections = $result;
191 }
192
199 public function brewingStandSlots(array &$slots) : void{
200 $this->enumSet($slots, BrewingStandSlot::cases());
201 }
202
203 public function railShape(int &$railShape) : void{
204 $result = $this->readInt(4);
205 if(!isset(RailConnectionInfo::CONNECTIONS[$result]) && !isset(RailConnectionInfo::CURVE_CONNECTIONS[$result])){
206 throw new InvalidSerializedRuntimeDataException("Invalid rail shape $result");
207 }
208
209 $railShape = $result;
210 }
211
212 public function straightOnlyRailShape(int &$railShape) : void{
213 $result = $this->readInt(3);
214 if(!isset(RailConnectionInfo::CONNECTIONS[$result])){
215 throw new InvalidSerializedRuntimeDataException("No rail shape matches meta $result");
216 }
217
218 $railShape = $result;
219 }
220
221 public function enum(\UnitEnum &$case) : void{
222 $metadata = RuntimeEnumMetadata::from($case);
223 $raw = $this->readInt($metadata->bits);
224 $result = $metadata->intToEnum($raw);
225 if($result === null){
226 throw new InvalidSerializedRuntimeDataException("Invalid serialized value $raw for " . get_class($case));
227 }
228
229 $case = $result;
230 }
231
232 public function enumSet(array &$set, array $allCases) : void{
233 $result = [];
234 foreach($allCases as $case){
235 if($this->readBool()){
236 $result[spl_object_id($case)] = $case;
237 }
238 }
239 $set = $result;
240 }
241
242 public function getOffset() : int{ return $this->offset; }
243}
boundedIntAuto(int $min, int $max, int &$value)
boundedInt(int $bits, int $min, int $max, int &$value)