PocketMine-MP 5.23.3 git-f7687af337d001ddbcc47b8e773f014a33faa662
Loading...
Searching...
No Matches
generate-protocol-info.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\tools\generate_protocol_info;
16
21use pocketmine\network\mcpe\protocol\PacketHandlerDefaultImplTrait;
25use function array_fill_keys;
26use function asort;
27use function ceil;
28use function count;
29use function dechex;
30use function dirname;
31use function file_exists;
32use function file_get_contents;
33use function file_put_contents;
34use function fwrite;
35use function implode;
36use function is_array;
37use function is_bool;
38use function is_int;
39use function is_string;
40use function json_decode;
41use function max;
42use function preg_split;
43use function scandir;
44use function sprintf;
45use function str_contains;
46use function str_ends_with;
47use function str_pad;
48use function strlen;
49use function strtoupper;
50use function substr;
51use const DIRECTORY_SEPARATOR;
52use const JSON_THROW_ON_ERROR;
53use const PHP_EOL;
54use const PREG_SPLIT_DELIM_CAPTURE;
55use const PREG_SPLIT_NO_EMPTY;
56use const SORT_NUMERIC;
57use const STDERR;
58use const STR_PAD_LEFT;
59
60const DATA_PACKET_TEMPLATE = <<<'CODE'
61<?php
62
63/*
64 * This file is part of BedrockProtocol.
65 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
66 *
67 * BedrockProtocol is free software: you can redistribute it and/or modify
68 * it under the terms of the GNU Lesser General Public License as published by
69 * the Free Software Foundation, either version 3 of the License, or
70 * (at your option) any later version.
71 */
72
73declare(strict_types=1);
74
75namespace pocketmine\network\mcpe\protocol;
76
78
79class %s extends DataPacket{
80 public const NETWORK_ID = ProtocolInfo::%s;
81
85 public static function create() : self{
86 $result = new self;
87 //TODO: add fields
88 return $result;
89 }
90
91 protected function decodePayload(PacketSerializer $in) : void{
92 //TODO
93 }
94
95 protected function encodePayload(PacketSerializer $out) : void{
96 //TODO
97 }
98
99 public function handle(PacketHandlerInterface $handler) : bool{
100 return $handler->handle%s($this);
101 }
102}
103
104CODE;
105
106const PACKET_HANDLER_TRAIT_TEMPLATE = <<<'CODE'
107<?php
108
109/*
110 * This file is part of BedrockProtocol.
111 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
112 *
113 * BedrockProtocol is free software: you can redistribute it and/or modify
114 * it under the terms of the GNU Lesser General Public License as published by
115 * the Free Software Foundation, either version 3 of the License, or
116 * (at your option) any later version.
117 */
118
119declare(strict_types=1);
120
121namespace pocketmine\network\mcpe\protocol;
122
129trait PacketHandlerDefaultImplTrait{
130
131%s
132}
133
134CODE;
135
136const PACKET_HANDLER_INTERFACE_TEMPLATE = <<<'CODE'
137<?php
138
139/*
140 * This file is part of BedrockProtocol.
141 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
142 *
143 * BedrockProtocol is free software: you can redistribute it and/or modify
144 * it under the terms of the GNU Lesser General Public License as published by
145 * the Free Software Foundation, either version 3 of the License, or
146 * (at your option) any later version.
147 */
148
149declare(strict_types=1);
150
151namespace pocketmine\network\mcpe\protocol;
152
156interface PacketHandlerInterface{
157%s
158}
159
160CODE;
161
162const PACKET_POOL_TEMPLATE = <<<'CODE'
163<?php
164
165/*
166 * This file is part of BedrockProtocol.
167 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
168 *
169 * BedrockProtocol is free software: you can redistribute it and/or modify
170 * it under the terms of the GNU Lesser General Public License as published by
171 * the Free Software Foundation, either version 3 of the License, or
172 * (at your option) any later version.
173 */
174
175declare(strict_types=1);
176
177namespace pocketmine\network\mcpe\protocol;
178
181
182class PacketPool{
183 protected static ?PacketPool $instance = null;
184
185 public static function getInstance() : self{
186 if(self::$instance === null){
187 self::$instance = new self;
188 }
189 return self::$instance;
190 }
191
193 protected \SplFixedArray $pool;
194
195 public function __construct(){
196 $this->pool = new \SplFixedArray(%d);
197%s
198 }
199
200 public function registerPacket(Packet $packet) : void{
201 $this->pool[$packet->pid()] = clone $packet;
202 }
203
204 public function getPacketById(int $pid) : ?Packet{
205 return isset($this->pool[$pid]) ? clone $this->pool[$pid] : null;
206 }
207
211 public function getPacket(string $buffer) : ?Packet{
212 $offset = 0;
213 return $this->getPacketById(Binary::readUnsignedVarInt($buffer, $offset) & DataPacket::PID_MASK);
214 }
215}
216
217CODE;
218
219const PROTOCOL_INFO_TEMPLATE = <<<'CODE'
220<?php
221
222/*
223 * This file is part of BedrockProtocol.
224 * Copyright (C) 2014-2022 PocketMine Team <https://github.com/pmmp/BedrockProtocol>
225 *
226 * BedrockProtocol is free software: you can redistribute it and/or modify
227 * it under the terms of the GNU Lesser General Public License as published by
228 * the Free Software Foundation, either version 3 of the License, or
229 * (at your option) any later version.
230 */
231
232declare(strict_types=1);
233
234namespace pocketmine\network\mcpe\protocol;
235
239final class ProtocolInfo{
240
241 private function __construct(){
242 //NOOP
243 }
244
254 public const CURRENT_PROTOCOL = %d;
256 public const MINECRAFT_VERSION = '%s';
258 public const MINECRAFT_VERSION_NETWORK = '%s';
259
260%s
261}
262
263CODE;
264
268function split_upper(string $string) : array{
269 $split = preg_split('/([A-Z][^A-Z]*)/', $string, flags: PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
270 if($split === false){
271 throw new \Error("preg_split failed");
272 }
273 return $split;
274}
275
276function rchop(string $string, string $substring) : string{
277 if(str_ends_with($string, $substring)){
278 return substr($string, 0, -strlen($substring));
279 }
280 return $string;
281}
282
287function generate_new_packet_stubs(array $packetToIdList, string $packetsDir) : void{
288 foreach($packetToIdList as $name => $id){
289 $packetFilePath = $packetsDir . DIRECTORY_SEPARATOR . $name . '.php';
290 if(!file_exists($packetFilePath)){
291 echo "!!! New packet: $name" . PHP_EOL;
292 $constName = strtoupper(implode('_', split_upper($name)));
293 $baseName = rchop($name, 'Packet');
294 file_put_contents($packetFilePath, sprintf(DATA_PACKET_TEMPLATE, $name, $constName, $baseName));
295 echo "Created stub class for $name at $packetFilePath" . PHP_EOL;
296 }
297 }
298}
299
304function check_removed_packets(array $packetToIdList, string $packetsDir) : void{
305 $existing = scandir($packetsDir);
306 if($existing === false){
307 return;
308 }
309
310 //use ::class constants here so that they are checked for existence
311 $ignoredClasses = array_fill_keys([
312 DataPacket::class,
313 PacketPool::class,
314 Packet::class,
315 PacketDecodeException::class,
316 PacketHandlerDefaultImplTrait::class,
317 PacketHandlerInterface::class,
318 ClientboundPacket::class,
319 ServerboundPacket::class,
320 ], true);
321 foreach($existing as $fileName){
322 if(str_ends_with($fileName, ".php")){
323 $packetName = substr($fileName, 0, -strlen(".php"));
324 if(!str_contains($packetName, "Packet") || isset($ignoredClasses["pocketmine\\network\\mcpe\\protocol\\" . $packetName])){
325 continue;
326 }
327 if(!isset($packetToIdList[$packetName])){
328 echo "!!! Removed packet: $packetName" . PHP_EOL;
329 }
330 }
331 }
332}
333
338function generate_protocol_info(array $packetToIdList, int $protocolVersion, int $major, int $minor, int $patch, int $revision, bool $beta, string $packetsDir) : void{
339 $consts = "";
340 $last = 0;
341
342 foreach($packetToIdList as $name => $id){
343 if($id !== $last + 1){
344 $consts .= "\n";
345 }
346
347 $last = $id;
348 $consts .= sprintf(
349 "\tpublic const %s = %s;\n",
350 strtoupper(implode("_", split_upper($name))),
351 "0x" . str_pad(dechex($id), 2, "0", STR_PAD_LEFT)
352 );
353 }
354
355 $gameVersion = sprintf("v%d.%d.%d%s", $major, $minor, $patch, $beta ? ".$revision beta" : "");
356 $gameVersionNetwork = sprintf("%d.%d.%d%s", $major, $minor, $patch, $beta ? ".$revision" : "");
357 file_put_contents($packetsDir . DIRECTORY_SEPARATOR . "ProtocolInfo.php", sprintf(
358 PROTOCOL_INFO_TEMPLATE,
359 $protocolVersion,
360 $gameVersion,
361 $gameVersionNetwork,
362 $consts
363 ));
364
365 echo "Recreated ProtocolInfo" . PHP_EOL;
366}
367
372function generate_packet_pool(array $packetToIdList, string $packetsDir) : void{
373 $entries = "";
374
375 foreach($packetToIdList as $name => $id){
376 $entries .= sprintf("\n\t\t\$this->registerPacket(new %s());", $name);
377 }
378
379 $poolSize = (int) (ceil(max($packetToIdList) / 256) * 256);
380 file_put_contents($packetsDir . DIRECTORY_SEPARATOR . "PacketPool.php", sprintf(
381 PACKET_POOL_TEMPLATE,
382 $poolSize,
383 $entries
384 ));
385 echo "Recreated PacketPool\n";
386}
387
392function generate_packet_handler_classes(array $packetToIdList, string $packetsDir) : void{
393 $interfaceFunctions = [];
394 $traitFunctions = [];
395
396 foreach($packetToIdList as $name => $id){
397 $baseName = rchop($name, "Packet");
398 $interfaceFunctions[] = sprintf("\tpublic function handle%s(%s \$packet) : bool;", $baseName, $name);
399 $traitFunctions[] = sprintf("\tpublic function handle%s(%s \$packet) : bool{\n\t\treturn false;\n\t}", $baseName, $name);
400 }
401
402 file_put_contents($packetsDir . DIRECTORY_SEPARATOR . "PacketHandlerInterface.php", sprintf(
403 PACKET_HANDLER_INTERFACE_TEMPLATE,
404 implode("\n\n", $interfaceFunctions)
405 ));
406 echo "Recreated PacketHandlerInterface" . PHP_EOL;
407 file_put_contents($packetsDir . DIRECTORY_SEPARATOR . "PacketHandlerDefaultImplTrait.php", sprintf(
408 PACKET_HANDLER_TRAIT_TEMPLATE,
409 implode("\n\n", $traitFunctions)
410 ));
411 echo "Recreated PacketHandlerDefaultImplTrait" . PHP_EOL;
412}
413
414if(count($argv) < 2){
415 fwrite(STDERR, "Please provide an input protocol_info.json file" . PHP_EOL);
416 exit(1);
417}
418
419$rawData = file_get_contents($argv[1]);
420if($rawData === false){
421 fwrite(STDERR, "Couldn't read data from " . $argv[1] . PHP_EOL);
422 exit(1);
423}
424
425try{
426 $json = json_decode($rawData, associative: true, flags: JSON_THROW_ON_ERROR);
427}catch(\JsonException $e){
428 fwrite(STDERR, "Error decoding input file: " . $e->getMessage() . PHP_EOL);
429 exit(1);
430}
431if(!is_array($json) || count($json) !== 2 || !is_array($json["version"] ?? null) || !is_array($json["packets"] ?? null)){
432 fwrite(STDERR, "Invalid input file, expected 2 objects: \"version\" and \"packets\"" . PHP_EOL);
433 exit(1);
434}
435
436$versionInfo = $json["version"];
437$major = $versionInfo["major"] ?? null;
438$minor = $versionInfo["minor"] ?? null;
439$patch = $versionInfo["patch"] ?? null;
440$revision = $versionInfo["revision"] ?? null;
441$beta = $versionInfo["beta"] ?? null;
442$protocolVersion = $versionInfo["protocol_version"] ?? null;
443if(!is_int($major) || !is_int($minor) || !is_int($patch) || !is_int($revision) || !is_bool($beta) || !is_int($protocolVersion)){
444 fwrite(STDERR, "Invalid version info, expected \"major\" (int), \"minor\" (int), \"patch\" (int), \"revision\" (int), \"beta\" (bool) and \"protocol_version\" (int)" . PHP_EOL);
445 exit(1);
446}
447
448echo "Generating code basics for version $major.$minor.$patch.$revision " . ($beta ? "beta" : "") . " (protocol $protocolVersion)" . PHP_EOL;
449
450$packetToIdList = [];
451foreach($json["packets"] as $name => $id){
452 if(!is_string($name) || !is_int($id)){
453 fwrite(STDERR, "Invalid packet entry \"$name\", expected string => int" . PHP_EOL);
454 exit(1);
455 }
456 $packetToIdList[$name] = $id;
457}
458asort($packetToIdList, SORT_NUMERIC);
459
460$packetsDir = dirname(__DIR__) . '/src/';
461generate_protocol_info($packetToIdList, $protocolVersion, $major, $minor, $patch, $revision, $beta, $packetsDir);
462generate_packet_pool($packetToIdList, $packetsDir);
463generate_packet_handler_classes($packetToIdList, $packetsDir);
464check_removed_packets($packetToIdList, $packetsDir);
465generate_new_packet_stubs($packetToIdList, $packetsDir);
466
467echo "Done" . PHP_EOL;