Merge branch 'next-major' into modern-world-support

This commit is contained in:
Dylan K. Taylor 2022-05-11 13:14:42 +01:00
commit 60d3bddfbc
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
19 changed files with 332 additions and 120 deletions

@ -1 +1 @@
Subproject commit 7b357f8cf9b2d2ee3a9ad247cdc76c8ad62337f9
Subproject commit 19222cfb2867869cfc9b96fbc5f6cdbd601f5e8b

View File

@ -19,3 +19,14 @@ Released 23rd April 2022.
## Fixes
- Updated BedrockProtocol dependency to fix incorrect command argument types.
- Creative players no longer die in the void.
# 4.3.2
Released 10th May 2022.
## Fixes
- Fixed an assertion failure in certain edge cases during world generation.
- Fixed `Entity::setNameTagVisible()` not immediately showing results to players already online.
## Documentation
- Added more documentation in the template `pocketmine.yml` for the `aliases` config section.
- Removed useless doc comment in `PlayerChangeSkinEvent`.

View File

@ -42,7 +42,7 @@
"pocketmine/classloader": "^0.2.0",
"pocketmine/color": "^0.2.0",
"pocketmine/errorhandler": "^0.6.0",
"pocketmine/locale-data": "~2.6.0",
"pocketmine/locale-data": "~2.7.0",
"pocketmine/log": "^0.4.0",
"pocketmine/log-pthreads": "^0.4.0",
"pocketmine/math": "^0.4.0",
@ -54,7 +54,7 @@
"webmozart/path-util": "^2.3"
},
"require-dev": {
"phpstan/phpstan": "1.6.3",
"phpstan/phpstan": "1.6.8",
"phpstan/phpstan-phpunit": "^1.1.0",
"phpstan/phpstan-strict-rules": "^1.2.0",
"phpunit/phpunit": "^9.2"

38
composer.lock generated
View File

@ -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": "f28b4d0dae7984a204a5538b7f614ee4",
"content-hash": "f3349dee8c1925a1890757897b25a2dc",
"packages": [
{
"name": "adhocore/json-comment",
@ -563,16 +563,16 @@
},
{
"name": "pocketmine/locale-data",
"version": "2.6.2",
"version": "2.7.0",
"source": {
"type": "git",
"url": "https://github.com/pmmp/Language.git",
"reference": "eae9303493884edcc5321b50002a5dc956a36a23"
"reference": "f00216c4709d2c5a2af478498315206b336b8e2e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/pmmp/Language/zipball/eae9303493884edcc5321b50002a5dc956a36a23",
"reference": "eae9303493884edcc5321b50002a5dc956a36a23",
"url": "https://api.github.com/repos/pmmp/Language/zipball/f00216c4709d2c5a2af478498315206b336b8e2e",
"reference": "f00216c4709d2c5a2af478498315206b336b8e2e",
"shasum": ""
},
"type": "library",
@ -580,9 +580,9 @@
"description": "Language resources used by PocketMine-MP",
"support": {
"issues": "https://github.com/pmmp/Language/issues",
"source": "https://github.com/pmmp/Language/tree/2.6.2"
"source": "https://github.com/pmmp/Language/tree/2.7.0"
},
"time": "2022-04-10T20:44:40+00:00"
"time": "2022-05-10T13:29:27+00:00"
},
{
"name": "pocketmine/log",
@ -1846,16 +1846,16 @@
},
{
"name": "phpstan/phpstan",
"version": "1.6.3",
"version": "1.6.8",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "6128620b98292e0b69ea6d799871d77163681c8e"
"reference": "d76498c5531232cb8386ceb6004f7e013138d3ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/6128620b98292e0b69ea6d799871d77163681c8e",
"reference": "6128620b98292e0b69ea6d799871d77163681c8e",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/d76498c5531232cb8386ceb6004f7e013138d3ba",
"reference": "d76498c5531232cb8386ceb6004f7e013138d3ba",
"shasum": ""
},
"require": {
@ -1881,7 +1881,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/1.6.3"
"source": "https://github.com/phpstan/phpstan/tree/1.6.8"
},
"funding": [
{
@ -1901,7 +1901,7 @@
"type": "tidelift"
}
],
"time": "2022-04-28T11:27:53+00:00"
"time": "2022-05-10T06:54:21+00:00"
},
{
"name": "phpstan/phpstan-phpunit",
@ -1957,16 +1957,16 @@
},
{
"name": "phpstan/phpstan-strict-rules",
"version": "1.2.1",
"version": "1.2.3",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan-strict-rules.git",
"reference": "f3ca6464eae640a556c69a02b3b77a2507475d2f"
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/f3ca6464eae640a556c69a02b3b77a2507475d2f",
"reference": "f3ca6464eae640a556c69a02b3b77a2507475d2f",
"url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"reference": "0c82c96f2a55d8b91bbc7ee6512c94f68a206b43",
"shasum": ""
},
"require": {
@ -1999,9 +1999,9 @@
"description": "Extra strict and opinionated rules for PHPStan",
"support": {
"issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.2.1"
"source": "https://github.com/phpstan/phpstan-strict-rules/tree/1.2.3"
},
"time": "2022-04-28T07:20:18+00:00"
"time": "2022-05-04T15:20:40+00:00"
},
{
"name": "phpunit/php-code-coverage",

View File

@ -195,6 +195,12 @@ aliases:
#kill: [suicide, say "I tried to kill $1"] ## `kill alex` -> `suicide` + `say "I tried to kill alex"`
#giverandom: [give $1 $2, say "Someone has just received a $2!"] ## `giverandom alex diamond` -> `give alex diamond` + `say "Someone has just received a diamond!"`
##To make arguments mandatory (so that the command fails if they are not provided), use $$, e.g. $$1, $$2:
#makeadmin: [op $$1] ## `makeadmin alex` -> `op alex`, `makeadmin` with no arguments = error
##To pass through a range of arguments, put a - (hyphen) after the index:
#tpalias: [tp $1-] ## `tpalias 256 70 256` -> `tp 256 70 256` - this passes arguments 1 and everything after it to the `tp` command
##To change an existing command alias and make it do something else:
#tp: [suicide]

View File

@ -148,6 +148,9 @@ class Leaves extends Transparent{
if(($this->treeType->equals(TreeType::OAK()) || $this->treeType->equals(TreeType::DARK_OAK())) && mt_rand(1, 200) === 1){ //Apples
$drops[] = VanillaItems::APPLE();
}
if(mt_rand(1, 50) === 1){
$drops[] = VanillaItems::STICK()->setCount(mt_rand(1, 2));
}
return $drops;
}

View File

@ -23,15 +23,28 @@ declare(strict_types=1);
namespace pocketmine\command;
use pocketmine\Server;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\TextFormat;
use function array_map;
use function array_shift;
use function count;
use function ord;
use function preg_match;
use function strlen;
use function strpos;
use function substr;
class FormattedCommandAlias extends Command{
/**
* - matches a $
* - captures an optional second $ to indicate required/optional
* - captures a series of digits which don't start with a 0
* - captures an optional - to indicate variadic
*/
private const FORMAT_STRING_REGEX = '/\G\$(\$)?((?!0)+\d+)(-)?/';
/** @var string[] */
private array $formatStrings = [];
@ -44,103 +57,124 @@ class FormattedCommandAlias extends Command{
}
public function execute(CommandSender $sender, string $commandLabel, array $args){
$commands = [];
$result = false;
$result = true;
foreach($this->formatStrings as $formatString){
try{
$commands[] = $this->buildCommand($formatString, $args);
$formatArgs = CommandStringHelper::parseQuoteAware($formatString);
$commands[] = array_map(fn(string $formatArg) => $this->buildCommand($formatArg, $args), $formatArgs);
}catch(\InvalidArgumentException $e){
$sender->sendMessage(TextFormat::RED . $e->getMessage());
return false;
}
}
foreach($commands as $command){
$result |= Server::getInstance()->dispatchCommand($sender, $command, true);
$commandMap = $sender->getServer()->getCommandMap();
foreach($commands as $commandArgs){
//this approximately duplicates the logic found in SimpleCommandMap::dispatch()
//this is to allow directly invoking the commands without having to rebuild a command string and parse it
//again for no reason
//TODO: a method on CommandMap to invoke a command with pre-parsed arguments would probably be a good idea
//for a future major version
$commandLabel = array_shift($commandArgs);
if($commandLabel === null){
throw new AssumptionFailedError("This should have been checked before construction");
}
if(($target = $commandMap->getCommand($commandLabel)) !== null){
$target->timings->startTiming();
try{
$target->execute($sender, $commandLabel, $args);
}catch(InvalidCommandSyntaxException $e){
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::commands_generic_usage($target->getUsage())));
}finally{
$target->timings->stopTiming();
}
}else{
$sender->sendMessage($sender->getLanguage()->translate(KnownTranslationFactory::pocketmine_command_notFound($commandLabel, "/help")->prefix(TextFormat::RED)));
//to match the behaviour of SimpleCommandMap::dispatch()
//this shouldn't normally happen, but might happen if the command was unregistered or modified after
//the alias was installed
$result = false;
}
}
return (bool) $result;
return $result;
}
/**
* @param string[] $args
*/
private function buildCommand(string $formatString, array $args) : string{
$index = strpos($formatString, '$');
while($index !== false){
$index = 0;
while(($index = strpos($formatString, '$', $index)) !== false){
$start = $index;
if($index > 0 && $formatString[$start - 1] === "\\"){
$formatString = substr($formatString, 0, $start - 1) . substr($formatString, $start);
$index = strpos($formatString, '$', $index);
//offset is now pointing at the next character because we just deleted the \
continue;
}
$required = false;
if($formatString[$index + 1] == '$'){
$required = true;
++$index;
}
++$index;
$argStart = $index;
while($index < strlen($formatString) && self::inRange(ord($formatString[$index]) - 48, 0, 9)){
++$index;
}
if($argStart === $index){
$info = self::extractPlaceholderInfo($formatString, $index);
if($info === null){
throw new \InvalidArgumentException("Invalid replacement token");
}
$position = (int) substr($formatString, $argStart, $index);
if($position === 0){
throw new \InvalidArgumentException("Invalid replacement token");
}
--$position;
$rest = false;
if($index < strlen($formatString) && $formatString[$index] === "-"){
$rest = true;
++$index;
}
$end = $index;
[$fullPlaceholder, $required, $position, $rest] = $info;
$position--; //array offsets start at 0, but placeholders start at 1
if($required && $position >= count($args)){
throw new \InvalidArgumentException("Missing required argument " . ($position + 1));
}
$replacement = "";
if($rest && $position < count($args)){
for($i = $position, $c = count($args); $i < $c; ++$i){
if($i !== $position){
$replacement .= " ";
}
$replacement .= $args[$i];
}
}elseif($position < count($args)){
$replacement .= $args[$position];
}
$replacement = self::buildReplacement($args, $position, $rest);
$end = $index + strlen($fullPlaceholder);
$formatString = substr($formatString, 0, $start) . $replacement . substr($formatString, $end);
$index = $start + strlen($replacement);
$index = strpos($formatString, '$', $index);
}
return $formatString;
}
private static function inRange(int $i, int $j, int $k) : bool{
return $i >= $j && $i <= $k;
/**
* @param string[] $args
* @phpstan-param list<string> $args
*/
private static function buildReplacement(array $args, int $position, bool $rest) : string{
$replacement = "";
if($rest && $position < count($args)){
for($i = $position, $c = count($args); $i < $c; ++$i){
if($i !== $position){
$replacement .= " ";
}
$replacement .= $args[$i];
}
}elseif($position < count($args)){
$replacement .= $args[$position];
}
return $replacement;
}
/**
* @phpstan-return array{string, bool, int, bool}
*/
private static function extractPlaceholderInfo(string $commandString, int $offset) : ?array{
if(preg_match(self::FORMAT_STRING_REGEX, $commandString, $matches, 0, $offset) !== 1){
return null;
}
$fullPlaceholder = $matches[0];
$required = ($matches[1] ?? "") !== "";
$position = (int) $matches[2];
$variadic = ($matches[3] ?? "") !== "";
return [$fullPlaceholder, $required, $position, $variadic];
}
}

View File

@ -64,17 +64,15 @@ use pocketmine\command\defaults\TransferServerCommand;
use pocketmine\command\defaults\VanillaCommand;
use pocketmine\command\defaults\VersionCommand;
use pocketmine\command\defaults\WhitelistCommand;
use pocketmine\command\utils\CommandStringHelper;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\lang\KnownTranslationFactory;
use pocketmine\Server;
use pocketmine\utils\TextFormat;
use function array_shift;
use function count;
use function explode;
use function implode;
use function preg_match_all;
use function strcasecmp;
use function stripslashes;
use function strpos;
use function strtolower;
use function trim;
@ -197,16 +195,7 @@ class SimpleCommandMap implements CommandMap{
}
public function dispatch(CommandSender $sender, string $commandLine) : bool{
$args = [];
preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|(\S+)/u', $commandLine, $matches);
foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){
$args[$k] = $i === 1 ? stripslashes($matches[$i][$k]) : $matches[$i][$k];
break;
}
}
}
$args = CommandStringHelper::parseQuoteAware($commandLine);
$sentCommandLabel = array_shift($args);
if($sentCommandLabel !== null && ($target = $this->getCommand($sentCommandLabel)) !== null){
@ -259,8 +248,8 @@ class SimpleCommandMap implements CommandMap{
$recursive = [];
foreach($commandStrings as $commandString){
$args = explode(" ", $commandString);
$commandName = array_shift($args);
$args = CommandStringHelper::parseQuoteAware($commandString);
$commandName = array_shift($args) ?? "";
$command = $this->getCommand($commandName);
if($command === null){

View File

@ -0,0 +1,64 @@
<?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\command\utils;
use pocketmine\utils\AssumptionFailedError;
use function preg_last_error_msg;
use function preg_match_all;
use function preg_replace;
final class CommandStringHelper{
private function __construct(){
//NOOP
}
/**
* Parses a command string into its component parts. Parts of the string which are inside unescaped quotes are
* considered as one argument.
*
* Examples:
* - `give "steve jobs" apple` -> ['give', 'steve jobs', 'apple']
* - `say "This is a \"string containing quotes\""` -> ['say', 'This is a "string containing quotes"']
*
* @return string[]
* @phpstan-return list<string>
*/
public static function parseQuoteAware(string $commandLine) : array{
$args = [];
preg_match_all('/"((?:\\\\.|[^\\\\"])*)"|(\S+)/u', $commandLine, $matches);
foreach($matches[0] as $k => $_){
for($i = 1; $i <= 2; ++$i){
if($matches[$i][$k] !== ""){
/** @var string $match */ //phpstan can't understand preg_match and friends by itself :(
$match = $matches[$i][$k];
$args[(int) $k] = preg_replace('/\\\\([\\\\"])/u', '$1', $match) ?? throw new AssumptionFailedError(preg_last_error_msg());
break;
}
}
}
return $args;
}
}

View File

@ -282,6 +282,7 @@ abstract class Entity{
public function setNameTagVisible(bool $value = true) : void{
$this->nameTagVisible = $value;
$this->networkPropertiesDirty = true;
}
public function setNameTagAlwaysVisible(bool $value = true) : void{

View File

@ -42,5 +42,9 @@ class Boat extends Item{
return 1200; //400 in PC
}
public function getMaxStackSize() : int{
return 1;
}
//TODO
}

View File

@ -1759,6 +1759,13 @@ final class KnownTranslationFactory{
]);
}
public static function pocketmine_plugin_enableError(Translatable|string $param0, Translatable|string $param1) : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_ENABLEERROR, [
0 => $param0,
1 => $param1,
]);
}
public static function pocketmine_plugin_extensionNotLoaded(Translatable|string $extensionName) : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_EXTENSIONNOTLOADED, [
"extensionName" => $extensionName,
@ -1859,6 +1866,10 @@ final class KnownTranslationFactory{
]);
}
public static function pocketmine_plugin_suicide() : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_SUICIDE, []);
}
public static function pocketmine_plugin_unknownDependency(Translatable|string $param0) : Translatable{
return new Translatable(KnownTranslationKeys::POCKETMINE_PLUGIN_UNKNOWNDEPENDENCY, [
0 => $param0,

View File

@ -368,6 +368,7 @@ final class KnownTranslationKeys{
public const POCKETMINE_PLUGIN_DUPLICATEPERMISSIONERROR = "pocketmine.plugin.duplicatePermissionError";
public const POCKETMINE_PLUGIN_EMPTYEXTENSIONVERSIONCONSTRAINT = "pocketmine.plugin.emptyExtensionVersionConstraint";
public const POCKETMINE_PLUGIN_ENABLE = "pocketmine.plugin.enable";
public const POCKETMINE_PLUGIN_ENABLEERROR = "pocketmine.plugin.enableError";
public const POCKETMINE_PLUGIN_EXTENSIONNOTLOADED = "pocketmine.plugin.extensionNotLoaded";
public const POCKETMINE_PLUGIN_GENERICLOADERROR = "pocketmine.plugin.genericLoadError";
public const POCKETMINE_PLUGIN_INCOMPATIBLEAPI = "pocketmine.plugin.incompatibleAPI";
@ -385,6 +386,7 @@ final class KnownTranslationKeys{
public const POCKETMINE_PLUGIN_MAINCLASSWRONGTYPE = "pocketmine.plugin.mainClassWrongType";
public const POCKETMINE_PLUGIN_RESTRICTEDNAME = "pocketmine.plugin.restrictedName";
public const POCKETMINE_PLUGIN_SPACESDISCOURAGED = "pocketmine.plugin.spacesDiscouraged";
public const POCKETMINE_PLUGIN_SUICIDE = "pocketmine.plugin.suicide";
public const POCKETMINE_PLUGIN_UNKNOWNDEPENDENCY = "pocketmine.plugin.unknownDependency";
public const POCKETMINE_SAVE_START = "pocketmine.save.start";
public const POCKETMINE_SAVE_SUCCESS = "pocketmine.save.success";

View File

@ -447,6 +447,13 @@ class PluginManager{
$this->enabledPlugins[$plugin->getDescription()->getName()] = $plugin;
(new PluginEnableEvent($plugin))->call();
}else{
$this->server->getLogger()->critical($this->server->getLanguage()->translate(
KnownTranslationFactory::pocketmine_plugin_enableError(
$plugin->getName(),
KnownTranslationFactory::pocketmine_plugin_suicide()
)
));
}
}
}

View File

@ -59,6 +59,7 @@ use function interface_exists;
use function is_a;
use function is_array;
use function is_bool;
use function is_float;
use function is_infinite;
use function is_int;
use function is_nan;
@ -435,6 +436,19 @@ final class Utils{
return $lines;
}
private static function stringifyValueForTrace(mixed $value, int $maxStringLength) : string{
return match(true){
is_object($value) => "object " . self::getNiceClassName($value) . "#" . spl_object_id($value),
is_array($value) => "array[" . count($value) . "]",
is_string($value) => "string[" . strlen($value) . "] " . substr(Utils::printable($value), 0, $maxStringLength),
is_bool($value) => $value ? "true" : "false",
is_int($value) => "int " . $value,
is_float($value) => "float " . $value,
$value === null => "null",
default => gettype($value) . " " . Utils::printable((string) $value)
};
}
/**
* @param mixed[][] $trace
* @phpstan-param list<array<string, mixed>> $trace
@ -451,22 +465,15 @@ final class Utils{
}else{
$args = $trace[$i]["params"];
}
/** @var mixed[] $args */
$params = implode(", ", array_map(function($value) use($maxStringLength) : string{
if(is_object($value)){
return "object " . self::getNiceClassName($value) . "#" . spl_object_id($value);
}
if(is_array($value)){
return "array[" . count($value) . "]";
}
if(is_string($value)){
return "string[" . strlen($value) . "] " . substr(Utils::printable($value), 0, $maxStringLength);
}
if(is_bool($value)){
return $value ? "true" : "false";
}
return gettype($value) . " " . Utils::printable((string) $value);
}, $args));
$paramsList = [];
$offset = 0;
foreach($args as $argId => $value){
$paramsList[] = ($argId === $offset ? "" : "$argId: ") . self::stringifyValueForTrace($value, $maxStringLength);
$offset++;
}
$params = implode(", ", $paramsList);
}
$messages[] = "#$i " . (isset($trace[$i]["file"]) ? Filesystem::cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" || $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable($params) . ")";
}

View File

@ -3004,9 +3004,14 @@ class World implements ChunkManager{
unset($this->activeChunkPopulationTasks[$index]);
if($dirtyChunks === 0){
$promise = $this->chunkPopulationRequestMap[$index];
unset($this->chunkPopulationRequestMap[$index]);
$promise->resolve($chunk);
$promise = $this->chunkPopulationRequestMap[$index] ?? null;
if($promise !== null){
unset($this->chunkPopulationRequestMap[$index]);
$promise->resolve($chunk);
}else{
//Handlers of ChunkPopulateEvent, ChunkLoadEvent, or just ChunkListeners can cause this
$this->logger->debug("Unable to resolve population promise for chunk x=$x,z=$z - populated chunk was forcibly unloaded while setting modified chunks");
}
}else{
//request failed, stick it back on the queue
//we didn't resolve the promise or touch it in any way, so any fake chunk loaders are still valid and

View File

@ -440,6 +440,16 @@ parameters:
count: 1
path: ../../../src/command/Command.php
-
message: "#^Cannot call method startTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1
path: ../../../src/command/FormattedCommandAlias.php
-
message: "#^Cannot call method stopTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1
path: ../../../src/command/FormattedCommandAlias.php
-
message: "#^Cannot call method startTiming\\(\\) on pocketmine\\\\timings\\\\TimingsHandler\\|null\\.$#"
count: 1

View File

@ -45,6 +45,11 @@ parameters:
count: 1
path: ../../../src/network/mcpe/handler/InGamePacketHandler.php
-
message: "#^Negated boolean expression is always true\\.$#"
count: 1
path: ../../../src/network/mcpe/handler/InGamePacketHandler.php
-
message: "#^Property pocketmine\\\\network\\\\mcpe\\\\raklib\\\\PthreadsChannelWriter\\:\\:\\$buffer is never read, only written\\.$#"
count: 1
@ -90,8 +95,3 @@ parameters:
count: 2
path: ../../../src/world/format/io/region/RegionLoader.php
-
message: "#^Negated boolean expression is always true\\.$#"
count: 1
path: ../../../src/network/mcpe/handler/InGamePacketHandler.php

View File

@ -0,0 +1,58 @@
<?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\command\utils;
use PHPUnit\Framework\TestCase;
class CommandStringHelperTest extends TestCase{
public function parseQuoteAwareProvider() : \Generator{
yield [
'give "steve jobs" apple',
['give', 'steve jobs', 'apple']
];
yield [
'say \"escaped\"',
['say', '"escaped"']
];
yield [
'say This message contains \"escaped quotes\", which are ignored',
['say', 'This', 'message', 'contains', '"escaped', 'quotes",', 'which', 'are', 'ignored']
];
yield [
'say dontspliton"half"wayquotes',
['say', 'dontspliton"half"wayquotes']
];
}
/**
* @dataProvider parseQuoteAwareProvider
* @param string[] $expected
*/
public function testParseQuoteAware(string $commandLine, array $expected) : void{
$actual = CommandStringHelper::parseQuoteAware($commandLine);
self::assertSame($expected, $actual);
}
}