PocketMine-MP 5.36.1 git-eaa7c4834c8fe2f379d24e7f0ee6cc63cfb18ccc
Loading...
Searching...
No Matches
AvailableCommandsPacketDisassembler.php
1<?php
2
3/*
4 * This file is part of BedrockProtocol.
5 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
6 *
7 * BedrockProtocol is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 */
12
13declare(strict_types=1);
14
15namespace pocketmine\network\mcpe\protocol\serializer;
16
31
37
38 private AvailableCommandsPacket $packet;
39
44 private array $enumConstraintIndex = [];
45
50 private array $linkedEnumCache = [];
55 private array $linkedChainedSubCommandDataCache = [];
56
61 private array $unusedHardEnumValues;
66 private array $unusedPostfixes;
67
72 private array $unusedHardEnumRawData;
77 private array $unusedSoftEnums;
82 private array $unusedChainedSubCommandRawData;
87 private array $unusedChainedSubCommandValues;
88
89 private function __construct(AvailableCommandsPacket $packet){
90 $this->packet = $packet;
91 $this->unusedHardEnumValues = $packet->enumValues;
92 $this->unusedHardEnumRawData = $packet->enums;
93 $this->unusedSoftEnums = $packet->softEnums;
94 $this->unusedPostfixes = $packet->postfixes;
95 $this->unusedChainedSubCommandRawData = $packet->chainedSubCommandData;
96 $this->unusedChainedSubCommandValues = $packet->chainedSubCommandValues;
97 }
98
99 public static function disassemble(AvailableCommandsPacket $packet) : DisassembledAvailableCommandsData{
100 $result = new self($packet);
101
102 //this lets us put the data for the constraints inside the CommandEnum objects directly
103 $repeatedEnumConstraints = [];
104 foreach($packet->enumConstraints as $index => $rawConstraintData){
105 $enumIndex = $rawConstraintData->getEnumIndex();
106 $affectedValueIndex = $rawConstraintData->getAffectedValueIndex();
107 if(isset($result->enumConstraintIndex[$enumIndex][$affectedValueIndex])){
108 $repeatedEnumConstraints[$index] = $rawConstraintData;
109 }else{
110 $result->enumConstraintIndex[$rawConstraintData->getEnumIndex()][$rawConstraintData->getAffectedValueIndex()] = $rawConstraintData;
111 }
112 }
113
114 $unusedCommandData = [];
115 foreach($packet->commandData as $rawData){
116 $unusedCommandData[] = $result->processCommandData($rawData);
117 }
118 $unusedHardEnums = [];
119 foreach($result->unusedHardEnumRawData as $index => $rawData){
120 $unusedHardEnums[$index] = $result->lookupHardEnum($index);
121 }
122 $unusedChainedSubCommandData = [];
123 foreach($result->unusedChainedSubCommandRawData as $index => $rawData){
124 $unusedChainedSubCommandData[$index] = $result->lookupChainedSubCommandData($index);
125 }
126
128 $unusedCommandData,
129 $result->unusedHardEnumValues,
130 $result->unusedPostfixes,
131 $unusedHardEnums,
132 $result->unusedSoftEnums,
133 $unusedChainedSubCommandData,
134 $result->unusedChainedSubCommandValues,
135 $repeatedEnumConstraints
136 );
137 }
138
142 private function lookupHardEnumValue(int $index) : string{
143 $value = $this->packet->enumValues[$index] ?? throw new PacketDecodeException("No such enum value index $index");
144 unset($this->unusedHardEnumValues[$index]);
145 return $value;
146 }
147
151 private function lookupHardEnum(int $index) : CommandHardEnum{
152 if(!isset($this->linkedEnumCache[$index])){
153 $rawEnum = $this->packet->enums[$index] ?? throw new PacketDecodeException("No such enum index $index");
154
155 $enumValues = [];
156 foreach($rawEnum->getValueIndexes() as $valueIndex){
157 $value = $this->lookupHardEnumValue($valueIndex);
158
159 $rawConstraint = $this->enumConstraintIndex[$index][$valueIndex] ?? null;
160 if($rawConstraint !== null){
161 $enumValues[] = new ConstrainedEnumValue($value, $rawConstraint->getConstraints());
162 }else{
163 $enumValues[] = $value;
164 }
165 }
166
167 $this->linkedEnumCache[$index] = new CommandHardEnum($rawEnum->getName(), $enumValues);
168 unset($this->unusedHardEnumRawData[$index]);
169 }
170
171 return $this->linkedEnumCache[$index];
172 }
173
177 private function lookupSoftEnum(int $index) : CommandSoftEnum{
178 //no conversion needed - these are fully self-contained
179 return $this->packet->softEnums[$index] ?? throw new PacketDecodeException("No such soft enum index $index");
180 }
181
185 private function lookupChainedSubCommandValue(int $index) : string{
186 $value = $this->packet->chainedSubCommandValues[$index] ?? throw new PacketDecodeException("No such chained subcommand value index $index");
187 unset($this->unusedChainedSubCommandValues[$index]);
188 return $value;
189 }
190
194 private function lookupChainedSubCommandData(int $index) : ChainedSubCommandData{
195 if(!isset($this->linkedChainedSubCommandDataCache[$index])){
196 $rawData = $this->packet->chainedSubCommandData[$index] ?? throw new PacketDecodeException("No such chained subcommand index $index");
197
198 $values = [];
199 foreach($rawData->getValueData() as $rawValueData){
200 $valueName = $this->lookupChainedSubCommandValue($rawValueData->getNameIndex());
201 $values[] = new ChainedSubCommandValue($valueName, $rawValueData->getType());
202 }
203
204 $this->linkedChainedSubCommandDataCache[$index] = new ChainedSubCommandData($rawData->getName(), $values);
205 unset($this->unusedChainedSubCommandRawData[$index]);
206 }
207
208 return $this->linkedChainedSubCommandDataCache[$index];
209 }
210
214 private function lookupPostfix(int $index) : string{
215 $value = $this->packet->postfixes[$index] ?? throw new PacketDecodeException("No such postfix index $index");
216 unset($this->unusedPostfixes[$index]);
217 return $value;
218 }
219
220 private function processCommandData(CommandRawData $rawData) : CommandData{
221 $aliasesIndex = $rawData->getAliasEnumIndex();
222 $aliasesEnum = $aliasesIndex === -1 ? null : $this->lookupHardEnum($aliasesIndex);
223
224 $chainedSubCommandData = [];
225 foreach($rawData->getChainedSubCommandDataIndexes() as $dataIndex){
226 $chainedSubCommandData[] = $this->lookupChainedSubCommandData($dataIndex);
227 }
228
229 $overloads = [];
230 foreach($rawData->getOverloads() as $overloadIndex => $rawOverloadData){
231 $parameters = [];
232 foreach($rawOverloadData->getParameters() as $parameterIndex => $rawParameterData){
233 $typeInfo = $rawParameterData->getTypeInfo();
234 $flags = $typeInfo & (
236 AvailableCommandsPacket::ARG_FLAG_SOFT_ENUM |
239 );
240 //these flags are mutually exclusive - more than one is an error
241 $enum = null;
242 $postfix = null;
243 $highLevelTypeInfo = 0;
245 $index = $typeInfo & (AvailableCommandsPacket::ARG_FLAG_VALID - 1);
246 $enum = $this->lookupHardEnum($index);
247 }elseif($flags === (AvailableCommandsPacket::ARG_FLAG_SOFT_ENUM | AvailableCommandsPacket::ARG_FLAG_VALID)){
248 $index = $typeInfo & (AvailableCommandsPacket::ARG_FLAG_VALID - 1);
249 $enum = $this->lookupSoftEnum($index);
251 $index = $typeInfo & (AvailableCommandsPacket::ARG_FLAG_POSTFIX - 1);
252 $postfix = $this->lookupPostfix($index);
253 }elseif($flags === AvailableCommandsPacket::ARG_FLAG_VALID){
254 $highLevelTypeInfo = $typeInfo & (AvailableCommandsPacket::ARG_FLAG_VALID - 1);
255 }else{
256 throw new PacketDecodeException("Unrecognized arg flag combination $typeInfo for command " . $rawData->getName() . ", overload $overloadIndex, parameter $parameterIndex");
257 }
258
259 $parameters[] = CommandParameter::allFields(
260 paramName: $rawParameterData->getName(),
261 paramType: $highLevelTypeInfo,
262 isOptional: $rawParameterData->isOptional(),
263 flags: $rawParameterData->getFlags(),
264 enum: $enum,
265 postfix: $postfix
266 );
267 }
268
269 $overloads[] = new CommandOverload($rawOverloadData->isChaining(), $parameters);
270 }
271
272 return new CommandData(
273 name: $rawData->getName(),
274 description: $rawData->getDescription(),
275 flags: $rawData->getFlags(),
276 permission: $rawData->getPermission(),
277 aliases: $aliasesEnum,
278 overloads: $overloads,
279 chainedSubCommandData: $chainedSubCommandData
280 );
281 }
282}
static allFields(string $paramName, int $paramType, bool $isOptional, int $flags, CommandHardEnum|CommandSoftEnum|null $enum, ?string $postfix)