mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 19:24:12 +00:00
Compare commits
59 Commits
Author | SHA1 | Date | |
---|---|---|---|
b3b240e25b | |||
2b30ef1671 | |||
124ebf69c5 | |||
4274640845 | |||
58b665985e | |||
0f5c48e342 | |||
08ad5db05b | |||
94e8623c75 | |||
921f7e8f6a | |||
710e1d014d | |||
7fc22d3227 | |||
7bfe487ee5 | |||
d8cf835f92 | |||
65e44364e5 | |||
ebbbc581ca | |||
8aa8280a63 | |||
6a637d9099 | |||
06b80a9536 | |||
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 |
@ -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
|
||||
|
@ -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){
|
||||
@ -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.0.8";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
|
@ -1070,6 +1070,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{
|
||||
@ -2471,6 +2474,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){
|
||||
|
@ -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)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"));
|
||||
|
@ -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
|
||||
@ -606,7 +606,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1101,7 +1101,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{
|
||||
@ -1346,7 +1346,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 +1357,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;
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -165,7 +165,7 @@ class ExperienceOrb extends Entity{
|
||||
}
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
|
@ -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{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
@ -1624,7 +1627,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;
|
||||
}
|
||||
}
|
||||
@ -2346,21 +2349,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 +2370,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)){
|
||||
@ -2788,7 +2791,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$chunk = $this->getChunk($v->x >> 4, $v->z >> 4, false);
|
||||
$x = (int) $v->x;
|
||||
$z = (int) $v->z;
|
||||
if($chunk !== null){
|
||||
if($chunk !== null and $chunk->isGenerated()){
|
||||
$y = (int) min($max - 2, $v->y);
|
||||
$wasAir = ($chunk->getBlockId($x & 0x0f, $y - 1, $z & 0x0f) === 0);
|
||||
for(; $y > 0; --$y){
|
||||
@ -2944,16 +2947,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($populate){
|
||||
if(!isset($this->chunkPopulationQueue[$index])){
|
||||
$this->chunkPopulationQueue[$index] = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
|
||||
}
|
||||
$this->chunkPopulationQueue[$index] = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
|
||||
}
|
||||
$task = new PopulationTask($this, $chunk);
|
||||
$this->server->getAsyncPool()->submitTask($task);
|
||||
}
|
||||
$task = new PopulationTask($this, $chunk);
|
||||
$this->server->getAsyncPool()->submitTask($task);
|
||||
}
|
||||
|
||||
Timings::$populationTimer->stopTiming();
|
||||
|
@ -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"];
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -256,6 +256,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))));
|
||||
|
@ -596,11 +596,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 +665,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 +783,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;
|
||||
|
@ -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{
|
||||
|
@ -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)){
|
||||
|
@ -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]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
50
tests/phpunit/utils/UtilsTest.php
Normal file
50
tests/phpunit/utils/UtilsTest.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UtilsTest extends TestCase{
|
||||
|
||||
public function parseDocCommentNewlineProvider() : array{
|
||||
return [
|
||||
["\t/**\r\n\t * @param PlayerJoinEvent \$event\r\n\t * @priority HIGHEST\r\n\t * @notHandler\r\n\t */"],
|
||||
["\t/**\n\t * @param PlayerJoinEvent \$event\n\t * @priority HIGHEST\n\t * @notHandler\n\t */"],
|
||||
["\t/**\r\t * @param PlayerJoinEvent \$event\r\t * @priority HIGHEST\r\t * @notHandler\r\t */"]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $docComment
|
||||
* @dataProvider parseDocCommentNewlineProvider
|
||||
*/
|
||||
public function testParseDocCommentNewlines(string $docComment) : void{
|
||||
$tags = Utils::parseDocComment($docComment);
|
||||
|
||||
self::assertArrayHasKey("notHandler", $tags);
|
||||
self::assertEquals("", $tags["notHandler"]);
|
||||
self::assertArrayHasKey("priority", $tags);
|
||||
self::assertEquals("HIGHEST", $tags["priority"]);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user