mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-11 20:10:17 +00:00
Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
2751c59979 | |||
d99ffbd66c | |||
a34f3261cb | |||
8ce0022de6 | |||
fb6491ddeb | |||
3b961d0e5f | |||
a60fc4cc28 | |||
b747899fdd | |||
57b6451e16 | |||
8cf025a2df | |||
8480ee82ea | |||
a6c1b7bf9c | |||
c267137fde |
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)]));
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user