mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Merge branch 'minor-next' into major-next
This commit is contained in:
commit
c4ff6d7757
@ -32,12 +32,11 @@
|
||||
"ext-zlib": ">=1.2.11",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"adhocore/json-comment": "~1.2.0",
|
||||
"fgrosse/phpasn1": "~2.5.0",
|
||||
"pocketmine/netresearch-jsonmapper": "~v4.2.1000",
|
||||
"pocketmine/bedrock-block-upgrade-schema": "~3.1.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-data": "~2.4.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-item-upgrade-schema": "~1.4.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-protocol": "~23.0.0+bedrock-1.20.10",
|
||||
"pocketmine/bedrock-protocol": "~23.0.2+bedrock-1.20.10",
|
||||
"pocketmine/binaryutils": "^0.2.1",
|
||||
"pocketmine/callback-validator": "^1.0.2",
|
||||
"pocketmine/color": "^0.3.0",
|
||||
|
90
composer.lock
generated
90
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "74166e6c2f09b356c83a951efef349f2",
|
||||
"content-hash": "ccd20e7656bc05ec2acd8e28aad9fcf2",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -120,82 +120,6 @@
|
||||
],
|
||||
"time": "2023-01-15T23:15:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fgrosse/phpasn1",
|
||||
"version": "v2.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fgrosse/PHPASN1.git",
|
||||
"reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fgrosse/PHPASN1/zipball/42060ed45344789fb9f21f9f1864fc47b9e3507b",
|
||||
"reference": "42060ed45344789fb9f21f9f1864fc47b9e3507b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "~2.0",
|
||||
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "BCmath is the fallback extension for big integer calculations",
|
||||
"ext-curl": "For loading OID information from the web if they have not bee defined statically",
|
||||
"ext-gmp": "GMP is the preferred extension for big integer calculations",
|
||||
"phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FG\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Friedrich Große",
|
||||
"email": "friedrich.grosse@gmail.com",
|
||||
"homepage": "https://github.com/FGrosse",
|
||||
"role": "Author"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1",
|
||||
"keywords": [
|
||||
"DER",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"ber",
|
||||
"binary",
|
||||
"decoding",
|
||||
"encoding",
|
||||
"x.509",
|
||||
"x.690",
|
||||
"x509",
|
||||
"x690"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/fgrosse/PHPASN1/issues",
|
||||
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.5.0"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2022-12-19T11:08:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-block-upgrade-schema",
|
||||
"version": "3.1.0",
|
||||
@ -276,16 +200,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/bedrock-protocol",
|
||||
"version": "23.0.1+bedrock-1.20.10",
|
||||
"version": "23.0.2+bedrock-1.20.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BedrockProtocol.git",
|
||||
"reference": "db48400736799cc3833a2644a02e308992a98fa8"
|
||||
"reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/db48400736799cc3833a2644a02e308992a98fa8",
|
||||
"reference": "db48400736799cc3833a2644a02e308992a98fa8",
|
||||
"url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/69a309a2dd7dcf3ec8c316385b866397e8c2cbfd",
|
||||
"reference": "69a309a2dd7dcf3ec8c316385b866397e8c2cbfd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -317,9 +241,9 @@
|
||||
"description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/BedrockProtocol/issues",
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.1+bedrock-1.20.10"
|
||||
"source": "https://github.com/pmmp/BedrockProtocol/tree/23.0.2+bedrock-1.20.10"
|
||||
},
|
||||
"time": "2023-07-18T21:07:24+00:00"
|
||||
"time": "2023-07-24T15:35:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
|
@ -24,9 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
|
||||
class CarvedPumpkin extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
}
|
||||
|
@ -24,14 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class ChemistryTable extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
//TODO
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Chest as TileChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\event\block\ChestPairEvent;
|
||||
use pocketmine\item\Item;
|
||||
@ -36,7 +35,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class Chest extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
|
@ -24,14 +24,12 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
class EndPortalFrame extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected bool $eye = false;
|
||||
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\inventory\EnderChestInventory;
|
||||
use pocketmine\block\tile\EnderChest as TileEnderChest;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -36,7 +35,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class EnderChest extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return 7;
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Furnace as TileFurnace;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\crafting\FurnaceType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -35,7 +34,6 @@ use function mt_rand;
|
||||
|
||||
class Furnace extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected FurnaceType $furnaceType;
|
||||
|
||||
|
@ -26,12 +26,10 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
|
||||
class GlazedTerracotta extends Opaque{
|
||||
use ColoredTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::BLACK();
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\Lectern as TileLectern;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -39,7 +38,6 @@ use function count;
|
||||
|
||||
class Lectern extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
protected int $viewedPage = 0;
|
||||
protected ?WritableBookBase $book = null;
|
||||
|
@ -25,14 +25,12 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\LoomInventory;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
|
||||
final class Loom extends Opaque{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\inventory\StonecutterInventory;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
@ -35,7 +34,6 @@ use pocketmine\player\Player;
|
||||
|
||||
class Stonecutter extends Transparent{
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($player !== null){
|
||||
|
@ -23,28 +23,26 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe;
|
||||
|
||||
use FG\ASN1\Exception\ParserException;
|
||||
use FG\ASN1\Universal\Integer;
|
||||
use FG\ASN1\Universal\Sequence;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\BinaryStream;
|
||||
use pocketmine\utils\Utils;
|
||||
use function base64_decode;
|
||||
use function base64_encode;
|
||||
use function bin2hex;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function explode;
|
||||
use function gmp_export;
|
||||
use function gmp_import;
|
||||
use function gmp_init;
|
||||
use function gmp_strval;
|
||||
use function is_array;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function json_last_error_msg;
|
||||
use function ltrim;
|
||||
use function openssl_error_string;
|
||||
use function openssl_pkey_get_details;
|
||||
use function openssl_pkey_get_public;
|
||||
use function openssl_sign;
|
||||
use function openssl_verify;
|
||||
use function ord;
|
||||
use function preg_match;
|
||||
use function rtrim;
|
||||
use function sprintf;
|
||||
@ -54,8 +52,7 @@ use function str_replace;
|
||||
use function str_split;
|
||||
use function strlen;
|
||||
use function strtr;
|
||||
use const GMP_BIG_ENDIAN;
|
||||
use const GMP_MSW_FIRST;
|
||||
use function substr;
|
||||
use const JSON_THROW_ON_ERROR;
|
||||
use const OPENSSL_ALGO_SHA384;
|
||||
use const STR_PAD_LEFT;
|
||||
@ -63,6 +60,12 @@ use const STR_PAD_LEFT;
|
||||
final class JwtUtils{
|
||||
public const BEDROCK_SIGNING_KEY_CURVE_NAME = "secp384r1";
|
||||
|
||||
private const ASN1_INTEGER_TAG = "\x02";
|
||||
private const ASN1_SEQUENCE_TAG = "\x30";
|
||||
|
||||
private const SIGNATURE_PART_LENGTH = 48;
|
||||
private const SIGNATURE_ALGORITHM = OPENSSL_ALGO_SHA384;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return array{string, string, string}
|
||||
@ -98,30 +101,84 @@ final class JwtUtils{
|
||||
return [$header, $body, $signature];
|
||||
}
|
||||
|
||||
private static function signaturePartToAsn1(string $part) : string{
|
||||
if(strlen($part) !== self::SIGNATURE_PART_LENGTH){
|
||||
throw new JwtException("R and S for a SHA384 signature must each be exactly 48 bytes, but have " . strlen($part) . " bytes");
|
||||
}
|
||||
$part = ltrim($part, "\x00");
|
||||
if(ord($part[0]) >= 128){
|
||||
//ASN.1 integers with a leading 1 bit are considered negative - add a leading 0 byte to prevent this
|
||||
//ECDSA signature R and S values are always positive
|
||||
$part = "\x00" . $part;
|
||||
}
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
return self::ASN1_INTEGER_TAG . chr(strlen($part)) . $part;
|
||||
}
|
||||
|
||||
private static function rawSignatureToDer(string $rawSignature) : string{
|
||||
if(strlen($rawSignature) !== self::SIGNATURE_PART_LENGTH * 2){
|
||||
throw new JwtException("JWT signature has unexpected length, expected 96, got " . strlen($rawSignature));
|
||||
}
|
||||
|
||||
[$rString, $sString] = str_split($rawSignature, self::SIGNATURE_PART_LENGTH);
|
||||
$sequence = self::signaturePartToAsn1($rString) . self::signaturePartToAsn1($sString);
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
return self::ASN1_SEQUENCE_TAG . chr(strlen($sequence)) . $sequence;
|
||||
}
|
||||
|
||||
private static function signaturePartFromAsn1(BinaryStream $stream) : string{
|
||||
$prefix = $stream->get(1);
|
||||
if($prefix !== self::ASN1_INTEGER_TAG){
|
||||
throw new \InvalidArgumentException("Expected an ASN.1 INTEGER tag, got " . bin2hex($prefix));
|
||||
}
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
$length = $stream->getByte();
|
||||
if($length > self::SIGNATURE_PART_LENGTH + 1){ //each part may have an extra leading 0 byte to prevent it being interpreted as a negative number
|
||||
throw new \InvalidArgumentException("Expected at most 49 bytes for signature R or S, got $length");
|
||||
}
|
||||
$part = $stream->get($length);
|
||||
return str_pad(ltrim($part, "\x00"), self::SIGNATURE_PART_LENGTH, "\x00", STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
private static function rawSignatureFromDer(string $derSignature) : string{
|
||||
if($derSignature[0] !== self::ASN1_SEQUENCE_TAG){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, expected ASN.1 SEQUENCE tag, got " . bin2hex($derSignature[0]));
|
||||
}
|
||||
|
||||
//we can assume the length is 1 byte here - if it were larger than 127, more complex logic would be needed
|
||||
$length = ord($derSignature[1]);
|
||||
$parts = substr($derSignature, 2, $length);
|
||||
if(strlen($parts) !== $length){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, expected $length sequence bytes, got " . strlen($parts));
|
||||
}
|
||||
|
||||
$stream = new BinaryStream($parts);
|
||||
$rRaw = self::signaturePartFromAsn1($stream);
|
||||
$sRaw = self::signaturePartFromAsn1($stream);
|
||||
|
||||
if(!$stream->feof()){
|
||||
throw new \InvalidArgumentException("Invalid DER signature, unexpected trailing sequence data");
|
||||
}
|
||||
|
||||
return $rRaw . $sRaw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws JwtException
|
||||
*/
|
||||
public static function verify(string $jwt, \OpenSSLAsymmetricKey $signingKey) : bool{
|
||||
[$header, $body, $signature] = self::split($jwt);
|
||||
|
||||
$plainSignature = self::b64UrlDecode($signature);
|
||||
if(strlen($plainSignature) !== 96){
|
||||
throw new JwtException("JWT signature has unexpected length, expected 96, got " . strlen($plainSignature));
|
||||
}
|
||||
|
||||
[$rString, $sString] = str_split($plainSignature, 48);
|
||||
$convert = fn(string $str) => gmp_strval(gmp_import($str, 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST), 10);
|
||||
|
||||
$sequence = new Sequence(
|
||||
new Integer($convert($rString)),
|
||||
new Integer($convert($sString))
|
||||
);
|
||||
$rawSignature = self::b64UrlDecode($signature);
|
||||
$derSignature = self::rawSignatureToDer($rawSignature);
|
||||
|
||||
$v = openssl_verify(
|
||||
$header . '.' . $body,
|
||||
$sequence->getBinary(),
|
||||
$derSignature,
|
||||
$signingKey,
|
||||
OPENSSL_ALGO_SHA384
|
||||
self::SIGNATURE_ALGORITHM
|
||||
);
|
||||
switch($v){
|
||||
case 0: return false;
|
||||
@ -140,33 +197,13 @@ final class JwtUtils{
|
||||
|
||||
openssl_sign(
|
||||
$jwtBody,
|
||||
$rawDerSig,
|
||||
$derSignature,
|
||||
$signingKey,
|
||||
OPENSSL_ALGO_SHA384
|
||||
self::SIGNATURE_ALGORITHM
|
||||
);
|
||||
|
||||
try{
|
||||
$asnObject = Sequence::fromBinary($rawDerSig);
|
||||
}catch(ParserException $e){
|
||||
throw new AssumptionFailedError("Failed to parse OpenSSL signature: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
if(count($asnObject) !== 2){
|
||||
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected exactly 2 parts");
|
||||
}
|
||||
[$r, $s] = [$asnObject[0], $asnObject[1]];
|
||||
if(!($r instanceof Integer) || !($s instanceof Integer)){
|
||||
throw new AssumptionFailedError("OpenSSL produced invalid signature, expected 2 INTEGER parts");
|
||||
}
|
||||
$rString = $r->getContent();
|
||||
$sString = $s->getContent();
|
||||
|
||||
$toBinary = fn($str) => str_pad(
|
||||
gmp_export(gmp_init($str, 10), 1, GMP_BIG_ENDIAN | GMP_MSW_FIRST),
|
||||
48,
|
||||
"\x00",
|
||||
STR_PAD_LEFT
|
||||
);
|
||||
$jwtSig = JwtUtils::b64UrlEncode($toBinary($rString) . $toBinary($sString));
|
||||
$rawSignature = self::rawSignatureFromDer($derSignature);
|
||||
$jwtSig = self::b64UrlEncode($rawSignature);
|
||||
|
||||
return "$jwtBody.$jwtSig";
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ final class ItemStackContainerIdTranslator{
|
||||
ContainerUIIds::MATERIAL_REDUCER_OUTPUT,
|
||||
ContainerUIIds::SMITHING_TABLE_INPUT,
|
||||
ContainerUIIds::SMITHING_TABLE_MATERIAL,
|
||||
ContainerUIIds::SMITHING_TABLE_TEMPLATE,
|
||||
ContainerUIIds::STONECUTTER_INPUT,
|
||||
ContainerUIIds::TRADE2_INGREDIENT1,
|
||||
ContainerUIIds::TRADE2_INGREDIENT2,
|
||||
@ -84,6 +85,7 @@ final class ItemStackContainerIdTranslator{
|
||||
ContainerUIIds::FURNACE_FUEL,
|
||||
ContainerUIIds::FURNACE_INGREDIENT,
|
||||
ContainerUIIds::FURNACE_RESULT,
|
||||
ContainerUIIds::HORSE_EQUIP,
|
||||
ContainerUIIds::LEVEL_ENTITY, //chest
|
||||
ContainerUIIds::SHULKER_BOX,
|
||||
ContainerUIIds::SMOKER_INGREDIENT => [$currentWindowId, $slotId],
|
||||
|
Loading…
x
Reference in New Issue
Block a user