Compare commits

...

6 Commits

9 changed files with 113 additions and 19 deletions

View File

@ -4,6 +4,11 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Tag name to build'
required: true
jobs:
build:
@ -33,11 +38,23 @@ jobs:
repository: pmmp/PocketMine-Docker
fetch-depth: 1
- name: Get tag names
- name: Get tag name
id: tag-name
run: |
VERSION=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{')
echo TAG_NAME=$VERSION >> $GITHUB_OUTPUT
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Parse version
id: version
run: |
VERSION="${{ steps.tag-name.outputs.TAG_NAME }}"
echo MAJOR=$(echo $VERSION | cut -d. -f1) >> $GITHUB_OUTPUT
echo MINOR=$(echo $VERSION | cut -d. -f1-2) >> $GITHUB_OUTPUT
@ -71,8 +88,8 @@ jobs:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MAJOR }}
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MAJOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}
@ -84,8 +101,8 @@ jobs:
push: true
context: ./pocketmine-mp
tags: |
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.tag-name.outputs.MINOR }}
${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
ghcr.io/${{ steps.docker-repo-name.outputs.NAME }}:${{ steps.version.outputs.MINOR }}
build-args: |
PMMP_TAG=${{ steps.tag-name.outputs.TAG_NAME }}
PMMP_REPO=${{ github.repository }}

View File

@ -4,6 +4,11 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Release to make notification for'
required: true
jobs:
build:
@ -30,9 +35,17 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-dev --prefer-dist --no-interaction --ignore-platform-reqs
- name: Get actual tag name
- name: Get tag name
id: tag-name
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Run webhook post script
run: php .github/workflows/discord-release-embed.php ${{ github.repository }} ${{ steps.tag-name.outputs.TAG_NAME }} ${{ github.token }} ${{ secrets.DISCORD_RELEASE_WEBHOOK }} ${{ secrets.DISCORD_NEWS_PING_ROLE_ID }}

View File

@ -4,6 +4,11 @@ on:
release:
types:
- published
workflow_dispatch:
inputs:
release:
description: 'Release to publish info for'
required: true
jobs:
build:
@ -19,9 +24,17 @@ jobs:
repository: ${{ github.repository_owner }}/update.pmmp.io
ssh-key: ${{ secrets.UPDATE_PMMP_IO_DEPLOY_KEY }}
- name: Get actual tag name
- name: Get tag name
id: tag-name
run: echo TAG_NAME=$(echo "${{ github.ref }}" | sed 's{^refs/tags/{{') >> $GITHUB_OUTPUT
run: |
if [[ "${{ github.event_name }}" == "release" ]]; then
echo TAG_NAME="${{ github.event.release.tag_name }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo TAG_NAME="${{ github.event.inputs.release }}" >> $GITHUB_OUTPUT
else
echo "Unsupported event type: ${{ github.event_name }}"
exit 1
fi
- name: Download new release information
run: curl -f -L ${{ github.server_url }}/${{ github.repository }}/releases/download/${{ steps.tag-name.outputs.TAG_NAME }}/build_info.json -o new_build_info.json

View File

@ -15,3 +15,11 @@ Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if
## Fixes
- Fixed deadlock on RakLib thread crash (e.g. due to port binding failure).
# 5.32.1
Released 14th August 2025.
## Fixes
- Hardened checks when processing resource pack sending during player logins.
- Fixed content log warning about crafting recipe with missing ID.
- Fixed packets in a batch still being processed after one of them caused the session to be terminated.

View File

@ -31,7 +31,7 @@ use function str_repeat;
final class VersionInfo{
public const NAME = "PocketMine-MP";
public const BASE_VERSION = "5.32.0";
public const BASE_VERSION = "5.32.1";
public const IS_DEVELOPMENT_BUILD = false;
public const BUILD_CHANNEL = "stable";

View File

@ -415,6 +415,11 @@ class NetworkSession{
$this->logger->debug($packet->getName() . ": " . base64_encode($buffer));
throw PacketHandlingException::wrap($e, "Error processing " . $packet->getName());
}
if(!$this->isConnected()){
//handling this packet may have caused a disconnection
$this->logger->debug("Aborting batch processing due to server-side disconnection");
break;
}
}
}catch(PacketDecodeException|BinaryDataException $e){
$this->logger->logException($e);

View File

@ -56,6 +56,12 @@ final class CraftingDataCache{
*/
private array $caches = [];
/**
* The client doesn't like recipes with ID 0 (as of 1.21.100) and complains about them in the content log
* This doesn't actually affect the function of the recipe, but it is annoying, so this offset fixes it
*/
public const RECIPE_ID_OFFSET = 1;
public function getCache(CraftingManager $manager) : CraftingDataPacket{
$id = spl_object_id($manager);
if(!isset($this->caches[$id])){
@ -82,6 +88,8 @@ final class CraftingDataCache{
$noUnlockingRequirement = new RecipeUnlockingRequirement(null);
foreach($manager->getCraftingRecipeIndex() as $index => $recipe){
//the client doesn't like recipes with an ID of 0, so we need to offset them
$recipeNetId = $index + self::RECIPE_ID_OFFSET;
if($recipe instanceof ShapelessRecipe){
$typeTag = match($recipe->getType()){
ShapelessRecipeType::CRAFTING => CraftingRecipeBlockName::CRAFTING_TABLE,
@ -91,14 +99,14 @@ final class CraftingDataCache{
};
$recipesWithTypeIds[] = new ProtocolShapelessRecipe(
CraftingDataPacket::ENTRY_SHAPELESS,
Binary::writeInt($index),
Binary::writeInt($recipeNetId),
array_map($converter->coreRecipeIngredientToNet(...), $recipe->getIngredientList()),
array_map($converter->coreItemStackToNet(...), $recipe->getResults()),
$nullUUID,
$typeTag,
50,
$noUnlockingRequirement,
$index
$recipeNetId
);
}elseif($recipe instanceof ShapedRecipe){
$inputs = [];
@ -110,7 +118,7 @@ final class CraftingDataCache{
}
$recipesWithTypeIds[] = $r = new ProtocolShapedRecipe(
CraftingDataPacket::ENTRY_SHAPED,
Binary::writeInt($index),
Binary::writeInt($recipeNetId),
$inputs,
array_map($converter->coreItemStackToNet(...), $recipe->getResults()),
$nullUUID,
@ -118,7 +126,7 @@ final class CraftingDataCache{
50,
true,
$noUnlockingRequirement,
$index,
$recipeNetId,
);
}else{
//TODO: probably special recipe types

View File

@ -35,6 +35,7 @@ use pocketmine\inventory\transaction\TransactionBuilder;
use pocketmine\inventory\transaction\TransactionBuilderInventory;
use pocketmine\item\Durable;
use pocketmine\item\Item;
use pocketmine\network\mcpe\cache\CraftingDataCache;
use pocketmine\network\mcpe\InventoryManager;
use pocketmine\network\mcpe\protocol\types\inventory\ContainerUIIds;
use pocketmine\network\mcpe\protocol\types\inventory\FullContainerName;
@ -238,9 +239,10 @@ class ItemStackRequestExecutor{
throw new ItemStackRequestProcessException("Cannot craft a recipe more than 256 times");
}
$craftingManager = $this->player->getServer()->getCraftingManager();
$recipe = $craftingManager->getCraftingRecipeFromIndex($recipeId);
$recipeIndex = $recipeId - CraftingDataCache::RECIPE_ID_OFFSET;
$recipe = $craftingManager->getCraftingRecipeFromIndex($recipeIndex);
if($recipe === null){
throw new ItemStackRequestProcessException("No such crafting recipe index: $recipeId");
throw new ItemStackRequestProcessException("No such crafting recipe index: $recipeIndex");
}
$this->specialTransaction = new CraftingTransaction($this->player, $craftingManager, [], $recipe, $repetitions);

View File

@ -36,6 +36,7 @@ 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\network\PacketHandlingException;
use pocketmine\resourcepacks\ResourcePack;
use Ramsey\Uuid\Uuid;
use function array_keys;
@ -43,6 +44,7 @@ use function array_map;
use function ceil;
use function count;
use function implode;
use function sprintf;
use function strpos;
use function strtolower;
use function substr;
@ -66,6 +68,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
*/
private array $resourcePacksById = [];
private bool $requestedMetadata = false;
private bool $requestedStack = false;
/** @var bool[][] uuid => [chunk index => hasSent] */
private array $downloadedChunks = [];
@ -140,6 +145,21 @@ class ResourcePacksPacketHandler extends PacketHandler{
$this->session->disconnect("Refused resource packs", "You must accept resource packs to join this server.", true);
break;
case ResourcePackClientResponsePacket::STATUS_SEND_PACKS:
if($this->requestedMetadata){
throw new PacketHandlingException("Cannot request resource pack metadata multiple times");
}
$this->requestedMetadata = true;
if($this->requestedStack){
//client already told us that they have all the packs, they shouldn't be asking for more
throw new PacketHandlingException("Cannot request resource pack metadata after resource pack stack");
}
if(count($packet->packIds) > count($this->resourcePacksById)){
throw new PacketHandlingException(sprintf("Requested metadata for more resource packs (%d) than available on the server (%d)", count($packet->packIds), count($this->resourcePacksById)));
}
$seen = [];
foreach($packet->packIds as $uuid){
//dirty hack for mojang's dirty hack for versions
$splitPos = strpos($uuid, "_");
@ -153,6 +173,9 @@ class ResourcePacksPacketHandler extends PacketHandler{
$this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById)));
return false;
}
if(isset($seen[$pack->getPackId()])){
throw new PacketHandlingException("Repeated metadata request for pack $uuid");
}
$this->session->sendDataPacket(ResourcePackDataInfoPacket::create(
$pack->getPackId(),
@ -163,11 +186,16 @@ class ResourcePacksPacketHandler extends PacketHandler{
false,
ResourcePackType::RESOURCES //TODO: this might be an addon (not behaviour pack), needed to properly support client-side custom items
));
$seen[$pack->getPackId()] = true;
}
$this->session->getLogger()->debug("Player requested download of " . count($packet->packIds) . " resource packs");
break;
case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS:
if($this->requestedStack){
throw new PacketHandlingException("Cannot request resource pack stack multiple times");
}
$this->requestedStack = true;
$stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{
return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks
}, $this->resourcePackStack);