PocketMine-MP 5.15.1 git-fb9a74e8799c71ed8292cfa53abe7a4c9204629d
RuntimeDataWriter.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;
27use pocketmine\block\utils\WallConnectionType;
30use function array_flip;
31use function log;
32use function spl_object_id;
33
35 use LegacyRuntimeEnumDescriberTrait;
36
37 private int $value = 0;
38 private int $offset = 0;
39
40 public function __construct(
41 private int $maxBits
42 ){}
43
44 public function writeInt(int $bits, int $value) : void{
45 if($this->offset + $bits > $this->maxBits){
46 throw new \InvalidArgumentException("Bit buffer cannot be larger than $this->maxBits bits (already have $this->offset bits)");
47 }
48 if(($value & (~0 << $bits)) !== 0){
49 throw new \InvalidArgumentException("Value $value does not fit into $bits bits");
50 }
51
52 $this->value |= ($value << $this->offset);
53 $this->offset += $bits;
54 }
55
56 public function int(int $bits, int &$value) : void{
57 $this->writeInt($bits, $value);
58 }
59
63 public function boundedInt(int $bits, int $min, int $max, int &$value) : void{
64 $offset = $this->offset;
65 $this->writeBoundedIntAuto($min, $max, $value);
66 $actualBits = $this->offset - $offset;
67 if($actualBits !== $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 writeBoundedIntAuto(int $min, int $max, int $value) : void{
73 if($value < $min || $value > $max){
74 throw new \InvalidArgumentException("Value $value is outside the range $min - $max");
75 }
76 $bits = ((int) log($max - $min, 2)) + 1;
77 $this->writeInt($bits, $value - $min);
78 }
79
80 public function boundedIntAuto(int $min, int $max, int &$value) : void{
81 $this->writeBoundedIntAuto($min, $max, $value);
82 }
83
84 protected function writeBool(bool $value) : void{
85 $this->writeInt(1, $value ? 1 : 0);
86 }
87
88 public function bool(bool &$value) : void{
89 $this->writeBool($value);
90 }
91
92 public function horizontalFacing(int &$facing) : void{
93 $this->writeInt(2, match($facing){
94 Facing::NORTH => 0,
95 Facing::EAST => 1,
96 Facing::SOUTH => 2,
97 Facing::WEST => 3,
98 default => throw new \InvalidArgumentException("Invalid horizontal facing $facing")
99 });
100 }
101
105 public function facingFlags(array &$faces) : void{
106 $uniqueFaces = array_flip($faces);
107 foreach(Facing::ALL as $facing){
108 $this->writeBool(isset($uniqueFaces[$facing]));
109 }
110 }
111
115 public function horizontalFacingFlags(array &$faces) : void{
116 $uniqueFaces = array_flip($faces);
117 foreach(Facing::HORIZONTAL as $facing){
118 $this->writeBool(isset($uniqueFaces[$facing]));
119 }
120 }
121
122 public function facing(int &$facing) : void{
123 $this->writeInt(3, match($facing){
124 0 => Facing::DOWN,
125 1 => Facing::UP,
126 2 => Facing::NORTH,
127 3 => Facing::SOUTH,
128 4 => Facing::WEST,
129 5 => Facing::EAST,
130 default => throw new \InvalidArgumentException("Invalid facing $facing")
131 });
132 }
133
134 public function facingExcept(int &$facing, int $except) : void{
135 $this->facing($facing);
136 }
137
138 public function axis(int &$axis) : void{
139 $this->writeInt(2, match($axis){
140 Axis::X => 0,
141 Axis::Z => 1,
142 Axis::Y => 2,
143 default => throw new \InvalidArgumentException("Invalid axis $axis")
144 });
145 }
146
147 public function horizontalAxis(int &$axis) : void{
148 $this->writeInt(1, match($axis){
149 Axis::X => 0,
150 Axis::Z => 1,
151 default => throw new \InvalidArgumentException("Invalid horizontal axis $axis")
152 });
153 }
154
159 public function wallConnections(array &$connections) : void{
160 $packed = 0;
161 $offset = 0;
162 foreach(Facing::HORIZONTAL as $facing){
163 $packed += match($connections[$facing] ?? null){
164 null => 0,
165 WallConnectionType::SHORT => 1,
166 WallConnectionType::TALL => 2,
167 } * (3 ** $offset);
168 $offset++;
169 }
170 $this->writeBoundedIntAuto(0, (3 ** 4) - 1, $packed);
171 }
172
179 public function brewingStandSlots(array &$slots) : void{
180 $this->enumSet($slots, BrewingStandSlot::cases());
181 }
182
183 public function railShape(int &$railShape) : void{
184 $this->int(4, $railShape);
185 }
186
187 public function straightOnlyRailShape(int &$railShape) : void{
188 $this->int(3, $railShape);
189 }
190
191 public function enum(\UnitEnum &$case) : void{
192 $metadata = RuntimeEnumMetadata::from($case);
193 $this->writeInt($metadata->bits, $metadata->enumToInt($case));
194 }
195
196 public function enumSet(array &$set, array $allCases) : void{
197 foreach($allCases as $case){
198 $this->writeBool(isset($set[spl_object_id($case)]));
199 }
200 }
201
202 public function getValue() : int{ return $this->value; }
203
204 public function getOffset() : int{ return $this->offset; }
205}
boundedIntAuto(int $min, int $max, int &$value)
boundedInt(int $bits, int $min, int $max, int &$value)