$memberNames * * @return string[] * @phpstan-return list */ function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{ $bits = getBitsRequired($memberNames); $lines = []; $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; $lines[] = "\t\$this->writeInt($bits, match(\$value){"; foreach($memberNames as $key => $memberName){ $lines[] = "\t\t$memberName => $key,"; } $lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")"; $lines[] = "\t});"; $lines[] = "}"; return $lines; } /** * @param string[] $memberNames * @phpstan-param list $memberNames * * @return string[] * @phpstan-return list */ function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{ $bits = getBitsRequired($memberNames); $lines = []; $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; $lines[] = "\t\$value = match(\$this->readInt($bits)){"; foreach($memberNames as $key => $memberName){ $lines[] = "\t\t$key => $memberName,"; } $lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")"; $lines[] = "\t};"; $lines[] = "}"; return $lines; } function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{ return "public function $functionName(\\$nativeTypeName &\$value) : void;"; } /** * @param string[] $memberNames * @phpstan-param list $memberNames * * @return string[] * @phpstan-return list */ function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{ $lines = []; $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; $lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");"; $lines[] = "}"; return $lines; } /** * @param mixed[] $members */ function getBitsRequired(array $members) : int{ return (int) ceil(log(count($members), 2)); } /** * @param object[] $members * @phpstan-param array $members * * @return string[] * @phpstan-return list */ function stringifyEnumMembers(array $members, string $enumClass) : array{ ksort($members, SORT_STRING); return array_map(fn(string $enumCaseName) => "\\$enumClass::$enumCaseName()", array_keys($members)); } $enumsUsed = [ BellAttachmentType::getAll(), CopperOxidation::getAll(), CoralType::getAll(), DirtType::getAll(), DyeColor::getAll(), FroglightType::getAll(), LeverFacing::getAll(), MedicineType::getAll(), MushroomBlockType::getAll(), SkullType::getAll(), SlabType::getAll(), SuspiciousStewType::getAll(), PotionType::getAll() ]; $readerFuncs = [ "" => [ "abstract protected function readInt(int \$bits) : int;" ] ]; $writerFuncs = [ "" => [ "abstract protected function writeInt(int \$bits, int \$value) : void;" ] ]; $interfaceFuncs = []; $sizeCalculationFuncs = [ "" => [ "abstract protected function addBits(int \$bits) : void;" ] ]; foreach($enumsUsed as $enumMembers){ if(count($enumMembers) === 0){ throw new \InvalidArgumentException("Enum members cannot be empty"); } $reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]); $virtualTypeName = $reflect->getShortName(); $nativeTypeName = $reflect->getName(); $functionName = lcfirst($virtualTypeName); $stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName); $writerFuncs[$functionName] = buildWriterFunc( $virtualTypeName, $nativeTypeName, $stringifiedMembers, $functionName ); $readerFuncs[$functionName] = buildReaderFunc( $virtualTypeName, $nativeTypeName, $stringifiedMembers, $functionName ); $interfaceFuncs[$functionName] = [buildInterfaceFunc( $nativeTypeName, $functionName )]; $sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc( $nativeTypeName, $functionName, $stringifiedMembers ); } /** * @param string[][] $functions * @phpstan-param array> $functions */ function printFunctions(array $functions, string $className, string $classType) : void{ ksort($functions, SORT_STRING); ob_start(); echo <<<'HEADER' "\t" . implode("\n\t", $functionLines), $functions)); echo "\n\n}\n"; file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean()); } printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait"); printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait"); printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface"); printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait"); echo "Done. Don't forget to run CS fixup after generating code.\n";