Compare commits

..

13 Commits

Author SHA1 Message Date
2751c59979 Release 3.14.1 2020-07-08 10:32:07 +01:00
d99ffbd66c Attribute: register lava_movement attribute
this is purely to fix crashes when decoding net packets
2020-07-08 10:21:20 +01:00
a34f3261cb event: harden APIs that accept arrays
plugin devs can't be relied on to pass the proper types to these APIs, and when the wrong types get passed it makes type errors appear from inside the internals.
2020-07-04 21:55:23 +01:00
8ce0022de6 protocol: added UUInventorySlotOffset constants 2020-07-04 21:37:37 +01:00
fb6491ddeb BanListCommand: sort output into lexical order 2020-07-03 11:23:00 +01:00
3b961d0e5f WhitelistCommand: sort output of /whitelist list into lexical order 2020-07-03 11:19:23 +01:00
a60fc4cc28 ListCommand: sort output into lexical order 2020-07-03 11:15:31 +01:00
b747899fdd PluginsCommand: sort plugins list into lexical order 2020-07-03 11:13:32 +01:00
57b6451e16 Fix projectile motion being changed by the ladder, close #3602 (#3631) 2020-06-27 21:18:39 +01:00
8cf025a2df Default isVerified to true (#3644) 2020-06-27 21:17:34 +01:00
8480ee82ea Player: track hardcoded window state, fixes crashes opening inventory on high-latency connections 2020-06-27 18:34:39 +01:00
a6c1b7bf9c InventoryTransactionPacket: added missing field for encode 2020-06-26 20:57:48 +01:00
c267137fde 3.14.1 is next 2020-06-26 14:19:02 +01:00
21 changed files with 203 additions and 23 deletions

View File

@ -15,3 +15,11 @@ Plugin developers should **only** update their required API to this version if y
- Pumpkin and melon stems may not connect to their corresponding pumpkin/melon
- New blocks, items & mobs aren't implemented
- Nether doesn't exist
# 3.14.1
- All skins are now trusted, bypassing the client-side trusted skin setting. Note that this means that NSFW skin filtering will **not** apply.
- Fixed projectile motion being altered by ladders.
- Fixed client-sided crashes when pressing E repeatedly very quickly on a high-latency connection.
- `/plugins`, `/whitelist list`, `/banlist` and `/list` now show output in alphabetical order.
- Some `pocketmine\event` APIs which accept arrays now have more robust type checking, fixing type errors caused by plugin input occurring in core code.
- `Attribute::getAttributeByName()` is now aware of the `minecraft:lava_movement` attribute.

View File

@ -176,6 +176,7 @@ use pocketmine\timings\Timings;
use pocketmine\utils\TextFormat;
use pocketmine\utils\UUID;
use function abs;
use function array_key_exists;
use function array_merge;
use function array_values;
use function assert;
@ -315,6 +316,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
/** @var CraftingTransaction|null */
protected $craftingTransaction = null;
/**
* TODO: HACK! This tracks GUIs for inventories that the server considers "always open" so that the client can't
* open them twice. (1.16 hack)
* @var true[]
* @phpstan-var array<int, true>
* @internal
*/
public $openHardcodedWindows = [];
/** @var int */
protected $messageCounter = 2;
/** @var bool */
@ -2795,12 +2805,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
case InteractPacket::ACTION_MOUSEOVER:
break; //TODO: handle these
case InteractPacket::ACTION_OPEN_INVENTORY:
if($target === $this){
if($target === $this && !array_key_exists($windowId = self::HARDCODED_INVENTORY_WINDOW_ID, $this->openHardcodedWindows)){
//TODO: HACK! this restores 1.14ish behaviour, but this should be able to be listened to and
//controlled by plugins. However, the player is always a subscriber to their own inventory so it
//doesn't integrate well with the regular container system right now.
$this->openHardcodedWindows[$windowId] = true;
$pk = new ContainerOpenPacket();
$pk->windowId = self::HARDCODED_INVENTORY_WINDOW_ID;
$pk->windowId = $windowId;
$pk->type = WindowTypes::INVENTORY;
$pk->x = $pk->y = $pk->z = 0;
$pk->entityUniqueId = $this->getId();
@ -3027,7 +3038,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
$this->doCloseInventory();
if($packet->windowId >= self::RESERVED_WINDOW_ID_RANGE_START and $packet->windowId <= self::RESERVED_WINDOW_ID_RANGE_END){
if(array_key_exists($packet->windowId, $this->openHardcodedWindows)){
unset($this->openHardcodedWindows[$packet->windowId]);
$pk = new ContainerClosePacket();
$pk->windowId = $packet->windowId;
$this->sendDataPacket($pk);

View File

@ -33,6 +33,6 @@ if(defined('pocketmine\_VERSION_INFO_INCLUDED')){
const _VERSION_INFO_INCLUDED = true;
const NAME = "PocketMine-MP";
const BASE_VERSION = "3.14.0";
const BASE_VERSION = "3.14.1";
const IS_DEVELOPMENT_BUILD = false;
const BUILD_NUMBER = 0;

View File

@ -28,6 +28,7 @@ use pocketmine\item\Item;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\types\WindowTypes;
use pocketmine\Player;
use function array_key_exists;
class CraftingTable extends Solid{
@ -53,15 +54,18 @@ class CraftingTable extends Solid{
if($player instanceof Player){
$player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG));
//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
$pk = new ContainerOpenPacket();
$pk->windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID;
$pk->type = WindowTypes::WORKBENCH;
$pk->x = $this->getFloorX();
$pk->y = $this->getFloorY();
$pk->z = $this->getFloorZ();
$player->sendDataPacket($pk);
if(!array_key_exists($windowId = Player::HARDCODED_CRAFTING_GRID_WINDOW_ID, $player->openHardcodedWindows)){
//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
$pk = new ContainerOpenPacket();
$pk->windowId = $windowId;
$pk->type = WindowTypes::WORKBENCH;
$pk->x = $this->getFloorX();
$pk->y = $this->getFloorY();
$pk->z = $this->getFloorZ();
$player->sendDataPacket($pk);
$player->openHardcodedWindows[$windowId] = true;
}
}
return true;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\entity\Living;
use pocketmine\item\Item;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
@ -58,7 +59,7 @@ class Ladder extends Transparent{
}
public function onEntityCollide(Entity $entity) : void{
if($entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
if($entity instanceof Living and $entity->asVector3()->floor()->distanceSquared($this) < 1){ //entity coordinates must be inside block
$entity->resetFallDistance();
$entity->onGround = true;
}

View File

@ -30,7 +30,9 @@ use pocketmine\permission\BanEntry;
use function array_map;
use function count;
use function implode;
use function sort;
use function strtolower;
use const SORT_STRING;
class BanListCommand extends VanillaCommand{
@ -62,10 +64,11 @@ class BanListCommand extends VanillaCommand{
$args[0] = "players";
}
$list = $list->getEntries();
$message = implode(", ", array_map(function(BanEntry $entry) : string{
$list = array_map(function(BanEntry $entry) : string{
return $entry->getName();
}, $list));
}, $list->getEntries());
sort($list, SORT_STRING);
$message = implode(", ", $list);
if($args[0] === "ips"){
$sender->sendMessage(new TranslationContainer("commands.banlist.ips", [count($list)]));

View File

@ -30,6 +30,8 @@ use function array_filter;
use function array_map;
use function count;
use function implode;
use function sort;
use const SORT_STRING;
class ListCommand extends VanillaCommand{
@ -52,6 +54,7 @@ class ListCommand extends VanillaCommand{
}, array_filter($sender->getServer()->getOnlinePlayers(), function(Player $player) use ($sender) : bool{
return $player->isOnline() and (!($sender instanceof Player) or $sender->canSee($player));
}));
sort($playerNames, SORT_STRING);
$sender->sendMessage(new TranslationContainer("commands.players.list", [count($playerNames), $sender->getServer()->getMaxPlayers()]));
$sender->sendMessage(implode(", ", $playerNames));

View File

@ -30,6 +30,8 @@ use pocketmine\utils\TextFormat;
use function array_map;
use function count;
use function implode;
use function sort;
use const SORT_STRING;
class PluginsCommand extends VanillaCommand{
@ -51,6 +53,7 @@ class PluginsCommand extends VanillaCommand{
$list = array_map(function(Plugin $plugin) : string{
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
}, $sender->getServer()->getPluginManager()->getPlugins());
sort($list, SORT_STRING);
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($list), implode(TextFormat::WHITE . ", ", $list)]));
return true;

View File

@ -31,7 +31,9 @@ use pocketmine\Player;
use pocketmine\utils\TextFormat;
use function count;
use function implode;
use function sort;
use function strtolower;
use const SORT_STRING;
class WhitelistCommand extends VanillaCommand{
@ -71,6 +73,7 @@ class WhitelistCommand extends VanillaCommand{
return true;
case "list":
$entries = $sender->getServer()->getWhitelisted()->getAll(true);
sort($entries, SORT_STRING);
$result = implode($entries, ", ");
$count = count($entries);

View File

@ -45,6 +45,7 @@ class Attribute{
public const FALL_DAMAGE = 13;
public const HORSE_JUMP_STRENGTH = 14;
public const ZOMBIE_SPAWN_REINFORCEMENTS = 15;
public const LAVA_MOVEMENT = 16;
/** @var int */
private $id;
@ -84,6 +85,7 @@ class Attribute{
self::addAttribute(self::FALL_DAMAGE, "minecraft:fall_damage", 0.0, 340282346638528859811704183484516925440.0, 1.0);
self::addAttribute(self::HORSE_JUMP_STRENGTH, "minecraft:horse.jump_strength", 0.0, 2.0, 0.7);
self::addAttribute(self::ZOMBIE_SPAWN_REINFORCEMENTS, "minecraft:zombie.spawn_reinforcements", 0.0, 1.0, 0.0);
self::addAttribute(self::LAVA_MOVEMENT, "minecraft:lava_movement", 0.0, 340282346638528859811704183484516925440.0, 0.02);
}
/**

View File

@ -26,6 +26,7 @@ namespace pocketmine\event\block;
use pocketmine\block\Block;
use pocketmine\event\Cancellable;
use pocketmine\Player;
use pocketmine\utils\Utils;
use function count;
/**
@ -79,6 +80,7 @@ class SignChangeEvent extends BlockEvent implements Cancellable{
if(count($lines) !== 4){
throw new \InvalidArgumentException("Array size must be 4!");
}
Utils::validateArrayValueType($lines, function(string $_) : void{});
$this->lines = $lines;
}

View File

@ -25,6 +25,7 @@ namespace pocketmine\event\entity;
use pocketmine\entity\Living;
use pocketmine\item\Item;
use pocketmine\utils\Utils;
/**
* @phpstan-extends EntityEvent<Living>
@ -62,6 +63,7 @@ class EntityDeathEvent extends EntityEvent{
* @param Item[] $drops
*/
public function setDrops(array $drops) : void{
Utils::validateArrayValueType($drops, function(Item $_) : void{});
$this->drops = $drops;
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\Block;
use pocketmine\entity\Entity;
use pocketmine\event\Cancellable;
use pocketmine\level\Position;
use pocketmine\utils\Utils;
/**
* Called when a entity explodes
@ -67,6 +68,7 @@ class EntityExplodeEvent extends EntityEvent implements Cancellable{
* @param Block[] $blocks
*/
public function setBlockList(array $blocks) : void{
Utils::validateArrayValueType($blocks, function(Block $_) : void{});
$this->blocks = $blocks;
}

View File

@ -28,6 +28,8 @@ use pocketmine\event\Cancellable;
use pocketmine\permission\PermissionManager;
use pocketmine\Player;
use pocketmine\Server;
use pocketmine\utils\Utils;
use function array_values;
use function spl_object_id;
/**
@ -97,6 +99,7 @@ class PlayerChatEvent extends PlayerEvent implements Cancellable{
* @param CommandSender[] $recipients
*/
public function setRecipients(array $recipients) : void{
Utils::validateArrayValueType($recipients, function(CommandSender $_) : void{});
$this->recipients = $recipients;
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\Player;
use pocketmine\plugin\Plugin;
use pocketmine\Server;
use pocketmine\utils\Binary;
use pocketmine\utils\Utils;
use function chr;
use function count;
use function str_replace;
@ -145,6 +146,7 @@ class QueryRegenerateEvent extends ServerEvent{
* @param Plugin[] $plugins
*/
public function setPlugins(array $plugins) : void{
Utils::validateArrayValueType($plugins, function(Plugin $_) : void{});
$this->plugins = $plugins;
$this->destroyCache();
}
@ -160,6 +162,7 @@ class QueryRegenerateEvent extends ServerEvent{
* @param Player[] $players
*/
public function setPlayerList(array $players) : void{
Utils::validateArrayValueType($players, function(Player $_) : void{});
$this->players = $players;
$this->destroyCache();
}

View File

@ -129,6 +129,8 @@ class InventoryTransactionPacket extends DataPacket{
$this->putUnsignedVarInt($this->transactionType);
$this->putBool($this->hasItemStackIds);
$this->putUnsignedVarInt(count($this->actions));
foreach($this->actions as $action){
$action->write($this, $this->hasItemStackIds);

View File

@ -30,7 +30,9 @@ use pocketmine\inventory\transaction\action\InventoryAction;
use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\item\Item;
use pocketmine\network\mcpe\NetworkBinaryStream;
use pocketmine\network\mcpe\protocol\types\inventory\UIInventorySlotOffset;
use pocketmine\Player;
use function array_key_exists;
class NetworkInventoryAction{
public const SOURCE_CONTAINER = 0;
@ -162,21 +164,21 @@ class NetworkInventoryAction{
switch($this->sourceType){
case self::SOURCE_CONTAINER:
if($this->windowId === ContainerIds::UI and $this->inventorySlot > 0){
if($this->inventorySlot === 50){
if($this->inventorySlot === UIInventorySlotOffset::CREATED_ITEM_OUTPUT){
return null; //useless noise
}
if($this->inventorySlot >= 28 and $this->inventorySlot <= 31){
if(array_key_exists($this->inventorySlot, UIInventorySlotOffset::CRAFTING2X2_INPUT)){
$window = $player->getCraftingGrid();
if($window->getGridWidth() !== CraftingGrid::SIZE_SMALL){
throw new \UnexpectedValueException("Expected small crafting grid");
}
$slot = $this->inventorySlot - 28;
}elseif($this->inventorySlot >= 32 and $this->inventorySlot <= 40){
$slot = UIInventorySlotOffset::CRAFTING2X2_INPUT[$this->inventorySlot];
}elseif(array_key_exists($this->inventorySlot, UIInventorySlotOffset::CRAFTING3X3_INPUT)){
$window = $player->getCraftingGrid();
if($window->getGridWidth() !== CraftingGrid::SIZE_BIG){
throw new \UnexpectedValueException("Expected big crafting grid");
}
$slot = $this->inventorySlot - 32;
$slot = UIInventorySlotOffset::CRAFTING3X3_INPUT[$this->inventorySlot];
}else{
throw new \UnexpectedValueException("Unhandled magic UI slot offset $this->inventorySlot");
}

View File

@ -70,7 +70,7 @@ class SkinData{
* @param PersonaSkinPiece[] $personaPieces
* @param PersonaPieceTintColor[] $pieceTintColors
*/
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = "", ?string $fullSkinId = null, string $armSize = self::ARM_SIZE_WIDE, string $skinColor = "", array $personaPieces = [], array $pieceTintColors = [], bool $isVerified = false){
public function __construct(string $skinId, string $resourcePatch, SkinImage $skinImage, array $animations = [], SkinImage $capeImage = null, string $geometryData = "", string $animationData = "", bool $premium = false, bool $persona = false, bool $personaCapeOnClassic = false, string $capeId = "", ?string $fullSkinId = null, string $armSize = self::ARM_SIZE_WIDE, string $skinColor = "", array $personaPieces = [], array $pieceTintColors = [], bool $isVerified = true){
$this->skinId = $skinId;
$this->resourcePatch = $resourcePatch;
$this->skinImage = $skinImage;

View File

@ -0,0 +1,105 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe\protocol\types\inventory;
final class UIInventorySlotOffset{
private function __construct(){
//NOOP
}
public const CURSOR = 0;
public const ANVIL = [
1 => 0,
2 => 1,
];
public const STONE_CUTTER_INPUT = 3;
public const TRADE2_INGREDIENT = [
4 => 0,
5 => 1,
];
public const TRADE_INGREDIENT = [
6 => 0,
7 => 1,
];
public const MATERIAL_REDUCER_INPUT = 8;
public const LOOM = [
9 => 0,
10 => 1,
11 => 2,
];
public const CARTOGRAPHY_TABLE = [
12 => 0,
13 => 1,
];
public const ENCHANTING_TABLE = [
14 => 0,
15 => 1,
];
public const GRINDSTONE = [
16 => 0,
17 => 1,
];
public const COMPOUND_CREATOR_INPUT = [
18 => 0,
19 => 1,
20 => 2,
21 => 3,
22 => 4,
23 => 5,
24 => 6,
25 => 7,
26 => 8,
];
public const BEACON_PAYMENT = 27;
public const CRAFTING2X2_INPUT = [
28 => 0,
29 => 1,
30 => 2,
31 => 3,
];
public const CRAFTING3X3_INPUT = [
32 => 0,
33 => 1,
34 => 2,
35 => 3,
36 => 4,
37 => 5,
38 => 6,
39 => 7,
40 => 8,
];
public const MATERIAL_REDUCER_OUTPUT = [
41 => 0,
42 => 1,
43 => 2,
44 => 3,
45 => 4,
46 => 5,
47 => 6,
48 => 7,
49 => 8,
];
public const CREATED_ITEM_OUTPUT = 50;
}

View File

@ -685,6 +685,21 @@ class Utils{
}
}
/**
* @phpstan-template TMemberType
* @phpstan-param array<mixed, TMemberType> $array
* @phpstan-param \Closure(TMemberType) : void $validator
*/
public static function validateArrayValueType(array $array, \Closure $validator) : void{
foreach($array as $k => $v){
try{
$validator($v);
}catch(\TypeError $e){
throw new \TypeError("Incorrect type of element at \"$k\": " . $e->getMessage(), 0, $e);
}
}
}
public static function recursiveUnlink(string $dir) : void{
if(is_dir($dir)){
$objects = scandir($dir, SCANDIR_SORT_NONE);

View File

@ -35,6 +35,11 @@ parameters:
count: 1
path: ../../../src/pocketmine/utils/Utils.php
-
message: "#^Parameter \\#1 \\$ of closure expects TMemberType, TMemberType given\\.$#"
count: 1
path: ../../../src/pocketmine/utils/Utils.php
-
message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertNotNull\\(\\) with int and string will always evaluate to true\\.$#"
count: 1