mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-10 11:38:08 +00:00
Compare commits
130 Commits
Author | SHA1 | Date | |
---|---|---|---|
6e6cda91ce | |||
69500fe183 | |||
c7d58db7eb | |||
a3b78236eb | |||
c447d51e3f | |||
b615cad22d | |||
b93e219231 | |||
a4a9309193 | |||
3a85e6cab9 | |||
17f1bf5512 | |||
5179bb1d30 | |||
6bff840293 | |||
08897c6941 | |||
dfe2aa9c67 | |||
4006be35d9 | |||
c7af1cf785 | |||
22fcfffa53 | |||
7dd53f2397 | |||
298259b473 | |||
c123f2d10b | |||
4cc7573a64 | |||
9d80802e53 | |||
ec1e257e21 | |||
6fce2b3349 | |||
64ed8adefc | |||
91be5aba0c | |||
5df601c817 | |||
1a47735d84 | |||
0cdf4d0c55 | |||
e6e28b74b5 | |||
72f46b4631 | |||
3892f2f404 | |||
4a1d67cb91 | |||
b4694092b7 | |||
d99ee515c6 | |||
17f7dc34be | |||
a63d66c048 | |||
95f6995ae0 | |||
4a24d7909e | |||
4e2387edc1 | |||
a5e38576ef | |||
381151dedc | |||
a604e6835e | |||
142a6d7678 | |||
b2ca364de0 | |||
09ed40a921 | |||
565373cee6 | |||
c29723e3c4 | |||
a8811ab2b3 | |||
974583a853 | |||
03f8fe62d4 | |||
699f35cc05 | |||
8fa196efc9 | |||
b1ab881b99 | |||
69c54e789a | |||
e33d1279fa | |||
9e1fa453ad | |||
6a05edb4e9 | |||
70635d0870 | |||
46bd096f06 | |||
51a8905fb3 | |||
f954d7c3dc | |||
7ad0aa56b1 | |||
1ff6f8846e | |||
e6f53cc56b | |||
87f458f9bd | |||
5a7e575c3a | |||
20b37d0208 | |||
d6d98183ea | |||
89cf76363f | |||
9ff5c65fb6 | |||
1532b0ef6d | |||
61accee682 | |||
9ece971a2b | |||
5546c88f88 | |||
4c4761d200 | |||
5492495d38 | |||
6bef07db7c | |||
e8c7ae595d | |||
0d9f40873f | |||
f7358cd7e1 | |||
a4aee98cba | |||
a97c7d3132 | |||
808d289610 | |||
4a1ed21e52 | |||
06c035bfe6 | |||
1b053c7928 | |||
c684f99cc4 | |||
8d47a222b4 | |||
695793795e | |||
9f425bbe2b | |||
a4965842d6 | |||
d0339796b4 | |||
5e13e2e777 | |||
1ef6f5d166 | |||
eccc249009 | |||
4be36914d6 | |||
e3ef1ecb30 | |||
dbaf7287bc | |||
3640062142 | |||
9af70283fd | |||
b3b240e25b | |||
b18872fbc6 | |||
2b30ef1671 | |||
dd8499e202 | |||
124ebf69c5 | |||
4d1e56069d | |||
4274640845 | |||
45d30d53cc | |||
cfc8dfa369 | |||
93a2f397c6 | |||
62fc875cdc | |||
58b665985e | |||
0f5c48e342 | |||
08ad5db05b | |||
94e8623c75 | |||
921f7e8f6a | |||
710e1d014d | |||
165aac1ba3 | |||
7fc22d3227 | |||
7bfe487ee5 | |||
d8cf835f92 | |||
65e44364e5 | |||
ebbbc581ca | |||
8aa8280a63 | |||
6a637d9099 | |||
4a5ff32d2e | |||
06b80a9536 | |||
b5dcdea6d8 | |||
b3ffce9729 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -7,9 +7,6 @@
|
||||
[submodule "tests/plugins/PocketMine-DevTools"]
|
||||
path = tests/plugins/PocketMine-DevTools
|
||||
url = https://github.com/pmmp/PocketMine-DevTools.git
|
||||
[submodule "tests/plugins/PocketMine-TesterPlugin"]
|
||||
path = tests/plugins/PocketMine-TesterPlugin
|
||||
url = https://github.com/pmmp/PocketMine-TesterPlugin.git
|
||||
[submodule "src/pocketmine/resources/vanilla"]
|
||||
path = src/pocketmine/resources/vanilla
|
||||
url = https://github.com/pmmp/BedrockData.git
|
||||
|
@ -6,9 +6,9 @@ php:
|
||||
before_script:
|
||||
# - pecl install channel://pecl.php.net/pthreads-3.1.6
|
||||
- echo | pecl install channel://pecl.php.net/yaml-2.0.2
|
||||
- git clone https://github.com/krakjoe/pthreads.git
|
||||
- git clone https://github.com/pmmp/pthreads.git
|
||||
- cd pthreads
|
||||
- git checkout d32079fb4a88e6e008104d36dbbf0c2dd7deb403
|
||||
- git checkout c8cfacda84f21032d6014b53e72bf345ac901dac
|
||||
- phpize
|
||||
- ./configure
|
||||
- make
|
||||
|
@ -27,7 +27,7 @@
|
||||
"pocketmine/raklib": "^0.12.0",
|
||||
"pocketmine/spl": "^0.3.0",
|
||||
"pocketmine/binaryutils": "^0.1.0",
|
||||
"pocketmine/nbt": "^0.2.0",
|
||||
"pocketmine/nbt": "^0.2.1",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0"
|
||||
},
|
||||
|
36
composer.lock
generated
36
composer.lock
generated
@ -4,20 +4,20 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2670b9e2a730ff758909be8b9e9d609a",
|
||||
"content-hash": "3536995c56bfc3dbd6ccc0994e88a636",
|
||||
"packages": [
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595"
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"reference": "c824ac67eeeb6899c2a9ec91a769eb9ed6e3f595",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -38,20 +38,20 @@
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2018-04-16T09:05:08+00:00"
|
||||
"time": "2018-08-26T18:11:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/Math.git",
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef"
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"reference": "95ae5600328ed2add44c0bc830a68d3660e9e0ef",
|
||||
"url": "https://api.github.com/repos/pmmp/Math/zipball/ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"reference": "ee299f5c9c444ca526c9c691b920f321458cf0b6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -69,23 +69,23 @@
|
||||
],
|
||||
"description": "PHP library containing math related code used in PocketMine-MP",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/Math/tree/master",
|
||||
"source": "https://github.com/pmmp/Math/tree/0.2.1",
|
||||
"issues": "https://github.com/pmmp/Math/issues"
|
||||
},
|
||||
"time": "2018-06-09T09:26:30+00:00"
|
||||
"time": "2018-08-15T15:43:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c"
|
||||
"reference": "a4ee39f313c6870153fb7c7e513b211217f7daf8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"reference": "da19487ff92f6f7a16b5ce8894132bb1d1e9ea0c",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/a4ee39f313c6870153fb7c7e513b211217f7daf8",
|
||||
"reference": "a4ee39f313c6870153fb7c7e513b211217f7daf8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -109,10 +109,10 @@
|
||||
],
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.0",
|
||||
"source": "https://github.com/pmmp/NBT/tree/0.2.1",
|
||||
"issues": "https://github.com/pmmp/NBT/issues"
|
||||
},
|
||||
"time": "2018-06-13T09:56:00+00:00"
|
||||
"time": "2018-09-04T10:36:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
|
@ -44,7 +44,7 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
public function __construct(Server $server, string $name){
|
||||
$this->server = $server;
|
||||
$this->name = $name;
|
||||
if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->getName()) . ".dat")){
|
||||
if(file_exists($this->server->getDataPath() . "players/" . strtolower($this->name) . ".dat")){
|
||||
$this->namedtag = $this->server->getOfflinePlayerData($this->name);
|
||||
}else{
|
||||
$this->namedtag = null;
|
||||
@ -64,7 +64,7 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
}
|
||||
|
||||
public function isOp() : bool{
|
||||
return $this->server->isOp(strtolower($this->getName()));
|
||||
return $this->server->isOp($this->name);
|
||||
}
|
||||
|
||||
public function setOp(bool $value){
|
||||
@ -73,38 +73,38 @@ class OfflinePlayer implements IPlayer, Metadatable{
|
||||
}
|
||||
|
||||
if($value){
|
||||
$this->server->addOp(strtolower($this->getName()));
|
||||
$this->server->addOp($this->name);
|
||||
}else{
|
||||
$this->server->removeOp(strtolower($this->getName()));
|
||||
$this->server->removeOp($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
public function isBanned() : bool{
|
||||
return $this->server->getNameBans()->isBanned(strtolower($this->getName()));
|
||||
return $this->server->getNameBans()->isBanned($this->name);
|
||||
}
|
||||
|
||||
public function setBanned(bool $value){
|
||||
if($value){
|
||||
$this->server->getNameBans()->addBan($this->getName(), null, null, null);
|
||||
$this->server->getNameBans()->addBan($this->name, null, null, null);
|
||||
}else{
|
||||
$this->server->getNameBans()->remove($this->getName());
|
||||
$this->server->getNameBans()->remove($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
public function isWhitelisted() : bool{
|
||||
return $this->server->isWhitelisted(strtolower($this->getName()));
|
||||
return $this->server->isWhitelisted($this->name);
|
||||
}
|
||||
|
||||
public function setWhitelisted(bool $value){
|
||||
if($value){
|
||||
$this->server->addWhitelist(strtolower($this->getName()));
|
||||
$this->server->addWhitelist($this->name);
|
||||
}else{
|
||||
$this->server->removeWhitelist(strtolower($this->getName()));
|
||||
$this->server->removeWhitelist($this->name);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPlayer(){
|
||||
return $this->server->getPlayerExact($this->getName());
|
||||
return $this->server->getPlayerExact($this->name);
|
||||
}
|
||||
|
||||
public function getFirstPlayed(){
|
||||
|
@ -1487,14 +1487,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
protected function checkGroundState(float $movX, float $movY, float $movZ, float $dx, float $dy, float $dz) : void{
|
||||
if(!$this->onGround or $movY != 0){
|
||||
$bb = clone $this->boundingBox;
|
||||
$bb->minY = $this->y - 0.2;
|
||||
$bb->maxY = $this->y + 0.2;
|
||||
$bb = clone $this->boundingBox;
|
||||
$bb->minY = $this->y - 0.2;
|
||||
$bb->maxY = $this->y + 0.2;
|
||||
|
||||
$this->onGround = count($this->level->getCollisionBlocks($bb, true)) > 0;
|
||||
}
|
||||
$this->isCollided = $this->onGround;
|
||||
$this->onGround = $this->isCollided = count($this->level->getCollisionBlocks($bb, true)) > 0;
|
||||
}
|
||||
|
||||
public function canBeMovedByCurrents() : bool{
|
||||
@ -2168,7 +2165,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$message = TextFormat::clean($message, $this->removeFormat);
|
||||
foreach(explode("\n", $message) as $messagePart){
|
||||
@ -2243,7 +2240,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
switch($packet->event){
|
||||
case EntityEventPacket::EATING_ITEM:
|
||||
@ -2394,7 +2391,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
return true;
|
||||
case InventoryTransactionPacket::USE_ITEM_ACTION_BREAK_BLOCK:
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$item = $this->inventory->getItemInHand();
|
||||
$oldItem = clone $item;
|
||||
@ -2635,7 +2632,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$target = $this->level->getEntity($packet->target);
|
||||
if($target === null){
|
||||
@ -2849,7 +2846,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
if(isset($this->windowIndex[$packet->windowId])){
|
||||
$this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowId], $this));
|
||||
@ -2899,7 +2896,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$pos = new Vector3($packet->x, $packet->y, $packet->z);
|
||||
if($pos->distanceSquared($this) > 10000 or $this->level->checkSpawnProtection($this, $pos)){
|
||||
@ -3617,7 +3614,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
//Crafting grid must always be evacuated even if keep-inventory is true. This dumps the contents into the
|
||||
//main inventory and drops the rest on the ground.
|
||||
$this->resetCraftingGridType();
|
||||
$this->doCloseInventory();
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), new TranslationContainer($message, $params)));
|
||||
|
||||
@ -3800,15 +3797,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$this->craftingGrid = $grid;
|
||||
}
|
||||
|
||||
public function resetCraftingGridType() : void{
|
||||
$contents = $this->craftingGrid->getContents();
|
||||
if(count($contents) > 0){
|
||||
$drops = $this->inventory->addItem(...$contents);
|
||||
foreach($drops as $drop){
|
||||
$this->dropItem($drop);
|
||||
}
|
||||
public function doCloseInventory() : void{
|
||||
/** @var Inventory[] $inventories */
|
||||
$inventories = [$this->craftingGrid, $this->cursorInventory];
|
||||
foreach($inventories as $inventory){
|
||||
$contents = $inventory->getContents();
|
||||
if(count($contents) > 0){
|
||||
$drops = $this->inventory->addItem(...$contents);
|
||||
foreach($drops as $drop){
|
||||
$this->dropItem($drop);
|
||||
}
|
||||
|
||||
$this->craftingGrid->clearAll();
|
||||
$inventory->clearAll();
|
||||
}
|
||||
}
|
||||
|
||||
if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){
|
||||
|
@ -37,7 +37,7 @@ namespace pocketmine {
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.0.6";
|
||||
const BASE_VERSION = "3.1.7";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
|
@ -1026,6 +1026,7 @@ class Server{
|
||||
}
|
||||
|
||||
try{
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
}catch(\Throwable $e){
|
||||
|
||||
@ -1070,6 +1071,9 @@ class Server{
|
||||
|
||||
if(($providerClass = LevelProviderManager::getProviderByName($this->getProperty("level-settings.default-format", "pmanvil"))) === null){
|
||||
$providerClass = LevelProviderManager::getProviderByName("pmanvil");
|
||||
if($providerClass === null){
|
||||
throw new \InvalidStateException("Default level provider has not been registered");
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
@ -1077,6 +1081,7 @@ class Server{
|
||||
/** @var LevelProvider $providerClass */
|
||||
$providerClass::generate($path, $name, $seed, $generator, $options);
|
||||
|
||||
/** @see LevelProvider::__construct() */
|
||||
$level = new Level($this, $name, new $providerClass($path));
|
||||
$this->levels[$level->getId()] = $level;
|
||||
|
||||
@ -2010,6 +2015,10 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->doTitleTick){
|
||||
echo "\x1b]0;\x07";
|
||||
}
|
||||
|
||||
try{
|
||||
if(!$this->isRunning()){
|
||||
$this->sendUsage(SendUsageTask::TYPE_CLOSE);
|
||||
@ -2141,10 +2150,6 @@ class Server{
|
||||
* @param array|null $trace
|
||||
*/
|
||||
public function exceptionHandler(\Throwable $e, $trace = null){
|
||||
if($e === null){
|
||||
return;
|
||||
}
|
||||
|
||||
global $lastError;
|
||||
|
||||
if($trace === null){
|
||||
@ -2202,7 +2207,7 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
if($dump->getData()["error"]["type"] === "E_PARSE" or $dump->getData()["error"]["type"] === "E_COMPILE_ERROR"){
|
||||
if($dump->getData()["error"]["type"] === \ParseError::class){
|
||||
$report = false;
|
||||
}
|
||||
|
||||
@ -2351,37 +2356,29 @@ class Server{
|
||||
if($level->getTickRate() > $this->baseTickRate and --$level->tickRateCounter > 0){
|
||||
continue;
|
||||
}
|
||||
try{
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$tickMs = (microtime(true) - $levelTime) * 1000;
|
||||
$level->tickRateTime = $tickMs;
|
||||
|
||||
if($this->autoTickRate){
|
||||
if($tickMs < 50 and $level->getTickRate() > $this->baseTickRate){
|
||||
$level->setTickRate($r = $level->getTickRate() - 1);
|
||||
if($r > $this->baseTickRate){
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
$this->getLogger()->debug("Raising level \"{$level->getName()}\" tick rate to {$level->getTickRate()} ticks");
|
||||
}elseif($tickMs >= 50){
|
||||
if($level->getTickRate() === $this->baseTickRate){
|
||||
$level->setTickRate(max($this->baseTickRate + 1, min($this->autoTickRateLimit, (int) floor($tickMs / 50))));
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}elseif(($tickMs / $level->getTickRate()) >= 50 and $level->getTickRate() < $this->autoTickRateLimit){
|
||||
$level->setTickRate($level->getTickRate() + 1);
|
||||
$this->getLogger()->debug(sprintf("Level \"%s\" took %gms, setting tick rate to %d ticks", $level->getName(), (int) round($tickMs, 2), $level->getTickRate()));
|
||||
}
|
||||
$level->tickRateCounter = $level->getTickRate();
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(!$level->isClosed()){
|
||||
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickError", [$level->getName(), $e->getMessage()]));
|
||||
}else{
|
||||
$this->logger->critical($this->getLanguage()->translateString("pocketmine.level.tickUnloadError", [$level->getName()]));
|
||||
}
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2471,6 +2468,8 @@ class Server{
|
||||
try{
|
||||
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
|
||||
$this->queryHandler->handle($interface, $address, $port, $payload);
|
||||
}else{
|
||||
$this->logger->debug("Unhandled raw packet from $address $port: " . bin2hex($payload));
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
|
@ -67,14 +67,10 @@ abstract class Thread extends \Thread{
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,12 +79,9 @@ abstract class Thread extends \Thread{
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
||||
$this->notify();
|
||||
|
||||
if(!$this->isJoined()){
|
||||
if(!$this->isTerminated()){
|
||||
$this->join();
|
||||
}
|
||||
$this->notify();
|
||||
$this->join();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
|
@ -67,14 +67,10 @@ abstract class Worker extends \Worker{
|
||||
public function start(?int $options = \PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,16 +79,10 @@ abstract class Worker extends \Worker{
|
||||
public function quit(){
|
||||
$this->isKilled = true;
|
||||
|
||||
$this->notify();
|
||||
|
||||
if($this->isRunning()){
|
||||
$this->shutdown();
|
||||
while($this->unstack() !== null);
|
||||
$this->notify();
|
||||
$this->unstack();
|
||||
}elseif(!$this->isJoined()){
|
||||
if(!$this->isTerminated()){
|
||||
$this->join();
|
||||
}
|
||||
$this->shutdown();
|
||||
}
|
||||
|
||||
ThreadManager::getInstance()->remove($this);
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\utils\MainLogger;
|
||||
|
||||
/**
|
||||
* Manages block registration and instance creation
|
||||
@ -436,19 +435,12 @@ class BlockFactory{
|
||||
* @return int
|
||||
*/
|
||||
public static function toStaticRuntimeId(int $id, int $meta = 0) : int{
|
||||
if($id === Block::AIR){
|
||||
//TODO: HACK! (weird air blocks with non-zero damage values shouldn't turn into update! blocks)
|
||||
$meta = 0;
|
||||
}
|
||||
|
||||
$index = ($id << 4) | $meta;
|
||||
if(!isset(self::$staticRuntimeIdMap[$index])){
|
||||
self::registerMapping($rtId = ++self::$lastRuntimeId, $id, $meta);
|
||||
MainLogger::getLogger()->error("ID $id meta $meta does not have a corresponding block static runtime ID, added a new unknown runtime ID ($rtId)");
|
||||
return $rtId;
|
||||
}
|
||||
|
||||
return self::$staticRuntimeIdMap[$index];
|
||||
/*
|
||||
* try id+meta first
|
||||
* if not found, try id+0 (strip meta)
|
||||
* if still not found, return update! block
|
||||
*/
|
||||
return self::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockIds::INFO_UPDATE << 4];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,7 +77,7 @@ class Cactus extends Transparent{
|
||||
}else{
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if(!$b->canBeFlowedInto()){
|
||||
if($b->isSolid()){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
break;
|
||||
}
|
||||
@ -117,7 +117,7 @@ class Cactus extends Transparent{
|
||||
$block1 = $this->getSide(Vector3::SIDE_SOUTH);
|
||||
$block2 = $this->getSide(Vector3::SIDE_WEST);
|
||||
$block3 = $this->getSide(Vector3::SIDE_EAST);
|
||||
if($block0->isTransparent() and $block1->isTransparent() and $block2->isTransparent() and $block3->isTransparent()){
|
||||
if(!$block0->isSolid() and !$block1->isSolid() and !$block2->isSolid() and !$block3->isSolid()){
|
||||
$this->getLevel()->setBlock($this, $this, true);
|
||||
|
||||
return true;
|
||||
|
@ -57,7 +57,7 @@ class CoalOre extends Solid{
|
||||
];
|
||||
}
|
||||
|
||||
public function getXpDropForTool(Item $item) : int{
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(0, 2);
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class ConcretePowder extends Fallable{
|
||||
private function checkAdjacentWater() : ?Block{
|
||||
for($i = 1; $i < 6; ++$i){ //Do not check underneath
|
||||
if($this->getSide($i) instanceof Water){
|
||||
return Block::get(Block::CONCRETE, $this->meta);
|
||||
return BlockFactory::get(Block::CONCRETE, $this->meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,9 +302,9 @@ class SimpleCommandMap implements CommandMap{
|
||||
}
|
||||
|
||||
$targets = [];
|
||||
$bad = [];
|
||||
$recursive = [];
|
||||
|
||||
$bad = "";
|
||||
$recursive = "";
|
||||
foreach($commandStrings as $commandString){
|
||||
$args = explode(" ", $commandString);
|
||||
$commandName = "";
|
||||
@ -312,27 +312,21 @@ class SimpleCommandMap implements CommandMap{
|
||||
|
||||
|
||||
if($command === null){
|
||||
if(strlen($bad) > 0){
|
||||
$bad .= ", ";
|
||||
}
|
||||
$bad .= $commandString;
|
||||
$bad[] = $commandString;
|
||||
}elseif($commandName === $alias){
|
||||
if($recursive !== ""){
|
||||
$recursive .= ", ";
|
||||
}
|
||||
$recursive .= $commandString;
|
||||
$recursive[] = $commandString;
|
||||
}else{
|
||||
$targets[] = $commandString;
|
||||
}
|
||||
}
|
||||
|
||||
if($recursive !== ""){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, $recursive]));
|
||||
if(!empty($recursive)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.recursive", [$alias, implode(", ", $recursive)]));
|
||||
continue;
|
||||
}
|
||||
|
||||
if(strlen($bad) > 0){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, $bad]));
|
||||
if(!empty($bad)){
|
||||
$this->server->getLogger()->warning($this->server->getLanguage()->translateString("pocketmine.command.alias.notFound", [$alias, implode(", ", $bad)]));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -62,15 +62,7 @@ class KillCommand extends VanillaCommand{
|
||||
$player = $sender->getServer()->getPlayer($args[0]);
|
||||
|
||||
if($player instanceof Player){
|
||||
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
|
||||
if($ev->isCancelled()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$player->setLastDamageCause($ev);
|
||||
$player->setHealth(0);
|
||||
|
||||
$player->attack(new EntityDamageEvent($player, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.kill.successful", [$player->getName()]));
|
||||
}else{
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
|
||||
@ -86,14 +78,7 @@ class KillCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
|
||||
if($ev->isCancelled()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$sender->setLastDamageCause($ev);
|
||||
$sender->setHealth(0);
|
||||
$sender->attack(new EntityDamageEvent($sender, EntityDamageEvent::CAUSE_SUICIDE, 1000));
|
||||
$sender->sendMessage(new TranslationContainer("commands.kill.successful", [$sender->getName()]));
|
||||
}else{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class PluginsCommand extends VanillaCommand{
|
||||
@ -43,20 +44,12 @@ class PluginsCommand extends VanillaCommand{
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
$this->sendPluginList($sender);
|
||||
|
||||
$list = array_map(function(Plugin $plugin) : string{
|
||||
return ($plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED) . $plugin->getDescription()->getFullName();
|
||||
}, $sender->getServer()->getPluginManager()->getPlugins());
|
||||
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($list), implode(TextFormat::WHITE . ", ", $list)]));
|
||||
return true;
|
||||
}
|
||||
|
||||
private function sendPluginList(CommandSender $sender){
|
||||
$list = "";
|
||||
foreach(($plugins = $sender->getServer()->getPluginManager()->getPlugins()) as $plugin){
|
||||
if(strlen($list) > 0){
|
||||
$list .= TextFormat::WHITE . ", ";
|
||||
}
|
||||
$list .= $plugin->isEnabled() ? TextFormat::GREEN : TextFormat::RED;
|
||||
$list .= $plugin->getDescription()->getFullName();
|
||||
}
|
||||
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.plugins.success", [count($plugins), $list]));
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ class TeleportCommand extends VanillaCommand{
|
||||
}
|
||||
|
||||
$args = array_values(array_filter($args, function($arg){
|
||||
return strlen($arg) > 0;
|
||||
return $arg !== "";
|
||||
}));
|
||||
if(count($args) < 1 or count($args) > 6){
|
||||
throw new InvalidCommandSyntaxException();
|
||||
|
@ -98,21 +98,33 @@ class TimingsCommand extends VanillaCommand{
|
||||
if($paste){
|
||||
fseek($fileTimings, 0);
|
||||
$data = [
|
||||
"syntax" => "text",
|
||||
"poster" => $sender->getServer()->getName(),
|
||||
"content" => stream_get_contents($fileTimings)
|
||||
"browser" => $agent = $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion(),
|
||||
"data" => $content = stream_get_contents($fileTimings)
|
||||
];
|
||||
fclose($fileTimings);
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new class([
|
||||
["page" => "http://paste.ubuntu.com", "extraOpts" => [
|
||||
CURLOPT_HTTPHEADER => ["User-Agent: " . $sender->getServer()->getName() . " " . $sender->getServer()->getPocketMineVersion()],
|
||||
CURLOPT_POST => 1,
|
||||
CURLOPT_POSTFIELDS => $data,
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]]
|
||||
], $sender) extends BulkCurlTask{
|
||||
$host = $sender->getServer()->getProperty("timings.host", "timings.pmmp.io");
|
||||
|
||||
$sender->getServer()->getAsyncPool()->submitTask(new class($sender, $host, $agent, $data) extends BulkCurlTask{
|
||||
/** @var string */
|
||||
private $host;
|
||||
|
||||
public function __construct(CommandSender $sender, string $host, string $agent, array $data){
|
||||
parent::__construct([
|
||||
["page" => "https://$host?upload=true", "extraOpts" => [
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"User-Agent: $agent",
|
||||
"Content-Type: application/x-www-form-urlencoded"
|
||||
],
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query($data),
|
||||
CURLOPT_AUTOREFERER => false,
|
||||
CURLOPT_FOLLOWLOCATION => false
|
||||
]]
|
||||
], $sender);
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$sender = $this->fetchLocal();
|
||||
if($sender instanceof Player and !$sender->isOnline()){ // TODO replace with a more generic API method for checking availability of CommandSender
|
||||
@ -123,23 +135,14 @@ class TimingsCommand extends VanillaCommand{
|
||||
$server->getLogger()->logException($result);
|
||||
return;
|
||||
}
|
||||
list(, $headers) = $result;
|
||||
foreach($headers as $headerGroup){
|
||||
if(isset($headerGroup["location"]) and preg_match('#^http://paste\\.ubuntu\\.com/([A-Za-z0-9+\/=]+)/#', trim($headerGroup["location"]), $match)){
|
||||
$pasteId = $match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(isset($pasteId)){
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsUpload", ["http://paste.ubuntu.com/" . $pasteId . "/"]));
|
||||
if(isset($result[0]) && is_array($response = json_decode($result[0], true)) && isset($response["id"])){
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsRead",
|
||||
["http://" . $sender->getServer()->getProperty("timings.host", "timings.pmmp.io") . "/?url=" . urlencode($pasteId)]));
|
||||
["https://" . $this->host . "/?id=" . $response["id"]]));
|
||||
}else{
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.pasteError"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}else{
|
||||
fclose($fileTimings);
|
||||
$sender->sendMessage(new TranslationContainer("pocketmine.command.timings.timingsWrite", [$timings]));
|
||||
|
@ -64,7 +64,7 @@ use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\AddEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityMotionPacket;
|
||||
@ -169,7 +169,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_LIMITED_LIFE = 77;
|
||||
public const DATA_ARMOR_STAND_POSE_INDEX = 78; //int
|
||||
public const DATA_ENDER_CRYSTAL_TIME_OFFSET = 79; //int
|
||||
/* 80 (byte) something to do with nametag visibility? */
|
||||
public const DATA_ALWAYS_SHOW_NAMETAG = 80; //byte: -1 = default, 0 = only when looked at, 1 = always
|
||||
public const DATA_COLOR_2 = 81; //byte
|
||||
/* 82 (unknown) */
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
@ -228,7 +228,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_FLAG_FIRE_IMMUNE = 48;
|
||||
public const DATA_FLAG_DANCING = 49;
|
||||
public const DATA_FLAG_ENCHANTED = 50;
|
||||
//51 is something to do with tridents
|
||||
public const DATA_FLAG_SHOW_TRIDENT_ROPE = 51; // tridents show an animated rope when enchanted with loyalty after they are thrown and return to their owner. To be combined with DATA_OWNER_EID
|
||||
public const DATA_FLAG_CONTAINER_PRIVATE = 52; //inventory is private, doesn't drop contents when killed if true
|
||||
//53 TransformationComponent
|
||||
public const DATA_FLAG_SPIN_ATTACK = 54;
|
||||
@ -285,6 +285,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public static function createEntity($type, Level $level, CompoundTag $nbt, ...$args) : ?Entity{
|
||||
if(isset(self::$knownEntities[$type])){
|
||||
$class = self::$knownEntities[$type];
|
||||
/** @see Entity::__construct() */
|
||||
return new $class($level, $nbt, ...$args);
|
||||
}
|
||||
|
||||
@ -407,8 +408,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public $boundingBox;
|
||||
/** @var bool */
|
||||
public $onGround;
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
/** @var float */
|
||||
public $eyeHeight = null;
|
||||
@ -606,7 +605,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setNameTagAlwaysVisible(bool $value = true) : void{
|
||||
$this->setGenericFlag(self::DATA_FLAG_ALWAYS_SHOW_NAMETAG, $value);
|
||||
$this->propertyManager->setByte(self::DATA_ALWAYS_SHOW_NAMETAG, $value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1015,8 +1014,8 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->isOnFire()){
|
||||
$hasUpdate = ($hasUpdate || $this->doOnFireTick($tickDiff));
|
||||
if($this->isOnFire() and $this->doOnFireTick($tickDiff)){
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->noDamageTicks > 0){
|
||||
@ -1026,7 +1025,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
}
|
||||
|
||||
$this->age += $tickDiff;
|
||||
$this->ticksLived += $tickDiff;
|
||||
|
||||
return $hasUpdate;
|
||||
@ -1134,13 +1132,20 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
protected function broadcastMovement(bool $teleport = false) : void{
|
||||
if($this->chunk !== null){
|
||||
$pk = new MoveEntityPacket();
|
||||
$pk = new MoveEntityAbsolutePacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->position = $this->getOffsetPosition($this);
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->headYaw = $this->yaw; //TODO
|
||||
$pk->teleported = $teleport;
|
||||
|
||||
//this looks very odd but is correct as of 1.5.0.7
|
||||
//for arrows this is actually x/y/z rotation
|
||||
//for mobs x and z are used for pitch and yaw, and y is used for headyaw
|
||||
$pk->xRot = $this->pitch;
|
||||
$pk->yRot = $this->yaw; //TODO: head yaw
|
||||
$pk->zRot = $this->yaw;
|
||||
|
||||
if($teleport){
|
||||
$pk->flags |= MoveEntityAbsolutePacket::FLAG_TELEPORT;
|
||||
}
|
||||
|
||||
$this->level->addChunkPacket($this->chunk->getX(), $this->chunk->getZ(), $pk);
|
||||
}
|
||||
@ -1346,7 +1351,6 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
|
||||
if($this->hasMovementUpdate()){
|
||||
$this->tryChangeMovement();
|
||||
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
|
||||
|
||||
if(abs($this->motion->x) <= self::MOTION_THRESHOLD){
|
||||
$this->motion->x = 0;
|
||||
@ -1358,6 +1362,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
$this->motion->z = 0;
|
||||
}
|
||||
|
||||
if($this->motion->x != 0 or $this->motion->y != 0 or $this->motion->z != 0){
|
||||
$this->move($this->motion->x, $this->motion->y, $this->motion->z);
|
||||
}
|
||||
|
||||
$this->forceMovementUpdate = false;
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,9 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\UUID;
|
||||
|
||||
@ -798,6 +800,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
throw new \InvalidStateException((new \ReflectionClass($this))->getShortName() . " must have a valid skin set");
|
||||
}
|
||||
|
||||
if(!($this instanceof Player)){
|
||||
/* we don't use Server->updatePlayerListData() because that uses batches, which could cause race conditions in async compression mode */
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_ADD;
|
||||
$pk->entries = [PlayerListEntry::createAdditionEntry($this->uuid, $this->id, $this->getName(), $this->getName(), 0, $this->skin)];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
$pk->uuid = $this->getUniqueId();
|
||||
$pk->username = $this->getName();
|
||||
@ -816,7 +826,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$this->armorInventory->sendContents($player);
|
||||
|
||||
if(!($this instanceof Player)){
|
||||
$this->sendSkin([$player]);
|
||||
$pk = new PlayerListPacket();
|
||||
$pk->type = PlayerListPacket::TYPE_REMOVE;
|
||||
$pk->entries = [PlayerListEntry::createRemovalEntry($this->uuid)];
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -575,17 +575,14 @@ abstract class Living extends Entity implements Damageable{
|
||||
$motion = clone $this->motion;
|
||||
|
||||
$motion->x /= 2;
|
||||
$motion->y /= 2;
|
||||
$motion->z /= 2;
|
||||
$motion->x += $x * $f * $base;
|
||||
$motion->y += $base;
|
||||
$motion->z += $z * $f * $base;
|
||||
|
||||
if($this->onGround){
|
||||
$motion->y /= 2;
|
||||
$motion->y += $base;
|
||||
|
||||
if($motion->y > 0.4){ //this is hardcoded in vanilla
|
||||
$motion->y = 0.4;
|
||||
}
|
||||
if($motion->y > $base){
|
||||
$motion->y = $base;
|
||||
}
|
||||
|
||||
$this->setMotion($motion);
|
||||
@ -632,7 +629,9 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
$this->doEffectsTick($tickDiff);
|
||||
if($this->doEffectsTick($tickDiff)){
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->isAlive()){
|
||||
if($this->isInsideOfSolid()){
|
||||
@ -644,6 +643,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
if(!$this->canBreathe()){
|
||||
$this->setBreathing(false);
|
||||
$this->doAirSupplyTick($tickDiff);
|
||||
$hasUpdate = true;
|
||||
}elseif(!$this->isBreathing()){
|
||||
$this->setBreathing(true);
|
||||
$this->setAirSupplyTicks($this->getMaxAirSupplyTicks());
|
||||
@ -659,7 +659,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function doEffectsTick(int $tickDiff = 1) : void{
|
||||
protected function doEffectsTick(int $tickDiff = 1) : bool{
|
||||
foreach($this->effects as $instance){
|
||||
$type = $instance->getType();
|
||||
if($type->canTick($instance)){
|
||||
@ -670,6 +670,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->removeEffect($instance->getId());
|
||||
}
|
||||
}
|
||||
|
||||
return !empty($this->effects);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,6 +88,9 @@ class ExperienceOrb extends Entity{
|
||||
public $gravity = 0.04;
|
||||
public $drag = 0.02;
|
||||
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* Ticker used for determining interval in which to look for new target players.
|
||||
@ -159,6 +162,7 @@ class ExperienceOrb extends Entity{
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
$this->age += $tickDiff;
|
||||
if($this->age > 6000){
|
||||
$this->flagForDespawn();
|
||||
return true;
|
||||
|
@ -134,6 +134,7 @@ class FallingBlock extends Entity{
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setInt("TileID", $this->block->getId(), true);
|
||||
$this->namedtag->setByte("Data", $this->block->getDamage());
|
||||
}
|
||||
|
@ -53,6 +53,9 @@ class ItemEntity extends Entity{
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
/** @var int */
|
||||
protected $age = 0;
|
||||
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
|
||||
@ -82,14 +85,13 @@ class ItemEntity extends Entity{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if(!$this->isFlaggedForDespawn()){
|
||||
if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
$this->pickupDelay -= $tickDiff;
|
||||
if($this->pickupDelay < 0){
|
||||
$this->pickupDelay = 0;
|
||||
}
|
||||
if(!$this->isFlaggedForDespawn() and $this->pickupDelay > -1 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
$this->pickupDelay -= $tickDiff;
|
||||
if($this->pickupDelay < 0){
|
||||
$this->pickupDelay = 0;
|
||||
}
|
||||
|
||||
$this->age += $tickDiff;
|
||||
if($this->age > 6000){
|
||||
$this->server->getPluginManager()->callEvent($ev = new ItemDespawnEvent($this));
|
||||
if($ev->isCancelled()){
|
||||
@ -99,7 +101,6 @@ class ItemEntity extends Entity{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
@ -197,7 +198,7 @@ class ItemEntity extends Entity{
|
||||
}
|
||||
|
||||
public function onCollideWithPlayer(Player $player) : void{
|
||||
if($this->getPickupDelay() > 0){
|
||||
if($this->getPickupDelay() !== 0){
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,8 @@ class Painting extends Entity{
|
||||
|
||||
$this->namedtag->setByte("Facing", (int) $this->direction);
|
||||
$this->namedtag->setByte("Direction", (int) $this->direction); //Save both for full compatibility
|
||||
|
||||
$this->namedtag->setString("Motive", $this->motive);
|
||||
}
|
||||
|
||||
public function kill() : void{
|
||||
|
@ -48,11 +48,24 @@ class Arrow extends Projectile{
|
||||
|
||||
protected $damage = 2;
|
||||
|
||||
/** @var int */
|
||||
protected $collideTicks = 0;
|
||||
|
||||
public function __construct(Level $level, CompoundTag $nbt, ?Entity $shootingEntity = null, bool $critical = false){
|
||||
parent::__construct($level, $nbt, $shootingEntity);
|
||||
$this->setCritical($critical);
|
||||
}
|
||||
|
||||
protected function initEntity() : void{
|
||||
parent::initEntity();
|
||||
$this->collideTicks = $this->namedtag->getShort("life", $this->collideTicks);
|
||||
}
|
||||
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
$this->namedtag->setShort("life", $this->collideTicks);
|
||||
}
|
||||
|
||||
public function isCritical() : bool{
|
||||
return $this->getGenericFlag(self::DATA_FLAG_CRITICAL);
|
||||
}
|
||||
@ -77,9 +90,14 @@ class Arrow extends Projectile{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if($this->age > 1200){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
if($this->isCollided){
|
||||
$this->collideTicks += $tickDiff;
|
||||
if($this->collideTicks > 1200){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}else{
|
||||
$this->collideTicks = 0;
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
|
@ -66,7 +66,5 @@ class EnderPearl extends Throwable{
|
||||
|
||||
$owner->attack(new EntityDamageEvent($owner, EntityDamageEvent::CAUSE_FALL, 5));
|
||||
}
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,5 @@ class ExperienceBottle extends Throwable{
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_GLASS);
|
||||
|
||||
$this->level->dropExperience($this, mt_rand(3, 11));
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,6 @@ abstract class Projectile extends Entity{
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
$this->age = $this->namedtag->getShort("Age", $this->age);
|
||||
|
||||
do{
|
||||
$blockHit = null;
|
||||
@ -123,8 +122,6 @@ abstract class Projectile extends Entity{
|
||||
public function saveNBT() : void{
|
||||
parent::saveNBT();
|
||||
|
||||
$this->namedtag->setShort("Age", $this->age);
|
||||
|
||||
if($this->blockHit !== null){
|
||||
$this->namedtag->setInt("tileX", $this->blockHit->x);
|
||||
$this->namedtag->setInt("tileY", $this->blockHit->y);
|
||||
|
@ -82,7 +82,7 @@ class SplashPotion extends Throwable{
|
||||
if($hasEffects){
|
||||
if(!$this->willLinger()){
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->expandedCopy(4.125, 2.125, 4.125), $this) as $entity){
|
||||
if($entity instanceof Living){
|
||||
if($entity instanceof Living and $entity->isAlive()){
|
||||
$distanceSquared = $entity->distanceSquared($this);
|
||||
if($distanceSquared > 16){ //4 blocks
|
||||
continue;
|
||||
@ -124,8 +124,6 @@ class SplashPotion extends Throwable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity\projectile;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
|
||||
abstract class Throwable extends Projectile{
|
||||
|
||||
public $width = 0.25;
|
||||
@ -31,18 +34,8 @@ abstract class Throwable extends Projectile{
|
||||
protected $gravity = 0.03;
|
||||
protected $drag = 0.01;
|
||||
|
||||
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
if($this->age > 1200 or $this->isCollided){
|
||||
$this->flagForDespawn();
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
protected function onHitBlock(Block $blockHit, RayTraceResult $hitResult) : void{
|
||||
parent::onHitBlock($blockHit, $hitResult);
|
||||
$this->flagForDespawn();
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ use pocketmine\event\entity\EntityEvent;
|
||||
* Called when a player gains or loses XP levels and/or progress.
|
||||
*/
|
||||
class PlayerExperienceChangeEvent extends EntityEvent implements Cancellable{
|
||||
public static $handlerList = null;
|
||||
/** @var Human */
|
||||
protected $entity;
|
||||
/** @var int */
|
||||
|
@ -38,7 +38,7 @@ class DropItemAction extends InventoryAction{
|
||||
}
|
||||
|
||||
public function isValid(Player $source) : bool{
|
||||
return true;
|
||||
return !$this->targetItem->isNull();
|
||||
}
|
||||
|
||||
public function onPreExecute(Player $source) : bool{
|
||||
|
@ -57,8 +57,8 @@ class Bucket extends Item implements Consumable{
|
||||
if($blockClicked instanceof Liquid and $blockClicked->getDamage() === 0){
|
||||
$stack = clone $this;
|
||||
|
||||
$resultItem = $stack->pop();
|
||||
$resultItem->setDamage($blockClicked->getFlowingForm()->getId());
|
||||
$stack->pop();
|
||||
$resultItem = ItemFactory::get(Item::BUCKET, $blockClicked->getFlowingForm()->getId());
|
||||
$player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketFillEvent($player, $blockReplace, $face, $this, $resultItem));
|
||||
if(!$ev->isCancelled()){
|
||||
$player->getLevel()->setBlock($blockClicked, BlockFactory::get(Block::AIR), true, true);
|
||||
@ -80,9 +80,7 @@ class Bucket extends Item implements Consumable{
|
||||
}
|
||||
}
|
||||
}elseif($resultBlock instanceof Liquid and $blockReplace->canBeReplaced()){
|
||||
$resultItem = clone $this;
|
||||
$resultItem->setDamage(0);
|
||||
$player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, $resultItem));
|
||||
$player->getServer()->getPluginManager()->callEvent($ev = new PlayerBucketEmptyEvent($player, $blockReplace, $face, $this, ItemFactory::get(Item::BUCKET)));
|
||||
if(!$ev->isCancelled()){
|
||||
$player->getLevel()->setBlock($blockReplace, $resultBlock->getFlowingForm(), true, true);
|
||||
$player->getLevel()->broadcastLevelSoundEvent($blockClicked->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound());
|
||||
|
@ -191,7 +191,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
/** @var Player[][] */
|
||||
private $chunkSendQueue = [];
|
||||
/** @var bool[] */
|
||||
/** @var ChunkRequestTask[] */
|
||||
private $chunkSendTasks = [];
|
||||
|
||||
/** @var bool[] */
|
||||
@ -797,8 +797,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->checkSleep();
|
||||
}
|
||||
|
||||
if(!empty($this->players) and !empty($this->globalPackets)){
|
||||
$this->server->batchPackets($this->players, $this->globalPackets);
|
||||
if(!empty($this->globalPackets)){
|
||||
if(!empty($this->players)){
|
||||
$this->server->batchPackets($this->players, $this->globalPackets);
|
||||
}
|
||||
$this->globalPackets = [];
|
||||
}
|
||||
|
||||
@ -1627,7 +1629,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$spawnLocation = $this->getSpawnLocation();
|
||||
$s = new Vector2($spawnLocation->x, $spawnLocation->z);
|
||||
if(count($this->server->getOps()->getAll()) > 0 and $t->distance($s) <= $distance){
|
||||
if($t->distance($s) <= $distance){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -2456,7 +2458,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
private function sendChunkFromCache(int $x, int $z){
|
||||
if(isset($this->chunkSendTasks[$index = Level::chunkHash($x, $z)])){
|
||||
if(isset($this->chunkSendQueue[$index = Level::chunkHash($x, $z)])){
|
||||
foreach($this->chunkSendQueue[$index] as $player){
|
||||
/** @var Player $player */
|
||||
if($player->isConnected() and isset($player->usedChunks[$index])){
|
||||
@ -2464,7 +2466,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
unset($this->chunkSendQueue[$index]);
|
||||
unset($this->chunkSendTasks[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2473,11 +2474,17 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->syncChunkSendTimer->startTiming();
|
||||
|
||||
foreach($this->chunkSendQueue as $index => $players){
|
||||
if(isset($this->chunkSendTasks[$index])){
|
||||
continue;
|
||||
}
|
||||
Level::getXZ($index, $x, $z);
|
||||
$this->chunkSendTasks[$index] = true;
|
||||
|
||||
if(isset($this->chunkSendTasks[$index])){
|
||||
if($this->chunkSendTasks[$index]->isCrashed()){
|
||||
unset($this->chunkSendTasks[$index]);
|
||||
$this->server->getLogger()->error("Failed to prepare chunk $x $z for sending, retrying");
|
||||
}else{
|
||||
//Not ready for sending yet
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(isset($this->chunkCache[$index])){
|
||||
$this->sendChunkFromCache($x, $z);
|
||||
continue;
|
||||
@ -2490,7 +2497,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
assert($chunk->getX() === $x and $chunk->getZ() === $z, "Chunk coordinate mismatch: expected $x $z, but chunk has coordinates " . $chunk->getX() . " " . $chunk->getZ() . ", did you forget to clone a chunk before setting?");
|
||||
|
||||
$this->server->getAsyncPool()->submitTask(new ChunkRequestTask($this, $x, $z, $chunk));
|
||||
$this->server->getAsyncPool()->submitTask($task = new ChunkRequestTask($this, $x, $z, $chunk));
|
||||
$this->chunkSendTasks[$index] = $task;
|
||||
|
||||
$this->timings->syncChunkSendPrepareTimer->stopTiming();
|
||||
}
|
||||
@ -2503,24 +2511,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->syncChunkSendTimer->startTiming();
|
||||
|
||||
$index = Level::chunkHash($x, $z);
|
||||
unset($this->chunkSendTasks[$index]);
|
||||
|
||||
if(!isset($this->chunkCache[$index]) and $this->server->getMemoryManager()->canUseChunkCache()){
|
||||
$this->chunkCache[$index] = $payload;
|
||||
$this->sendChunkFromCache($x, $z);
|
||||
$this->timings->syncChunkSendTimer->stopTiming();
|
||||
return;
|
||||
$this->chunkCache[$index] = $payload;
|
||||
$this->sendChunkFromCache($x, $z);
|
||||
if(!$this->server->getMemoryManager()->canUseChunkCache()){
|
||||
unset($this->chunkCache[$index]);
|
||||
}
|
||||
|
||||
if(isset($this->chunkSendTasks[$index])){
|
||||
foreach($this->chunkSendQueue[$index] as $player){
|
||||
/** @var Player $player */
|
||||
if($player->isConnected() and isset($player->usedChunks[$index])){
|
||||
$player->sendChunk($x, $z, $payload);
|
||||
}
|
||||
}
|
||||
unset($this->chunkSendQueue[$index]);
|
||||
unset($this->chunkSendTasks[$index]);
|
||||
}
|
||||
$this->timings->syncChunkSendTimer->stopTiming();
|
||||
}
|
||||
|
||||
@ -2754,6 +2752,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
unset($this->chunkCache[$chunkHash]);
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
unset($this->chunkSendQueue[$chunkHash]);
|
||||
unset($this->chunkSendTasks[$chunkHash]);
|
||||
|
||||
$this->timings->doChunkUnload->stopTiming();
|
||||
|
||||
@ -2791,7 +2791,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$chunk = $this->getChunk($v->x >> 4, $v->z >> 4, false);
|
||||
$x = (int) $v->x;
|
||||
$z = (int) $v->z;
|
||||
if($chunk !== null){
|
||||
if($chunk !== null and $chunk->isGenerated()){
|
||||
$y = (int) min($max - 2, $v->y);
|
||||
$wasAir = ($chunk->getBlockId($x & 0x0f, $y - 1, $z & 0x0f) === 0);
|
||||
for(; $y > 0; --$y){
|
||||
@ -2947,16 +2947,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($populate){
|
||||
if(!isset($this->chunkPopulationQueue[$index])){
|
||||
$this->chunkPopulationQueue[$index] = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
|
||||
}
|
||||
$this->chunkPopulationQueue[$index] = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
|
||||
}
|
||||
$task = new PopulationTask($this, $chunk);
|
||||
$this->server->getAsyncPool()->submitTask($task);
|
||||
}
|
||||
$task = new PopulationTask($this, $chunk);
|
||||
$this->server->getAsyncPool()->submitTask($task);
|
||||
}
|
||||
|
||||
Timings::$populationTimer->stopTiming();
|
||||
|
@ -377,7 +377,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
/** @var CompoundTag[] $entities */
|
||||
$entities = [];
|
||||
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false and strlen($entityData) > 0){
|
||||
if(($entityData = $this->db->get($index . self::TAG_ENTITY)) !== false and $entityData !== ""){
|
||||
$entities = $nbt->read($entityData, true);
|
||||
if(!is_array($entities)){
|
||||
$entities = [$entities];
|
||||
@ -392,7 +392,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
$tiles = [];
|
||||
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false and strlen($tileData) > 0){
|
||||
if(($tileData = $this->db->get($index . self::TAG_BLOCK_ENTITY)) !== false and $tileData !== ""){
|
||||
$tiles = $nbt->read($tileData, true);
|
||||
if(!is_array($tiles)){
|
||||
$tiles = [$tiles];
|
||||
@ -402,7 +402,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
//TODO: extra data should be converted into blockstorage layers (first they need to be implemented!)
|
||||
/*
|
||||
$extraData = [];
|
||||
if(($extraRawData = $this->db->get($index . self::TAG_BLOCK_EXTRA_DATA)) !== false and strlen($extraRawData) > 0){
|
||||
if(($extraRawData = $this->db->get($index . self::TAG_BLOCK_EXTRA_DATA)) !== false and $extraRawData !== ""){
|
||||
$binaryStream->setBuffer($extraRawData, 0);
|
||||
$count = $binaryStream->getLInt();
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
|
@ -52,7 +52,10 @@ class GeneratorRegisterTask extends AsyncTask{
|
||||
$manager = new SimpleChunkManager($this->seed, $this->worldHeight);
|
||||
$this->saveToThreadStore("generation.level{$this->levelId}.manager", $manager);
|
||||
|
||||
/** @var Generator $generator */
|
||||
/**
|
||||
* @var Generator $generator
|
||||
* @see Generator::__construct()
|
||||
*/
|
||||
$generator = new $this->generatorClass(unserialize($this->settings));
|
||||
$generator->init($manager, new Random($manager->getSeed()));
|
||||
$this->saveToThreadStore("generation.level{$this->levelId}.generator", $generator);
|
||||
|
@ -54,14 +54,6 @@ abstract class LightUpdate{
|
||||
$this->subChunkHandler = new SubChunkIteratorManager($this->level);
|
||||
}
|
||||
|
||||
public function addSpreadNode(int $x, int $y, int $z){
|
||||
$this->spreadQueue->enqueue([$x, $y, $z]);
|
||||
}
|
||||
|
||||
public function addRemoveNode(int $x, int $y, int $z, int $oldLight){
|
||||
$this->spreadQueue->enqueue([$x, $y, $z, $oldLight]);
|
||||
}
|
||||
|
||||
abstract protected function getLight(int $x, int $y, int $z) : int;
|
||||
|
||||
abstract protected function setLight(int $x, int $y, int $z, int $level);
|
||||
|
@ -29,8 +29,9 @@ use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerSkinPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerListPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\PlayerListEntry;
|
||||
use pocketmine\utils\UUID;
|
||||
|
||||
class FloatingTextParticle extends Particle{
|
||||
@ -89,9 +90,17 @@ class FloatingTextParticle extends Particle{
|
||||
}
|
||||
|
||||
if(!$this->invisible){
|
||||
$uuid = UUID::fromRandom();
|
||||
$name = $this->title . ($this->text !== "" ? "\n" . $this->text : "");
|
||||
|
||||
$add = new PlayerListPacket();
|
||||
$add->type = PlayerListPacket::TYPE_ADD;
|
||||
$add->entries = [PlayerListEntry::createAdditionEntry($uuid, $this->entityId, $name, $name, 0, new Skin("Standard_Custom", str_repeat("\x00", 8192)))];
|
||||
$p[] = $add;
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
$pk->uuid = $uuid = UUID::fromRandom();
|
||||
$pk->username = $this->title . ($this->text !== "" ? "\n" . $this->text : "");
|
||||
$pk->uuid = $uuid;
|
||||
$pk->username = $name;
|
||||
$pk->entityRuntimeId = $this->entityId;
|
||||
$pk->position = $this->asVector3(); //TODO: check offset
|
||||
$pk->item = ItemFactory::get(Item::AIR, 0, 0);
|
||||
@ -106,10 +115,10 @@ class FloatingTextParticle extends Particle{
|
||||
|
||||
$p[] = $pk;
|
||||
|
||||
$skinPk = new PlayerSkinPacket();
|
||||
$skinPk->uuid = $uuid;
|
||||
$skinPk->skin = new Skin("Standard_Custom", str_repeat("\x00", 8192));
|
||||
$p[] = $skinPk;
|
||||
$remove = new PlayerListPacket();
|
||||
$remove->type = PlayerListPacket::TYPE_REMOVE;
|
||||
$remove->entries = [PlayerListEntry::createRemovalEntry($uuid)];
|
||||
$p[] = $remove;
|
||||
}
|
||||
|
||||
return $p;
|
||||
|
@ -76,7 +76,8 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityDeltaPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\PhotoTransferPacket;
|
||||
@ -111,6 +112,7 @@ use pocketmine\network\mcpe\protocol\SetEntityLinkPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityMotionPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetHealthPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLastHurtByPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetScorePacket;
|
||||
use pocketmine\network\mcpe\protocol\SetSpawnPositionPacket;
|
||||
@ -207,7 +209,7 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMoveEntity(MoveEntityPacket $packet) : bool{
|
||||
public function handleMoveEntityAbsolute(MoveEntityAbsolutePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -578,4 +580,12 @@ abstract class NetworkSession{
|
||||
public function handleUpdateBlockSynced(UpdateBlockSyncedPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleMoveEntityDelta(MoveEntityDeltaPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleSetLocalPlayerAsInitialized(SetLocalPlayerAsInitializedPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -141,6 +141,10 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
$class = $ev->getPlayerClass();
|
||||
|
||||
/**
|
||||
* @var Player $player
|
||||
* @see Player::__construct()
|
||||
*/
|
||||
$player = new $class($this, $ev->getAddress(), $ev->getPort());
|
||||
$this->players[$identifier] = $player;
|
||||
$this->identifiersACK[$identifier] = 0;
|
||||
|
@ -145,7 +145,7 @@ class VerifyLoginTask extends AsyncTask{
|
||||
public function onCompletion(Server $server){
|
||||
/** @var Player $player */
|
||||
$player = $this->fetchLocal();
|
||||
if($player->isClosed()){
|
||||
if(!$player->isConnected()){
|
||||
$server->getLogger()->error("Player " . $player->getName() . " was disconnected before their login could be verified");
|
||||
}else{
|
||||
$player->onVerifyCompleted($this->packet, $this->error, $this->authenticated);
|
||||
|
@ -44,9 +44,11 @@ class AddEntityPacket extends DataPacket{
|
||||
/** @var Vector3|null */
|
||||
public $motion;
|
||||
/** @var float */
|
||||
public $pitch = 0.0;
|
||||
/** @var float */
|
||||
public $yaw = 0.0;
|
||||
/** @var float */
|
||||
public $pitch = 0.0;
|
||||
public $headYaw = 0.0;
|
||||
|
||||
/** @var Attribute[] */
|
||||
public $attributes = [];
|
||||
@ -63,6 +65,7 @@ class AddEntityPacket extends DataPacket{
|
||||
$this->motion = $this->getVector3();
|
||||
$this->pitch = $this->getLFloat();
|
||||
$this->yaw = $this->getLFloat();
|
||||
$this->headYaw = $this->getLFloat();
|
||||
|
||||
$attrCount = $this->getUnsignedVarInt();
|
||||
for($i = 0; $i < $attrCount; ++$i){
|
||||
@ -97,6 +100,7 @@ class AddEntityPacket extends DataPacket{
|
||||
$this->putVector3Nullable($this->motion);
|
||||
$this->putLFloat($this->pitch);
|
||||
$this->putLFloat($this->yaw);
|
||||
$this->putLFloat($this->headYaw);
|
||||
|
||||
$this->putUnsignedVarInt(count($this->attributes));
|
||||
foreach($this->attributes as $attribute){
|
||||
|
@ -29,6 +29,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
use pocketmine\network\mcpe\protocol\types\MapTrackedObject;
|
||||
use pocketmine\utils\Color;
|
||||
|
||||
class ClientboundMapItemDataPacket extends DataPacket{
|
||||
@ -49,8 +50,8 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
/** @var int */
|
||||
public $scale;
|
||||
|
||||
/** @var int[] */
|
||||
public $decorationEntityUniqueIds = [];
|
||||
/** @var MapTrackedObject[] */
|
||||
public $trackedEntities = [];
|
||||
/** @var array */
|
||||
public $decorations = [];
|
||||
|
||||
@ -83,7 +84,16 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
|
||||
if(($this->type & self::BITFLAG_DECORATION_UPDATE) !== 0){
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
$this->decorationEntityUniqueIds[] = $this->getEntityUniqueId();
|
||||
$object = new MapTrackedObject();
|
||||
$object->type = $this->getLInt();
|
||||
if($object->type === MapTrackedObject::TYPE_BLOCK){
|
||||
$this->getBlockPosition($object->x, $object->y, $object->z);
|
||||
}elseif($object->type === MapTrackedObject::TYPE_ENTITY){
|
||||
$object->entityUniqueId = $this->getEntityUniqueId();
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Unknown map object type");
|
||||
}
|
||||
$this->trackedEntities[] = $object;
|
||||
}
|
||||
|
||||
for($i = 0, $count = $this->getUnsignedVarInt(); $i < $count; ++$i){
|
||||
@ -143,9 +153,16 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
}
|
||||
|
||||
if(($type & self::BITFLAG_DECORATION_UPDATE) !== 0){
|
||||
$this->putUnsignedVarInt(count($this->decorationEntityUniqueIds));
|
||||
foreach($this->decorationEntityUniqueIds as $id){
|
||||
$this->putEntityUniqueId($id);
|
||||
$this->putUnsignedVarInt(count($this->trackedEntities));
|
||||
foreach($this->trackedEntities as $object){
|
||||
$this->putLInt($object->type);
|
||||
if($object->type === MapTrackedObject::TYPE_BLOCK){
|
||||
$this->putBlockPosition($object->x, $object->y, $object->z);
|
||||
}elseif($object->type === MapTrackedObject::TYPE_ENTITY){
|
||||
$this->putEntityUniqueId($object->entityUniqueId);
|
||||
}else{
|
||||
throw new \UnexpectedValueException("Unknown map object type");
|
||||
}
|
||||
}
|
||||
|
||||
$this->putUnsignedVarInt($decorationCount);
|
||||
|
@ -30,14 +30,22 @@ use pocketmine\network\mcpe\NetworkSession;
|
||||
class GuiDataPickItemPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::GUI_DATA_PICK_ITEM_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $itemDescription;
|
||||
/** @var string */
|
||||
public $itemEffects;
|
||||
/** @var int */
|
||||
public $hotbarSlot;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->itemDescription = $this->getString();
|
||||
$this->itemEffects = $this->getString();
|
||||
$this->hotbarSlot = $this->getLInt();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putString($this->itemDescription);
|
||||
$this->putString($this->itemEffects);
|
||||
$this->putLInt($this->hotbarSlot);
|
||||
}
|
||||
|
||||
|
@ -29,45 +29,44 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class MoveEntityPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MOVE_ENTITY_PACKET;
|
||||
class MoveEntityAbsolutePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MOVE_ENTITY_ABSOLUTE_PACKET;
|
||||
|
||||
public const FLAG_GROUND = 0x01;
|
||||
public const FLAG_TELEPORT = 0x02;
|
||||
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
/** @var int */
|
||||
public $flags = 0;
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var float */
|
||||
public $yaw;
|
||||
public $xRot;
|
||||
/** @var float */
|
||||
public $headYaw;
|
||||
public $yRot;
|
||||
/** @var float */
|
||||
public $pitch;
|
||||
/** @var bool */
|
||||
public $onGround = false;
|
||||
/** @var bool */
|
||||
public $teleported = false;
|
||||
public $zRot;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->flags = $this->getByte();
|
||||
$this->position = $this->getVector3();
|
||||
$this->pitch = $this->getByteRotation();
|
||||
$this->headYaw = $this->getByteRotation();
|
||||
$this->yaw = $this->getByteRotation();
|
||||
$this->onGround = $this->getBool();
|
||||
$this->teleported = $this->getBool();
|
||||
$this->xRot = $this->getByteRotation();
|
||||
$this->yRot = $this->getByteRotation();
|
||||
$this->zRot = $this->getByteRotation();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putByte($this->flags);
|
||||
$this->putVector3($this->position);
|
||||
$this->putByteRotation($this->pitch);
|
||||
$this->putByteRotation($this->headYaw);
|
||||
$this->putByteRotation($this->yaw);
|
||||
$this->putBool($this->onGround);
|
||||
$this->putBool($this->teleported);
|
||||
$this->putByteRotation($this->xRot);
|
||||
$this->putByteRotation($this->yRot);
|
||||
$this->putByteRotation($this->zRot);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleMoveEntity($this);
|
||||
return $session->handleMoveEntityAbsolute($this);
|
||||
}
|
||||
}
|
104
src/pocketmine/network/mcpe/protocol/MoveEntityDeltaPacket.php
Normal file
104
src/pocketmine/network/mcpe/protocol/MoveEntityDeltaPacket.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?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;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class MoveEntityDeltaPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::MOVE_ENTITY_DELTA_PACKET;
|
||||
|
||||
public const FLAG_HAS_X = 0x01;
|
||||
public const FLAG_HAS_Y = 0x02;
|
||||
public const FLAG_HAS_Z = 0x04;
|
||||
public const FLAG_HAS_ROT_X = 0x08;
|
||||
public const FLAG_HAS_ROT_Y = 0x10;
|
||||
public const FLAG_HAS_ROT_Z = 0x20;
|
||||
|
||||
/** @var int */
|
||||
public $flags;
|
||||
/** @var int */
|
||||
public $xDiff = 0;
|
||||
/** @var int */
|
||||
public $yDiff = 0;
|
||||
/** @var int */
|
||||
public $zDiff = 0;
|
||||
/** @var float */
|
||||
public $xRot = 0.0;
|
||||
/** @var float */
|
||||
public $yRot = 0.0;
|
||||
/** @var float */
|
||||
public $zRot = 0.0;
|
||||
|
||||
private function maybeReadCoord(int $flag) : int{
|
||||
if($this->flags & $flag){
|
||||
return $this->getVarInt();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function maybeReadRotation(int $flag) : float{
|
||||
if($this->flags & $flag){
|
||||
return $this->getByteRotation();
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->flags = $this->getByte();
|
||||
$this->xDiff = $this->maybeReadCoord(self::FLAG_HAS_X);
|
||||
$this->yDiff = $this->maybeReadCoord(self::FLAG_HAS_Y);
|
||||
$this->zDiff = $this->maybeReadCoord(self::FLAG_HAS_Z);
|
||||
$this->xRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_X);
|
||||
$this->yRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Y);
|
||||
$this->zRot = $this->maybeReadRotation(self::FLAG_HAS_ROT_Z);
|
||||
}
|
||||
|
||||
private function maybeWriteCoord(int $flag, int $val) : void{
|
||||
if($this->flags & $flag){
|
||||
$this->putVarInt($val);
|
||||
}
|
||||
}
|
||||
|
||||
private function maybeWriteRotation(int $flag, float $val) : void{
|
||||
if($this->flags & $flag){
|
||||
$this->putByteRotation($val);
|
||||
}
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->flags);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_X, $this->xDiff);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Y, $this->yDiff);
|
||||
$this->maybeWriteCoord(self::FLAG_HAS_Z, $this->zDiff);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_X, $this->xRot);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Y, $this->yRot);
|
||||
$this->maybeWriteRotation(self::FLAG_HAS_ROT_Z, $this->zRot);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleMoveEntityDelta($this);
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ class PacketPool{
|
||||
static::registerPacket(new AddItemEntityPacket());
|
||||
static::registerPacket(new AddHangingEntityPacket());
|
||||
static::registerPacket(new TakeItemEntityPacket());
|
||||
static::registerPacket(new MoveEntityPacket());
|
||||
static::registerPacket(new MoveEntityAbsolutePacket());
|
||||
static::registerPacket(new MovePlayerPacket());
|
||||
static::registerPacket(new RiderJumpPacket());
|
||||
static::registerPacket(new UpdateBlockPacket());
|
||||
@ -141,6 +141,8 @@ class PacketPool{
|
||||
static::registerPacket(new SetScorePacket());
|
||||
static::registerPacket(new LabTablePacket());
|
||||
static::registerPacket(new UpdateBlockSyncedPacket());
|
||||
static::registerPacket(new MoveEntityDeltaPacket());
|
||||
static::registerPacket(new SetLocalPlayerAsInitializedPacket());
|
||||
|
||||
static::registerPacket(new BatchPacket());
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class PlayStatusPacket extends DataPacket{
|
||||
* @var int
|
||||
* Used to determine how to write the packet when we disconnect incompatible clients.
|
||||
*/
|
||||
public $protocol;
|
||||
public $protocol = ProtocolInfo::CURRENT_PROTOCOL;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->status = $this->getInt();
|
||||
|
@ -40,7 +40,8 @@ class PlayerSkinPacket extends DataPacket{
|
||||
public $newSkinName = "";
|
||||
/** @var Skin */
|
||||
public $skin;
|
||||
|
||||
/** @var bool */
|
||||
public $premiumSkin = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->uuid = $this->getUUID();
|
||||
@ -54,6 +55,8 @@ class PlayerSkinPacket extends DataPacket{
|
||||
$geometryData = $this->getString();
|
||||
|
||||
$this->skin = new Skin($skinId, $skinData, $capeData, $geometryModel, $geometryData);
|
||||
|
||||
$this->premiumSkin = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -66,6 +69,8 @@ class PlayerSkinPacket extends DataPacket{
|
||||
$this->putString($this->skin->getCapeData());
|
||||
$this->putString($this->skin->getGeometryName());
|
||||
$this->putString($this->skin->getGeometryData());
|
||||
|
||||
$this->putBool($this->premiumSkin);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -39,15 +39,15 @@ interface ProtocolInfo{
|
||||
/**
|
||||
* Actual Minecraft: PE protocol version
|
||||
*/
|
||||
public const CURRENT_PROTOCOL = 261;
|
||||
public const CURRENT_PROTOCOL = 274;
|
||||
/**
|
||||
* Current Minecraft PE version reported by the server. This is usually the earliest currently supported version.
|
||||
*/
|
||||
public const MINECRAFT_VERSION = 'v1.4.0';
|
||||
public const MINECRAFT_VERSION = 'v1.5.0';
|
||||
/**
|
||||
* Version number sent to clients in ping responses.
|
||||
*/
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.4.0';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.5.0';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -66,7 +66,7 @@ interface ProtocolInfo{
|
||||
public const ADD_ITEM_ENTITY_PACKET = 0x0f;
|
||||
public const ADD_HANGING_ENTITY_PACKET = 0x10;
|
||||
public const TAKE_ITEM_ENTITY_PACKET = 0x11;
|
||||
public const MOVE_ENTITY_PACKET = 0x12;
|
||||
public const MOVE_ENTITY_ABSOLUTE_PACKET = 0x12;
|
||||
public const MOVE_PLAYER_PACKET = 0x13;
|
||||
public const RIDER_JUMP_PACKET = 0x14;
|
||||
public const UPDATE_BLOCK_PACKET = 0x15;
|
||||
@ -159,5 +159,7 @@ interface ProtocolInfo{
|
||||
public const SET_SCORE_PACKET = 0x6c;
|
||||
public const LAB_TABLE_PACKET = 0x6d;
|
||||
public const UPDATE_BLOCK_SYNCED_PACKET = 0x6e;
|
||||
public const MOVE_ENTITY_DELTA_PACKET = 0x6f;
|
||||
public const SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET = 0x70;
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
<?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;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class SetLocalPlayerAsInitializedPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $entityRuntimeId;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleSetLocalPlayerAsInitialized($this);
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?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;
|
||||
|
||||
class MapTrackedObject{
|
||||
public const TYPE_ENTITY = 0;
|
||||
public const TYPE_BLOCK = 1;
|
||||
|
||||
/** @var int */
|
||||
public $type;
|
||||
|
||||
/** @var int Only set if is TYPE_ENTITY */
|
||||
public $entityUniqueId;
|
||||
|
||||
/** @var int */
|
||||
public $x;
|
||||
/** @var int */
|
||||
public $y;
|
||||
/** @var int */
|
||||
public $z;
|
||||
|
||||
}
|
@ -58,6 +58,11 @@ class QueryHandler{
|
||||
$this->server->getLogger()->info($this->server->getLanguage()->translateString("pocketmine.server.query.running", [$addr, $port]));
|
||||
}
|
||||
|
||||
private function debug(string $message) : void{
|
||||
//TODO: replace this with a proper prefixed logger
|
||||
$this->server->getLogger()->debug("[Query] $message");
|
||||
}
|
||||
|
||||
public function regenerateInfo(){
|
||||
$ev = $this->server->getQueryInformation();
|
||||
$this->longData = $ev->getLongQuery();
|
||||
@ -91,7 +96,8 @@ class QueryHandler{
|
||||
break;
|
||||
case self::STATISTICS: //Stat
|
||||
$token = Binary::readInt(substr($payload, 0, 4));
|
||||
if($token !== self::getTokenString($this->token, $address) and $token !== self::getTokenString($this->lastToken, $address)){
|
||||
if($token !== ($t1 = self::getTokenString($this->token, $address)) and $token !== ($t2 = self::getTokenString($this->lastToken, $address))){
|
||||
$this->debug("Bad token $token from $address $port, expected $t1 or $t2");
|
||||
break;
|
||||
}
|
||||
$reply = chr(self::STATISTICS);
|
||||
@ -108,6 +114,9 @@ class QueryHandler{
|
||||
}
|
||||
$interface->sendRawPacket($address, $port, $reply);
|
||||
break;
|
||||
default:
|
||||
$this->debug("Unhandled packet from $address $port: 0x" . bin2hex($packet));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,7 +177,7 @@ class RCONInstance extends Thread{
|
||||
$disconnect[$id] = $sock;
|
||||
break;
|
||||
}
|
||||
if(strlen($payload) > 0){
|
||||
if($payload !== ""){
|
||||
$this->cmd = ltrim($payload);
|
||||
$this->synchronized(function(){
|
||||
$this->notifier->wakeupSleeper();
|
||||
|
@ -168,7 +168,7 @@ class BanEntry{
|
||||
}
|
||||
|
||||
$expire = trim(array_shift($str));
|
||||
if(strtolower($expire) !== "forever" and strlen($expire) > 0){
|
||||
if($expire !== "" and strtolower($expire) !== "forever"){
|
||||
$entry->setExpires(self::parseDate($expire));
|
||||
}
|
||||
if(empty($str)){
|
||||
|
@ -102,9 +102,6 @@ class PluginManager{
|
||||
/** @var string|null */
|
||||
private $pluginDataDirectory;
|
||||
|
||||
/** @var TimingsHandler */
|
||||
public static $pluginParentTimer;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @param SimpleCommandMap $commandMap
|
||||
@ -416,7 +413,7 @@ class PluginManager{
|
||||
continue;
|
||||
}
|
||||
|
||||
if($pluginNumbers[2] > $serverNumbers[2]){ //If the plugin requires bug fixes in patches, being backwards compatible
|
||||
if($pluginNumbers[1] === $serverNumbers[1] and $pluginNumbers[2] > $serverNumbers[2]){ //If the plugin requires bug fixes in patches, being backwards compatible
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -844,7 +841,7 @@ class PluginManager{
|
||||
throw new PluginException("Plugin attempted to register " . $event . " while not enabled");
|
||||
}
|
||||
|
||||
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")", self::$pluginParentTimer);
|
||||
$timings = new TimingsHandler("Plugin: " . $plugin->getDescription()->getFullName() . " Event: " . get_class($listener) . "::" . ($executor instanceof MethodEventExecutor ? $executor->getMethod() : "???") . "(" . (new \ReflectionClass($event))->getShortName() . ")");
|
||||
|
||||
$this->getEventListeners($event)->register(new RegisteredListener($listener, $executor, $priority, $plugin, $ignoreCancelled, $timings));
|
||||
}
|
||||
|
@ -86,8 +86,11 @@ class ZippedResourcePack implements ResourcePack{
|
||||
$archive->close();
|
||||
|
||||
$manifest = json_decode($manifestData);
|
||||
if($manifest === null or !self::verifyManifest($manifest)){
|
||||
throw new ResourcePackException("manifest.json is invalid or incomplete");
|
||||
if($manifest === null){
|
||||
throw new ResourcePackException("Failed to parse manifest.json: " . json_last_error_msg());
|
||||
}
|
||||
if(!self::verifyManifest($manifest)){
|
||||
throw new ResourcePackException("manifest.json is missing required fields");
|
||||
}
|
||||
|
||||
$this->manifest = $manifest;
|
||||
|
File diff suppressed because one or more lines are too long
@ -295,7 +295,7 @@ class AsyncPool{
|
||||
}
|
||||
|
||||
$this->removeTask($task);
|
||||
}elseif($task->isTerminated() or $task->isCrashed()){
|
||||
}elseif($task->isCrashed()){
|
||||
$this->logger->critical("Could not execute asynchronous task " . (new \ReflectionClass($task))->getShortName() . ": Task crashed");
|
||||
$this->removeTask($task, true);
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ abstract class AsyncTask extends Collectable{
|
||||
}
|
||||
|
||||
public function isCrashed() : bool{
|
||||
return $this->crashed;
|
||||
return $this->crashed or $this->isTerminated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -103,6 +103,7 @@ abstract class Tile extends Position{
|
||||
public static function createTile($type, Level $level, CompoundTag $nbt, ...$args) : ?Tile{
|
||||
if(isset(self::$knownTiles[$type])){
|
||||
$class = self::$knownTiles[$type];
|
||||
/** @see Tile::__construct() */
|
||||
return new $class($level, $nbt, ...$args);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\timings;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\scheduler\TaskHandler;
|
||||
use pocketmine\tile\Tile;
|
||||
|
||||
@ -134,7 +133,7 @@ abstract class Timings{
|
||||
self::$timerEntityBaseTick = new TimingsHandler("** entityBaseTick");
|
||||
self::$timerLivingEntityBaseTick = new TimingsHandler("** livingEntityBaseTick");
|
||||
|
||||
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks", PluginManager::$pluginParentTimer);
|
||||
self::$schedulerSyncTimer = new TimingsHandler("** Scheduler - Sync Tasks");
|
||||
self::$schedulerAsyncTimer = new TimingsHandler("** Scheduler - Async Tasks");
|
||||
|
||||
self::$playerCommandTimer = new TimingsHandler("** playerCommand");
|
||||
|
@ -111,7 +111,7 @@ class Config{
|
||||
* @return string
|
||||
*/
|
||||
public static function fixYAMLIndexes(string $str) : string{
|
||||
return preg_replace("#^([ ]*)([a-zA-Z_]{1}[ ]*)\\:$#m", "$1\"$2\":", $str);
|
||||
return preg_replace("#^( *)(y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)( *)\:#m", "$1\"$2\"$3:", $str);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -144,7 +144,6 @@ class Config{
|
||||
$content = file_get_contents($this->file);
|
||||
switch($this->type){
|
||||
case Config::PROPERTIES:
|
||||
case Config::CNF:
|
||||
$this->parseProperties($content);
|
||||
break;
|
||||
case Config::JSON:
|
||||
@ -197,7 +196,6 @@ class Config{
|
||||
$content = null;
|
||||
switch($this->type){
|
||||
case Config::PROPERTIES:
|
||||
case Config::CNF:
|
||||
$content = $this->writeProperties();
|
||||
break;
|
||||
case Config::JSON:
|
||||
|
@ -204,10 +204,16 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
$errno = $errorConversion[$errno] ?? $errno;
|
||||
$errstr = preg_replace('/\s+/', ' ', trim($errstr));
|
||||
$errfile = Utils::cleanPath($errfile);
|
||||
$this->log($type, get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline");
|
||||
foreach(Utils::getTrace(0, $trace) as $i => $line){
|
||||
$this->debug($line, true);
|
||||
}
|
||||
|
||||
$message = get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline";
|
||||
$stack = Utils::getTrace(0, $trace);
|
||||
|
||||
$this->synchronized(function() use ($type, $message, $stack) : void{
|
||||
$this->log($type, $message);
|
||||
foreach($stack as $line){
|
||||
$this->debug($line, true);
|
||||
}
|
||||
});
|
||||
|
||||
$this->syncFlushBuffer();
|
||||
}
|
||||
@ -259,19 +265,22 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
}
|
||||
|
||||
$message = sprintf($this->format, date("H:i:s", $now), $color, $threadName, $prefix, $message);
|
||||
$cleanMessage = TextFormat::clean($message);
|
||||
|
||||
if($this->mainThreadHasFormattingCodes and Terminal::hasFormattingCodes()){ //hasFormattingCodes() lazy-inits colour codes because we don't know if they've been registered on this thread
|
||||
echo Terminal::toANSI($message) . PHP_EOL;
|
||||
}else{
|
||||
echo $cleanMessage . PHP_EOL;
|
||||
}
|
||||
$this->synchronized(function() use ($message, $level, $now) : void{
|
||||
$cleanMessage = TextFormat::clean($message);
|
||||
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->call($level, $message);
|
||||
}
|
||||
if($this->mainThreadHasFormattingCodes and Terminal::hasFormattingCodes()){ //hasFormattingCodes() lazy-inits colour codes because we don't know if they've been registered on this thread
|
||||
echo Terminal::toANSI($message) . PHP_EOL;
|
||||
}else{
|
||||
echo $cleanMessage . PHP_EOL;
|
||||
}
|
||||
|
||||
$this->logStream[] = date("Y-m-d", $now) . " " . $cleanMessage . PHP_EOL;
|
||||
foreach($this->attachments as $attachment){
|
||||
$attachment->call($level, $message);
|
||||
}
|
||||
|
||||
$this->logStream[] = date("Y-m-d", $now) . " " . $cleanMessage . PHP_EOL;
|
||||
});
|
||||
}
|
||||
|
||||
public function syncFlushBuffer(){
|
||||
|
71
tests/phpunit/utils/ConfigTest.php
Normal file
71
tests/phpunit/utils/ConfigTest.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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\utils;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ConfigTest extends TestCase{
|
||||
|
||||
/**
|
||||
* @return \Generator
|
||||
*/
|
||||
public function fixYamlIndexesProvider() : \Generator{
|
||||
yield ["x: 1\ny: 2\nz: 3\n", [
|
||||
"x" => 1,
|
||||
"y" => 2,
|
||||
"z" => 3
|
||||
]];
|
||||
yield [" x : 1\n y : 2\n z : 3\n", [
|
||||
"x" => 1,
|
||||
"y" => 2,
|
||||
"z" => 3
|
||||
]];
|
||||
yield ["parent:\n x: 1\n y: 2\n z: 3\n", [
|
||||
"parent" => [
|
||||
"x" => 1,
|
||||
"y" => 2,
|
||||
"z" => 3
|
||||
]
|
||||
]];
|
||||
yield ["yes: notransform", [
|
||||
"yes" => "notransform"
|
||||
]];
|
||||
yield ["on: 1\nyes: true", [ //this would previously have resulted in a key collision
|
||||
"on" => 1,
|
||||
"yes" => true
|
||||
]];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider fixYamlIndexesProvider
|
||||
*
|
||||
* @param string $test
|
||||
* @param array $expected
|
||||
*/
|
||||
public function testFixYamlIndexes(string $test, array $expected) : void{
|
||||
$fixed = Config::fixYAMLIndexes($test);
|
||||
$decoded = yaml_parse($fixed);
|
||||
self::assertEquals($expected, $decoded);
|
||||
}
|
||||
}
|
Submodule tests/plugins/PocketMine-TesterPlugin deleted from 5fd76d5718
7
tests/plugins/TesterPlugin/plugin.yml
Normal file
7
tests/plugins/TesterPlugin/plugin.yml
Normal file
@ -0,0 +1,7 @@
|
||||
name: TesterPlugin
|
||||
main: pmmp\TesterPlugin\Main
|
||||
version: 0.1.0
|
||||
api: [3.0.0]
|
||||
load: POSTWORLD
|
||||
author: pmmp
|
||||
description: Plugin used to run tests on PocketMine-MP
|
@ -0,0 +1,50 @@
|
||||
<?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 pmmp\TesterPlugin;
|
||||
|
||||
use pocketmine\scheduler\Task;
|
||||
|
||||
class CheckTestCompletionTask extends Task{
|
||||
|
||||
/** @var Main */
|
||||
private $plugin;
|
||||
|
||||
public function __construct(Main $plugin){
|
||||
$this->plugin = $plugin;
|
||||
}
|
||||
|
||||
public function onRun(int $currentTick){
|
||||
$test = $this->plugin->getCurrentTest();
|
||||
if($test === null){
|
||||
if(!$this->plugin->startNextTest()){
|
||||
$this->plugin->getScheduler()->cancelTask($this->getHandler()->getTaskId());
|
||||
$this->plugin->onAllTestsCompleted();
|
||||
}
|
||||
}elseif($test->isFinished() or $test->isTimedOut()){
|
||||
$this->plugin->onTestCompleted($test);
|
||||
}else{
|
||||
$test->tick();
|
||||
}
|
||||
}
|
||||
}
|
109
tests/plugins/TesterPlugin/src/pmmp/TesterPlugin/Main.php
Normal file
109
tests/plugins/TesterPlugin/src/pmmp/TesterPlugin/Main.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?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 pmmp\TesterPlugin;
|
||||
|
||||
use pocketmine\event\Listener;
|
||||
use pocketmine\event\server\ServerCommandEvent;
|
||||
use pocketmine\plugin\PluginBase;
|
||||
|
||||
class Main extends PluginBase implements Listener{
|
||||
|
||||
/** @var Test[] */
|
||||
protected $waitingTests = [];
|
||||
/** @var Test|null */
|
||||
protected $currentTest = null;
|
||||
/** @var Test[] */
|
||||
protected $completedTests = [];
|
||||
/** @var int */
|
||||
protected $currentTestNumber = 0;
|
||||
|
||||
public function onEnable(){
|
||||
$this->getServer()->getPluginManager()->registerEvents($this, $this);
|
||||
$this->getScheduler()->scheduleRepeatingTask(new CheckTestCompletionTask($this), 10);
|
||||
|
||||
$this->waitingTests = [
|
||||
new tests\AsyncTaskMemoryLeakTest($this),
|
||||
new tests\AsyncTaskMainLoggerTest($this)
|
||||
];
|
||||
}
|
||||
|
||||
public function onServerCommand(ServerCommandEvent $event){
|
||||
//The CI will send this command as a failsafe to prevent the build from hanging if the tester plugin failed to
|
||||
//run. However, if the plugin loaded successfully we don't want to allow this to stop the server as there may
|
||||
//be asynchronous tests running. Instead we cancel this and stop the server of our own accord once all tests
|
||||
//have completed.
|
||||
if($event->getCommand() === "stop"){
|
||||
$event->setCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Test|null
|
||||
*/
|
||||
public function getCurrentTest(){
|
||||
return $this->currentTest;
|
||||
}
|
||||
|
||||
public function startNextTest() : bool{
|
||||
$this->currentTest = array_shift($this->waitingTests);
|
||||
if($this->currentTest !== null){
|
||||
$this->getLogger()->notice("Running test #" . (++$this->currentTestNumber) . " (" . $this->currentTest->getName() . ")");
|
||||
$this->currentTest->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onTestCompleted(Test $test){
|
||||
$message = "Finished test #" . $this->currentTestNumber . " (" . $test->getName() . "): ";
|
||||
switch($test->getResult()){
|
||||
case Test::RESULT_OK:
|
||||
$message .= "PASS";
|
||||
break;
|
||||
case Test::RESULT_FAILED:
|
||||
$message .= "FAIL";
|
||||
break;
|
||||
case Test::RESULT_ERROR:
|
||||
$message .= "ERROR";
|
||||
break;
|
||||
case Test::RESULT_WAITING:
|
||||
$message .= "TIMEOUT";
|
||||
break;
|
||||
default:
|
||||
$message .= "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
|
||||
$this->getLogger()->notice($message);
|
||||
|
||||
$this->completedTests[$this->currentTestNumber] = $test;
|
||||
$this->currentTest = null;
|
||||
}
|
||||
|
||||
public function onAllTestsCompleted(){
|
||||
$this->getLogger()->notice("All tests finished, stopping the server");
|
||||
$this->getServer()->shutdown();
|
||||
}
|
||||
}
|
87
tests/plugins/TesterPlugin/src/pmmp/TesterPlugin/Test.php
Normal file
87
tests/plugins/TesterPlugin/src/pmmp/TesterPlugin/Test.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?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 pmmp\TesterPlugin;
|
||||
|
||||
abstract class Test{
|
||||
const RESULT_WAITING = -1;
|
||||
const RESULT_OK = 0;
|
||||
const RESULT_FAILED = 1;
|
||||
const RESULT_ERROR = 2;
|
||||
|
||||
private $plugin;
|
||||
private $result = Test::RESULT_WAITING;
|
||||
private $startTime;
|
||||
private $timeout = 60; //seconds
|
||||
|
||||
public function __construct(Main $plugin){
|
||||
$this->plugin = $plugin;
|
||||
}
|
||||
|
||||
public function getPlugin() : Main{
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
final public function start(){
|
||||
$this->startTime = time();
|
||||
try{
|
||||
$this->run();
|
||||
}catch(TestFailedException $e){
|
||||
$this->getPlugin()->getLogger()->error($e->getMessage());
|
||||
$this->setResult(Test::RESULT_FAILED);
|
||||
}catch(\Throwable $e){
|
||||
$this->getPlugin()->getLogger()->logException($e);
|
||||
$this->setResult(Test::RESULT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public function tick(){
|
||||
|
||||
}
|
||||
|
||||
abstract public function run();
|
||||
|
||||
public function isFinished() : bool{
|
||||
return $this->result !== Test::RESULT_WAITING;
|
||||
}
|
||||
|
||||
public function isTimedOut() : bool{
|
||||
return !$this->isFinished() and time() - $this->timeout > $this->startTime;
|
||||
}
|
||||
|
||||
protected function setTimeout(int $timeout){
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
public function getResult() : int{
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
public function setResult(int $result){
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
abstract public function getName() : string;
|
||||
|
||||
abstract public function getDescription() : string;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?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 pmmp\TesterPlugin;
|
||||
|
||||
class TestFailedException extends \Exception{
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?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 pmmp\TesterPlugin\tests;
|
||||
|
||||
use pmmp\TesterPlugin\Test;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\MainLogger;
|
||||
|
||||
class AsyncTaskMainLoggerTest extends Test{
|
||||
|
||||
public function run(){
|
||||
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new class($this) extends AsyncTask{
|
||||
|
||||
/** @var bool */
|
||||
protected $success = false;
|
||||
|
||||
public function __construct(AsyncTaskMainLoggerTest $testObject){
|
||||
$this->storeLocal($testObject);
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
ob_start();
|
||||
MainLogger::getLogger()->info("Testing");
|
||||
if(strpos(ob_get_contents(), "Testing") !== false){
|
||||
$this->success = true;
|
||||
}
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
/** @var AsyncTaskMainLoggerTest $test */
|
||||
$test = $this->fetchLocal();
|
||||
$test->setResult($this->success ? Test::RESULT_OK : Test::RESULT_FAILED);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return "MainLogger::getLogger() works in AsyncTasks";
|
||||
}
|
||||
|
||||
public function getDescription() : string{
|
||||
return "Verifies that the MainLogger is accessible by MainLogger::getLogger() in an AsyncTask";
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
<?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 pmmp\TesterPlugin\tests;
|
||||
|
||||
use pmmp\TesterPlugin\Test;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
|
||||
class AsyncTaskMemoryLeakTest extends Test{
|
||||
|
||||
public function run(){
|
||||
$this->getPlugin()->getServer()->getAsyncPool()->submitTask(new TestAsyncTask());
|
||||
}
|
||||
|
||||
public function tick(){
|
||||
if(TestAsyncTask::$destroyed === true){
|
||||
$this->setResult(Test::RESULT_OK);
|
||||
}
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
return "AsyncTask memory leak after completion";
|
||||
}
|
||||
|
||||
public function getDescription() : string{
|
||||
return "Regression test for AsyncTasks objects not being destroyed after completion";
|
||||
}
|
||||
}
|
||||
|
||||
class TestAsyncTask extends AsyncTask{
|
||||
public static $destroyed = false;
|
||||
|
||||
public function onRun(){
|
||||
usleep(50 * 1000); //1 server tick
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
self::$destroyed = true;
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ fi
|
||||
mkdir "$DATA_DIR"
|
||||
mkdir "$PLUGINS_DIR"
|
||||
mv DevTools.phar "$PLUGINS_DIR"
|
||||
cp -r tests/plugins/PocketMine-TesterPlugin "$PLUGINS_DIR"
|
||||
cp -r tests/plugins/TesterPlugin "$PLUGINS_DIR"
|
||||
echo -e "stop\n" | "$PHP_BINARY" PocketMine-MP.phar --no-wizard --disable-ansi --disable-readline --debug.level=2 --data="$DATA_DIR" --plugins="$PLUGINS_DIR" --anonymous-statistics.enabled=0 --settings.async-workers="$PM_WORKERS" --settings.enable-dev-builds=1
|
||||
|
||||
output=$(grep '\[TesterPlugin\]' "$DATA_DIR/server.log")
|
||||
|
Reference in New Issue
Block a user