TypeConverter: account for items without properly mapped IDs

fixes #4459
This commit is contained in:
Dylan K. Taylor 2021-10-11 15:05:08 +01:00
parent 19a66a8d03
commit 8ac16345a3
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
2 changed files with 50 additions and 19 deletions

View File

@ -142,10 +142,10 @@ final class ItemTranslator{
}
/**
* @return int[]
* @phpstan-return array{int, int}
* @return int[]|null
* @phpstan-return array{int, int}|null
*/
public function toNetworkId(int $internalId, int $internalMeta) : array{
public function toNetworkIdQuiet(int $internalId, int $internalMeta) : ?array{
if($internalMeta === -1){
$internalMeta = 0x7fff;
}
@ -156,7 +156,16 @@ final class ItemTranslator{
return [$this->simpleCoreToNetMapping[$internalId], $internalMeta];
}
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
return null;
}
/**
* @return int[]
* @phpstan-return array{int, int}
*/
public function toNetworkId(int $internalId, int $internalMeta) : array{
return $this->toNetworkIdQuiet($internalId, $internalMeta) ??
throw new \InvalidArgumentException("Unmapped ID/metadata combination $internalId:$internalMeta");
}
/**

View File

@ -58,6 +58,7 @@ class TypeConverter{
private const DAMAGE_TAG = "Damage"; //TAG_Int
private const DAMAGE_TAG_CONFLICT_RESOLUTION = "___Damage_ProtocolCollisionResolution___";
private const PM_ID_TAG = "___Id___";
private const PM_META_TAG = "___Meta___";
/** @var int */
@ -143,26 +144,40 @@ class TypeConverter{
}
$isBlockItem = $itemStack->getId() < 256;
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
if($nbt !== null){
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
$nbt->removeTag(self::DAMAGE_TAG);
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
}
}else{
$nbt = new CompoundTag();
}
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
$idMeta = ItemTranslator::getInstance()->toNetworkIdQuiet($itemStack->getId(), $itemStack->getMeta());
if($idMeta === null){
//Display unmapped items as INFO_UPDATE, but stick something in their NBT to make sure they don't stack with
//other unmapped items.
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId(ItemIds::INFO_UPDATE, 0);
if($nbt === null){
$nbt = new CompoundTag();
}
$nbt->setInt(self::PM_ID_TAG, $itemStack->getId());
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
}else{
[$id, $meta] = $idMeta;
if($itemStack instanceof Durable and $itemStack->getDamage() > 0){
if($nbt !== null){
if(($existing = $nbt->getTag(self::DAMAGE_TAG)) !== null){
$nbt->removeTag(self::DAMAGE_TAG);
$nbt->setTag(self::DAMAGE_TAG_CONFLICT_RESOLUTION, $existing);
}
}else{
$nbt = new CompoundTag();
}
$nbt->setInt(self::DAMAGE_TAG, $itemStack->getDamage());
}elseif($isBlockItem && $itemStack->getMeta() !== 0){
//TODO HACK: This foul-smelling code ensures that we can correctly deserialize an item when the
//client sends it back to us, because as of 1.16.220, blockitems quietly discard their metadata
//client-side. Aside from being very annoying, this also breaks various server-side behaviours.
if($nbt === null){
$nbt = new CompoundTag();
}
$nbt->setInt(self::PM_META_TAG, $itemStack->getMeta());
}
}
[$id, $meta] = ItemTranslator::getInstance()->toNetworkId($itemStack->getId(), $itemStack->getMeta());
$blockRuntimeId = 0;
if($isBlockItem){
@ -197,6 +212,13 @@ class TypeConverter{
if($compound !== null){
$compound = clone $compound;
if(($idTag = $compound->getTag(self::PM_ID_TAG)) instanceof IntTag){
$id = $idTag->getValue();
$compound->removeTag(self::PM_ID_TAG);
if($compound->count() === 0){
$compound = null;
}
}
if(($damageTag = $compound->getTag(self::DAMAGE_TAG)) instanceof IntTag){
$meta = $damageTag->getValue();
$compound->removeTag(self::DAMAGE_TAG);