CraftingManager: make a less dumb hash function

fixes #4415
This commit is contained in:
Dylan K. Taylor
2025-02-17 15:35:18 +00:00
parent 51cf6817b1
commit 9744bd7073

View File

@ -29,8 +29,12 @@ use pocketmine\nbt\TreeRoot;
use pocketmine\utils\BinaryStream; use pocketmine\utils\BinaryStream;
use pocketmine\utils\DestructorCallbackTrait; use pocketmine\utils\DestructorCallbackTrait;
use pocketmine\utils\ObjectSet; use pocketmine\utils\ObjectSet;
use function array_shift;
use function count;
use function implode;
use function ksort;
use function spl_object_id; use function spl_object_id;
use function usort; use const SORT_STRING;
class CraftingManager{ class CraftingManager{
use DestructorCallbackTrait; use DestructorCallbackTrait;
@ -100,6 +104,7 @@ class CraftingManager{
/** /**
* Function used to arrange Shapeless Recipe ingredient lists into a consistent order. * Function used to arrange Shapeless Recipe ingredient lists into a consistent order.
* @deprecated
*/ */
public static function sort(Item $i1, Item $i2) : int{ public static function sort(Item $i1, Item $i2) : int{
//Use spaceship operator to compare each property, then try the next one if they are equivalent. //Use spaceship operator to compare each property, then try the next one if they are equivalent.
@ -108,45 +113,30 @@ class CraftingManager{
return $retval; return $retval;
} }
/** private static function hashOutput(Item $output) : string{
* @param Item[] $items $write = new BinaryStream();
* $write->putVarInt($output->getStateId());
* @return Item[] $write->put((new LittleEndianNbtSerializer())->write(new TreeRoot($output->getNamedTag())));
* @phpstan-return list<Item>
*/
private static function pack(array $items) : array{
$result = [];
foreach($items as $item){ return $write->getBuffer();
foreach($result as $otherItem){
if($item->canStackWith($otherItem)){
$otherItem->setCount($otherItem->getCount() + $item->getCount());
continue 2;
}
}
//No matching item found
$result[] = clone $item;
}
return $result;
} }
/** /**
* @param Item[] $outputs * @param Item[] $outputs
*/ */
private static function hashOutputs(array $outputs) : string{ private static function hashOutputs(array $outputs) : string{
$outputs = self::pack($outputs); if(count($outputs) === 1){
usort($outputs, [self::class, "sort"]); return self::hashOutput(array_shift($outputs));
$result = new BinaryStream(); }
$unique = [];
foreach($outputs as $o){ foreach($outputs as $o){
//count is not written because the outputs might be from multiple repetitions of a single recipe //count is not written because the outputs might be from multiple repetitions of a single recipe
//this reduces the accuracy of the hash, but it won't matter in most cases. //this reduces the accuracy of the hash, but it won't matter in most cases.
$result->putVarInt($o->getStateId()); $hash = self::hashOutput($o);
$result->put((new LittleEndianNbtSerializer())->write(new TreeRoot($o->getNamedTag()))); $unique[$hash] = $hash;
} }
ksort($unique, SORT_STRING);
return $result->getBuffer(); return implode("", $unique);
} }
/** /**