This commit is contained in:
Dylan K. Taylor
2021-10-23 01:16:45 +01:00
parent c262c2e726
commit c773e43eda
54 changed files with 178 additions and 135 deletions

View File

@@ -45,6 +45,7 @@ use pocketmine\network\mcpe\protocol\CreativeContentPacket;
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerIds;
use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
@@ -164,26 +165,27 @@ class InventoryManager{
//TODO: we should be using some kind of tagging system to identify the types. Instanceof is flaky especially
//if the class isn't final, not to mention being inflexible.
if($inv instanceof BlockInventory){
$blockPosition = BlockPosition::fromVector3($inv->getHolder());
switch(true){
case $inv instanceof LoomInventory:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::LOOM, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::LOOM, $blockPosition)];
case $inv instanceof FurnaceInventory:
return match($inv->getFurnaceType()->id()){
FurnaceType::FURNACE()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::FURNACE, $inv->getHolder())],
FurnaceType::BLAST_FURNACE()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::BLAST_FURNACE, $inv->getHolder())],
FurnaceType::SMOKER()->id() => [ContainerOpenPacket::blockInvVec3($id, WindowTypes::SMOKER, $inv->getHolder())],
FurnaceType::FURNACE()->id() => [ContainerOpenPacket::blockInv($id, WindowTypes::FURNACE, $blockPosition)],
FurnaceType::BLAST_FURNACE()->id() => [ContainerOpenPacket::blockInv($id, WindowTypes::BLAST_FURNACE, $blockPosition)],
FurnaceType::SMOKER()->id() => [ContainerOpenPacket::blockInv($id, WindowTypes::SMOKER, $blockPosition)],
default => throw new AssumptionFailedError("Unreachable")
};
case $inv instanceof EnchantInventory:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ENCHANTMENT, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::ENCHANTMENT, $blockPosition)];
case $inv instanceof BrewingStandInventory:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::BREWING_STAND, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::BREWING_STAND, $blockPosition)];
case $inv instanceof AnvilInventory:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ANVIL, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::ANVIL, $blockPosition)];
case $inv instanceof HopperInventory:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::HOPPER, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::HOPPER, $blockPosition)];
default:
return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::CONTAINER, $inv->getHolder())];
return [ContainerOpenPacket::blockInv($id, WindowTypes::CONTAINER, $blockPosition)];
}
}
return null;
@@ -279,6 +281,7 @@ class InventoryManager{
$this->player->getId(),
ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand())),
$selected,
$selected,
ContainerIds::INVENTORY
));
$this->clientSelectedHotbarSlot = $selected;

View File

@@ -88,6 +88,7 @@ use pocketmine\network\mcpe\protocol\SetTitlePacket;
use pocketmine\network\mcpe\protocol\TakeItemActorPacket;
use pocketmine\network\mcpe\protocol\TextPacket;
use pocketmine\network\mcpe\protocol\TransferPacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\command\CommandData;
use pocketmine\network\mcpe\protocol\types\command\CommandEnum;
use pocketmine\network\mcpe\protocol\types\command\CommandParameter;
@@ -727,7 +728,7 @@ class NetworkSession{
$pitch = $pitch ?? $location->getPitch();
$pk = new MovePlayerPacket();
$pk->entityRuntimeId = $this->player->getId();
$pk->actorRuntimeId = $this->player->getId();
$pk->position = $this->player->getOffsetPosition($pos);
$pk->pitch = $pitch;
$pk->headYaw = $yaw;
@@ -748,16 +749,17 @@ class NetworkSession{
}
public function syncViewAreaCenterPoint(Vector3 $newPos, int $viewDistance) : void{
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create($newPos->getFloorX(), $newPos->getFloorY(), $newPos->getFloorZ(), $viewDistance * 16)); //blocks, not chunks >.>
$this->sendDataPacket(NetworkChunkPublisherUpdatePacket::create(BlockPosition::fromVector3($newPos), $viewDistance * 16)); //blocks, not chunks >.>
}
public function syncPlayerSpawnPoint(Position $newSpawn) : void{
[$x, $y, $z] = [$newSpawn->getFloorX(), $newSpawn->getFloorY(), $newSpawn->getFloorZ()];
$this->sendDataPacket(SetSpawnPositionPacket::playerSpawn($x, $y, $z, DimensionIds::OVERWORLD, $x, $y, $z));
$newSpawnBlockPosition = BlockPosition::fromVector3($newSpawn);
//TODO: respawn causing block position (bed, respawn anchor)
$this->sendDataPacket(SetSpawnPositionPacket::playerSpawn($newSpawnBlockPosition, DimensionIds::OVERWORLD, $newSpawnBlockPosition));
}
public function syncWorldSpawnPoint(Position $newSpawn) : void{
$this->sendDataPacket(SetSpawnPositionPacket::worldSpawn($newSpawn->getFloorX(), $newSpawn->getFloorY(), $newSpawn->getFloorZ(), DimensionIds::OVERWORLD));
$this->sendDataPacket(SetSpawnPositionPacket::worldSpawn(BlockPosition::fromVector3($newSpawn), DimensionIds::OVERWORLD));
}
public function syncGameMode(GameMode $mode, bool $isRollback = false) : void{
@@ -788,7 +790,7 @@ class NetworkSession{
$isOp = $for->hasPermission(DefaultPermissions::ROOT_OPERATOR);
$pk->commandPermission = ($isOp ? AdventureSettingsPacket::PERMISSION_OPERATOR : AdventureSettingsPacket::PERMISSION_NORMAL);
$pk->playerPermission = ($isOp ? PlayerPermissions::OPERATOR : PlayerPermissions::MEMBER);
$pk->entityUniqueId = $for->getId();
$pk->targetActorUniqueId = $for->getId();
$this->sendDataPacket($pk);
}
@@ -966,12 +968,12 @@ class NetworkSession{
public function onMobMainHandItemChange(Human $mob) : void{
//TODO: we could send zero for slot here because remote players don't need to know which slot was selected
$inv = $mob->getInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItemInHand())), $inv->getHeldItemIndex(), $inv->getHeldItemIndex(), ContainerIds::INVENTORY));
}
public function onMobOffHandItemChange(Human $mob) : void{
$inv = $mob->getOffHandInventory();
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, ContainerIds::OFFHAND));
$this->sendDataPacket(MobEquipmentPacket::create($mob->getId(), ItemStackWrapper::legacy(TypeConverter::getInstance()->coreItemStackToNet($inv->getItem(0))), 0, 0, ContainerIds::OFFHAND));
}
public function onMobArmorChange(Living $mob) : void{

View File

@@ -79,7 +79,7 @@ final class CraftingDataCache{
$converter = TypeConverter::getInstance();
foreach($manager->getShapelessRecipes() as $list){
foreach($list as $recipe){
$pk->entries[] = new ProtocolShapelessRecipe(
$pk->recipesWithTypeIds[] = new ProtocolShapelessRecipe(
CraftingDataPacket::ENTRY_SHAPELESS,
Binary::writeInt(++$counter),
array_map(function(Item $item) use ($converter) : RecipeIngredient{
@@ -104,7 +104,7 @@ final class CraftingDataCache{
$inputs[$row][$column] = $converter->coreItemStackToRecipeIngredient($recipe->getIngredient($column, $row));
}
}
$pk->entries[] = $r = new ProtocolShapedRecipe(
$pk->recipesWithTypeIds[] = $r = new ProtocolShapedRecipe(
CraftingDataPacket::ENTRY_SHAPED,
Binary::writeInt(++$counter),
$inputs,
@@ -128,7 +128,7 @@ final class CraftingDataCache{
};
foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){
$input = $converter->coreItemStackToNet($recipe->getInput());
$pk->entries[] = new ProtocolFurnaceRecipe(
$pk->recipesWithTypeIds[] = new ProtocolFurnaceRecipe(
CraftingDataPacket::ENTRY_FURNACE_DATA,
$input->getId(),
$input->getMeta(),

View File

@@ -199,13 +199,13 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleActorEvent(ActorEventPacket $packet) : bool{
if($packet->entityRuntimeId !== $this->player->getId()){
if($packet->actorRuntimeId !== $this->player->getId()){
//TODO HACK: EATING_ITEM is sent back to the server when the server sends it for other players (1.14 bug, maybe earlier)
return $packet->event === ActorEventPacket::EATING_ITEM;
return $packet->actorRuntimeId === ActorEventPacket::EATING_ITEM;
}
$this->player->doCloseInventory();
switch($packet->event){
switch($packet->eventId){
case ActorEventPacket::EATING_ITEM: //TODO: ignore this and handle it server-side
$item = $this->player->getInventory()->getItemInHand();
if($item->isNull()){
@@ -356,12 +356,12 @@ class InGamePacketHandler extends PacketHandler{
switch($data->getActionType()){
case UseItemTransactionData::ACTION_CLICK_BLOCK:
//TODO: start hack for client spam bug
$clickPos = $data->getClickPos();
$clickPos = $data->getClickPosition();
$spamBug = ($this->lastRightClickData !== null and
microtime(true) - $this->lastRightClickTime < 0.1 and //100ms
$this->lastRightClickData->getPlayerPos()->distanceSquared($data->getPlayerPos()) < 0.00001 and
$this->lastRightClickData->getBlockPos()->equals($data->getBlockPos()) and
$this->lastRightClickData->getClickPos()->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
$this->lastRightClickData->getPlayerPosition()->distanceSquared($data->getPlayerPosition()) < 0.00001 and
$this->lastRightClickData->getBlockPosition()->equals($data->getBlockPosition()) and
$this->lastRightClickData->getClickPosition()->distanceSquared($clickPos) < 0.00001 //signature spam bug has 0 distance, but allow some error
);
//get rid of continued spam if the player clicks and holds right-click
$this->lastRightClickData = $data;
@@ -371,9 +371,10 @@ class InGamePacketHandler extends PacketHandler{
}
//TODO: end hack for client spam bug
$blockPos = $data->getBlockPos();
if(!$this->player->interactBlock($blockPos, $data->getFace(), $clickPos)){
$this->onFailedBlockAction($blockPos, $data->getFace());
$blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->interactBlock($vBlockPos, $data->getFace(), $clickPos)){
$this->onFailedBlockAction($vBlockPos, $data->getFace());
}elseif(
!array_key_exists($windowId = InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID, $this->openHardcodedWindows) &&
$this->player->getCraftingGrid()->getGridWidth() === CraftingGrid::SIZE_BIG
@@ -381,7 +382,7 @@ class InGamePacketHandler extends PacketHandler{
//TODO: HACK! crafting grid doesn't fit very well into the current PM container system, so this hack
//allows it to carry on working approximately the same way as it did in 1.14
$this->openHardcodedWindows[$windowId] = true;
$this->session->sendDataPacket(ContainerOpenPacket::blockInvVec3(
$this->session->sendDataPacket(ContainerOpenPacket::blockInv(
InventoryManager::HARDCODED_CRAFTING_GRID_WINDOW_ID,
WindowTypes::WORKBENCH,
$blockPos
@@ -389,9 +390,10 @@ class InGamePacketHandler extends PacketHandler{
}
return true;
case UseItemTransactionData::ACTION_BREAK_BLOCK:
$blockPos = $data->getBlockPos();
if(!$this->player->breakBlock($blockPos)){
$this->onFailedBlockAction($blockPos, null);
$blockPos = $data->getBlockPosition();
$vBlockPos = new Vector3($blockPos->getX(), $blockPos->getY(), $blockPos->getZ());
if(!$this->player->breakBlock($vBlockPos)){
$this->onFailedBlockAction($vBlockPos, null);
}
return true;
case UseItemTransactionData::ACTION_CLICK_AIR:
@@ -432,7 +434,7 @@ class InGamePacketHandler extends PacketHandler{
}
private function handleUseItemOnEntityTransaction(UseItemOnEntityTransactionData $data) : bool{
$target = $this->player->getWorld()->getEntity($data->getEntityRuntimeId());
$target = $this->player->getWorld()->getEntity($data->getActorRuntimeId());
if($target === null){
return false;
}
@@ -442,7 +444,7 @@ class InGamePacketHandler extends PacketHandler{
//TODO: use transactiondata for rollbacks here
switch($data->getActionType()){
case UseItemOnEntityTransactionData::ACTION_INTERACT:
if(!$this->player->interactEntity($target, $data->getClickPos())){
if(!$this->player->interactEntity($target, $data->getClickPosition())){
$this->inventoryManager->syncSlot($this->player->getInventory(), $this->player->getInventory()->getHeldItemIndex());
}
return true;
@@ -498,7 +500,7 @@ class InGamePacketHandler extends PacketHandler{
//TODO: implement handling for this where it matters
return true;
}
$target = $this->player->getWorld()->getEntity($packet->target);
$target = $this->player->getWorld()->getEntity($packet->targetActorRuntimeId);
if($target === null){
return false;
}
@@ -521,7 +523,7 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleBlockPickRequest(BlockPickRequestPacket $packet) : bool{
return $this->player->pickBlock(new Vector3($packet->blockX, $packet->blockY, $packet->blockZ), $packet->addUserData);
return $this->player->pickBlock(new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ()), $packet->addUserData);
}
public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{
@@ -529,7 +531,7 @@ class InGamePacketHandler extends PacketHandler{
}
public function handlePlayerAction(PlayerActionPacket $packet) : bool{
$pos = new Vector3($packet->x, $packet->y, $packet->z);
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
switch($packet->action){
case PlayerActionPacket::ACTION_START_BREAK:
@@ -628,7 +630,7 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleAdventureSettings(AdventureSettingsPacket $packet) : bool{
if($packet->entityUniqueId !== $this->player->getId()){
if($packet->targetActorUniqueId !== $this->player->getId()){
return false; //TODO: operators can change other people's permissions using this
}
@@ -648,13 +650,13 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleBlockActorData(BlockActorDataPacket $packet) : bool{
$pos = new Vector3($packet->x, $packet->y, $packet->z);
$pos = new Vector3($packet->blockPosition->getX(), $packet->blockPosition->getY(), $packet->blockPosition->getZ());
if($pos->distanceSquared($this->player->getLocation()) > 10000){
return false;
}
$block = $this->player->getLocation()->getWorld()->getBlock($pos);
$nbt = $packet->namedtag->getRoot();
$nbt = $packet->nbt->getRoot();
if(!($nbt instanceof CompoundTag)) throw new AssumptionFailedError("PHPStan should ensure this is a CompoundTag"); //for phpstorm's benefit
if($block instanceof BaseSign){
@@ -678,7 +680,7 @@ class InGamePacketHandler extends PacketHandler{
return true;
}
$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->namedtag->getEncodedNbt()));
$this->session->getLogger()->debug("Invalid sign update data: " . base64_encode($packet->nbt->getEncodedNbt()));
}
return false;
@@ -712,9 +714,10 @@ class InGamePacketHandler extends PacketHandler{
}
public function handleItemFrameDropItem(ItemFrameDropItemPacket $packet) : bool{
$block = $this->player->getWorld()->getBlockAt($packet->x, $packet->y, $packet->z);
$blockPosition = $packet->blockPosition;
$block = $this->player->getWorld()->getBlockAt($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ());
if($block instanceof ItemFrame and $block->getFramedItem() !== null){
return $this->player->attackBlock(new Vector3($packet->x, $packet->y, $packet->z), $block->getFacing());
return $this->player->attackBlock(new Vector3($blockPosition->getX(), $blockPosition->getY(), $blockPosition->getZ()), $block->getFacing());
}
return false;
}

View File

@@ -31,9 +31,11 @@ use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
use pocketmine\network\mcpe\protocol\StartGamePacket;
use pocketmine\network\mcpe\protocol\types\BlockPosition;
use pocketmine\network\mcpe\protocol\types\BoolGameRule;
use pocketmine\network\mcpe\protocol\types\DimensionIds;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\LevelSettings;
use pocketmine\network\mcpe\protocol\types\PlayerMovementSettings;
use pocketmine\network\mcpe\protocol\types\PlayerMovementType;
use pocketmine\network\mcpe\protocol\types\SpawnSettings;
@@ -64,39 +66,46 @@ class PreSpawnPacketHandler extends PacketHandler{
}
public function setUp() : void{
$spawnPosition = $this->player->getSpawn();
$location = $this->player->getLocation();
$pk = new StartGamePacket();
$pk->entityUniqueId = $this->player->getId();
$pk->entityRuntimeId = $this->player->getId();
$pk->playerGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->player->getGamemode());
$pk->playerPosition = $this->player->getOffsetPosition($location);
$pk->pitch = $location->pitch;
$pk->yaw = $location->yaw;
$pk->seed = -1;
$pk->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
$pk->worldGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->server->getGamemode());
$pk->difficulty = $location->getWorld()->getDifficulty();
$pk->spawnX = $spawnPosition->getFloorX();
$pk->spawnY = $spawnPosition->getFloorY();
$pk->spawnZ = $spawnPosition->getFloorZ();
$pk->hasAchievementsDisabled = true;
$pk->time = $location->getWorld()->getTime();
$pk->eduEditionOffer = 0;
$pk->rainLevel = 0; //TODO: implement these properly
$pk->lightningLevel = 0;
$pk->commandsEnabled = true;
$pk->gameRules = [
$levelSettings = new LevelSettings();
$levelSettings->seed = -1;
$levelSettings->spawnSettings = new SpawnSettings(SpawnSettings::BIOME_TYPE_DEFAULT, "", DimensionIds::OVERWORLD); //TODO: implement this properly
$levelSettings->worldGamemode = TypeConverter::getInstance()->coreGameModeToProtocol($this->server->getGamemode());
$levelSettings->difficulty = $location->getWorld()->getDifficulty();
$levelSettings->spawnPosition = BlockPosition::fromVector3($location->getWorld()->getSpawnLocation());
$levelSettings->hasAchievementsDisabled = true;
$levelSettings->time = $location->getWorld()->getTime();
$levelSettings->eduEditionOffer = 0;
$levelSettings->rainLevel = 0; //TODO: implement these properly
$levelSettings->lightningLevel = 0;
$levelSettings->commandsEnabled = true;
$levelSettings->gameRules = [
"naturalregeneration" => new BoolGameRule(false, false) //Hack for client side regeneration
];
$pk->experiments = new Experiments([], false);
$pk->levelId = "";
$pk->worldName = $this->server->getMotd();
$pk->itemTable = GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries(); //TODO: check if this is actually needed
$pk->playerMovementSettings = new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false);
$pk->serverSoftwareVersion = sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true));
$this->session->sendDataPacket($pk);
$levelSettings->experiments = new Experiments([], false);
$this->session->sendDataPacket(StartGamePacket::create(
$this->player->getId(),
$this->player->getId(),
TypeConverter::getInstance()->coreGameModeToProtocol($this->player->getGamemode()),
$this->player->getOffsetPosition($location),
$location->pitch,
$location->yaw,
$levelSettings,
"",
$this->server->getMotd(),
"",
false,
new PlayerMovementSettings(PlayerMovementType::LEGACY, 0, false),
0,
0,
"",
false,
sprintf("%s %s", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true)),
[],
GlobalItemTypeDictionary::getInstance()->getDictionary()->getEntries()
));
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getAvailableActorIdentifiers());
$this->session->sendDataPacket(StaticPacketCache::getInstance()->getBiomeDefs());

View File

@@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe\handler;
use pocketmine\lang\KnownTranslationKeys;
use pocketmine\network\mcpe\NetworkSession;
use pocketmine\network\mcpe\protocol\ProtocolInfo;
use pocketmine\network\mcpe\protocol\ResourcePackChunkDataPacket;
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
@@ -34,6 +35,7 @@ use pocketmine\network\mcpe\protocol\ResourcePackStackPacket;
use pocketmine\network\mcpe\protocol\types\Experiments;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry;
use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType;
use pocketmine\resourcepacks\ResourcePack;
use pocketmine\resourcepacks\ResourcePackManager;
use function array_map;
@@ -113,7 +115,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
self::PACK_CHUNK_SIZE,
(int) ceil($pack->getPackSize() / self::PACK_CHUNK_SIZE),
$pack->getPackSize(),
$pack->getSha256()
$pack->getSha256(),
false,
ResourcePackType::ADDON //TODO: this might be an addon (not behaviour pack), needed to properly support client-side custom items
));
}
$this->session->getLogger()->debug("Player requested download of " . count($packet->packIds) . " resource packs");
@@ -130,7 +134,7 @@ class ResourcePacksPacketHandler extends PacketHandler{
//we don't force here, because it doesn't have user-facing effects
//but it does have an annoying side-effect when true: it makes
//the client remove its own non-server-supplied resource packs.
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, new Experiments([], false)));
$this->session->sendDataPacket(ResourcePackStackPacket::create($stack, [], false, ProtocolInfo::MINECRAFT_VERSION_NETWORK, new Experiments([], false)));
$this->session->getLogger()->debug("Applying resource pack stack");
break;
case ResourcePackClientResponsePacket::STATUS_COMPLETED: