mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-15 22:05:06 +00:00
Compare commits
196 Commits
Author | SHA1 | Date | |
---|---|---|---|
e79cc98883 | |||
10fa74b417 | |||
adbd1c7bed | |||
d75c830a7e | |||
60e1b29462 | |||
426dee04a6 | |||
d1a20ecb4a | |||
28137efb53 | |||
cea146e335 | |||
5d56030afa | |||
8085b81f5c | |||
7c092b93b4 | |||
aa05650994 | |||
758d9b9784 | |||
24a6bf7365 | |||
9a5d51fd3d | |||
c52e1ea9f9 | |||
4bc0d850b1 | |||
107192c753 | |||
0e2bbc44db | |||
e9b84ecc8b | |||
5863d4c066 | |||
bfbc845efa | |||
06c4f31db7 | |||
7d0e631a75 | |||
15e654131c | |||
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 | |||
ce9f18c6b4 | |||
9610c55b19 | |||
b01b477a2a | |||
2d454ae56f | |||
066c9d4fd4 | |||
23829952c3 | |||
7ee98ff139 | |||
f1cab91ac9 | |||
e0bc9c5e96 | |||
70caa00266 | |||
ee7c838040 | |||
34e9e93210 | |||
5dbb0d177e | |||
58f0ad3e3e | |||
0df3585c81 | |||
697723b551 | |||
5926d80525 | |||
a57ec1b1ba | |||
905259a4e1 | |||
ca6930006c | |||
33eeeb856e | |||
c43ce5c8fa | |||
57cfe9fd43 | |||
d8824e7ee1 | |||
3455d0f3b9 | |||
6b2250cbce | |||
8dae497610 | |||
cade15e2dd | |||
d3e54db146 | |||
0081e30a89 | |||
76174f1920 | |||
dd6b5902a6 | |||
87852f2fe1 | |||
056d24c67d | |||
484d34fe04 | |||
6c6630d845 | |||
a5a236084f | |||
641a5a5e23 | |||
ebacb8525f | |||
579ab5866b |
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(){
|
||||
|
@ -535,7 +535,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
public function canBeCollidedWith() : bool{
|
||||
return !$this->isSpectator();
|
||||
return !$this->isSpectator() and parent::canBeCollidedWith();
|
||||
}
|
||||
|
||||
public function resetFallDistance() : void{
|
||||
@ -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;
|
||||
@ -2529,11 +2526,15 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
if($this->isAlive()){
|
||||
//reactive damage like thorns might cause us to be killed by attacking another mob, which
|
||||
//would mean we'd already have dropped the inventory by the time we reached here
|
||||
if($heldItem->onAttackEntity($target) and $this->isSurvival()){ //always fire the hook, even if we are survival
|
||||
$this->inventory->setItemInHand($heldItem);
|
||||
}
|
||||
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
$this->exhaust(0.3, PlayerExhaustEvent::CAUSE_ATTACK);
|
||||
}
|
||||
|
||||
return true;
|
||||
default:
|
||||
@ -2631,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){
|
||||
@ -2697,7 +2698,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$target = $this->level->getBlock($pos);
|
||||
|
||||
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK);
|
||||
$ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $packet->face, PlayerInteractEvent::LEFT_CLICK_BLOCK);
|
||||
if($this->level->checkSpawnProtection($this, $target)){
|
||||
$ev->setCancelled();
|
||||
}
|
||||
@ -2761,7 +2762,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
break; //TODO
|
||||
case PlayerActionPacket::ACTION_CONTINUE_BREAK:
|
||||
$block = $this->level->getBlock($pos);
|
||||
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, BlockFactory::toStaticRuntimeId($block->getId(), $block->getDamage()) | ($packet->face << 24));
|
||||
$this->level->broadcastLevelEvent($pos, LevelEventPacket::EVENT_PARTICLE_PUNCH_BLOCK, $block->getRuntimeId() | ($packet->face << 24));
|
||||
//TODO: destroy-progress level event
|
||||
break;
|
||||
case PlayerActionPacket::ACTION_START_SWIMMING:
|
||||
@ -2845,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));
|
||||
@ -2895,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)){
|
||||
@ -3613,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)));
|
||||
|
||||
@ -3796,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.2";
|
||||
const BASE_VERSION = "3.1.8";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
@ -139,12 +139,18 @@ namespace pocketmine {
|
||||
define('pocketmine\PATH', dirname(__FILE__, 3) . DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', \pocketmine\PATH . 'vendor/autoload.php');
|
||||
$opts = getopt("", ["bootstrap:"]);
|
||||
if(isset($opts["bootstrap"])){
|
||||
$bootstrap = realpath($opts["bootstrap"]) ?: $opts["bootstrap"];
|
||||
}else{
|
||||
$bootstrap = \pocketmine\PATH . 'vendor/autoload.php';
|
||||
}
|
||||
define('pocketmine\COMPOSER_AUTOLOADER_PATH', $bootstrap);
|
||||
|
||||
if(is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
if(\pocketmine\COMPOSER_AUTOLOADER_PATH !== false and is_file(\pocketmine\COMPOSER_AUTOLOADER_PATH)){
|
||||
require_once(\pocketmine\COMPOSER_AUTOLOADER_PATH);
|
||||
}else{
|
||||
critical_error("Composer autoloader not found.");
|
||||
critical_error("Composer autoloader not found at " . $bootstrap);
|
||||
critical_error("Please install/update Composer dependencies or use provided builds.");
|
||||
exit(1);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
@ -2347,41 +2352,37 @@ class Server{
|
||||
}
|
||||
|
||||
//Do level ticks
|
||||
foreach($this->getLevels() as $level){
|
||||
foreach($this->levels as $k => $level){
|
||||
if(!isset($this->levels[$k])){
|
||||
// Level unloaded during the tick of a level earlier in this loop, perhaps by plugin
|
||||
continue;
|
||||
}
|
||||
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 +2472,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\inventory\AnvilInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\item\TieredTool;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -55,13 +54,17 @@ class Anvil extends Fallable{
|
||||
return 6000;
|
||||
}
|
||||
|
||||
public function getVariantBitmask() : int{
|
||||
return 0x0c;
|
||||
}
|
||||
|
||||
public function getName() : string{
|
||||
static $names = [
|
||||
self::TYPE_NORMAL => "Anvil",
|
||||
self::TYPE_SLIGHTLY_DAMAGED => "Slightly Damaged Anvil",
|
||||
self::TYPE_VERY_DAMAGED => "Very Damaged Anvil"
|
||||
];
|
||||
return $names[$this->meta & 0x0c] ?? "Anvil";
|
||||
return $names[$this->getVariant()] ?? "Anvil";
|
||||
}
|
||||
|
||||
public function getToolType() : int{
|
||||
@ -106,13 +109,7 @@ class Anvil extends Fallable{
|
||||
|
||||
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, Player $player = null) : bool{
|
||||
$direction = ($player !== null ? $player->getDirection() : 0) & 0x03;
|
||||
$this->meta = ($this->meta & 0x0c) | $direction;
|
||||
$this->meta = $this->getVariant() | $direction;
|
||||
return $this->getLevel()->setBlock($blockReplace, $this, true, true);
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), $this->getDamage() >> 2)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -192,21 +192,25 @@ class Bed extends Transparent{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if($this->isHeadPart()){
|
||||
$tile = $this->getLevel()->getTile($this);
|
||||
if($tile instanceof TileBed){
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), $tile->getColor())
|
||||
];
|
||||
}else{
|
||||
return [
|
||||
ItemFactory::get($this->getItemId(), 14) //Red
|
||||
];
|
||||
}
|
||||
return [$this->getItem()];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return $this->getItem();
|
||||
}
|
||||
|
||||
private function getItem() : Item{
|
||||
$tile = $this->getLevel()->getTile($this);
|
||||
if($tile instanceof TileBed){
|
||||
return ItemFactory::get($this->getItemId(), $tile->getColor());
|
||||
}
|
||||
|
||||
return ItemFactory::get($this->getItemId(), 14); //Red
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
@ -109,6 +109,14 @@ class Block extends Position implements BlockIds, Metadatable{
|
||||
return $this->itemId ?? $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @return int
|
||||
*/
|
||||
public function getRuntimeId() : int{
|
||||
return BlockFactory::toStaticRuntimeId($this->getId(), $this->getDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -23,12 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\TieredTool;
|
||||
|
||||
class BrewingStand extends Transparent{
|
||||
|
||||
protected $id = self::BREWING_STAND_BLOCK;
|
||||
|
||||
protected $itemId = Item::BREWING_STAND;
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -35,6 +35,8 @@ class Cake extends Transparent implements FoodSource{
|
||||
|
||||
protected $id = self::CAKE_BLOCK;
|
||||
|
||||
protected $itemId = Item::CAKE;
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,4 +47,8 @@ abstract class DoubleSlab extends Solid{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return ItemFactory::get($this->getSlabId(), $this->getVariant());
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,11 @@ class EmeraldOre extends Solid{
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [
|
||||
ItemFactory::get(Item::EMERALD)
|
||||
ItemFactory::get(Item::EMERALD)
|
||||
];
|
||||
}
|
||||
|
||||
protected function getXpDropAmount() : int{
|
||||
return mt_rand(3, 7);
|
||||
}
|
||||
}
|
||||
|
@ -111,4 +111,8 @@ class Farmland extends Transparent{
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return ItemFactory::get(Item::DIRT);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,8 @@ class RedstoneOre extends Solid{
|
||||
}
|
||||
|
||||
public function onActivate(Item $item, Player $player = null) : bool{
|
||||
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
|
||||
$this->getLevel()->setBlock($this, BlockFactory::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
|
||||
return false; //this shouldn't prevent block placement
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -61,7 +61,7 @@ class SignPost extends Transparent{
|
||||
if($face !== Vector3::SIDE_DOWN){
|
||||
|
||||
if($face === Vector3::SIDE_UP){
|
||||
$this->meta = floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f;
|
||||
$this->meta = $player !== null ? (floor((($player->yaw + 180) * 16 / 360) + 0.5) & 0x0f) : 0;
|
||||
$this->getLevel()->setBlock($blockReplace, $this, true);
|
||||
}else{
|
||||
$this->meta = $face;
|
||||
|
@ -71,18 +71,20 @@ class Skull extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
private function getItem() : Item{
|
||||
$tile = $this->level->getTile($this);
|
||||
if($tile instanceof TileSkull){
|
||||
return [
|
||||
ItemFactory::get(Item::SKULL, $tile->getType())
|
||||
];
|
||||
}
|
||||
return ItemFactory::get(Item::SKULL, $tile instanceof TileSkull ? $tile->getType() : 0);
|
||||
}
|
||||
|
||||
return [];
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->getItem()];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPickedItem() : Item{
|
||||
return $this->getItem();
|
||||
}
|
||||
}
|
||||
|
@ -100,11 +100,7 @@ abstract class Thin extends Transparent{
|
||||
}
|
||||
|
||||
//FIXME: currently there's no proper way to tell if a block is a full-block, so we check the bounding box size
|
||||
$bbs = $block->getCollisionBoxes();
|
||||
if(count($bbs) === 1){
|
||||
return $bbs[0]->getAverageEdgeLength() >= 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
$bb = $block->getBoundingBox();
|
||||
return $bb !== null and $bb->getAverageEdgeLength() >= 1;
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ class Torch extends Flowable{
|
||||
5 => Vector3::SIDE_DOWN
|
||||
];
|
||||
|
||||
if($this->getSide($faces[$side])->isTransparent() and !($side === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
if($this->getSide($faces[$side])->isTransparent() and !($faces[$side] === Vector3::SIDE_DOWN and ($below->getId() === self::FENCE or $below->getId() === self::COBBLESTONE_WALL))){
|
||||
$this->getLevel()->useBreakOn($this);
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +127,7 @@ abstract class Command{
|
||||
if($this->permissionMessage === null){
|
||||
$target->sendMessage($target->getServer()->getLanguage()->translateString(TextFormat::RED . "%commands.generic.permission"));
|
||||
}elseif($this->permissionMessage !== ""){
|
||||
$target->sendMessage(str_replace("<permission>", $this->getPermission(), $this->permissionMessage));
|
||||
$target->sendMessage(str_replace("<permission>", $this->permission, $this->permissionMessage));
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -139,11 +139,11 @@ abstract class Command{
|
||||
* @return bool
|
||||
*/
|
||||
public function testPermissionSilent(CommandSender $target) : bool{
|
||||
if(($perm = $this->getPermission()) === null or $perm === ""){
|
||||
if($this->permission === null or $this->permission === ""){
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach(explode(";", $perm) as $permission){
|
||||
foreach(explode(";", $this->permission) as $permission){
|
||||
if($target->hasPermission($permission)){
|
||||
return true;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\command;
|
||||
|
||||
use pocketmine\snooze\SleeperNotifier;
|
||||
use pocketmine\Thread;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class CommandReader extends Thread{
|
||||
|
||||
@ -47,9 +48,9 @@ class CommandReader extends Thread{
|
||||
$this->buffer = new \Threaded;
|
||||
$this->notifier = $notifier;
|
||||
|
||||
$opts = getopt("", ["disable-readline"]);
|
||||
$opts = getopt("", ["disable-readline", "enable-readline"]);
|
||||
|
||||
if(extension_loaded("readline") and !isset($opts["disable-readline"]) and !$this->isPipe(STDIN)){
|
||||
if(extension_loaded("readline") and (Utils::getOS() === "win" ? isset($opts["enable-readline"]) : !isset($opts["disable-readline"])) and !$this->isPipe(STDIN)){
|
||||
$this->type = self::TYPE_READLINE;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -50,9 +50,7 @@ class TellCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$name = strtolower(array_shift($args));
|
||||
|
||||
$player = $sender->getServer()->getPlayer($name);
|
||||
$player = $sender->getServer()->getPlayer(array_shift($args));
|
||||
|
||||
if($player === $sender){
|
||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.message.sameTarget"));
|
||||
|
@ -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;
|
||||
@ -1101,7 +1099,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
}
|
||||
|
||||
public function canBeCollidedWith() : bool{
|
||||
return true;
|
||||
return $this->isAlive();
|
||||
}
|
||||
|
||||
protected function updateMovement(bool $teleport = false) : void{
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,6 +469,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
/**
|
||||
* Called after EntityDamageEvent execution to apply post-hurt effects, such as reducing absorption or modifying
|
||||
* armour durability.
|
||||
* This will not be called by damage sources causing death.
|
||||
*
|
||||
* @param EntityDamageEvent $source
|
||||
*/
|
||||
@ -534,6 +535,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
if($source instanceof EntityDamageByChildEntityEvent){
|
||||
@ -551,15 +554,10 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->applyPostDamageEffects($source);
|
||||
|
||||
if($this->isAlive()){
|
||||
$this->applyPostDamageEffects($source);
|
||||
$this->doHitAnimation();
|
||||
}else{
|
||||
$this->startDeathAnimation();
|
||||
}
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
}
|
||||
|
||||
protected function doHitAnimation() : void{
|
||||
@ -594,6 +592,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
public function kill() : void{
|
||||
parent::kill();
|
||||
$this->onDeath();
|
||||
$this->startDeathAnimation();
|
||||
}
|
||||
|
||||
protected function onDeath() : void{
|
||||
@ -630,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()){
|
||||
@ -642,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());
|
||||
@ -657,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)){
|
||||
@ -668,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,13 +162,14 @@ 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;
|
||||
}
|
||||
|
||||
$currentTarget = $this->getTargetPlayer();
|
||||
if($currentTarget !== null and $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2){
|
||||
if($currentTarget !== null and (!$currentTarget->isAlive() or $currentTarget->distanceSquared($this) > self::MAX_TARGET_DISTANCE ** 2)){
|
||||
$currentTarget = null;
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ class FallingBlock extends Entity{
|
||||
|
||||
$this->block = BlockFactory::get($blockId, $damage);
|
||||
|
||||
$this->propertyManager->setInt(self::DATA_VARIANT, BlockFactory::toStaticRuntimeId($this->block->getId(), $this->block->getDamage()));
|
||||
$this->propertyManager->setInt(self::DATA_VARIANT, $this->block->getRuntimeId());
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity) : bool{
|
||||
@ -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();
|
||||
|
||||
@ -70,6 +73,9 @@ class ItemEntity extends Entity{
|
||||
}
|
||||
|
||||
$this->item = Item::nbtDeserialize($itemTag);
|
||||
if($this->item->isNull()){
|
||||
throw new \UnexpectedValueException("Item for " . get_class($this) . " is invalid");
|
||||
}
|
||||
|
||||
|
||||
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
|
||||
@ -82,14 +88,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 +104,6 @@ class ItemEntity extends Entity{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $hasUpdate;
|
||||
@ -197,7 +201,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 */
|
||||
|
@ -60,15 +60,16 @@ class DoubleChestInventory extends ChestInventory implements InventoryHolder{
|
||||
}
|
||||
|
||||
public function getItem(int $index) : Item{
|
||||
return $index < $this->left->getSize() ? $this->left->getItem($index) : $this->right->getItem($index - $this->right->getSize());
|
||||
return $index < $this->left->getSize() ? $this->left->getItem($index) : $this->right->getItem($index - $this->left->getSize());
|
||||
}
|
||||
|
||||
public function setItem(int $index, Item $item, bool $send = true) : bool{
|
||||
return $index < $this->left->getSize() ? $this->left->setItem($index, $item, $send) : $this->right->setItem($index - $this->right->getSize(), $item, $send);
|
||||
}
|
||||
|
||||
public function clear(int $index, bool $send = true) : bool{
|
||||
return $index < $this->left->getSize() ? $this->left->clear($index, $send) : $this->right->clear($index - $this->right->getSize(), $send);
|
||||
$old = $this->getItem($index);
|
||||
if($index < $this->left->getSize() ? $this->left->setItem($index, $item, $send) : $this->right->setItem($index - $this->left->getSize(), $item, $send)){
|
||||
$this->onSlotChange($index, $old, $send);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getContents(bool $includeEmpty = false) : array{
|
||||
|
@ -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());
|
||||
|
@ -47,17 +47,17 @@ class ChorusFruit extends Food{
|
||||
}
|
||||
|
||||
public function onConsume(Living $consumer){
|
||||
$level = $consumer->getLevel();
|
||||
assert($level !== null);
|
||||
|
||||
$minX = $consumer->getFloorX() - 8;
|
||||
$minY = $consumer->getFloorY() - 8;
|
||||
$minY = min($consumer->getFloorY(), $consumer->getLevel()->getWorldHeight()) - 8;
|
||||
$minZ = $consumer->getFloorZ() - 8;
|
||||
|
||||
$maxX = $minX + 16;
|
||||
$maxY = $minY + 16;
|
||||
$maxZ = $minZ + 16;
|
||||
|
||||
$level = $consumer->getLevel();
|
||||
assert($level !== null);
|
||||
|
||||
for($attempts = 0; $attempts < 16; ++$attempts){
|
||||
$x = mt_rand($minX, $maxX);
|
||||
$y = mt_rand($minY, $maxY);
|
||||
|
@ -953,7 +953,12 @@ class Item implements ItemIds, \JsonSerializable{
|
||||
if($idTag instanceof ShortTag){
|
||||
$item = ItemFactory::get(Binary::unsignShort($idTag->getValue()), $meta, $count);
|
||||
}elseif($idTag instanceof StringTag){ //PC item save format
|
||||
$item = ItemFactory::fromString($idTag->getValue());
|
||||
try{
|
||||
$item = ItemFactory::fromString($idTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
//TODO: improve error handling
|
||||
return ItemFactory::get(Item::AIR, 0, 0);
|
||||
}
|
||||
$item->setDamage($meta);
|
||||
$item->setCount($count);
|
||||
}else{
|
||||
|
@ -193,7 +193,7 @@ interface ItemIds extends BlockIds{
|
||||
public const LEAD = 420;
|
||||
public const NAMETAG = 421, NAME_TAG = 421;
|
||||
public const PRISMARINE_CRYSTALS = 422;
|
||||
public const MUTTONRAW = 423, MUTTON_RAW = 423, RAW_MUTTON = 423;
|
||||
public const MUTTON = 423, MUTTONRAW = 423, MUTTON_RAW = 423, RAW_MUTTON = 423;
|
||||
public const COOKED_MUTTON = 424, MUTTONCOOKED = 424, MUTTON_COOKED = 424;
|
||||
public const ARMOR_STAND = 425;
|
||||
public const END_CRYSTAL = 426;
|
||||
|
@ -72,8 +72,6 @@ class Potion extends Item implements Consumable{
|
||||
*
|
||||
* @param int $id
|
||||
* @return EffectInstance[]
|
||||
*
|
||||
* @throws \InvalidArgumentException if the potion type is unknown
|
||||
*/
|
||||
public static function getPotionEffectsById(int $id) : array{
|
||||
switch($id){
|
||||
@ -213,7 +211,7 @@ class Potion extends Item implements Consumable{
|
||||
];
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException("Unknown potion type $id");
|
||||
return [];
|
||||
}
|
||||
|
||||
public function __construct(int $meta = 0){
|
||||
|
Submodule src/pocketmine/lang/locale updated: 638dc473ca...0cd3f8ef30
@ -236,6 +236,9 @@ class Explosion{
|
||||
if(!isset($this->affectedBlocks[$index = Level::blockHash($sideBlock->x, $sideBlock->y, $sideBlock->z)]) and !isset($updateBlocks[$index])){
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new BlockUpdateEvent($this->level->getBlockAt($sideBlock->x, $sideBlock->y, $sideBlock->z)));
|
||||
if(!$ev->isCancelled()){
|
||||
foreach($this->level->getNearbyEntities(new AxisAlignedBB($sideBlock->x - 1, $sideBlock->y - 1, $sideBlock->z - 1, $sideBlock->x + 2, $sideBlock->y + 2, $sideBlock->z + 2)) as $entity){
|
||||
$entity->onNearbyBlockChange();
|
||||
}
|
||||
$ev->getBlock()->onNearbyBlockChange();
|
||||
}
|
||||
$updateBlocks[$index] = true;
|
||||
|
@ -191,7 +191,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
/** @var Player[][] */
|
||||
private $chunkSendQueue = [];
|
||||
/** @var bool[] */
|
||||
/** @var ChunkRequestTask[] */
|
||||
private $chunkSendTasks = [];
|
||||
|
||||
/** @var bool[] */
|
||||
@ -769,6 +769,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(count($this->changedBlocks) > 0){
|
||||
if(count($this->players) > 0){
|
||||
foreach($this->changedBlocks as $index => $blocks){
|
||||
if(empty($blocks)){ //blocks can be set normally and then later re-set with direct send
|
||||
continue;
|
||||
}
|
||||
unset($this->chunkCache[$index]);
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
if(count($blocks) > 512){
|
||||
@ -776,7 +779,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
foreach($this->getChunkPlayers($chunkX, $chunkZ) as $p){
|
||||
$p->onChunkChanged($chunk);
|
||||
}
|
||||
}elseif(!empty($blocks)){
|
||||
}else{
|
||||
$this->sendBlocks($this->getChunkPlayers($chunkX, $chunkZ), $blocks, UpdateBlockPacket::FLAG_ALL);
|
||||
}
|
||||
}
|
||||
@ -794,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 = [];
|
||||
}
|
||||
|
||||
@ -869,16 +874,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
$pk->z = $b->z;
|
||||
|
||||
if($b instanceof Block){
|
||||
$blockId = $b->getId();
|
||||
$blockData = $b->getDamage();
|
||||
$pk->blockRuntimeId = $b->getRuntimeId();
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$blockId = $fullBlock >> 4;
|
||||
$blockData = $fullBlock & 0xf;
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
}
|
||||
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($blockId, $blockData);
|
||||
|
||||
$pk->flags = $first ? $flags : UpdateBlockPacket::FLAG_NONE;
|
||||
|
||||
$packets[] = $pk;
|
||||
@ -895,16 +896,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
$pk->z = $b->z;
|
||||
|
||||
if($b instanceof Block){
|
||||
$blockId = $b->getId();
|
||||
$blockData = $b->getDamage();
|
||||
$pk->blockRuntimeId = $b->getRuntimeId();
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$blockId = $fullBlock >> 4;
|
||||
$blockData = $fullBlock & 0xf;
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf);
|
||||
}
|
||||
|
||||
$pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($blockId, $blockData);
|
||||
|
||||
$pk->flags = $flags;
|
||||
|
||||
$packets[] = $pk;
|
||||
@ -1624,7 +1621,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;
|
||||
}
|
||||
}
|
||||
@ -1773,7 +1770,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($player !== null){
|
||||
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, $blockClicked->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
if($this->checkSpawnProtection($player, $blockClicked)){
|
||||
$ev->setCancelled(); //set it to cancelled so plugins can bypass this
|
||||
}
|
||||
@ -1861,7 +1858,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($playSound){
|
||||
$this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, 1, BlockFactory::toStaticRuntimeId($hand->getId(), $hand->getDamage()));
|
||||
$this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, 1, $hand->getRuntimeId());
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
@ -2346,21 +2343,20 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$chunkHash = Level::chunkHash($chunkX, $chunkZ);
|
||||
$oldChunk = $this->getChunk($chunkX, $chunkZ, false);
|
||||
if($unload and $oldChunk !== null){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false, false);
|
||||
}else{
|
||||
$oldEntities = $oldChunk !== null ? $oldChunk->getEntities() : [];
|
||||
$oldTiles = $oldChunk !== null ? $oldChunk->getTiles() : [];
|
||||
if($oldChunk !== null){
|
||||
if($unload){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false, false);
|
||||
}else{
|
||||
foreach($oldChunk->getEntities() as $entity){
|
||||
$chunk->addEntity($entity);
|
||||
$oldChunk->removeEntity($entity);
|
||||
$entity->chunk = $chunk;
|
||||
}
|
||||
|
||||
foreach($oldEntities as $entity){
|
||||
$chunk->addEntity($entity);
|
||||
$oldChunk->removeEntity($entity);
|
||||
$entity->chunk = $chunk;
|
||||
}
|
||||
|
||||
foreach($oldTiles as $tile){
|
||||
$chunk->addTile($tile);
|
||||
$oldChunk->removeTile($tile);
|
||||
foreach($oldChunk->getTiles() as $tile){
|
||||
$chunk->addTile($tile);
|
||||
$oldChunk->removeTile($tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2368,6 +2364,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->chunkCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
$chunk->setChanged();
|
||||
|
||||
if(!$this->isChunkInUse($chunkX, $chunkZ)){
|
||||
@ -2453,7 +2450,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])){
|
||||
@ -2461,7 +2458,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
unset($this->chunkSendQueue[$index]);
|
||||
unset($this->chunkSendTasks[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2470,11 +2466,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;
|
||||
@ -2487,7 +2489,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();
|
||||
}
|
||||
@ -2500,24 +2503,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();
|
||||
}
|
||||
|
||||
@ -2751,6 +2744,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();
|
||||
|
||||
@ -2788,7 +2783,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){
|
||||
@ -2944,16 +2939,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){
|
||||
|
@ -35,7 +35,6 @@ class RegionLoader{
|
||||
|
||||
public const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB)
|
||||
public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
|
||||
public const MAX_REGION_FILE_SIZE = 32 * 32 * self::MAX_SECTOR_LENGTH + self::REGION_HEADER_LENGTH; //32 * 32 1MiB chunks + header size
|
||||
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
|
||||
@ -64,13 +63,8 @@ class RegionLoader{
|
||||
$exists = file_exists($this->filePath);
|
||||
if(!$exists){
|
||||
touch($this->filePath);
|
||||
}else{
|
||||
$fileSize = filesize($this->filePath);
|
||||
if($fileSize > self::MAX_REGION_FILE_SIZE){
|
||||
throw new CorruptedRegionException("Corrupted oversized region file found, should be a maximum of " . self::MAX_REGION_FILE_SIZE . " bytes, got " . $fileSize . " bytes");
|
||||
}elseif($fileSize % 4096 !== 0){
|
||||
throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB");
|
||||
}
|
||||
}elseif(filesize($this->filePath) % 4096 !== 0){
|
||||
throw new CorruptedRegionException("Region file should be padded to a multiple of 4KiB");
|
||||
}
|
||||
|
||||
$this->filePointer = fopen($this->filePath, "r+b");
|
||||
|
@ -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);
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\particle;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
|
||||
@ -35,7 +34,7 @@ class DestroyBlockParticle extends Particle{
|
||||
|
||||
public function __construct(Vector3 $pos, Block $b){
|
||||
parent::__construct($pos->x, $pos->y, $pos->z);
|
||||
$this->data = BlockFactory::toStaticRuntimeId($b->getId(), $b->getDamage());
|
||||
$this->data = $b->getRuntimeId();
|
||||
}
|
||||
|
||||
public function encode(){
|
||||
|
@ -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;
|
||||
|
@ -24,11 +24,10 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\particle;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
class TerrainParticle extends GenericParticle{
|
||||
public function __construct(Vector3 $pos, Block $b){
|
||||
parent::__construct($pos, Particle::TYPE_TERRAIN, BlockFactory::toStaticRuntimeId($b->getId(), $b->getDamage()));
|
||||
parent::__construct($pos, Particle::TYPE_TERRAIN, $b->getRuntimeId());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -76,10 +76,6 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
$this->server = $server;
|
||||
|
||||
$this->sleeper = new SleeperNotifier();
|
||||
$server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
|
||||
$this->server->getNetwork()->processInterface($this);
|
||||
});
|
||||
|
||||
$this->rakLib = new RakLibServer(
|
||||
$this->server->getLogger(),
|
||||
\pocketmine\COMPOSER_AUTOLOADER_PATH,
|
||||
@ -92,6 +88,9 @@ class RakLibInterface implements ServerInstance, AdvancedSourceInterface{
|
||||
}
|
||||
|
||||
public function start(){
|
||||
$this->server->getTickSleeper()->addNotifier($this->sleeper, function() : void{
|
||||
$this->server->getNetwork()->processInterface($this);
|
||||
});
|
||||
$this->rakLib->start(PTHREADS_INHERIT_CONSTANTS | PTHREADS_INHERIT_INI); //HACK: MainLogger needs INI and constants
|
||||
}
|
||||
|
||||
@ -141,6 +140,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);
|
||||
}
|
||||
|
||||
|
@ -105,9 +105,15 @@ class LoginPacket extends DataPacket{
|
||||
$buffer = new BinaryStream($this->getString());
|
||||
|
||||
$this->chainData = json_decode($buffer->get($buffer->getLInt()), true);
|
||||
|
||||
$hasExtraData = false;
|
||||
foreach($this->chainData["chain"] as $chain){
|
||||
$webtoken = Utils::decodeJWT($chain);
|
||||
if(isset($webtoken["extraData"])){
|
||||
if($hasExtraData){
|
||||
throw new \RuntimeException("Found 'extraData' multiple times in key chain");
|
||||
}
|
||||
$hasExtraData = true;
|
||||
if(isset($webtoken["extraData"]["displayName"])){
|
||||
$this->username = $webtoken["extraData"]["displayName"];
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,6 @@ class RCON{
|
||||
public function stop(){
|
||||
$this->instance->close();
|
||||
socket_write($this->ipcMainSocket, "\x00"); //make select() return
|
||||
Server::microSleep(50000);
|
||||
$this->instance->quit();
|
||||
|
||||
@socket_close($this->socket);
|
||||
|
@ -80,10 +80,13 @@ class RCONInstance extends Thread{
|
||||
}
|
||||
|
||||
private function readPacket($client, ?int &$requestID, ?int &$packetType, ?string &$payload){
|
||||
$d = socket_read($client, 4);
|
||||
$d = @socket_read($client, 4);
|
||||
if($this->stop){
|
||||
return false;
|
||||
}elseif($d === false){
|
||||
if(socket_last_error($client) === SOCKET_ECONNRESET){ //client crashed, terminate connection
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}elseif($d === "" or strlen($d) < 4){
|
||||
return false;
|
||||
@ -174,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)){
|
||||
|
@ -30,7 +30,7 @@ use pocketmine\timings\Timings;
|
||||
|
||||
class PermissibleBase implements Permissible{
|
||||
/** @var ServerOperator */
|
||||
private $opable = null;
|
||||
private $opable;
|
||||
|
||||
/** @var Permissible */
|
||||
private $parent = null;
|
||||
@ -59,24 +59,14 @@ class PermissibleBase implements Permissible{
|
||||
* @return bool
|
||||
*/
|
||||
public function isOp() : bool{
|
||||
if($this->opable === null){
|
||||
return false;
|
||||
}else{
|
||||
return $this->opable->isOp();
|
||||
}
|
||||
return $this->opable->isOp();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setOp(bool $value){
|
||||
if($this->opable === null){
|
||||
throw new \LogicException("Cannot change op value as no ServerOperator is set");
|
||||
}else{
|
||||
$this->opable->setOp($value);
|
||||
}
|
||||
$this->opable->setOp($value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,9 +166,7 @@ class PermissibleBase implements Permissible{
|
||||
|
||||
public function clearPermissions(){
|
||||
$pluginManager = Server::getInstance()->getPluginManager();
|
||||
foreach(array_keys($this->permissions) as $name){
|
||||
$pluginManager->unsubscribeFromPermission($name, $this->parent ?? $this);
|
||||
}
|
||||
$pluginManager->unsubscribeFromAllPermissions($this->parent ?? $this);
|
||||
|
||||
$pluginManager->unsubscribeFromDefaultPerms(false, $this->parent ?? $this);
|
||||
$pluginManager->unsubscribeFromDefaultPerms(true, $this->parent ?? $this);
|
||||
|
@ -55,6 +55,12 @@ interface Plugin extends CommandExecutor{
|
||||
public function isEnabled() : bool;
|
||||
|
||||
/**
|
||||
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
|
||||
*
|
||||
* @internal This is intended for core use only and should not be used by plugins
|
||||
* @see PluginManager::enablePlugin()
|
||||
* @see PluginManager::disablePlugin()
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function setEnabled(bool $enabled = true) : void;
|
||||
|
@ -93,6 +93,12 @@ abstract class PluginBase implements Plugin{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the plugin manager when the plugin is enabled or disabled to inform the plugin of its enabled state.
|
||||
*
|
||||
* @internal This is intended for core use only and should not be used by plugins
|
||||
* @see PluginManager::enablePlugin()
|
||||
* @see PluginManager::disablePlugin()
|
||||
*
|
||||
* @param bool $enabled
|
||||
*/
|
||||
final public function setEnabled(bool $enabled = true) : void{
|
||||
@ -256,6 +262,7 @@ abstract class PluginBase implements Plugin{
|
||||
}
|
||||
|
||||
public function reloadConfig(){
|
||||
@mkdir($this->dataFolder);
|
||||
$this->config = new Config($this->configFile);
|
||||
if(($configStream = $this->getResource("config.yml")) !== null){
|
||||
$this->config->setDefaults(yaml_parse(Config::fixYAMLIndexes(stream_get_contents($configStream))));
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
@ -537,6 +534,18 @@ class PluginManager{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Permissible $permissible
|
||||
*/
|
||||
public function unsubscribeFromAllPermissions(Permissible $permissible) : void{
|
||||
foreach($this->permSubs as $permission => &$subs){
|
||||
unset($subs[spl_object_hash($permissible)]);
|
||||
if(empty($subs)){
|
||||
unset($this->permSubs[$permission]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $permission
|
||||
*
|
||||
@ -596,11 +605,7 @@ class PluginManager{
|
||||
* @return bool
|
||||
*/
|
||||
public function isPluginEnabled(Plugin $plugin) : bool{
|
||||
if($plugin instanceof Plugin and isset($this->plugins[$plugin->getDescription()->getName()])){
|
||||
return $plugin->isEnabled();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
return isset($this->plugins[$plugin->getDescription()->getName()]) and $plugin->isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -669,7 +674,7 @@ class PluginManager{
|
||||
}elseif(is_string($data["permission"])){
|
||||
$newCmd->setPermission($data["permission"]);
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Permission must be a string or boolean, " . gettype($data["permission"] . " given"));
|
||||
throw new \InvalidArgumentException("Permission must be a string or boolean, " . gettype($data["permission"]) . " given");
|
||||
}
|
||||
}
|
||||
|
||||
@ -787,7 +792,7 @@ class PluginManager{
|
||||
|
||||
$reflection = new \ReflectionClass(get_class($listener));
|
||||
foreach($reflection->getMethods(\ReflectionMethod::IS_PUBLIC) as $method){
|
||||
if(!$method->isStatic()){
|
||||
if(!$method->isStatic() and $method->getDeclaringClass()->implementsInterface(Listener::class)){
|
||||
$tags = Utils::parseDocComment((string) $method->getDocComment());
|
||||
if(isset($tags["notHandler"])){
|
||||
continue;
|
||||
@ -848,7 +853,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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -113,10 +113,14 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
$pair->checkPairing();
|
||||
}
|
||||
if($this->doubleInventory === null){
|
||||
if(($pair->x + ($pair->z << 15)) > ($this->x + ($this->z << 15))){ //Order them correctly
|
||||
$this->doubleInventory = new DoubleChestInventory($pair, $this);
|
||||
if($pair->doubleInventory !== null){
|
||||
$this->doubleInventory = $pair->doubleInventory;
|
||||
}else{
|
||||
$this->doubleInventory = new DoubleChestInventory($this, $pair);
|
||||
if(($pair->x + ($pair->z << 15)) > ($this->x + ($this->z << 15))){ //Order them correctly
|
||||
$this->doubleInventory = new DoubleChestInventory($pair, $this);
|
||||
}else{
|
||||
$this->doubleInventory = new DoubleChestInventory($this, $pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
|
@ -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:
|
||||
@ -550,7 +548,7 @@ class Config{
|
||||
* @param string $content
|
||||
*/
|
||||
private function parseProperties(string $content){
|
||||
if(preg_match_all('/([a-zA-Z0-9\-_\.]*)=([^\r\n]*)/u', $content, $matches) > 0){ //false or 0 matches
|
||||
if(preg_match_all('/([a-zA-Z0-9\-_\.]+)[ \t]*=([^\r\n]*)/u', $content, $matches) > 0){ //false or 0 matches
|
||||
foreach($matches[1] as $i => $k){
|
||||
$v = trim($matches[2][$i]);
|
||||
switch(strtolower($v)){
|
||||
|
@ -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(){
|
||||
|
@ -630,9 +630,9 @@ class Utils{
|
||||
* @return string[] an array of tagName => tag value. If the tag has no value, an empty string is used as the value.
|
||||
*/
|
||||
public static function parseDocComment(string $docComment) : array{
|
||||
preg_match_all('/^[\t ]*\* @([a-zA-Z]+)(?:[\t ]+(.+))?[\t ]*$/m', $docComment, $matches);
|
||||
preg_match_all('/(*ANYCRLF)^[\t ]*\* @([a-zA-Z]+)(?:[\t ]+(.+))?[\t ]*$/m', $docComment, $matches);
|
||||
|
||||
return array_combine($matches[1], array_map("trim", $matches[2]));
|
||||
return array_combine($matches[1], $matches[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,8 +2,9 @@
|
||||
|
||||
PHP_BINARY="php"
|
||||
DIR=""
|
||||
FIND="find"
|
||||
|
||||
while getopts "p:d:" OPTION 2> /dev/null; do
|
||||
while getopts "p:d:f:" OPTION 2> /dev/null; do
|
||||
case ${OPTION} in
|
||||
p)
|
||||
PHP_BINARY="$OPTARG"
|
||||
@ -11,6 +12,9 @@ while getopts "p:d:" OPTION 2> /dev/null; do
|
||||
d)
|
||||
DIR="$OPTARG"
|
||||
;;
|
||||
f)
|
||||
FIND="$OPTARG"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
@ -21,7 +25,7 @@ fi
|
||||
|
||||
echo Running PHP lint scans on \"$DIR\"...
|
||||
|
||||
OUTPUT=`find "$DIR" -name "*.php" -print0 | xargs -0 -n1 -P4 "$PHP_BINARY" -l`
|
||||
OUTPUT=`$FIND "$DIR" -name "*.php" -print0 | xargs -0 -n1 -P4 "$PHP_BINARY" -l`
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $OUTPUT | grep -v "No syntax errors"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user