mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-05 03:17:12 +00:00
Merge branch 'next-major' into modern-world-support
This commit is contained in:
commit
60d3bddfbc
@ -1 +1 @@
|
||||
Subproject commit 7b357f8cf9b2d2ee3a9ad247cdc76c8ad62337f9
|
||||
Subproject commit 19222cfb2867869cfc9b96fbc5f6cdbd601f5e8b
|
@ -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`.
|
||||
|
@ -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
38
composer.lock
generated
@ -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",
|
||||
|
@ -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]
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
@ -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){
|
||||
|
64
src/command/utils/CommandStringHelper.php
Normal file
64
src/command/utils/CommandStringHelper.php
Normal 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;
|
||||
}
|
||||
}
|
@ -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{
|
||||
|
@ -42,5 +42,9 @@ class Boat extends Item{
|
||||
return 1200; //400 in PC
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
//TODO
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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()
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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) . ")";
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
58
tests/phpunit/command/utils/CommandStringHelperTest.php
Normal file
58
tests/phpunit/command/utils/CommandStringHelperTest.php
Normal 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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user