mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-12 20:35:11 +00:00
Compare commits
97 Commits
Alpha_1.4d
...
Alpha_1.4d
Author | SHA1 | Date | |
---|---|---|---|
582c165479 | |||
5fb205493a | |||
e346d245e2 | |||
4fece32ca8 | |||
0b79d74a2f | |||
b83c6fbfa3 | |||
dda9c598f1 | |||
8769d2bcd1 | |||
3b7ece3363 | |||
b6025e3f2b | |||
35de331b74 | |||
de11cce154 | |||
d53ba52d32 | |||
9db2fe40eb | |||
57bb8f14fa | |||
761cd59514 | |||
4c2a1c8684 | |||
31bb6d1a68 | |||
cd65179aef | |||
9abd2c63f4 | |||
376e359577 | |||
571e2f8895 | |||
7106ea87e6 | |||
6b65b68ebc | |||
d4c75ce68a | |||
fae330d499 | |||
5e03e157ad | |||
b0c40dc1ab | |||
6840589f4e | |||
19e4aaa16a | |||
e0a7944faa | |||
eeda22d0ba | |||
769f1effb0 | |||
42c7322273 | |||
c8cf6b715e | |||
41f94f7385 | |||
602bdf27a5 | |||
539fa232f8 | |||
f8378c09ba | |||
69fb7ae525 | |||
ee8ad6f92a | |||
f888acbd7c | |||
215691f1c4 | |||
1252dd65a9 | |||
762c27affe | |||
706e1099a1 | |||
9cd66dc969 | |||
5b6b789ab3 | |||
85ff696ae5 | |||
25f8e8318b | |||
61d84c73d0 | |||
f5822c6de8 | |||
b0bd927545 | |||
fde61b7d21 | |||
886ad8442c | |||
7b5869bea8 | |||
3063863c65 | |||
0dfaa19380 | |||
7fea29e874 | |||
2ded2013bf | |||
aa27c28e65 | |||
05a81bebf4 | |||
ce91f2943a | |||
1d8562fb8c | |||
1dfb17b932 | |||
16384c2b20 | |||
48041b2f19 | |||
529bf743db | |||
48bc919a33 | |||
474091c013 | |||
666e5553c2 | |||
ae6f532b1d | |||
b231eba803 | |||
82d903733d | |||
8e2903da86 | |||
d720113ac9 | |||
5db45222c6 | |||
2975509d0f | |||
7e49d073fa | |||
ef3674a296 | |||
6cb7e36f8a | |||
b88f19bb74 | |||
8c3fcf0798 | |||
6b312a7826 | |||
9907e84eee | |||
01f299a7f1 | |||
91f20f6789 | |||
4c1edc3f7e | |||
5fd1e271c5 | |||
b3ae6eae04 | |||
3b56b536b6 | |||
9258281546 | |||
e7897be7cd | |||
5ff01de413 | |||
f191ada405 | |||
addd74d09e | |||
4e9d2d0a7f |
@ -9,7 +9,7 @@ branches:
|
||||
before_script:
|
||||
- mkdir plugins
|
||||
- wget -O plugins/DevTools.phar https://github.com/PocketMine/DevTools/releases/download/v1.9.0/DevTools_v1.9.0.phar
|
||||
- pecl install channel://pecl.php.net/pthreads-2.0.8
|
||||
- pecl install channel://pecl.php.net/pthreads-2.0.10
|
||||
- pecl install channel://pecl.php.net/weakref-0.2.4
|
||||
- echo | pecl install channel://pecl.php.net/yaml-1.1.1
|
||||
|
||||
|
@ -72,6 +72,19 @@ class ExampleClass{
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
### RFC and Voting
|
||||
* These are big Pull Requests or contributions that change important behavior.
|
||||
* RFCs will be tagged with the *PR: RFC* label
|
||||
* A vote will be held once the RFC is ready. All users can vote commenting on the Pull Request
|
||||
* Comments MUST use "Yes" or "No" on the FIRST sentence to signify the vote, except when they don't want it to be counted.
|
||||
* If your comment is a voting comment, specify the reason of your vote or it won't be counted.
|
||||
* After voting has been closed, no further votes will be counted.
|
||||
* An RFC will be rejected if less than 50% + 1 (simple majority) has voted Yes.
|
||||
* If the RFC is approved, Team Members have the final word on its implementation or rejection.
|
||||
* RFCs with complex voting options will specify the vote percentage or other details.
|
||||
|
||||
|
||||
## Bug Tracking for Collaborators
|
||||
|
||||
### Labels
|
||||
@ -91,6 +104,7 @@ Category labels are prefixed by `C:`. Multiple category labels may be applied to
|
||||
Pull Requests are prefixed by `PR:`. Only one label may be applied for a Pull Request.
|
||||
- PR: Bug Fix - This label is applied when the Pull Request fixes a bug.
|
||||
- PR: Contribution - This label is applied when the Pull Request contributes code to PocketMine-MP such as a new feature or an improvement.
|
||||
- PR: RFC - Request for Comments
|
||||
|
||||
#### Status
|
||||
Status labels show the status of the issue. Multiple status labels may be applied.
|
||||
|
@ -57,6 +57,7 @@ use pocketmine\inventory\CraftingTransactionGroup;
|
||||
use pocketmine\inventory\FurnaceInventory;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\inventory\InventoryHolder;
|
||||
use pocketmine\inventory\PlayerInventory;
|
||||
use pocketmine\inventory\SimpleTransactionGroup;
|
||||
use pocketmine\inventory\StonecutterShapelessRecipe;
|
||||
use pocketmine\item\Item;
|
||||
@ -64,6 +65,7 @@ use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\Vector2;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
use pocketmine\nbt\NBT;
|
||||
@ -165,10 +167,16 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
/** @var Player[] */
|
||||
protected $hiddenPlayers = [];
|
||||
|
||||
/** @var Vector3 */
|
||||
protected $newPosition;
|
||||
|
||||
private $viewDistance;
|
||||
private $spawnPosition;
|
||||
/** @var null|Position */
|
||||
private $spawnPosition = null;
|
||||
private $inAction = false;
|
||||
|
||||
protected $inAirTicks = 0;
|
||||
|
||||
|
||||
private $needACK = [];
|
||||
|
||||
@ -410,6 +418,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->gamemode = $this->server->getGamemode();
|
||||
$this->setLevel($this->server->getDefaultLevel(), true);
|
||||
$this->viewDistance = $this->server->getViewDistance();
|
||||
$this->newPosition = new Vector3(0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -555,7 +564,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
Level::getXZ($index, $X, $Z);
|
||||
|
||||
foreach($this->getLevel()->getChunkEntities($X, $Z) as $entity){
|
||||
if($entity !== $this){
|
||||
if($entity !== $this and !$entity->closed and !$entity->dead){
|
||||
$entity->spawnTo($this);
|
||||
}
|
||||
}
|
||||
@ -605,7 +614,11 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$Z = null;
|
||||
Level::getXZ($index, $X, $Z);
|
||||
if(!$this->getLevel()->isChunkPopulated($X, $Z)){
|
||||
continue;
|
||||
if($this->spawned === true){
|
||||
continue;
|
||||
}else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
unset($this->loadQueue[$index]);
|
||||
@ -654,13 +667,13 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->inventory->sendContents($this);
|
||||
$this->inventory->sendArmorContents($this);
|
||||
|
||||
$this->spawnToAll();
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerJoinEvent($this, TextFormat::YELLOW . $this->getName() . " joined the game"));
|
||||
if(strlen(trim($ev->getJoinMessage())) > 0){
|
||||
$this->server->broadcastMessage($ev->getJoinMessage());
|
||||
}
|
||||
|
||||
$this->spawnToAll();
|
||||
|
||||
if($this->server->getUpdater()->hasUpdate() and $this->hasPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE)){
|
||||
$this->server->getUpdater()->showPlayerUpdate($this);
|
||||
}
|
||||
@ -689,7 +702,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$generateQueue = new ReversePriorityQueue();
|
||||
for($X = -$radius; $X <= $radius; ++$X){
|
||||
for($Z = -$radius; $Z <= $radius; ++$Z){
|
||||
$distance = ($X * $X) + ($Z * $Z);
|
||||
$distance = $X ** 2 + $Z ** 2;
|
||||
if($distance > $radiusSquared){
|
||||
continue;
|
||||
}
|
||||
@ -697,7 +710,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$chunkZ = $Z + $centerZ;
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(!isset($this->usedChunks[$index])){
|
||||
if($this->level->isChunkPopulated($chunkX, $chunkZ)){
|
||||
if($this->spawned === false or $this->level->isChunkPopulated($chunkX, $chunkZ)){
|
||||
$newOrder[$index] = $distance;
|
||||
}else{
|
||||
$generateQueue->insert([$chunkX, $chunkZ], $distance);
|
||||
@ -722,7 +735,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
while(count($this->loadQueue) < 3 and $generateQueue->count() > 0 and $i < 16){
|
||||
while($generateQueue->count() > 0 and $i < 32){
|
||||
$d = $generateQueue->extract();
|
||||
$this->getLevel()->generateChunk($d[0], $d[1]);
|
||||
++$i;
|
||||
@ -1088,13 +1101,120 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function processMovement(){
|
||||
if($this->dead or !$this->spawned or !($this->newPosition instanceof Vector3)){
|
||||
return;
|
||||
}
|
||||
$oldPos = new Vector3($this->x, $this->y, $this->z);
|
||||
if(($distance = $oldPos->distance($this->newPosition)) == 0){
|
||||
return;
|
||||
}
|
||||
|
||||
$revert = false;
|
||||
|
||||
if($distance > 100){
|
||||
$revert = true;
|
||||
}else{
|
||||
if($this->chunk === null or !$this->chunk->isGenerated()){
|
||||
$chunk = $this->getLevel()->getChunk($this->newPosition->x >> 4, $this->newPosition->z >> 4);
|
||||
if(!($chunk instanceof FullChunk) or !$chunk->isGenerated()){
|
||||
$revert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$revert){
|
||||
$dx = $this->newPosition->x - $this->x;
|
||||
$dy = $this->newPosition->y - $this->y;
|
||||
$dz = $this->newPosition->z - $this->z;
|
||||
|
||||
//$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$revert = !$this->move($dx, $dy, $dz);
|
||||
|
||||
$diffX = $this->x - $this->newPosition->x;
|
||||
$diffZ = $this->z - $this->newPosition->z;
|
||||
$diffY = $this->y - $this->newPosition->y;
|
||||
if($diffY > -0.5 or $diffY < 0.5){
|
||||
$diffY = 0;
|
||||
}
|
||||
|
||||
$diff = $diffX ** 2 + $diffY ** 2 + $diffZ ** 2;
|
||||
|
||||
if(!$revert and !$this->isSleeping() and $this->isSurvival()){
|
||||
if($diff > 0.0625){
|
||||
$revert = true;
|
||||
//$this->server->getLogger()->warning($this->getName()." moved wrongly!");
|
||||
}elseif($diff > 0){
|
||||
$revert = !$this->setPosition($this->newPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($revert){
|
||||
$pk = new MovePlayerPacket();
|
||||
$pk->eid = 0;
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y + $this->getEyeHeight();
|
||||
$pk->z = $this->z;
|
||||
$pk->bodyYaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->teleport = true;
|
||||
$this->directDataPacket($pk);
|
||||
$this->forceMovement = new Vector3($this->x, $this->y, $this->z);
|
||||
}else{
|
||||
$this->updateMovement();
|
||||
$this->forceMovement = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
if($this->spawned === false or $this->dead === true){
|
||||
return true;
|
||||
}
|
||||
$hasUpdate = $this->entityBaseTick();
|
||||
foreach($this->getLevel()->getNearbyEntities($this->boundingBox->grow(1, 1, 1), $this) as $entity){
|
||||
if($entity instanceof DroppedItem){
|
||||
|
||||
$this->timings->startTiming();
|
||||
|
||||
$this->processMovement();
|
||||
|
||||
$this->entityBaseTick();
|
||||
|
||||
if($this->onGround or $this->fallDistance === 0){
|
||||
$this->inAirTicks = 0;
|
||||
}else{
|
||||
if($this->inAirTicks > 100 and $this->isSurvival() and !$this->isSleeping() and $this->spawned and !$this->server->getAllowFlight()){
|
||||
$this->kick("Flying is not enabled on this server");
|
||||
return false;
|
||||
}else{
|
||||
++$this->inAirTicks;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->level->getNearbyEntities($this->boundingBox->grow(1, 1, 1), $this) as $entity){
|
||||
if($entity instanceof Arrow and $entity->onGround and $this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0){
|
||||
if($entity->dead !== true){
|
||||
$item = Item::get(Item::ARROW, 0, 1);
|
||||
if($this->isSurvival() and !$this->inventory->canAddItem($item)){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new InventoryPickupItemEvent($this->inventory, $item));
|
||||
if($ev->isCancelled()){
|
||||
continue;
|
||||
}
|
||||
|
||||
$pk = new TakeItemEntityPacket;
|
||||
$pk->eid = 0;
|
||||
$pk->target = $entity->getID();
|
||||
$this->dataPacket($pk);
|
||||
$pk = new TakeItemEntityPacket;
|
||||
$pk->eid = $this->getID();
|
||||
$pk->target = $entity->getID();
|
||||
Server::broadcastPacket($entity->getViewers(), $pk);
|
||||
$this->inventory->addItem(clone $item);
|
||||
$entity->kill();
|
||||
}
|
||||
}elseif($entity instanceof DroppedItem){
|
||||
if($entity->dead !== true and $entity->getPickupDelay() <= 0){
|
||||
$item = $entity->getItem();
|
||||
|
||||
@ -1121,7 +1241,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$pk->eid = 0;
|
||||
$pk->target = $entity->getID();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$pk = new TakeItemEntityPacket;
|
||||
$pk->eid = $this->getID();
|
||||
$pk->target = $entity->getID();
|
||||
@ -1133,6 +1252,8 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1264,7 +1385,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
$nbt["lastPlayed"] = floor(microtime(true) * 1000);
|
||||
$this->server->saveOfflinePlayerData($this->username, $nbt);
|
||||
parent::__construct($this->getLevel()->getChunkAt($nbt["Pos"][0], $nbt["Pos"][2], true), $nbt);
|
||||
parent::__construct($this->getLevel()->getChunk($nbt["Pos"][0], $nbt["Pos"][2], true), $nbt);
|
||||
$this->loggedIn = true;
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerLoginEvent($this, "Plugin reason"));
|
||||
@ -1284,13 +1405,14 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$pk->status = 0;
|
||||
$this->dataPacket($pk);
|
||||
|
||||
if(isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){
|
||||
if($this->spawnPosition === null and isset($this->namedtag->SpawnLevel) and ($level = $this->server->getLevelByName($this->namedtag["SpawnLevel"])) instanceof Level){
|
||||
$this->spawnPosition = new Position($this->namedtag["SpawnX"], $this->namedtag["SpawnY"], $this->namedtag["SpawnZ"], $level);
|
||||
}
|
||||
|
||||
$spawnPosition = $this->getSpawn();
|
||||
|
||||
$this->dead = false;
|
||||
|
||||
$pk = new StartGamePacket;
|
||||
$pk->seed = $this->getLevel()->getSeed();
|
||||
$pk->x = $this->x;
|
||||
@ -1339,60 +1461,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
case ProtocolInfo::MOVE_PLAYER_PACKET:
|
||||
|
||||
$newPos = new Vector3($packet->x, $packet->y, $packet->z);
|
||||
$oldPos = new Vector3($this->x, $this->y, $this->z);
|
||||
|
||||
$revert = ($this->dead === true or $this->spawned !== true);
|
||||
|
||||
if($this->forceMovement instanceof Vector3 and $newPos->distance($this->forceMovement) > 0.1){
|
||||
$revert = true;
|
||||
}elseif($newPos->distance($this) > 100){
|
||||
$this->server->getLogger()->warning($this->getName()." moved too quickly!");
|
||||
$revert = true;
|
||||
}else{
|
||||
if($this->chunk === null or !$this->chunk->isGenerated()){
|
||||
$chunk = $this->getLevel()->getChunkAt($newPos->x >> 4, $newPos->z >> 4);
|
||||
if(!($chunk instanceof FullChunk) or !$chunk->isGenerated()){
|
||||
$revert = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$revert){
|
||||
$dx = $newPos->x - $this->x;
|
||||
$dy = $newPos->y - $this->y;
|
||||
$dz = $newPos->z - $this->z;
|
||||
|
||||
$packet->yaw %= 360;
|
||||
$packet->pitch %= 360;
|
||||
|
||||
if($packet->yaw < 0){
|
||||
$packet->yaw += 360;
|
||||
}
|
||||
|
||||
if($packet->pitch < 0){
|
||||
$packet->pitch += 360;
|
||||
}
|
||||
|
||||
$this->setRotation($packet->yaw, $packet->pitch);
|
||||
//$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$revert = !$this->move($dx, $dy, $dz);
|
||||
|
||||
$diffX = $this->x - $newPos->x;
|
||||
$diffZ = $this->z - $newPos->z;
|
||||
$diffY = $this->y - $newPos->y;
|
||||
if($diffY > -0.5 or $diffY < 0.5){
|
||||
$diffY = 0;
|
||||
}
|
||||
|
||||
$diff = $diffX ** 2 + $diffY ** 2 + $diffZ ** 2;
|
||||
|
||||
if(!$revert and $diff > 0.0625 and !$this->isSleeping() and $this->isSurvival()){
|
||||
$revert = true;
|
||||
$this->server->getLogger()->warning($this->getName()." moved wrongly!");
|
||||
}
|
||||
}
|
||||
|
||||
if($revert){
|
||||
if($revert or ($this->forceMovement instanceof Vector3 and $newPos->distance($this->forceMovement) > 0.1)){
|
||||
$pk = new MovePlayerPacket();
|
||||
$pk->eid = 0;
|
||||
$pk->x = $this->x;
|
||||
@ -1403,8 +1475,16 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->teleport = true;
|
||||
$this->directDataPacket($pk);
|
||||
$this->forceMovement = new Vector3($this->x, $this->y, $this->z);
|
||||
}else{
|
||||
$packet->yaw %= 360;
|
||||
$packet->pitch %= 360;
|
||||
|
||||
if($packet->yaw < 0){
|
||||
$packet->yaw += 360;
|
||||
}
|
||||
|
||||
$this->setRotation($packet->yaw, $packet->pitch);
|
||||
$this->newPosition = $newPos;
|
||||
$this->forceMovement = null;
|
||||
}
|
||||
|
||||
@ -1515,7 +1595,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$item = clone $this->inventory->getItemInHand();
|
||||
//TODO: Implement adventure mode checks
|
||||
if($this->getLevel()->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->setItemInHand($item, $this);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
break;
|
||||
}
|
||||
@ -1595,9 +1675,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
if($this->isSurvival()){
|
||||
$this->inventory->removeItem(Item::get(Item::ARROW, 0, 1));
|
||||
$bow->setDamage($bow->getDamage() + 1);
|
||||
$this->inventory->setItemInHand($bow);
|
||||
$this->inventory->setItemInHand($bow, $this);
|
||||
if($bow->getDamage() >= 385){
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0));
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0), $this);
|
||||
}
|
||||
}
|
||||
$arrow->spawnToAll();
|
||||
@ -1630,7 +1710,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
if($this->getLevel()->useBreakOn($vector, $item, $this) === true){
|
||||
if($this->isSurvival()){
|
||||
$this->inventory->setItemInHand($item);
|
||||
$this->inventory->setItemInHand($item, $this);
|
||||
$this->inventory->sendHeldItem($this->hasSpawned);
|
||||
}
|
||||
break;
|
||||
@ -1652,41 +1732,10 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$tile->spawnTo($this);
|
||||
}
|
||||
break;
|
||||
|
||||
case ProtocolInfo::PLAYER_ARMOR_EQUIPMENT_PACKET:
|
||||
if($this->spawned === false or $this->blocked === true or $this->dead === true){
|
||||
break;
|
||||
}
|
||||
|
||||
for($i = 0; $i < 4; ++$i){
|
||||
$s = $packet->slots[$i];
|
||||
if($s === 0 or $s === 255){
|
||||
$s = Item::get(Item::AIR, 0, 1);
|
||||
}else{
|
||||
$s = Item::get($s + 256, 0, 1);
|
||||
}
|
||||
$slot = $this->inventory->getArmorItem($i);
|
||||
if($slot->getID() !== Item::AIR and $s->getID() === Item::AIR){
|
||||
$this->inventory->setArmorItem($i, Item::get(Item::AIR, 0, 1));
|
||||
}elseif($s->getID() !== Item::AIR and $slot->getID() === Item::AIR and ($sl = $this->inventory->first($s)) !== -1){
|
||||
if($this->inventory->setArmorItem($i, $this->inventory->getItem($sl)) === false){
|
||||
$this->inventory->sendContents($this);
|
||||
}else{
|
||||
$this->inventory->setItem($sl, Item::get(Item::AIR, 0, 1));
|
||||
}
|
||||
}elseif($s->getID() !== Item::AIR and $slot->getID() !== Item::AIR and ($slot->getID() !== $s->getID() or $slot->getDamage() !== $s->getDamage()) and ($sl = $this->inventory->first($s)) !== -1){
|
||||
if($this->inventory->setArmorItem($i, $this->inventory->getItem($sl)) === false){
|
||||
$this->inventory->sendContents($this);
|
||||
}else{
|
||||
$this->inventory->setItem($sl, $slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($this->inAction === true){
|
||||
$this->inAction = false;
|
||||
$this->sendMetadata($this->getViewers());
|
||||
}
|
||||
break;
|
||||
|
||||
case ProtocolInfo::INTERACT_PACKET:
|
||||
if($this->spawned === false or $this->dead === true){
|
||||
break;
|
||||
@ -1801,7 +1850,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
if($item->isTool() and $this->isSurvival()){
|
||||
if($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()){
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1));
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1), $this);
|
||||
}else{
|
||||
$this->inventory->setItemInHand($item, $this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1839,6 +1890,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->setHealth(20);
|
||||
$this->dead = false;
|
||||
$this->sendMetadata($this->getViewers());
|
||||
$this->sendMetadata($this);
|
||||
|
||||
$this->sendSettings();
|
||||
$this->inventory->sendContents($this);
|
||||
@ -1900,7 +1952,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
$this->heal($items[$slot->getID()]);
|
||||
--$slot->count;
|
||||
$this->inventory->setItemInHand($slot);
|
||||
$this->inventory->setItemInHand($slot, $this);
|
||||
if($slot->getID() === Item::MUSHROOM_STEW or $slot->getID() === Item::BEETROOT_SOUP){
|
||||
$this->inventory->addItem(Item::get(Item::BOWL, 0, 1));
|
||||
}
|
||||
@ -1921,7 +1973,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
break;
|
||||
}
|
||||
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1));
|
||||
$this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1), $this);
|
||||
$motion = $this->getDirectionVector()->multiply(0.4);
|
||||
|
||||
$this->getLevel()->dropItem($this->add(0, 1.3, 0), $item, $motion, 40);
|
||||
@ -1978,7 +2030,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
|
||||
if($packet->windowid === 0){ //Our inventory
|
||||
if($packet->slot > $this->inventory->getSize()){
|
||||
if($packet->slot >= $this->inventory->getSize()){
|
||||
break;
|
||||
}
|
||||
if($this->isCreative()){
|
||||
@ -1986,10 +2038,14 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->inventory->setItem($packet->slot, $packet->item);
|
||||
$this->inventory->setHotbarSlotIndex($packet->slot, $packet->slot); //links $hotbar[$packet->slot] to $slots[$packet->slot]
|
||||
}
|
||||
}else{
|
||||
|
||||
}
|
||||
$transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item);
|
||||
}elseif($packet->windowid === 0x78){ //Our armor
|
||||
if($packet->slot >= 4){
|
||||
break;
|
||||
}
|
||||
|
||||
$transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $this->inventory->getArmorItem($packet->slot), $packet->item);
|
||||
}elseif(isset($this->windowIndex[$packet->windowid])){
|
||||
$this->craftingType = 0;
|
||||
$inv = $this->windowIndex[$packet->windowid];
|
||||
@ -2008,6 +2064,9 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
if($this->currentTransaction instanceof SimpleTransactionGroup){
|
||||
foreach($this->currentTransaction->getInventories() as $inventory){
|
||||
$inventory->sendContents($inventory->getViewers());
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($inventory->getViewers());
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->currentTransaction = new SimpleTransactionGroup($this);
|
||||
@ -2161,29 +2220,11 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
public function sendMessage($message){
|
||||
$mes = explode("\n", $message);
|
||||
foreach($mes as $m){
|
||||
if(preg_match_all('#@([@A-Za-z_]{1,})#', $m, $matches, PREG_OFFSET_CAPTURE) > 0){
|
||||
$offsetshift = 0;
|
||||
foreach($matches[1] as $selector){
|
||||
if($selector[0]{0} === "@"){ //Escape!
|
||||
$m = substr_replace($m, $selector[0], $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1);
|
||||
--$offsetshift;
|
||||
continue;
|
||||
}
|
||||
switch(strtolower($selector[0])){
|
||||
case "player":
|
||||
case "username":
|
||||
$m = substr_replace($m, $this->username, $selector[1] + $offsetshift - 1, strlen($selector[0]) + 1);
|
||||
$offsetshift += strlen($selector[0]) - strlen($this->username) + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($m !== ""){
|
||||
$pk = new MessagePacket;
|
||||
$pk->source = ""; //Do not use this ;)
|
||||
$pk->message = $this->removeFormat === false ? $m : TextFormat::clean($m);
|
||||
$this->directDataPacket($pk);
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2199,15 +2240,15 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
}
|
||||
$this->tasks = [];
|
||||
|
||||
if($this->connected === true){
|
||||
if($this->connected and !$this->closed){
|
||||
$this->connected = false;
|
||||
if($this->username != ""){
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerQuitEvent($this, $message));
|
||||
if($this->loggedIn === true){
|
||||
if($this->server->getAutoSave() and $this->loggedIn === true){
|
||||
$this->save();
|
||||
}
|
||||
}
|
||||
|
||||
$this->connected = false;
|
||||
$this->interface->close($this, $reason);
|
||||
$this->getLevel()->freeAllChunks($this);
|
||||
|
||||
@ -2242,7 +2283,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
parent::saveNBT();
|
||||
if($this->getLevel() instanceof Level){
|
||||
$this->namedtag["Level"] = $this->getLevel()->getName();
|
||||
$this->namedtag->Level = new String("Level", $this->getLevel()->getName());
|
||||
if($this->spawnPosition instanceof Position and $this->spawnPosition->getLevel() instanceof Level){
|
||||
$this->namedtag["SpawnLevel"] = $this->spawnPosition->getLevel()->getName();
|
||||
$this->namedtag["SpawnX"] = (int) $this->spawnPosition->x;
|
||||
@ -2257,7 +2298,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->namedtag["playerGameType"] = $this->gamemode;
|
||||
$this->namedtag["lastPlayed"] = floor(microtime(true) * 1000);
|
||||
|
||||
if($this->username != "" and $this->isOnline() and $this->namedtag instanceof Compound){
|
||||
if($this->username != "" and $this->namedtag instanceof Compound){
|
||||
$this->server->saveOfflinePlayerData($this->username, $this->namedtag);
|
||||
}
|
||||
}
|
||||
@ -2276,11 +2317,6 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
if($this->dead === true or $this->spawned === false){
|
||||
return;
|
||||
}
|
||||
parent::kill();
|
||||
|
||||
if($this->inventory !== null){
|
||||
$this->inventory->clearAll();
|
||||
}
|
||||
|
||||
$message = $this->getName() . " died";
|
||||
$cause = $this->getLastDamageCause();
|
||||
@ -2363,12 +2399,27 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
|
||||
}
|
||||
|
||||
if($this->dead){
|
||||
return;
|
||||
}
|
||||
|
||||
Entity::kill();
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev = new PlayerDeathEvent($this, $this->getDrops(), $message));
|
||||
foreach($ev->getDrops() as $item){
|
||||
$this->getLevel()->dropItem($this, $item);
|
||||
}
|
||||
|
||||
if($this->inventory !== null){
|
||||
$this->inventory->clearAll();
|
||||
}
|
||||
|
||||
if($ev->getDeathMessage() != ""){
|
||||
$this->server->broadcast($ev->getDeathMessage(), Server::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
|
||||
$this->despawnFromAll();
|
||||
|
||||
}
|
||||
|
||||
public function setHealth($amount){
|
||||
@ -2440,6 +2491,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{
|
||||
$this->orderChunks();
|
||||
$this->chunkLoadTask->setNextRun(0);
|
||||
$this->forceMovement = $pos;
|
||||
$this->newPosition = $pos;
|
||||
|
||||
$pk = new MovePlayerPacket;
|
||||
$pk->eid = 0;
|
||||
|
@ -73,7 +73,7 @@ namespace pocketmine {
|
||||
use raklib\RakLib;
|
||||
|
||||
const VERSION = "Alpha_1.4dev";
|
||||
const API_VERSION = "1.4.0";
|
||||
const API_VERSION = "1.5.0";
|
||||
const CODENAME = "絶好(Zekkou)ケーキ(Cake)";
|
||||
const MINECRAFT_VERSION = "v0.9.5 alpha";
|
||||
|
||||
@ -111,36 +111,6 @@ namespace pocketmine {
|
||||
|
||||
set_time_limit(0); //Who set it to 30 seconds?!?!
|
||||
|
||||
if(ini_get("date.timezone") == ""){ //No Timezone set
|
||||
date_default_timezone_set("GMT");
|
||||
if(strpos(" " . strtoupper(php_uname("s")), " WIN") !== false){
|
||||
$time = time();
|
||||
$time -= $time % 60;
|
||||
//TODO: Parse different time & date formats by region. ¬¬ world
|
||||
//Example: USA
|
||||
@exec("time.exe /T", $hour);
|
||||
$i = array_map("intval", explode(":", trim($hour[0])));
|
||||
@exec("date.exe /T", $date);
|
||||
$j = array_map("intval", explode(substr($date[0], 2, 1), trim($date[0])));
|
||||
$offset = @round((mktime($i[0], $i[1], 0, $j[1], $j[0], $j[2]) - $time) / 60) * 60;
|
||||
}else{
|
||||
@exec("date +%s", $t);
|
||||
$offset = @round((intval(trim($t[0])) - time()) / 60) * 60;
|
||||
}
|
||||
|
||||
$daylight = (int) date("I");
|
||||
$d = timezone_name_from_abbr("", $offset, $daylight);
|
||||
@ini_set("date.timezone", $d);
|
||||
@date_default_timezone_set($d);
|
||||
}else{
|
||||
$d = @date_default_timezone_get();
|
||||
if(strpos($d, "/") === false){
|
||||
$d = timezone_name_from_abbr($d);
|
||||
@ini_set("date.timezone", $d);
|
||||
@date_default_timezone_set($d);
|
||||
}
|
||||
}
|
||||
|
||||
gc_enable();
|
||||
error_reporting(-1);
|
||||
ini_set("allow_url_fopen", 1);
|
||||
@ -158,8 +128,151 @@ namespace pocketmine {
|
||||
|
||||
define("pocketmine\\ANSI", ((strpos(strtoupper(php_uname("s")), "WIN") === false or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"])));
|
||||
|
||||
//Logger has a dependency on timezone, so we'll set it to UTC until we can get the actual timezone.
|
||||
date_default_timezone_set("UTC");
|
||||
$logger = new MainLogger(\pocketmine\DATA . "server.log", \pocketmine\ANSI);
|
||||
|
||||
if(!ini_get("date.timezone")){
|
||||
if(($timezone = detect_system_timezone()) and date_default_timezone_set($timezone)){
|
||||
//Success! Timezone has already been set and validated in the if statement.
|
||||
//This here is just for redundancy just in case some stupid program wants to read timezone data from the ini.
|
||||
ini_set("date.timezone", $timezone);
|
||||
}else{
|
||||
//If system timezone detection fails or timezone is an invalid value.
|
||||
if($response = Utils::getURL("http://ip-api.com/json")
|
||||
and $ip_geolocation_data = json_decode($response, true)
|
||||
and $ip_geolocation_data['status'] != 'fail'
|
||||
and date_default_timezone_set($ip_geolocation_data['timezone']))
|
||||
{
|
||||
//Again, for redundancy.
|
||||
ini_set("date.timezone", $ip_geolocation_data['timezone']);
|
||||
}else{
|
||||
ini_set("date.timezone", "UTC");
|
||||
date_default_timezone_set("UTC");
|
||||
$logger->warning("Timezone could not be automatically determined. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file.");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/*
|
||||
* This is here so that stupid idiots don't come to us complaining and fill up the issue tracker when they put an incorrect timezone abbreviation in php.ini apparently.
|
||||
*/
|
||||
$default_timezone = date_default_timezone_get();
|
||||
if(strpos($default_timezone, "/") === false){
|
||||
$default_timezone = timezone_name_from_abbr($default_timezone);
|
||||
ini_set("date.timezone", $default_timezone);
|
||||
date_default_timezone_set($default_timezone);
|
||||
}
|
||||
}
|
||||
|
||||
function detect_system_timezone(){
|
||||
switch(Utils::getOS()){
|
||||
case 'win':
|
||||
$regex = '/(?:Time Zone:\s*\()(UTC)(\+*\-*\d*\d*\:*\d*\d*)(?:\))/';
|
||||
|
||||
exec("systeminfo", $output);
|
||||
|
||||
$string = trim(implode("\n", $output));
|
||||
|
||||
//Detect the Time Zone string in systeminfo
|
||||
preg_match($regex, $string, $matches);
|
||||
|
||||
if(!isset($matches[2]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$offset = $matches[2];
|
||||
|
||||
if($offset == ""){
|
||||
return "UTC";
|
||||
}
|
||||
|
||||
return parse_offset($offset);
|
||||
break;
|
||||
case 'linux':
|
||||
// Ubuntu / Debian.
|
||||
if(file_exists('/etc/timezone')){
|
||||
$data = file_get_contents('/etc/timezone');
|
||||
if($data){
|
||||
return trim($data);
|
||||
}
|
||||
}
|
||||
|
||||
// RHEL / CentOS
|
||||
if(file_exists('/etc/sysconfig/clock')){
|
||||
$data = parse_ini_file('/etc/sysconfig/clock');
|
||||
if(!empty($data['ZONE'])){
|
||||
return trim($data['ZONE']);
|
||||
}
|
||||
}
|
||||
|
||||
//Portable method for incompatible linux distributions.
|
||||
|
||||
$offset = trim(exec('date +%:z'));
|
||||
|
||||
if($offset == "+00:00"){
|
||||
return "UTC";
|
||||
}
|
||||
|
||||
return parse_offset($offset);
|
||||
break;
|
||||
case 'mac':
|
||||
if(is_link('/etc/localtime')){
|
||||
$filename = readlink('/etc/localtime');
|
||||
if(strpos($filename, '/usr/share/zoneinfo/') === 0){
|
||||
$timezone = substr($filename, 20);
|
||||
return trim($timezone);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $offset In the format of +09:00, +02:00, -04:00 etc.
|
||||
* @return string
|
||||
*/
|
||||
function parse_offset($offset){
|
||||
//Make signed offsets unsigned for date_parse
|
||||
if(strpos($offset, '-') !== false){
|
||||
$negative_offset = true;
|
||||
$offset = str_replace('-', '', $offset);
|
||||
}else{
|
||||
if(strpos($offset, '+') !== false){
|
||||
$negative_offset = false;
|
||||
$offset = str_replace('+', '', $offset);
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$parsed = date_parse($offset);
|
||||
$offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second'];
|
||||
|
||||
//After date_parse is done, put the sign back
|
||||
if($negative_offset == true){
|
||||
$offset = -abs($offset);
|
||||
}
|
||||
|
||||
//And then, look the offset up.
|
||||
//timezone_name_from_abbr is not used because it returns false on some(most) offsets because it's mapping function is weird.
|
||||
//That's been a bug in PHP since 2008!
|
||||
foreach(timezone_abbreviations_list() as $zones){
|
||||
foreach($zones as $timezone){
|
||||
if($timezone['offset'] == $offset){
|
||||
return $timezone['timezone_id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(isset($opts["enable-profiler"])){
|
||||
if(function_exists("profiler_enable")){
|
||||
\profiler_enable();
|
||||
|
@ -46,6 +46,7 @@ use pocketmine\level\format\anvil\Anvil;
|
||||
use pocketmine\level\format\LevelProviderManager;
|
||||
use pocketmine\level\format\mcregion\McRegion;
|
||||
use pocketmine\level\generator\Flat;
|
||||
use pocketmine\level\generator\GenerationInstanceManager;
|
||||
use pocketmine\level\generator\GenerationRequestManager;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\Normal;
|
||||
@ -140,6 +141,7 @@ class Server{
|
||||
|
||||
/** @var CommandReader */
|
||||
private $console = null;
|
||||
private $consoleThreaded;
|
||||
|
||||
/** @var SimpleCommandMap */
|
||||
private $commandMap = null;
|
||||
@ -153,6 +155,9 @@ class Server{
|
||||
/** @var int */
|
||||
private $maxPlayers;
|
||||
|
||||
/** @var bool */
|
||||
private $autoSave;
|
||||
|
||||
/** @var RCON */
|
||||
private $rcon;
|
||||
|
||||
@ -285,14 +290,31 @@ class Server{
|
||||
* @return string
|
||||
*/
|
||||
public function getIp(){
|
||||
return $this->getConfigString("server-ip", "");
|
||||
return $this->getConfigString("server-ip", "0.0.0.0");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getServerName(){
|
||||
return $this->getConfigString("server-name", "Unknown server");
|
||||
return $this->getConfigString("motd", "Unknown server");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function getAutoSave(){
|
||||
return $this->autoSave;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setAutoSave($value){
|
||||
$this->autoSave = (bool) $value;
|
||||
foreach($this->getLevels() as $level){
|
||||
$level->setAutoSave($this->autoSave);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -893,7 +915,7 @@ class Server{
|
||||
* @return bool
|
||||
*/
|
||||
public function unloadLevel(Level $level, $forceUnload = false){
|
||||
if($level->unload($forceUnload) === true and $this->isLevelLoaded($level->getFolderName())){
|
||||
if($level->unload($forceUnload) === true){
|
||||
unset($this->levels[$level->getID()]);
|
||||
|
||||
return true;
|
||||
@ -947,6 +969,8 @@ class Server{
|
||||
|
||||
$this->levels[$level->getID()] = $level;
|
||||
|
||||
$level->initLevel();
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
|
||||
|
||||
/*foreach($entities->getAll() as $entity){
|
||||
@ -1080,6 +1104,8 @@ class Server{
|
||||
$level = new Level($this, $name, $path, $provider);
|
||||
$this->levels[$level->getID()] = $level;
|
||||
|
||||
$level->initLevel();
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelInitEvent($level));
|
||||
|
||||
$this->getPluginManager()->callEvent(new LevelLoadEvent($level));
|
||||
@ -1097,7 +1123,7 @@ class Server{
|
||||
|
||||
for($X = -$radius; $X <= $radius; ++$X){
|
||||
for($Z = -$radius; $Z <= $radius; ++$Z){
|
||||
$distance = ($X * $X) + ($Z * $Z);
|
||||
$distance = $X ** 2 + $Z ** 2;
|
||||
if($distance > $radiusSquared){
|
||||
continue;
|
||||
}
|
||||
@ -1424,7 +1450,8 @@ class Server{
|
||||
$this->banByIP = new BanList($this->dataPath . "banned-ips.txt");
|
||||
$this->banByIP->load();
|
||||
|
||||
$this->console = new CommandReader();
|
||||
$this->consoleThreaded = new \Threaded();
|
||||
$this->console = new CommandReader($this->consoleThreaded);
|
||||
|
||||
$version = new VersionString($this->getPocketMineVersion());
|
||||
$this->logger->info("Starting Minecraft: PE server version " . TextFormat::AQUA . $this->getVersion());
|
||||
@ -1475,6 +1502,7 @@ class Server{
|
||||
}
|
||||
|
||||
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
||||
$this->setAutoSave($this->getConfigBoolean("auto-save", true));
|
||||
|
||||
if(($memory = str_replace("B", "", strtoupper($this->getConfigString("memory-limit", "256M")))) !== false){
|
||||
$value = ["M" => 1, "G" => 1024];
|
||||
@ -1500,7 +1528,7 @@ class Server{
|
||||
$this->logger->info("Advanced cache enabled");
|
||||
}
|
||||
|
||||
Level::$COMPRESSION_LEVEL = $this->getProperty("chunk-sending.compression-level", 7);
|
||||
Level::$COMPRESSION_LEVEL = $this->getProperty("chunk-sending.compression-level", 8);
|
||||
|
||||
if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0){
|
||||
@cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion());
|
||||
@ -1541,7 +1569,11 @@ class Server{
|
||||
|
||||
$this->enablePlugins(PluginLoadOrder::STARTUP);
|
||||
|
||||
$this->generationManager = new GenerationRequestManager($this);
|
||||
if($this->getProperty("chunk-generation.use-async", true)){
|
||||
$this->generationManager = new GenerationRequestManager($this);
|
||||
}else{
|
||||
$this->generationManager = new GenerationInstanceManager($this);
|
||||
}
|
||||
|
||||
LevelProviderManager::addProvider($this, Anvil::class);
|
||||
LevelProviderManager::addProvider($this, McRegion::class);
|
||||
@ -1552,7 +1584,7 @@ class Server{
|
||||
Generator::addGenerator(Normal::class, "default");
|
||||
|
||||
//Temporal workaround, pthreads static property nullification test
|
||||
if(PluginManager::$pluginParentTimer === null){
|
||||
if(PluginManager::$pluginParentTimer === null or Timings::$serverTickTimer === null){
|
||||
$this->getLogger()->emergency("You are using an invalid pthreads version. Please update your binaries.");
|
||||
kill(getmypid());
|
||||
return;
|
||||
@ -1601,7 +1633,7 @@ class Server{
|
||||
}
|
||||
|
||||
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([Cache::class, "cleanup"]), $this->getProperty("ticks-per.cache-cleanup", 900), $this->getProperty("ticks-per.cache-cleanup", 900));
|
||||
if($this->getConfigBoolean("auto-save", true) === true and $this->getProperty("ticks-per.autosave", 6000) > 0){
|
||||
if($this->getAutoSave() and $this->getProperty("ticks-per.autosave", 6000) > 0){
|
||||
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([$this, "doAutoSave"]), $this->getProperty("ticks-per.autosave", 6000), $this->getProperty("ticks-per.autosave", 6000));
|
||||
}
|
||||
|
||||
@ -1616,11 +1648,17 @@ class Server{
|
||||
|
||||
/**
|
||||
* @param $message
|
||||
* @param Player[]|null $recipients
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function broadcastMessage($message){
|
||||
return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
|
||||
public function broadcastMessage($message, $recipients = null){
|
||||
if(!is_array($recipients)){
|
||||
return $this->broadcast($message, self::BROADCAST_CHANNEL_USERS);
|
||||
}
|
||||
foreach($recipients as $recipient){
|
||||
$recipient->sendMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1924,7 +1962,7 @@ class Server{
|
||||
}elseif(\Phar::running(true) == ""){
|
||||
return;
|
||||
}
|
||||
if($dump->getData()["type"] === "E_PARSE" or $dump->getData()["type"] === "E_COMPILE_ERROR"){
|
||||
if($dump->getData()["error"]["type"] === "E_PARSE" or $dump->getData()["error"]["type"] === "E_COMPILE_ERROR"){
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1972,19 +2010,21 @@ class Server{
|
||||
}
|
||||
|
||||
public function doAutoSave(){
|
||||
Timings::$worldSaveTimer->startTiming();
|
||||
foreach($this->getOnlinePlayers() as $index => $player){
|
||||
if($player->isOnline()){
|
||||
$player->save();
|
||||
}elseif(!$player->isConnected()){
|
||||
unset($this->players[$index]);
|
||||
if($this->getAutoSave()){
|
||||
Timings::$worldSaveTimer->startTiming();
|
||||
foreach($this->getOnlinePlayers() as $index => $player){
|
||||
if($player->isOnline()){
|
||||
$player->save();
|
||||
}elseif(!$player->isConnected()){
|
||||
unset($this->players[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getLevels() as $level){
|
||||
$level->save(false);
|
||||
foreach($this->getLevels() as $level){
|
||||
$level->save(false);
|
||||
}
|
||||
Timings::$worldSaveTimer->stopTiming();
|
||||
}
|
||||
Timings::$worldSaveTimer->stopTiming();
|
||||
}
|
||||
|
||||
public function doLevelGC(){
|
||||
@ -2068,7 +2108,7 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
$this->generationManager->handlePackets();
|
||||
$this->generationManager->process();
|
||||
|
||||
Timings::$serverTickTimer->stopTiming();
|
||||
|
||||
|
@ -49,7 +49,10 @@ class Beetroot extends Flowable{
|
||||
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($item->getID() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
$this->meta = 0x07;
|
||||
$this->meta += mt_rand(2, 5);
|
||||
if($this->meta > 7){
|
||||
$this->meta = 7;
|
||||
}
|
||||
$this->getLevel()->setBlock($this, $this, true, true);
|
||||
$item->count--;
|
||||
|
||||
|
@ -167,7 +167,8 @@ abstract class Block extends Position implements Metadatable{
|
||||
const MELON_BLOCK = 103;
|
||||
const PUMPKIN_STEM = 104;
|
||||
const MELON_STEM = 105;
|
||||
|
||||
const VINE = 106;
|
||||
const VINES = 106;
|
||||
const FENCE_GATE = 107;
|
||||
const BRICK_STAIRS = 108;
|
||||
const STONE_BRICK_STAIRS = 109;
|
||||
@ -345,7 +346,7 @@ abstract class Block extends Position implements Metadatable{
|
||||
[Item::SNOW_LAYER, 0],
|
||||
[Item::GLASS, 0],
|
||||
[Item::GLOWSTONE_BLOCK, 0],
|
||||
//TODO: Vines
|
||||
[Item::VINES, 0],
|
||||
[Item::NETHER_REACTOR, 0],
|
||||
[Item::LADDER, 0],
|
||||
[Item::SPONGE, 0],
|
||||
@ -621,7 +622,7 @@ abstract class Block extends Position implements Metadatable{
|
||||
self::MELON_BLOCK => Melon::class,
|
||||
self::PUMPKIN_STEM => PumpkinStem::class,
|
||||
self::MELON_STEM => MelonStem::class,
|
||||
|
||||
self::VINE => Vine::class,
|
||||
self::FENCE_GATE => FenceGate::class,
|
||||
self::BRICK_STAIRS => BrickStairs::class,
|
||||
self::STONE_BRICK_STAIRS => StoneBrickStairs::class,
|
||||
@ -686,7 +687,10 @@ abstract class Block extends Position implements Metadatable{
|
||||
}
|
||||
|
||||
if($pos instanceof Position){
|
||||
$block->position($pos);
|
||||
$block->x = $pos->x;
|
||||
$block->y = $pos->y;
|
||||
$block->z = $pos->z;
|
||||
$block->level = $pos->level;
|
||||
}
|
||||
|
||||
return $block;
|
||||
@ -724,6 +728,10 @@ abstract class Block extends Position implements Metadatable{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function addVelocityToEntity(Entity $entity, Vector3 $vector){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
|
@ -55,13 +55,13 @@ class BurningFurnace extends Solid{
|
||||
new Int("z", $this->z)
|
||||
]);
|
||||
$nbt->Items->setTagType(NBT::TAG_Compound);
|
||||
new Furnace($this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4), $nbt);
|
||||
new Furnace($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onBreak(Item $item){
|
||||
$this->getLevel()->setBlock($this, new Air(), true, true, true);
|
||||
$this->getLevel()->setBlock($this, new Air(), true, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -81,7 +81,7 @@ class BurningFurnace extends Solid{
|
||||
new Int("z", $this->z)
|
||||
]);
|
||||
$nbt->Items->setTagType(NBT::TAG_Compound);
|
||||
$furnace = new Furnace($this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4), $nbt);
|
||||
$furnace = new Furnace($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt);
|
||||
}
|
||||
|
||||
if(($player->getGamemode() & 0x01) === 0x01){
|
||||
@ -116,15 +116,6 @@ class BurningFurnace extends Solid{
|
||||
if($item->isPickaxe() >= 1){
|
||||
$drops[] = [Item::FURNACE, 0, 1];
|
||||
}
|
||||
$t = $this->getLevel()->getTile($this);
|
||||
if($t instanceof Furnace){
|
||||
for($s = 0; $s < $t->getInventory()->getSize(); ++$s){
|
||||
$slot = $t->getInventory()->getItem($s);
|
||||
if($slot->getID() > Item::AIR and $slot->getCount() > 0){
|
||||
$drops[] = [$slot->getID(), $slot->getDamage(), $slot->getCount()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $drops;
|
||||
}
|
||||
|
@ -50,7 +50,10 @@ class Carrot extends Flowable{
|
||||
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($item->getID() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
$this->meta = 0x07;
|
||||
$this->meta += mt_rand(2, 5);
|
||||
if($this->meta > 7){
|
||||
$this->meta = 7;
|
||||
}
|
||||
$this->getLevel()->setBlock($this, $this, true);
|
||||
$item->count--;
|
||||
|
||||
|
@ -89,7 +89,7 @@ class Chest extends Transparent{
|
||||
new Int("z", $this->z)
|
||||
]);
|
||||
$nbt->Items->setTagType(NBT::TAG_Compound);
|
||||
$tile = new TileChest($this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4), $nbt);
|
||||
$tile = new TileChest($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt);
|
||||
|
||||
if($chest instanceof TileChest){
|
||||
$chest->pairWith($tile);
|
||||
@ -104,7 +104,7 @@ class Chest extends Transparent{
|
||||
if($t instanceof TileChest){
|
||||
$t->unpair();
|
||||
}
|
||||
$this->getLevel()->setBlock($this, new Air(), true, true, true);
|
||||
$this->getLevel()->setBlock($this, new Air(), true, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -129,7 +129,7 @@ class Chest extends Transparent{
|
||||
new Int("z", $this->z)
|
||||
]);
|
||||
$nbt->Items->setTagType(NBT::TAG_Compound);
|
||||
$chest = new TileChest($this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4), $nbt);
|
||||
$chest = new TileChest($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), $nbt);
|
||||
}
|
||||
|
||||
|
||||
@ -143,19 +143,8 @@ class Chest extends Transparent{
|
||||
}
|
||||
|
||||
public function getDrops(Item $item){
|
||||
$drops = [
|
||||
return [
|
||||
[$this->id, 0, 1],
|
||||
];
|
||||
$t = $this->getLevel()->getTile($this);
|
||||
if($t instanceof TileChest){
|
||||
for($s = 0; $s < $t->getRealInventory()->getSize(); ++$s){
|
||||
$slot = $t->getRealInventory()->getItem($s);
|
||||
if($slot->getID() > Item::AIR and $slot->getCount() > 0){
|
||||
$drops[] = [$slot->getID(), $slot->getDamage(), $slot->getCount()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $drops;
|
||||
}
|
||||
}
|
@ -31,7 +31,7 @@ use pocketmine\nbt\tag\Double;
|
||||
use pocketmine\nbt\tag\Float;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
|
||||
class Fallable extends Solid{
|
||||
abstract class Fallable extends Solid{
|
||||
|
||||
public $hasPhysics = true;
|
||||
|
||||
@ -45,13 +45,12 @@ class Fallable extends Solid{
|
||||
if($this->hasPhysics === true and $type === Level::BLOCK_UPDATE_NORMAL){
|
||||
$down = $this->getSide(0);
|
||||
if($down->getID() === self::AIR or ($down instanceof Liquid)){
|
||||
$fall = new FallingBlock($this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4), new Compound("", [
|
||||
$fall = new FallingBlock($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [
|
||||
"Pos" => new Enum("Pos", [
|
||||
new Double("", $this->x + 0.5),
|
||||
new Double("", $this->y + 0.5),
|
||||
new Double("", $this->z + 0.5)
|
||||
]),
|
||||
//TODO: add random motion with physics
|
||||
"Motion" => new Enum("Motion", [
|
||||
new Double("", 0),
|
||||
new Double("", 0),
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\block;
|
||||
|
||||
|
||||
class Flowable extends Transparent{
|
||||
abstract class Flowable extends Transparent{
|
||||
|
||||
public $isFlowable = true;
|
||||
public $isFullBlock = false;
|
||||
|
@ -40,131 +40,20 @@ class Lava extends Liquid{
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$entity->fallDistance *= 0.5;
|
||||
$entity->setOnFire(15);
|
||||
$ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
if(!$ev->isCancelled()){
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$entity->attack(4, EntityDamageEvent::CAUSE_LAVA);
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){
|
||||
$ret = $this->getLevel()->setBlock($this, $this, true);
|
||||
$this->getLevel()->scheduleUpdate(clone $this, 40);
|
||||
$ret = $this->getLevel()->setBlock($this, $this, true, false);
|
||||
$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getSourceCount(){
|
||||
$count = 0;
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
if($this->getSide($side) instanceof Lava){
|
||||
$b = $this->getSide($side);
|
||||
$level = $b->meta & 0x07;
|
||||
if($level == 0x00){
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function checkWater(){
|
||||
for($side = 1; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Water){
|
||||
$level = $this->meta & 0x07;
|
||||
if($level == 0x00){
|
||||
$this->getLevel()->setBlock($this, new Obsidian(), false, false, true);
|
||||
}else{
|
||||
$this->getLevel()->setBlock($this, new Cobblestone(), false, false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getFrom(){
|
||||
for($side = 0; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Lava){
|
||||
$tlevel = $b->meta & 0x07;
|
||||
$level = $this->meta & 0x07;
|
||||
if(($tlevel + 2) == $level || ($side == 0x01 && $level == 0x01) || ($tlevel == 6 && $level == 7)){
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onUpdate($type){
|
||||
return false;
|
||||
$newId = $this->id;
|
||||
$level = $this->meta & 0x07;
|
||||
if($type !== Level::BLOCK_UPDATE_NORMAL){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->checkWater()){
|
||||
return;
|
||||
}
|
||||
|
||||
$falling = $this->meta >> 3;
|
||||
$down = $this->getSide(0);
|
||||
|
||||
$from = $this->getFrom();
|
||||
if($from !== null || $level == 0x00){
|
||||
if($level !== 0x07){
|
||||
if($down instanceof Air || $down instanceof Lava){
|
||||
$this->getLevel()->setBlock($down, new Lava(0x01), false, false, true);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(new Position($down, 0, 0, $this->level), 40, Level::BLOCK_UPDATE_NORMAL);
|
||||
}else{
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Lava){
|
||||
|
||||
}elseif($b->isFlowable === true){
|
||||
$this->getLevel()->setBlock($b, new Lava(min($level + 2, 7)), false, false, true);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 40, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//Extend Remove for Left Lavas
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$sb = $this->getSide($side);
|
||||
if($sb instanceof Lava){
|
||||
$tlevel = $sb->meta & 0x07;
|
||||
if($tlevel != 0x00){
|
||||
for($s = 0; $s <= 5; $s++){
|
||||
$ssb = $sb->getSide($s);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
$this->getLevel()->setBlock($sb, new Air(), false, false, true);
|
||||
}
|
||||
}
|
||||
$b = $this->getSide(0)->getSide($side);
|
||||
if($b instanceof Lava){
|
||||
$tlevel = $b->meta & 0x07;
|
||||
if($tlevel != 0x00){
|
||||
for($s = 0; $s <= 5; $s++){
|
||||
$ssb = $sb->getSide($s);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 40, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
$this->getLevel()->setBlock($b, new Air(), false, false, true);
|
||||
}
|
||||
}
|
||||
//Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
|
||||
$this->getLevel()->setBlock($this, new Air(), false, false, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,13 +22,22 @@
|
||||
namespace pocketmine\block;
|
||||
|
||||
|
||||
class Liquid extends Transparent{
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
public $isLiquid = true;
|
||||
public $breakable = false;
|
||||
public $isReplaceable = true;
|
||||
public $isSolid = false;
|
||||
public $isFullBlock = true;
|
||||
|
||||
public $adjacentSources = 0;
|
||||
public $isOptimalFlowDirection = [0, 0, 0];
|
||||
public $flowCost = [0, 0, 0];
|
||||
|
||||
public function getFluidHeightPercent(){
|
||||
$d = $this->meta;
|
||||
if($d >= 8){
|
||||
@ -37,4 +46,366 @@ class Liquid extends Transparent{
|
||||
|
||||
return ($d + 1) / 9;
|
||||
}
|
||||
|
||||
protected function getFlowDecay(Vector3 $pos){
|
||||
if(!($pos instanceof Block)){
|
||||
$pos = $this->getLevel()->getBlock($pos);
|
||||
}
|
||||
|
||||
if($pos->getID() !== $this->getID()){
|
||||
return -1;
|
||||
}else{
|
||||
return $pos->getDamage();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEffectiveFlowDecay(Vector3 $pos){
|
||||
if(!($pos instanceof Block)){
|
||||
$pos = $this->getLevel()->getBlock($pos);
|
||||
}
|
||||
|
||||
if($pos->getID() !== $this->getID()){
|
||||
return -1;
|
||||
}
|
||||
|
||||
$decay = $pos->getDamage();
|
||||
|
||||
if($decay >= 8){
|
||||
$decay = 0;
|
||||
}
|
||||
|
||||
return $decay;
|
||||
}
|
||||
|
||||
public function getFlowVector(){
|
||||
$vector = new Vector3(0, 0, 0);
|
||||
|
||||
$decay = $this->getEffectiveFlowDecay($this);
|
||||
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
|
||||
$x = $this->x;
|
||||
$y = $this->y;
|
||||
$z = $this->z;
|
||||
|
||||
if($j === 0){
|
||||
--$x;
|
||||
}elseif($j === 1){
|
||||
++$x;
|
||||
}elseif($j === 2){
|
||||
--$z;
|
||||
}elseif($j === 3){
|
||||
++$z;
|
||||
}
|
||||
$sideBlock = $this->getLevel()->getBlock(new Vector3($x, $y, $z));
|
||||
$blockDecay = $this->getEffectiveFlowDecay($sideBlock);
|
||||
|
||||
if($blockDecay < 0){
|
||||
if(!$sideBlock->isFlowable){
|
||||
continue;
|
||||
}
|
||||
|
||||
$blockDecay = $this->getEffectiveFlowDecay($sideBlock->getSide(0));
|
||||
|
||||
if($blockDecay >= 0){
|
||||
$realDecay = $blockDecay - ($decay - 8);
|
||||
$vector = $vector->add(($sideBlock->x - $this->x) * $realDecay, ($sideBlock->y - $this->y) * $realDecay, ($sideBlock->z - $this->z) * $realDecay);
|
||||
}
|
||||
|
||||
continue;
|
||||
}else{
|
||||
$realDecay = $blockDecay - $decay;
|
||||
$vector = $vector->add(($sideBlock->x - $this->x) * $realDecay, ($sideBlock->y - $this->y) * $realDecay, ($sideBlock->z - $this->z) * $realDecay);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->getDamage() >= 8){
|
||||
$falling = false;
|
||||
|
||||
if(!$this->getLevel()->getBlock($this->add(0, 0, -1))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(0, 0, 1))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(-1, 0, 0))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(1, 0, 0))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(0, 1, -1))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(0, 1, 1))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(-1, 1, 0))->isFlowable){
|
||||
$falling = true;
|
||||
}elseif(!$this->getLevel()->getBlock($this->add(1, 1, 0))->isFlowable){
|
||||
$falling = true;
|
||||
}
|
||||
|
||||
if($falling){
|
||||
$vector = $vector->normalize()->add(0, -6, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $vector->normalize();
|
||||
}
|
||||
|
||||
public function addVelocityToEntity(Entity $entity, Vector3 $vector){
|
||||
$flow = $this->getFlowVector();
|
||||
$vector->x += $flow->x;
|
||||
$vector->y += $flow->y;
|
||||
$vector->z += $flow->z;
|
||||
}
|
||||
|
||||
public function tickRate(){
|
||||
if($this instanceof Water){
|
||||
return 5;
|
||||
}elseif($this instanceof Lava){
|
||||
return 30;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function onUpdate($type){
|
||||
if($type === Level::BLOCK_UPDATE_NORMAL){
|
||||
$this->checkForHarden();
|
||||
$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
}elseif($type === Level::BLOCK_UPDATE_SCHEDULED){
|
||||
$decay = $this->getFlowDecay($this);
|
||||
$multiplier = $this instanceof Lava ? 2 : 1;
|
||||
|
||||
$flag = true;
|
||||
|
||||
if($decay > 0){
|
||||
$smallestFlowDecay = -100;
|
||||
$this->adjacentSources = 0;
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->getSide(4), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->getSide(5), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->getSide(2), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($this->getSide(3), $smallestFlowDecay);
|
||||
|
||||
$k = $smallestFlowDecay + $multiplier;
|
||||
|
||||
if($k >= 8 or $smallestFlowDecay < 0){
|
||||
$k = -1;
|
||||
}
|
||||
|
||||
if(($topFlowDecay = $this->getFlowDecay($this->getSide(1))) >= 0){
|
||||
if($topFlowDecay >= 8){
|
||||
$k = $topFlowDecay;
|
||||
}else{
|
||||
$k = $topFlowDecay | 0x08;
|
||||
}
|
||||
}
|
||||
|
||||
if($this->adjacentSources >= 2 and $this instanceof Water){
|
||||
$bottomBlock = $this->getSide(0);
|
||||
if($bottomBlock->isSolid){
|
||||
$k = 0;
|
||||
}elseif($bottomBlock instanceof Water and $bottomBlock->getDamage() === 0){
|
||||
$k = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if($this instanceof Lava and $decay < 8 and $k < 8 and $k > 1 and mt_rand(0, 4) !== 0){
|
||||
$k = $decay;
|
||||
$flag = false;
|
||||
}
|
||||
|
||||
if($k !== $decay){
|
||||
$decay = $k;
|
||||
if($decay < 0){
|
||||
$this->getLevel()->setBlock($this, Block::get(Item::AIR), true);
|
||||
}else{
|
||||
$this->getLevel()->setBlock($this, Block::get($this->id, $decay), true);
|
||||
$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
}
|
||||
}elseif($flag){
|
||||
$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
//$this->updateFlow();
|
||||
}
|
||||
}else{
|
||||
//$this->updateFlow();
|
||||
}
|
||||
|
||||
$bottomBlock = $this->getSide(0);
|
||||
|
||||
if($bottomBlock->isFlowable or $bottomBlock instanceof Liquid){
|
||||
if($this instanceof Lava and $bottomBlock instanceof Water){
|
||||
$this->getLevel()->setBlock($bottomBlock, Block::get(Item::STONE), true);
|
||||
return;
|
||||
}
|
||||
|
||||
if($decay >= 8){
|
||||
$this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay), true);
|
||||
$this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate());
|
||||
}else{
|
||||
$this->getLevel()->setBlock($bottomBlock, Block::get($this->id, $decay + 8), true);
|
||||
$this->getLevel()->scheduleUpdate($bottomBlock, $this->tickRate());
|
||||
}
|
||||
}elseif($decay >= 0 and ($decay === 0 or !$bottomBlock->isFlowable)){
|
||||
$flags = $this->getOptimalFlowDirections();
|
||||
|
||||
$l = $decay + $multiplier;
|
||||
|
||||
if($decay >= 8){
|
||||
$l = 1;
|
||||
}
|
||||
|
||||
if($l >= 8){
|
||||
$this->checkForHarden();
|
||||
return;
|
||||
}
|
||||
|
||||
if($flags[0]){
|
||||
$this->flowIntoBlock($this->getSide(4), $l);
|
||||
}
|
||||
|
||||
if($flags[1]){
|
||||
$this->flowIntoBlock($this->getSide(5), $l);
|
||||
}
|
||||
|
||||
if($flags[2]){
|
||||
$this->flowIntoBlock($this->getSide(2), $l);
|
||||
}
|
||||
|
||||
if($flags[3]){
|
||||
$this->flowIntoBlock($this->getSide(3), $l);
|
||||
}
|
||||
}
|
||||
|
||||
$this->checkForHarden();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private function flowIntoBlock(Block $block, $newFlowDecay){
|
||||
if($block->isFlowable){
|
||||
if($block->getID() > 0){
|
||||
$this->getLevel()->useBreakOn($block);
|
||||
}
|
||||
|
||||
$this->getLevel()->setBlock($block, Block::get($this->id, $newFlowDecay), true);
|
||||
$this->getLevel()->scheduleUpdate($block, $this->tickRate());
|
||||
}
|
||||
}
|
||||
|
||||
private function calculateFlowCost(Block $block, $accumulatedCost, $previousDirection){
|
||||
$cost = 1000;
|
||||
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
if(
|
||||
($j === 0 and $previousDirection === 1) or
|
||||
($j === 1 and $previousDirection === 0) or
|
||||
($j === 2 and $previousDirection === 3) or
|
||||
($j === 3 and $previousDirection === 2)
|
||||
){
|
||||
$x = $block->x;
|
||||
$y = $block->y;
|
||||
$z = $block->z;
|
||||
|
||||
if($j === 0){
|
||||
--$x;
|
||||
}elseif($j === 1){
|
||||
++$x;
|
||||
}elseif($j === 2){
|
||||
--$z;
|
||||
}elseif($j === 3){
|
||||
++$z;
|
||||
}
|
||||
$blockSide = $this->getLevel()->getBlock(new Vector3($x, $y, $z));
|
||||
|
||||
if(!$blockSide->isFlowable or ($blockSide instanceof Liquid and $blockSide->getDamage() === 0)){
|
||||
continue;
|
||||
}elseif($blockSide->getSide(0)->isFlowable){
|
||||
return $accumulatedCost;
|
||||
}
|
||||
|
||||
if($accumulatedCost >= 4){
|
||||
continue;
|
||||
}
|
||||
|
||||
$realCost = $this->calculateFlowCost($blockSide, $accumulatedCost + 1, $j);
|
||||
|
||||
if($realCost < $cost){
|
||||
$cost = $realCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $cost;
|
||||
}
|
||||
|
||||
private function getOptimalFlowDirections(){
|
||||
for($j = 0; $j < 4; ++$j){
|
||||
$this->flowCost[$j] = 1000;
|
||||
|
||||
$x = $this->x;
|
||||
$y = $this->y;
|
||||
$z = $this->z;
|
||||
|
||||
if($j === 0){
|
||||
--$x;
|
||||
}elseif($j === 1){
|
||||
++$x;
|
||||
}elseif($j === 2){
|
||||
--$z;
|
||||
}elseif($j === 3){
|
||||
++$z;
|
||||
}
|
||||
$block = $this->getLevel()->getBlock(new Vector3($x, $y, $z));
|
||||
|
||||
if(!$block->isFlowable or ($block instanceof Liquid and $block->getDamage() === 0)){
|
||||
continue;
|
||||
}elseif($block->getSide(0)->isFlowable){
|
||||
$this->flowCost[$j] = 0;
|
||||
}else{
|
||||
$this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j);
|
||||
}
|
||||
}
|
||||
|
||||
$minCost = $this->flowCost[0];
|
||||
|
||||
for($i = 1; $i < 4; ++$i){
|
||||
if($this->flowCost[$i] < $minCost){
|
||||
$minCost = $this->flowCost[$i];
|
||||
}
|
||||
}
|
||||
|
||||
for($i = 0; $i < 4; ++$i){
|
||||
$this->isOptimalFlowDirection[$i] = ($this->flowCost[$i] === $minCost);
|
||||
}
|
||||
|
||||
return $this->isOptimalFlowDirection;
|
||||
}
|
||||
|
||||
private function getSmallestFlowDecay(Vector3 $pos, $decay){
|
||||
$blockDecay = $this->getFlowDecay($pos);
|
||||
|
||||
if($blockDecay < 0){
|
||||
return $decay;
|
||||
}elseif($blockDecay === 0){
|
||||
++$this->adjacentSources;
|
||||
}elseif($blockDecay >= 8){
|
||||
$blockDecay = 0;
|
||||
}
|
||||
|
||||
return ($decay >= 0 && $blockDecay >= $decay) ? $decay : $blockDecay;
|
||||
}
|
||||
|
||||
private function checkForHarden(){
|
||||
if($this instanceof Lava){
|
||||
$colliding = false;
|
||||
for($side = 0; $side <= 5 and !$colliding; ++$side){
|
||||
$colliding = $this->getSide($side) instanceof Water;
|
||||
}
|
||||
|
||||
if($colliding){
|
||||
if($this->getDamage() === 0){
|
||||
$this->getLevel()->setBlock($this, Block::get(Item::OBSIDIAN), true);
|
||||
}elseif($this->getDamage() <= 4){
|
||||
$this->getLevel()->setBlock($this, Block::get(Item::COBBLESTONE), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,10 @@ class Potato extends Flowable{
|
||||
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($item->getID() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
$this->meta = 0x07;
|
||||
$this->meta += mt_rand(2, 5);
|
||||
if($this->meta > 7){
|
||||
$this->meta = 7;
|
||||
}
|
||||
$this->getLevel()->setBlock($this, $this, true);
|
||||
if(($player->gamemode & 0x01) === 0){
|
||||
$item->count--;
|
||||
|
@ -22,7 +22,7 @@
|
||||
namespace pocketmine\block;
|
||||
|
||||
|
||||
class Solid extends Generic{
|
||||
abstract class Solid extends Generic{
|
||||
|
||||
public function __construct($id, $meta = 0, $name = "Unknown"){
|
||||
parent::__construct($id, $meta, $name);
|
||||
|
@ -25,7 +25,7 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Stair extends Transparent{
|
||||
abstract class Stair extends Transparent{
|
||||
|
||||
public function __construct($id, $meta = 0, $name = "Unknown"){
|
||||
parent::__construct($id, $meta, $name);
|
||||
|
@ -25,7 +25,7 @@ use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class StillLava extends Liquid{
|
||||
class StillLava extends Lava{
|
||||
public function __construct($meta = 0){
|
||||
parent::__construct(self::STILL_LAVA, $meta, "Still Lava");
|
||||
$this->hardness = 500;
|
||||
@ -35,14 +35,4 @@ class StillLava extends Liquid{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$entity->setOnFire(15);
|
||||
$ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
if(!$ev->isCancelled()){
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$entity->attack(4, EntityDamageEvent::CAUSE_LAVA);
|
||||
}
|
||||
|
||||
}
|
@ -21,8 +21,16 @@
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\PrimedTNT;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Explosion;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Double;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Float;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class TNT extends Solid{
|
||||
public function __construct(){
|
||||
@ -33,9 +41,8 @@ class TNT extends Solid{
|
||||
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($item->getID() === Item::FLINT_STEEL){
|
||||
if(($player->gamemode & 0x01) === 0){
|
||||
$item->useOn($this);
|
||||
}
|
||||
$item->useOn($this);
|
||||
|
||||
$data = [
|
||||
"x" => $this->x + 0.5,
|
||||
"y" => $this->y + 0.5,
|
||||
@ -44,9 +51,27 @@ class TNT extends Solid{
|
||||
"fuse" => 20 * 4, //4 seconds
|
||||
];
|
||||
$this->getLevel()->setBlock($this, new Air(), false, false, true);
|
||||
//TODO
|
||||
//$e = Server::getInstance()->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data);
|
||||
//$e->spawnToAll();
|
||||
|
||||
$mot = (new Random())->nextSignedFloat() * M_PI * 2;
|
||||
$tnt = new PrimedTNT($this->getLevel()->getChunk($this->x >> 4, $this->z >> 4), new Compound("", [
|
||||
"Pos" => new Enum("Pos", [
|
||||
new Double("", $this->x + 0.5),
|
||||
new Double("", $this->y + 0.5),
|
||||
new Double("", $this->z + 0.5)
|
||||
]),
|
||||
"Motion" => new Enum("Motion", [
|
||||
new Double("", -sin($mot) * 0.02),
|
||||
new Double("", 0.2),
|
||||
new Double("", -cos($mot) * 0.02)
|
||||
]),
|
||||
"Rotation" => new Enum("Rotation", [
|
||||
new Float("", 0),
|
||||
new Float("", 0)
|
||||
]),
|
||||
"Fuse" => new Byte("Fuse", 80)
|
||||
]));
|
||||
|
||||
$tnt->spawnToAll();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
|
||||
class Thin extends Transparent{
|
||||
abstract class Thin extends Transparent{
|
||||
|
||||
public $isFullBlock = false;
|
||||
public $isSolid = false;
|
||||
|
@ -50,9 +50,8 @@ class Torch extends Flowable{
|
||||
];
|
||||
|
||||
if($this->getSide($faces[$side])->isTransparent === true and !($side === 0 and $this->getSide(0)->getID() === self::FENCE)){ //Replace with common break method
|
||||
//TODO
|
||||
//Server::getInstance()->api->entity->drop($this, Item::get($this->id, 0, 1));
|
||||
$this->getLevel()->setBlock($this, new Air(), true);
|
||||
$this->getLevel()->dropItem($this->add(0.5, 0.5, 0.5), Item::get(Item::TORCH));
|
||||
|
||||
return Level::BLOCK_UPDATE_NORMAL;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ namespace pocketmine\block;
|
||||
|
||||
|
||||
|
||||
class Transparent extends Generic{
|
||||
abstract class Transparent extends Generic{
|
||||
public $isActivable = false;
|
||||
public $breakable = true;
|
||||
public $isFlowable = false;
|
||||
|
168
src/pocketmine/block/Vine.php
Normal file
168
src/pocketmine/block/Vine.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\Tool;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class Vine extends Transparent{
|
||||
public function __construct($meta = 0){
|
||||
parent::__construct(self::VINE, $meta, "Vines");
|
||||
$this->isSolid = false;
|
||||
$this->isFullBlock = false;
|
||||
$this->hardness = 1;
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$entity->fallDistance = 0;
|
||||
}
|
||||
|
||||
public function getBoundingBox(){
|
||||
$f1 = 1;
|
||||
$f2 = 1;
|
||||
$f3 = 1;
|
||||
$f4 = 0;
|
||||
$f5 = 0;
|
||||
$f6 = 0;
|
||||
|
||||
$flag = $this->meta > 0;
|
||||
|
||||
if(($this->meta & 0x02) > 0){
|
||||
$f4 = max($f4, 0.0625);
|
||||
$f1 = 0;
|
||||
$f2 = 0;
|
||||
$f5 = 1;
|
||||
$f3 = 0;
|
||||
$f6 = 1;
|
||||
$flag = true;
|
||||
}
|
||||
|
||||
if(($this->meta & 0x08) > 0){
|
||||
$f1 = min($f1, 0.9375);
|
||||
$f4 = 1;
|
||||
$f2 = 0;
|
||||
$f5 = 1;
|
||||
$f3 = 0;
|
||||
$f6 = 1;
|
||||
$flag = true;
|
||||
}
|
||||
|
||||
if(($this->meta & 0x01) > 0){
|
||||
$f3 = min($f3, 0.9375);
|
||||
$f6 = 1;
|
||||
$f1 = 0;
|
||||
$f4 = 1;
|
||||
$f2 = 0;
|
||||
$f5 = 1;
|
||||
$flag = true;
|
||||
}
|
||||
|
||||
if(!$flag and $this->getSide(1)->isSolid){
|
||||
$f2 = min($f2, 0.9375);
|
||||
$f5 = 1;
|
||||
$f1 = 0;
|
||||
$f4 = 1;
|
||||
$f3 = 0;
|
||||
$f6 = 1;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(
|
||||
$this->x + $f1,
|
||||
$this->y + $f2,
|
||||
$this->z + $f3,
|
||||
$this->x + $f4,
|
||||
$this->y + $f5,
|
||||
$this->z + $f6
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){
|
||||
if($target->isSolid){
|
||||
$faces = [
|
||||
0 => 0,
|
||||
1 => 0,
|
||||
2 => 1,
|
||||
3 => 4,
|
||||
4 => 8,
|
||||
5 => 2,
|
||||
];
|
||||
if(isset($faces[$face])){
|
||||
$this->meta = $faces[$face];
|
||||
$this->getLevel()->setBlock($block, $this, true, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getBreakTime(Item $item){
|
||||
if($item->isShears()){
|
||||
return 0.02;
|
||||
}elseif($item->isSword()){
|
||||
return 0.2;
|
||||
}elseif($item->isAxe()){
|
||||
switch($item->isAxe()){
|
||||
case Tool::TIER_WOODEN:
|
||||
return 0.15;
|
||||
case Tool::TIER_STONE:
|
||||
return 0.075;
|
||||
case Tool::TIER_IRON:
|
||||
return 0.05;
|
||||
case Tool::TIER_DIAMOND:
|
||||
return 0.0375;
|
||||
case Tool::TIER_GOLD:
|
||||
return 0.025;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.3;
|
||||
}
|
||||
|
||||
public function onUpdate($type){
|
||||
if($type === Level::BLOCK_UPDATE_NORMAL){
|
||||
/*if($this->getSide(0)->getID() === self::AIR){ //Replace with common break method
|
||||
Server::getInstance()->api->entity->drop($this, Item::get(LADDER, 0, 1));
|
||||
$this->getLevel()->setBlock($this, new Air(), true, true, true);
|
||||
return Level::BLOCK_UPDATE_NORMAL;
|
||||
}*/
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item){
|
||||
if($item->isShears()){
|
||||
return [
|
||||
[$this->id, 0, 1],
|
||||
];
|
||||
}else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
@ -44,127 +44,9 @@ class Water extends Liquid{
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){
|
||||
$ret = $this->getLevel()->setBlock($this, $this, true);
|
||||
$this->getLevel()->scheduleUpdate(clone $this, 10);
|
||||
$ret = $this->getLevel()->setBlock($this, $this, true, false);
|
||||
$this->getLevel()->scheduleUpdate($this, $this->tickRate());
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
public function getSourceCount(){
|
||||
$count = 0;
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
if($this->getSide($side) instanceof Water){
|
||||
$b = $this->getSide($side);
|
||||
$level = $b->meta & 0x07;
|
||||
if($level == 0x00){
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function checkLava(){
|
||||
for($side = 0; $side <= 5; ++$side){
|
||||
if($side == 1){
|
||||
continue;
|
||||
}
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Lava){
|
||||
$level = $b->meta & 0x07;
|
||||
if($level == 0x00){
|
||||
$this->getLevel()->setBlock($b, new Obsidian(), false, false, true);
|
||||
}else{
|
||||
$this->getLevel()->setBlock($b, new Cobblestone(), false, false, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFrom(){
|
||||
for($side = 0; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Water){
|
||||
$tlevel = $b->meta & 0x07;
|
||||
$level = $this->meta & 0x07;
|
||||
if(($tlevel + 1) == $level || ($side == 0x01 && $level == 0x01)){
|
||||
return $b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function onUpdate($type){
|
||||
return false;
|
||||
$newId = $this->id;
|
||||
$level = $this->meta & 0x07;
|
||||
if($type !== Level::BLOCK_UPDATE_NORMAL){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->checkLava();
|
||||
|
||||
$falling = $this->meta >> 3;
|
||||
$down = $this->getSide(0);
|
||||
|
||||
$from = $this->getFrom();
|
||||
//Has Source or Its Source
|
||||
if($from !== null || $level == 0x00){
|
||||
if($level !== 0x07){
|
||||
if($down instanceof Air || $down instanceof Water){
|
||||
$this->getLevel()->setBlock($down, new Water(0x01), false, false, true);
|
||||
//Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($down, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}else{
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$b = $this->getSide($side);
|
||||
if($b instanceof Water){
|
||||
if($this->getSourceCount() >= 2 && $level != 0x00){
|
||||
$this->getLevel()->setBlock($this, new Water(0), false, false, true);
|
||||
}
|
||||
}elseif($b->isFlowable === true){
|
||||
$this->getLevel()->setBlock($b, new Water($level + 1), false, false, true);
|
||||
//Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//Extend Remove for Left Waters
|
||||
for($side = 2; $side <= 5; ++$side){
|
||||
$sb = $this->getSide($side);
|
||||
if($sb instanceof Water){
|
||||
$tlevel = $sb->meta & 0x07;
|
||||
if($tlevel != 0x00){
|
||||
for($s = 0; $s <= 5; $s++){
|
||||
$ssb = $sb->getSide($s);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
$this->getLevel()->setBlock($sb, new Air(), false, false, true);
|
||||
}
|
||||
}
|
||||
$b = $this->getSide(0)->getSide($side);
|
||||
if($b instanceof Water){
|
||||
$tlevel = $b->meta & 0x07;
|
||||
if($tlevel != 0x00){
|
||||
for($s = 0; $s <= 5; $s++){
|
||||
$ssb = $sb->getSide($s);
|
||||
Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($ssb, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
$this->getLevel()->setBlock($b, new Air(), false, false, true);
|
||||
}
|
||||
}
|
||||
//Server::getInstance()->api->block->scheduleBlockUpdate(Position::fromObject($b, $this->level), 10, Level::BLOCK_UPDATE_NORMAL);
|
||||
}
|
||||
$this->getLevel()->setBlock($this, new Air(), false, false, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,10 @@ class Wheat extends Flowable{
|
||||
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($item->getID() === Item::DYE and $item->getDamage() === 0x0F){ //Bonemeal
|
||||
$this->meta = 0x07;
|
||||
$this->meta += mt_rand(2, 5);
|
||||
if($this->meta > 7){
|
||||
$this->meta = 7;
|
||||
}
|
||||
$this->getLevel()->setBlock($this, $this, true);
|
||||
if(($player->gamemode & 0x01) === 0){
|
||||
$item->count--;
|
||||
|
@ -31,13 +31,15 @@ class CommandReader extends Thread{
|
||||
private $readline;
|
||||
|
||||
/** @var \Threaded */
|
||||
private $buffer;
|
||||
protected $buffer;
|
||||
|
||||
/**
|
||||
* @param \Threaded $threaded
|
||||
* @param string $stream
|
||||
*/
|
||||
public function __construct($stream = "php://stdin"){
|
||||
public function __construct(\Threaded $threaded, $stream = "php://stdin"){
|
||||
$this->stream = $stream;
|
||||
$this->buffer = $threaded;
|
||||
$this->start();
|
||||
}
|
||||
|
||||
@ -70,7 +72,6 @@ class CommandReader extends Thread{
|
||||
}
|
||||
|
||||
public function run(){
|
||||
$this->buffer = new \Threaded;
|
||||
$opts = getopt("", ["disable-readline"]);
|
||||
if(extension_loaded("readline") and $this->stream === "php://stdin" and !isset($opts["disable-readline"])){
|
||||
$this->readline = true;
|
||||
|
@ -41,9 +41,7 @@ class SaveOffCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach($sender->getServer()->getLevels() as $level){
|
||||
$level->setAutoSave(false);
|
||||
}
|
||||
$sender->getServer()->setAutoSave(false);
|
||||
|
||||
Command::broadcastCommandMessage($sender, "Disabled level saving");
|
||||
|
||||
|
@ -41,9 +41,7 @@ class SaveOnCommand extends VanillaCommand{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach($sender->getServer()->getLevels() as $level){
|
||||
$level->setAutoSave(true);
|
||||
}
|
||||
$sender->getServer()->setAutoSave(true);
|
||||
|
||||
Command::broadcastCommandMessage($sender, "Enabled level saving");
|
||||
|
||||
|
@ -65,104 +65,103 @@ class Arrow extends Projectile{
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
$this->entityBaseTick();
|
||||
|
||||
if($this->closed !== false){
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
|
||||
$movingObjectPosition = null;
|
||||
$this->timings->startTiming();
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
$this->entityBaseTick();
|
||||
|
||||
$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
if(!$this->dead){
|
||||
|
||||
$moveVector = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ);
|
||||
$movingObjectPosition = null;
|
||||
|
||||
$list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this);
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$nearDistance = PHP_INT_MAX;
|
||||
$nearEntity = null;
|
||||
$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
|
||||
foreach($list as $entity){
|
||||
if(/*!$entity->canCollideWith($this) or */($entity === $this->shootingEntity and $this->ticksLived < 5)){
|
||||
continue;
|
||||
}
|
||||
$moveVector = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ);
|
||||
|
||||
$axisalignedbb = $entity->boundingBox->grow(0.3, 0.3, 0.3);
|
||||
$ob = $axisalignedbb->calculateIntercept($this, $moveVector);
|
||||
$list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this);
|
||||
|
||||
if($ob === null){
|
||||
continue;
|
||||
}
|
||||
$nearDistance = PHP_INT_MAX;
|
||||
$nearEntity = null;
|
||||
|
||||
$distance = $this->distance($ob->hitVector);
|
||||
foreach($list as $entity){
|
||||
if(/*!$entity->canCollideWith($this) or */
|
||||
($entity === $this->shootingEntity and $this->ticksLived < 5)
|
||||
){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($distance < $nearDistance){
|
||||
$nearDistance = $distance;
|
||||
$nearEntity = $entity;
|
||||
}
|
||||
}
|
||||
$axisalignedbb = $entity->boundingBox->grow(0.3, 0.3, 0.3);
|
||||
$ob = $axisalignedbb->calculateIntercept($this, $moveVector);
|
||||
|
||||
if($nearEntity !== null){
|
||||
$movingObjectPosition = MovingObjectPosition::fromEntity($nearEntity);
|
||||
}
|
||||
if($ob === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
if($movingObjectPosition !== null){
|
||||
if($movingObjectPosition->entityHit !== null){
|
||||
$motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2);
|
||||
$damage = ceil($motion * $this->damage);
|
||||
$distance = $this->distance($ob->hitVector);
|
||||
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this->shootingEntity === null ? $this : $this->shootingEntity, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage);
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$movingObjectPosition->entityHit->attack($damage, $ev);
|
||||
if($this->fireTicks > 0){
|
||||
$movingObjectPosition->entityHit->setOnFire(5);
|
||||
}
|
||||
$this->kill();
|
||||
if($distance < $nearDistance){
|
||||
$nearDistance = $distance;
|
||||
$nearEntity = $entity;
|
||||
}
|
||||
}
|
||||
|
||||
if($nearEntity !== null){
|
||||
$movingObjectPosition = MovingObjectPosition::fromEntity($nearEntity);
|
||||
}
|
||||
|
||||
if($movingObjectPosition !== null){
|
||||
if($movingObjectPosition->entityHit !== null){
|
||||
$motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2);
|
||||
$damage = ceil($motion * $this->damage);
|
||||
|
||||
|
||||
$ev = new EntityDamageByEntityEvent($this->shootingEntity === null ? $this : $this->shootingEntity, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage);
|
||||
|
||||
$this->server->getPluginManager()->callEvent($ev);
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$movingObjectPosition->entityHit->attack($damage, $ev);
|
||||
if($this->fireTicks > 0){
|
||||
$movingObjectPosition->entityHit->setOnFire(5);
|
||||
}
|
||||
$this->kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionX = 0;
|
||||
$this->motionY = 0;
|
||||
$this->motionZ = 0;
|
||||
}
|
||||
|
||||
if($this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0){
|
||||
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
|
||||
$this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI);
|
||||
$this->pitch = (atan2($this->motionY, $f) * 180 / M_PI);
|
||||
}
|
||||
|
||||
if($this->age > 1200){
|
||||
$this->kill();
|
||||
}
|
||||
$this->updateMovement();
|
||||
|
||||
}
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
if($this->onGround){
|
||||
$friction = $this->getLevel()->getBlock(new Vector3($this->getFloorX(), $this->getFloorY() - 1, $this->getFloorZ()))->frictionFactor * $friction;
|
||||
}
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionX = 0;
|
||||
$this->motionY = 0;
|
||||
$this->motionZ = 0;
|
||||
}
|
||||
|
||||
if($this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0){
|
||||
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
|
||||
$this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI);
|
||||
$this->pitch = (atan2($this->motionY, $f) * 180 / M_PI);
|
||||
}
|
||||
|
||||
if($this->age > 1200){
|
||||
$this->kill();
|
||||
}
|
||||
$this->updateMovement();
|
||||
$this->timings->stopTiming();
|
||||
|
||||
return !$this->onGround or ($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0);
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
$this->setLastDamageCause($source);
|
||||
$this->setHealth($this->getHealth() - $damage);
|
||||
|
||||
}
|
||||
|
||||
public function heal($amount){
|
||||
@ -170,6 +169,7 @@ class Arrow extends Projectile{
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Age = new Short("Age", $this->age);
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,8 @@ class DroppedItem extends Entity{
|
||||
protected $gravity = 0.04;
|
||||
protected $drag = 0.02;
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
$this->namedtag->id = new String("id", "Item");
|
||||
$this->setMaxHealth(5);
|
||||
@ -66,39 +68,48 @@ class DroppedItem extends Entity{
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
$this->entityBaseTick();
|
||||
|
||||
if($this->closed !== false){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
--$this->pickupDelay;
|
||||
$this->timings->startTiming();
|
||||
|
||||
$this->entityBaseTick();
|
||||
|
||||
if(!$this->dead){
|
||||
|
||||
if($this->pickupDelay > 0 and $this->pickupDelay < 32767){ //Infinite delay
|
||||
--$this->pickupDelay;
|
||||
}
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
if($this->onGround and ($this->motionX != 0 or $this->motionZ != 0)){
|
||||
$friction = $this->getLevel()->getBlock(new Vector3($this->getFloorX(), $this->getFloorY() - 1, $this->getFloorZ()))->frictionFactor * $friction;
|
||||
}
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$this->updateMovement();
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionY *= -0.5;
|
||||
}
|
||||
|
||||
if($this->age > 6000){
|
||||
$this->kill();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
if($this->onGround){
|
||||
$friction = $this->getLevel()->getBlock(new Vector3($this->getFloorX(), $this->getFloorY() - 1, $this->getFloorZ()))->frictionFactor * $friction;
|
||||
}
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionY *= -0.5;
|
||||
}
|
||||
|
||||
if($this->age > 6000){
|
||||
$this->kill();
|
||||
}
|
||||
$this->updateMovement();
|
||||
$this->timings->stopTiming();
|
||||
|
||||
return !$this->onGround or ($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0);
|
||||
}
|
||||
@ -113,6 +124,7 @@ class DroppedItem extends Entity{
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Item = new Compound("Item", [
|
||||
"id" => new Short("id", $this->item->getID()),
|
||||
"Damage" => new Short("Damage", $this->item->getDamage()),
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\event\entity\EntityMoveEvent;
|
||||
use pocketmine\event\entity\EntitySpawnEvent;
|
||||
use pocketmine\event\entity\EntityTeleportEvent;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
@ -133,10 +134,11 @@ abstract class Entity extends Position implements Metadatable{
|
||||
public $fireTicks;
|
||||
public $airTicks;
|
||||
public $namedtag;
|
||||
public $canCollide = true;
|
||||
|
||||
protected $isStatic = false;
|
||||
protected $isColliding = false;
|
||||
|
||||
protected $inWater;
|
||||
public $noDamageTicks;
|
||||
private $justCreated;
|
||||
protected $fireProof;
|
||||
@ -152,12 +154,17 @@ abstract class Entity extends Position implements Metadatable{
|
||||
|
||||
public $closed = false;
|
||||
|
||||
/** @var \pocketmine\event\TimingsHandler */
|
||||
protected $timings;
|
||||
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
if($chunk === null or $chunk->getProvider() === null){
|
||||
throw new \Exception("Invalid garbage Chunk given to Entity");
|
||||
}
|
||||
|
||||
$this->timings = Timings::getEntityTimings($this);
|
||||
|
||||
if($this->eyeHeight === null){
|
||||
$this->eyeHeight = $this->height;
|
||||
}
|
||||
@ -198,7 +205,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$this->airTicks = $this->namedtag["Air"];
|
||||
|
||||
if(!isset($this->namedtag->OnGround)){
|
||||
$this->namedtag->OnGround = new Byte("OnGround", 1);
|
||||
$this->namedtag->OnGround = new Byte("OnGround", 0);
|
||||
}
|
||||
$this->onGround = $this->namedtag["OnGround"] > 0 ? true : false;
|
||||
|
||||
@ -274,10 +281,10 @@ abstract class Entity extends Position implements Metadatable{
|
||||
foreach($player as $p){
|
||||
if($p === $this){
|
||||
/** @var Player $p */
|
||||
$pk = new SetEntityDataPacket();
|
||||
$pk->eid = 0;
|
||||
$pk->metadata = $this->getData();
|
||||
$p->dataPacket($pk);
|
||||
$pk2 = new SetEntityDataPacket();
|
||||
$pk2->eid = 0;
|
||||
$pk2->metadata = $this->getData();
|
||||
$p->dataPacket($pk2);
|
||||
}else{
|
||||
$p->dataPacket($pk);
|
||||
}
|
||||
@ -454,31 +461,25 @@ abstract class Entity extends Position implements Metadatable{
|
||||
}
|
||||
|
||||
public function entityBaseTick(){
|
||||
|
||||
Timings::$tickEntityTimer->startTiming();
|
||||
//TODO: check vehicles
|
||||
|
||||
$this->justCreated = false;
|
||||
$isPlayer = $this instanceof Player;
|
||||
|
||||
if($this->dead === true and !($this instanceof Player)){
|
||||
if($this->dead === true and !$isPlayer){
|
||||
$this->close();
|
||||
|
||||
Timings::$tickEntityTimer->stopTiming();
|
||||
return false;
|
||||
}elseif($this->dead === true){
|
||||
$this->despawnFromAll();
|
||||
}
|
||||
|
||||
$hasUpdate = false;
|
||||
$this->updateMovement();
|
||||
|
||||
$this->checkBlockCollision();
|
||||
|
||||
if($this->handleWaterMovement()){
|
||||
$this->fallDistance = 0;
|
||||
$this->inWater = true;
|
||||
$this->extinguish();
|
||||
}else{
|
||||
$this->inWater = false;
|
||||
}
|
||||
|
||||
if($this->y < 0 and $this->dead !== true){
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_VOID, 10));
|
||||
if(!$ev->isCancelled()){
|
||||
@ -510,15 +511,10 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->handleLavaMovement()){
|
||||
//$this->attack(4, EntityDamageEvent::CAUSE_LAVA);
|
||||
//$this->setOnFire(15);
|
||||
$hasUpdate = true;
|
||||
$this->fallDistance *= 0.5;
|
||||
}
|
||||
|
||||
++$this->age;
|
||||
++$this->ticksLived;
|
||||
|
||||
Timings::$tickEntityTimer->stopTiming();
|
||||
}
|
||||
|
||||
public function updateMovement(){
|
||||
@ -548,7 +544,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
}
|
||||
|
||||
foreach($this->hasSpawned as $player){
|
||||
$player->directDataPacket($pk);
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +557,9 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$pk->entities = [
|
||||
[$this->getID(), $this->motionX, $this->motionY, $this->motionZ]
|
||||
];
|
||||
Server::broadcastPacket($this->hasSpawned, $pk);
|
||||
foreach($this->hasSpawned as $player){
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
|
||||
if($this instanceof Player){
|
||||
$this->motionX = 0;
|
||||
@ -587,12 +585,17 @@ abstract class Entity extends Position implements Metadatable{
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
if($this->closed !== false){
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
$this->timings->startTiming();
|
||||
|
||||
$hasUpdate = $this->entityBaseTick();
|
||||
|
||||
$this->updateMovement();
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
//if($this->isStatic())
|
||||
return true;
|
||||
//return !($this instanceof Player);
|
||||
@ -674,10 +677,6 @@ abstract class Entity extends Position implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
public function handleWaterMovement(){ //TODO
|
||||
|
||||
}
|
||||
|
||||
public function handleLavaMovement(){ //TODO
|
||||
|
||||
}
|
||||
@ -755,7 +754,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
|
||||
$bb = $block->getBoundingBox();
|
||||
|
||||
if($bb !== null and $block->isSolid and $bb->intersectsWith($this->getBoundingBox())){
|
||||
if($bb !== null and $block->isSolid and !$block->isTransparent and $bb->intersectsWith($this->getBoundingBox())){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -953,7 +952,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
|
||||
if($this instanceof Player){
|
||||
if(($this->onGround and $movY != 0) or (!$this->onGround and $movY <= 0)){
|
||||
if(count($this->getLevel()->getCollisionBlocks($this->boundingBox->getOffsetBoundingBox(0, $movY - 0.1, 0))) > 0){
|
||||
if(count($this->getLevel()->getCollisionBlocks($this->boundingBox->getOffsetBoundingBox(0, $movY - 0.1, 0)->expand(0.01, 0, 0.01))) > 0){
|
||||
$isColliding = true;
|
||||
}else{
|
||||
$isColliding = false;
|
||||
@ -995,16 +994,29 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$maxY = Math::floorFloat($this->boundingBox->maxY + 0.001);
|
||||
$maxZ = Math::floorFloat($this->boundingBox->maxZ + 0.001);
|
||||
|
||||
$vector = new Vector3(0, 0, 0);
|
||||
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($y = $minY; $y <= $maxY; ++$y){
|
||||
$block = $this->getLevel()->getBlock(new Vector3($x, $y, $z));
|
||||
if($block !== null and $block->getID() > 0){
|
||||
$block->onEntityCollide($this);
|
||||
if(!($this instanceof Player)){
|
||||
$block->addVelocityToEntity($this, $vector);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($vector->length() > 0){
|
||||
$vector = $vector->normalize();
|
||||
$d = 0.014;
|
||||
$this->motionX += $vector->x * $d;
|
||||
$this->motionY += $vector->y * $d;
|
||||
$this->motionZ += $vector->z * $d;
|
||||
}
|
||||
}
|
||||
|
||||
public function setPositionAndRotation(Vector3 $pos, $yaw, $pitch, $force = false){
|
||||
@ -1052,7 +1064,7 @@ abstract class Entity extends Position implements Metadatable{
|
||||
$this->chunk->removeEntity($this);
|
||||
}
|
||||
$this->getLevel()->loadChunk($this->x >> 4, $this->z >> 4);
|
||||
$this->chunk = $this->getLevel()->getChunkAt($this->x >> 4, $this->z >> 4);
|
||||
$this->chunk = $this->getLevel()->getChunk($this->x >> 4, $this->z >> 4);
|
||||
|
||||
if(!$this->justCreated){
|
||||
$newChunk = $this->getLevel()->getUsingChunk($this->x >> 4, $this->z >> 4);
|
||||
|
@ -24,4 +24,5 @@ namespace pocketmine\entity;
|
||||
|
||||
interface Explosive{
|
||||
|
||||
public function explode();
|
||||
}
|
@ -43,6 +43,8 @@ class FallingBlock extends Entity{
|
||||
protected $drag = 0.02;
|
||||
protected $blockId = 0;
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
$this->namedtag->id = new String("id", "FallingSand");
|
||||
if(isset($this->namedtag->Tile)){
|
||||
@ -63,45 +65,51 @@ class FallingBlock extends Entity{
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
$this->entityBaseTick();
|
||||
|
||||
if($this->closed !== false){
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->ticksLived === 1){
|
||||
$block = $this->level->getBlock($this->floor());
|
||||
if($block->getID() != $this->blockId){
|
||||
$this->timings->startTiming();
|
||||
|
||||
$this->entityBaseTick();
|
||||
|
||||
if(!$this->dead){
|
||||
if($this->ticksLived === 1){
|
||||
$block = $this->level->getBlock($this->floor());
|
||||
if($block->getID() != $this->blockId){
|
||||
$this->kill();
|
||||
return true;
|
||||
}
|
||||
$this->level->setBlock($this->floor(), Block::get(0, true));
|
||||
|
||||
}
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$pos = $this->floor();
|
||||
|
||||
if($this->onGround){
|
||||
$this->kill();
|
||||
return true;
|
||||
$block = $this->level->getBlock($pos);
|
||||
if(!$block->isFullBlock){
|
||||
$this->getLevel()->dropItem($this, Item::get($this->getBlock(), 0, 1));
|
||||
}else{
|
||||
$this->getLevel()->setBlock($pos, Block::get($this->getBlock(), 0), true);
|
||||
}
|
||||
}
|
||||
$this->level->setBlock($this->floor(), Block::get(0, true));
|
||||
|
||||
$this->updateMovement();
|
||||
}
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$pos = $this->floor();
|
||||
|
||||
if($this->onGround){
|
||||
$this->kill();
|
||||
$block = $this->level->getBlock($pos);
|
||||
if(!$block->isFullBlock){
|
||||
$this->getLevel()->dropItem($this, Item::get($this->getBlock(), 0, 1));
|
||||
}else{
|
||||
$this->getLevel()->setBlock($pos, Block::get($this->getBlock(), 0), true);
|
||||
}
|
||||
}
|
||||
|
||||
$this->updateMovement();
|
||||
|
||||
return !$this->onGround or ($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0);
|
||||
}
|
||||
|
@ -62,7 +62,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
public function hasLineOfSight(Entity $entity){
|
||||
//TODO: head height
|
||||
return $this->getLevel()->rayTraceBlocks(new Vector3($this->x, $this->y + $this->height, $this->z), new Vector3($entity->x, $entity->y + $entity->height, $entity->z)) === null;
|
||||
return true;
|
||||
//return $this->getLevel()->rayTraceBlocks(new Vector3($this->x, $this->y + $this->height, $this->z), new Vector3($entity->x, $entity->y + $entity->height, $entity->z)) === null;
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
@ -78,21 +79,40 @@ abstract class Living extends Entity implements Damageable{
|
||||
$pk->event = 2; //Ouch!
|
||||
Server::broadcastPacket($this->hasSpawned, $pk);
|
||||
$this->setLastDamageCause($source);
|
||||
$motion = new Vector3(0, 0, 0);
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
$deltaX = $this->x - $e->x;
|
||||
$deltaZ = $this->z - $e->z;
|
||||
$yaw = atan2($deltaX, $deltaZ);
|
||||
$motion->x = sin($yaw) * 0.5;
|
||||
$motion->z = cos($yaw) * 0.5;
|
||||
$this->knockBack($e, $damage, sin($yaw), cos($yaw));
|
||||
}
|
||||
$this->setMotion($motion);
|
||||
|
||||
$this->setHealth($this->getHealth() - $damage);
|
||||
|
||||
$this->attackTime = 10; //0.5 seconds cooldown
|
||||
}
|
||||
|
||||
public function knockBack(Entity $attacker, $damage, $x, $z){
|
||||
$f = sqrt($x ** 2 + $z ** 2);
|
||||
$base = 0.4;
|
||||
|
||||
$motion = new Vector3($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$motion->x /= 2;
|
||||
$motion->y /= 2;
|
||||
$motion->z /= 2;
|
||||
$motion->x += ($x / $f) * $base;
|
||||
$motion->y += $base;
|
||||
$motion->z += ($z / $f) * $base;
|
||||
|
||||
if($motion->y > $base){
|
||||
$motion->y = $base;
|
||||
}
|
||||
|
||||
$this->setMotion($motion);
|
||||
}
|
||||
|
||||
public function heal($amount){
|
||||
$this->server->getPluginManager()->callEvent($ev = new EntityRegainHealthEvent($this, $amount));
|
||||
if($ev->isCancelled()){
|
||||
|
@ -22,10 +22,124 @@
|
||||
namespace pocketmine\entity;
|
||||
|
||||
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\level\Explosion;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\network\protocol\SetEntityMotionPacket;
|
||||
use pocketmine\Player;
|
||||
|
||||
class PrimedTNT extends Entity implements Explosive{
|
||||
const NETWORK_ID = 65;
|
||||
|
||||
public $width = 0.98;
|
||||
public $length = 0.98;
|
||||
public $height = 0.98;
|
||||
|
||||
protected $gravity = 0.04;
|
||||
protected $drag = 0.02;
|
||||
|
||||
protected $fuse;
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
$this->namedtag->id = new String("id", "PrimedTNT");
|
||||
if(isset($this->namedtag->Fuse)){
|
||||
$this->fuse = $this->namedtag["Fuse"];
|
||||
}else{
|
||||
$this->fuse = 80;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function canCollideWith(Entity $entity){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getData(){
|
||||
return [
|
||||
16 => ["type" => 0, "value" => $this->fuse],
|
||||
];
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Fuse = new Byte("Fuse", $this->fuse);
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
|
||||
if($this->closed){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->timings->startTiming();
|
||||
|
||||
$this->entityBaseTick();
|
||||
|
||||
if(!$this->dead){
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= $friction;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$this->updateMovement();
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionY *= -0.5;
|
||||
$this->motionX *= 0.7;
|
||||
$this->motionZ *= 0.7;
|
||||
}
|
||||
|
||||
if($this->fuse-- <= 0){
|
||||
$this->kill();
|
||||
$this->explode();
|
||||
}else{
|
||||
$this->sendMetadata($this->getViewers());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return !$this->onGround or ($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0);
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function heal($amount){
|
||||
|
||||
}
|
||||
|
||||
public function explode(){
|
||||
(new Explosion($this, 4, $this))->explode();
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
$pk = new AddEntityPacket();
|
||||
$pk->type = PrimedTNT::NETWORK_ID;
|
||||
$pk->eid = $this->getID();
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->did = 0;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$pk = new SetEntityMotionPacket();
|
||||
$pk->entities = [
|
||||
[$this->getID(), $this->motionX, $this->motionY, $this->motionZ]
|
||||
];
|
||||
$player->dataPacket($pk);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
@ -101,7 +101,7 @@ class TimingsHandler{
|
||||
if(PluginManager::$useTimings){
|
||||
foreach(self::$HANDLERS as $timings){
|
||||
if($timings->curTickTotal > 0.05){
|
||||
$timings->violations += ceil($timings->curTickTotal / 0.05);
|
||||
$timings->violations += round($timings->curTickTotal / 0.05);
|
||||
}
|
||||
$timings->curTickTotal = 0;
|
||||
$timings->timingDepth = 0;
|
||||
|
222
src/pocketmine/event/server/QueryRegenerateEvent.php
Normal file
222
src/pocketmine/event/server/QueryRegenerateEvent.php
Normal file
@ -0,0 +1,222 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\event\server;
|
||||
|
||||
use pocketmine\event;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class QueryRegenerateEvent extends ServerEvent{
|
||||
public static $handlerList = null;
|
||||
|
||||
const GAME_ID = "MINECRAFTPE";
|
||||
|
||||
private $timeout;
|
||||
private $serverName;
|
||||
private $listPlugins;
|
||||
/** @var \pocketmine\plugin\Plugin[] */
|
||||
private $plugins;
|
||||
/** @var \pocketmine\Player[] */
|
||||
private $players;
|
||||
|
||||
private $gametype;
|
||||
private $version;
|
||||
private $server_engine;
|
||||
private $map;
|
||||
private $numPlayers;
|
||||
private $maxPlayers;
|
||||
private $whitelist;
|
||||
private $port;
|
||||
private $ip;
|
||||
|
||||
private $extraData = [];
|
||||
|
||||
|
||||
public function __construct(Server $server, $timeout = 5){
|
||||
$this->timeout = $timeout;
|
||||
$this->serverName = $server->getServerName();
|
||||
$this->listPlugins = $server->getProperty("settings.query-plugins", true);
|
||||
$this->plugins = $server->getPluginManager()->getPlugins();
|
||||
$this->players = [];
|
||||
foreach($server->getOnlinePlayers() as $player){
|
||||
if($player->getName() != "" and $player->isConnected()){
|
||||
$this->players[] = $player;
|
||||
}
|
||||
}
|
||||
|
||||
$this->gametype = ($server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP";
|
||||
$this->version = $server->getVersion();
|
||||
$this->server_engine = $server->getName() ." ". $server->getPocketMineVersion();
|
||||
$this->map = $server->getDefaultLevel() === null ? "unknown" : $server->getDefaultLevel()->getName();
|
||||
$this->numPlayers = count($this->players);
|
||||
$this->maxPlayers = $server->getMaxPlayers();
|
||||
$this->whitelist = $server->hasWhitelist() ? "on" : "off";
|
||||
$this->port = $server->getPort();
|
||||
$this->ip = $server->getIp();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the min. timeout for Query Regeneration
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTimeout(){
|
||||
return $this->timeout;
|
||||
}
|
||||
|
||||
public function setTimeout($timeout){
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
public function getServerName(){
|
||||
return $this->serverName;
|
||||
}
|
||||
|
||||
public function setServerName($serverName){
|
||||
$this->serverName = $serverName;
|
||||
}
|
||||
|
||||
public function canListPlugins(){
|
||||
return $this->listPlugins;
|
||||
}
|
||||
|
||||
public function setListPlugins($value){
|
||||
$this->listPlugins = (bool) $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \pocketmine\plugin\Plugin[]
|
||||
*/
|
||||
public function getPlugins(){
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \pocketmine\plugin\Plugin[] $plugins
|
||||
*/
|
||||
public function setPlugins(array $plugins){
|
||||
$this->plugins = $plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \pocketmine\Player[]
|
||||
*/
|
||||
public function getPlayerList(){
|
||||
return $this->players;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \pocketmine\Player[] $players
|
||||
*/
|
||||
public function setPlayerList(array $players){
|
||||
$this->players = $players;
|
||||
}
|
||||
|
||||
public function getPlayerCount(){
|
||||
return $this->numPlayers;
|
||||
}
|
||||
|
||||
public function setPlayerCount($count){
|
||||
$this->numPlayers = (int) $count;
|
||||
}
|
||||
|
||||
public function getMaxPlayerCount(){
|
||||
return $this->maxPlayers;
|
||||
}
|
||||
|
||||
public function setMaxPlayerCount($count){
|
||||
$this->maxPlayers = (int) $count;
|
||||
}
|
||||
|
||||
public function getWorld(){
|
||||
return $this->map;
|
||||
}
|
||||
|
||||
public function setWorld($world){
|
||||
$this->map = (string) $world;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extra Query data in key => value form
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getExtraData(){
|
||||
return $this->extraData;
|
||||
}
|
||||
|
||||
public function setExtraData(array $extraData){
|
||||
$this->extraData = $extraData;
|
||||
}
|
||||
|
||||
public function getLongQuery(){
|
||||
$query = "";
|
||||
|
||||
$plist = $this->server_engine;
|
||||
if(count($this->plugins) > 0 and $this->listPlugins){
|
||||
$plist .= ":";
|
||||
foreach($this->plugins as $p){
|
||||
$d = $p->getDescription();
|
||||
$plist .= " " . str_replace([";", ":", " "], ["", "", "_"], $d->getName()) . " " . str_replace([";", ":", " "], ["", "", "_"], $d->getVersion()) . ";";
|
||||
}
|
||||
$plist = substr($plist, 0, -1);
|
||||
}
|
||||
|
||||
$KVdata = [
|
||||
"splitnum" => chr(128),
|
||||
"hostname" => $this->serverName,
|
||||
"gametype" => $this->gametype,
|
||||
"game_id" => self::GAME_ID,
|
||||
"version" => $this->version,
|
||||
"server_engine" => $this->server_engine,
|
||||
"plugins" => $plist,
|
||||
"map" => $this->map,
|
||||
"numplayers" => $this->numPlayers,
|
||||
"maxplayers" => $this->maxPlayers,
|
||||
"whitelist" => $this->whitelist,
|
||||
"hostip" => $this->ip,
|
||||
"hostport" => $this->port
|
||||
];
|
||||
|
||||
foreach($KVdata as $key => $value){
|
||||
$query .= $key . "\x00" . $value . "\x00";
|
||||
}
|
||||
|
||||
foreach($this->extraData as $key => $value){
|
||||
$query .= $key . "\x00" . $value . "\x00";
|
||||
}
|
||||
|
||||
$query .= "\x00\x01player_\x00\x00";
|
||||
foreach($this->players as $player){
|
||||
$query .= $player->getName() . "\x00";
|
||||
}
|
||||
$query .= "\x00";
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getShortQuery(){
|
||||
return $this->serverName . "\x00" . $this->gametype . "\x00" . $this->map . "\x00" . $this->numPlayers . "\x00" . $this->maxPlayers . "\x00" . Binary::writeLShort($this->port) . $this->ip . "\x00";
|
||||
}
|
||||
|
||||
}
|
@ -89,11 +89,12 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
* @param $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setItemInHand(Item $item){
|
||||
return $this->setItem($this->getHeldItemSlot(), $item);
|
||||
public function setItemInHand(Item $item, $source = null){
|
||||
return $this->setItem($this->getHeldItemSlot(), $item, $source);
|
||||
}
|
||||
|
||||
public function getHeldItemSlot(){
|
||||
@ -160,8 +161,8 @@ class PlayerInventory extends BaseInventory{
|
||||
return $this->getItem($this->getSize() + $index);
|
||||
}
|
||||
|
||||
public function setArmorItem($index, Item $item){
|
||||
return $this->setItem($this->getSize() + $index, $item);
|
||||
public function setArmorItem($index, Item $item, $source = null){
|
||||
return $this->setItem($this->getSize() + $index, $item, $source);
|
||||
}
|
||||
|
||||
public function getHelmet(){
|
||||
@ -211,6 +212,7 @@ class PlayerInventory extends BaseInventory{
|
||||
}
|
||||
$item = $ev->getNewItem();
|
||||
}
|
||||
|
||||
if($item->getID() === 0){
|
||||
$this->clear($index, $source);
|
||||
}else{
|
||||
@ -291,6 +293,9 @@ class PlayerInventory extends BaseInventory{
|
||||
foreach($target as $player){
|
||||
if($player === $this->getHolder()){
|
||||
/** @var Player $player */
|
||||
//$pk2 = clone $pk;
|
||||
//$pk2->eid = 0;
|
||||
|
||||
$pk2 = new ContainerSetContentPacket;
|
||||
$pk2->windowid = 0x78; //Armor window id constant
|
||||
$pk2->slots = $armor;
|
||||
|
@ -143,6 +143,9 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
if($ev->isCancelled()){
|
||||
foreach($this->inventories as $inventory){
|
||||
$inventory->sendContents($inventory->getViewers());
|
||||
if($inventory instanceof PlayerInventory){
|
||||
$inventory->sendArmorContents($inventory->getViewers());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -49,8 +49,7 @@ class Bucket extends Item{
|
||||
}elseif($this->meta === Item::WATER){
|
||||
//Support Make Non-Support Water to Support Water
|
||||
if($block->getID() === self::AIR || ($block instanceof Water && ($block->getDamage() & 0x07) != 0x00)){
|
||||
$water = new Water();
|
||||
$level->setBlock($block, $water, true);
|
||||
$water = Block::get(Item::WATER, 0, $block);
|
||||
$water->place(clone $this, $block, $target, $face, $fx, $fy, $fz, $player);
|
||||
if(($player->gamemode & 0x01) === 0){
|
||||
$this->meta = 0;
|
||||
@ -59,8 +58,9 @@ class Bucket extends Item{
|
||||
return true;
|
||||
}
|
||||
}elseif($this->meta === Item::LAVA){
|
||||
$lava = Block::get(Item::LAVA, 0, $block);
|
||||
$lava->place(clone $this, $block, $target, $face, $fx, $fy, $fz, $player);
|
||||
if($block->getID() === self::AIR){
|
||||
$level->setBlock($block, new Lava(), true);
|
||||
if(($player->gamemode & 0x01) === 0){
|
||||
$this->meta = 0;
|
||||
}
|
||||
|
@ -24,7 +24,7 @@ namespace pocketmine\item;
|
||||
|
||||
class Coal extends Item{
|
||||
public function __construct($meta = 0, $count = 1){
|
||||
parent::__construct(self::COAL, $meta & 0x01, $count, "Coal");
|
||||
parent::__construct(self::COAL, $meta, $count, "Coal");
|
||||
if($this->meta === 1){
|
||||
$this->name = "Charcoal";
|
||||
}
|
||||
|
@ -27,4 +27,8 @@ class DiamondAxe extends Tool{
|
||||
parent::__construct(self::DIAMOND_AXE, $meta, $count, "Diamond Axe");
|
||||
}
|
||||
|
||||
public function isAxe(){
|
||||
return Tool::TIER_DIAMOND;
|
||||
}
|
||||
|
||||
}
|
@ -27,4 +27,7 @@ class DiamondHoe extends Tool{
|
||||
parent::__construct(self::DIAMOND_HOE, $meta, $count, "Diamond Hoe");
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
return Tool::TIER_DIAMOND;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class DiamondPickaxe extends Tool{
|
||||
parent::__construct(self::DIAMOND_PICKAXE, $meta, $count, "Diamond Pickaxe");
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
return Tool::TIER_DIAMOND;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class DiamondShovel extends Tool{
|
||||
parent::__construct(self::DIAMOND_SHOVEL, $meta, $count, "Diamond Shovel");
|
||||
}
|
||||
|
||||
public function isShovel(){
|
||||
return Tool::TIER_DIAMOND;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class DiamondSword extends Tool{
|
||||
parent::__construct(self::DIAMOND_SWORD, $meta, $count, "Diamond Sword");
|
||||
}
|
||||
|
||||
public function isSword(){
|
||||
return Tool::TIER_DIAMOND;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class GoldAxe extends Tool{
|
||||
parent::__construct(self::GOLD_AXE, $meta, $count, "Gold Axe");
|
||||
}
|
||||
|
||||
public function isAxe(){
|
||||
return Tool::TIER_GOLD;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class GoldHoe extends Tool{
|
||||
parent::__construct(self::GOLD_HOE, $meta, $count, "Gold Hoe");
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
return Tool::TIER_GOLD;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class GoldPickaxe extends Tool{
|
||||
parent::__construct(self::GOLD_PICKAXE, $meta, $count, "Gold Pickaxe");
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
return Tool::TIER_GOLD;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class GoldShovel extends Tool{
|
||||
parent::__construct(self::GOLD_SHOVEL, $meta, $count, "Gold Shovel");
|
||||
}
|
||||
|
||||
public function isShovel(){
|
||||
return Tool::TIER_GOLD;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class GoldSword extends Tool{
|
||||
parent::__construct(self::GOLD_SWORD, $meta, $count, "Gold Sword");
|
||||
}
|
||||
|
||||
public function isSword(){
|
||||
return Tool::TIER_GOLD;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class IronAxe extends Tool{
|
||||
parent::__construct(self::IRON_AXE, $meta, $count, "Iron Axe");
|
||||
}
|
||||
|
||||
public function isAxe(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class IronHoe extends Tool{
|
||||
parent::__construct(self::IRON_HOE, $meta, $count, "Iron Hoe");
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class IronPickaxe extends Tool{
|
||||
parent::__construct(self::IRON_PICKAXE, $meta, $count, "Iron Pickaxe");
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class IronShovel extends Tool{
|
||||
parent::__construct(self::IRON_SHOVEL, $meta, $count, "Iron Shovel");
|
||||
}
|
||||
|
||||
public function isShovel(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class IronSword extends Tool{
|
||||
parent::__construct(self::IRON_SWORD, $meta, $count, "Iron Sword");
|
||||
}
|
||||
|
||||
public function isSword(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
@ -159,7 +159,8 @@ class Item{
|
||||
const MELON_BLOCK = 103;
|
||||
const PUMPKIN_STEM = 104;
|
||||
const MELON_STEM = 105;
|
||||
|
||||
const VINE = 106;
|
||||
const VINES = 106;
|
||||
const FENCE_GATE = 107;
|
||||
const BRICK_STAIRS = 108;
|
||||
const STONE_BRICK_STAIRS = 109;
|
||||
|
@ -43,7 +43,7 @@ class SpawnEgg extends Item{
|
||||
|
||||
public function onActivate(Level $level, Player $player, Block $block, Block $target, $face, $fx, $fy, $fz){
|
||||
$entity = null;
|
||||
$chunk = $level->getChunkAt($block->getX() >> 4, $block->getZ() >> 4);
|
||||
$chunk = $level->getChunk($block->getX() >> 4, $block->getZ() >> 4);
|
||||
|
||||
if(!($chunk instanceof FullChunk)){
|
||||
return false;
|
||||
|
@ -27,4 +27,8 @@ class StoneAxe extends Tool{
|
||||
parent::__construct(self::STONE_AXE, $meta, $count, "Stone Axe");
|
||||
}
|
||||
|
||||
|
||||
public function isAxe(){
|
||||
return Tool::TIER_STONE;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class StoneHoe extends Tool{
|
||||
parent::__construct(self::STONE_HOE, $meta, $count, "Stone Hoe");
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
return Tool::TIER_STONE;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class StonePickaxe extends Tool{
|
||||
parent::__construct(self::STONE_PICKAXE, $meta, $count, "Stone Pickaxe");
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
return Tool::TIER_IRON;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class StoneShovel extends Tool{
|
||||
parent::__construct(self::STONE_SHOVEL, $meta, $count, "Stone Shovel");
|
||||
}
|
||||
|
||||
public function isShovel(){
|
||||
return Tool::TIER_STONE;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class StoneSword extends Tool{
|
||||
parent::__construct(self::STONE_SWORD, $meta, $count, "Stone Sword");
|
||||
}
|
||||
|
||||
public function isSword(){
|
||||
return Tool::TIER_STONE;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,11 @@ use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
abstract class Tool extends Item{
|
||||
const TIER_WOODEN = 1;
|
||||
const TIER_GOLD = 2;
|
||||
const TIER_STONE = 3;
|
||||
const TIER_IRON = 4;
|
||||
const TIER_DIAMOND = 5;
|
||||
|
||||
public function __construct($id, $meta = 0, $count = 1, $name = "Unknown"){
|
||||
parent::__construct($id, $meta, $count, $name);
|
||||
@ -87,84 +92,23 @@ abstract class Tool extends Item{
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
switch($this->id){
|
||||
case self::WOODEN_PICKAXE:
|
||||
return 1;
|
||||
case self::STONE_PICKAXE:
|
||||
return 3;
|
||||
case self::IRON_PICKAXE:
|
||||
return 4;
|
||||
case self::DIAMOND_PICKAXE:
|
||||
return 5;
|
||||
case self::GOLD_PICKAXE:
|
||||
return 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
final public function isAxe(){
|
||||
switch($this->id){
|
||||
case self::IRON_AXE:
|
||||
return 4;
|
||||
case self::WOODEN_AXE:
|
||||
return 1;
|
||||
case self::STONE_AXE:
|
||||
return 3;
|
||||
case self::DIAMOND_AXE:
|
||||
return 5;
|
||||
case self::GOLD_AXE:
|
||||
return 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
public function isAxe(){
|
||||
return false;
|
||||
}
|
||||
|
||||
final public function isSword(){
|
||||
switch($this->id){
|
||||
case self::IRON_SWORD:
|
||||
return 4;
|
||||
case self::WOODEN_SWORD:
|
||||
return 1;
|
||||
case self::STONE_SWORD:
|
||||
return 3;
|
||||
case self::DIAMOND_SWORD:
|
||||
return 5;
|
||||
case self::GOLD_SWORD:
|
||||
return 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
public function isSword(){
|
||||
return false;
|
||||
}
|
||||
|
||||
final public function isShovel(){
|
||||
switch($this->id){
|
||||
case self::IRON_SHOVEL:
|
||||
return 4;
|
||||
case self::WOODEN_SHOVEL:
|
||||
return 1;
|
||||
case self::STONE_SHOVEL:
|
||||
return 3;
|
||||
case self::DIAMOND_SHOVEL:
|
||||
return 5;
|
||||
case self::GOLD_SHOVEL:
|
||||
return 2;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
public function isShovel(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
switch($this->id){
|
||||
case self::IRON_HOE:
|
||||
case self::WOODEN_HOE:
|
||||
case self::STONE_HOE:
|
||||
case self::DIAMOND_HOE:
|
||||
case self::GOLD_HOE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isShears(){
|
||||
@ -172,8 +116,6 @@ abstract class Tool extends Item{
|
||||
}
|
||||
|
||||
public function isTool(){
|
||||
return false;
|
||||
|
||||
return ($this->id === self::FLINT_STEEL or $this->id === self::SHEARS or $this->id === self::BOW or $this->isPickaxe() !== false or $this->isAxe() !== false or $this->isShovel() !== false or $this->isSword() !== false);
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class WoodenAxe extends Tool{
|
||||
parent::__construct(self::WOODEN_AXE, $meta, $count, "Wooden Axe");
|
||||
}
|
||||
|
||||
public function isAxe(){
|
||||
return Tool::TIER_WOODEN;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class WoodenHoe extends Tool{
|
||||
parent::__construct(self::WOODEN_HOE, $meta, $count, "Wooden Hoe");
|
||||
}
|
||||
|
||||
public function isHoe(){
|
||||
return Tool::TIER_WOODEN;
|
||||
}
|
||||
}
|
@ -27,4 +27,7 @@ class WoodenPickaxe extends Tool{
|
||||
parent::__construct(self::WOODEN_PICKAXE, $meta, $count, "Wooden Pickaxe");
|
||||
}
|
||||
|
||||
public function isPickaxe(){
|
||||
return Tool::TIER_WOODEN;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class WoodenShovel extends Tool{
|
||||
parent::__construct(self::WOODEN_SHOVEL, $meta, $count, "Wooden Shovel");
|
||||
}
|
||||
|
||||
public function isShovel(){
|
||||
return Tool::TIER_WOODEN;
|
||||
}
|
||||
}
|
||||
|
@ -27,4 +27,7 @@ class WoodenSword extends Tool{
|
||||
parent::__construct(self::WOODEN_SWORD, $meta, $count, "Wooden Sword");
|
||||
}
|
||||
|
||||
public function isSword(){
|
||||
return Tool::TIER_WOODEN;
|
||||
}
|
||||
}
|
||||
|
@ -24,20 +24,24 @@ namespace pocketmine\level;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\TNT;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\PrimedTNT;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityExplodeEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Double;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Float;
|
||||
use pocketmine\network\protocol\ExplodePacket;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Explosion{
|
||||
public static $specialDrops = [
|
||||
Item::GRASS => Item::DIRT,
|
||||
Item::STONE => Item::COBBLESTONE,
|
||||
Item::COAL_ORE => Item::COAL,
|
||||
Item::DIAMOND_ORE => Item::DIAMOND,
|
||||
Item::REDSTONE_ORE => Item::REDSTONE,
|
||||
];
|
||||
|
||||
private $rays = 16; //Rays
|
||||
public $level;
|
||||
public $source;
|
||||
@ -47,6 +51,7 @@ class Explosion{
|
||||
*/
|
||||
public $affectedBlocks = [];
|
||||
public $stepLen = 0.3;
|
||||
/** @var Entity|Block|Tile */
|
||||
private $what;
|
||||
|
||||
public function __construct(Position $center, $size, $what = null){
|
||||
@ -56,6 +61,9 @@ class Explosion{
|
||||
$this->what = $what;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function explode(){
|
||||
if($this->size < 0.1){
|
||||
return false;
|
||||
@ -64,6 +72,7 @@ class Explosion{
|
||||
$mRays = $this->rays - 1;
|
||||
for($i = 0; $i < $this->rays; ++$i){
|
||||
for($j = 0; $j < $this->rays; ++$j){
|
||||
//break 2 gets here
|
||||
for($k = 0; $k < $this->rays; ++$k){
|
||||
if($i == 0 or $i == $mRays or $j == 0 or $j == $mRays or $k == 0 or $k == $mRays){
|
||||
$vector = new Vector3($i / $mRays * 2 - 1, $j / $mRays * 2 - 1, $k / $mRays * 2 - 1); //($i / $mRays) * 2 - 1
|
||||
@ -72,6 +81,9 @@ class Explosion{
|
||||
|
||||
for($blastForce = $this->size * (mt_rand(700, 1300) / 1000); $blastForce > 0; $blastForce -= $this->stepLen * 0.75){
|
||||
$vBlock = $pointer->floor();
|
||||
if($vBlock->y < 0 or $vBlock->y > 127){
|
||||
break;
|
||||
}
|
||||
$blockID = $this->level->getBlockIdAt($vBlock->x, $vBlock->y, $vBlock->z);
|
||||
|
||||
if($blockID > 0){
|
||||
@ -96,11 +108,10 @@ class Explosion{
|
||||
|
||||
$send = [];
|
||||
$source = $this->source->floor();
|
||||
$radius = 2 * $this->size;
|
||||
$yield = (1 / $this->size) * 100;
|
||||
|
||||
if($this->what instanceof Entity){
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield));
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new EntityExplodeEvent($this->what, $this->source, $this->affectedBlocks, $yield));
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
}else{
|
||||
@ -109,33 +120,66 @@ class Explosion{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
/*foreach($server->api->entity->getRadius($this->source, $radius) as $entity){
|
||||
$impact = (1 - $this->source->distance($entity) / $radius) * 0.5; //placeholder, 0.7 should be exposure
|
||||
$damage = (int) (($impact * $impact + $impact) * 8 * $this->size + 1);
|
||||
$entity->harm($damage, "explosion");
|
||||
}*/
|
||||
$explosionSize = $this->size * 2;
|
||||
$minX = Math::floorFloat($this->source->x - $explosionSize - 1);
|
||||
$maxX = Math::floorFloat($this->source->x + $explosionSize + 1);
|
||||
$minY = Math::floorFloat($this->source->y - $explosionSize - 1);
|
||||
$maxY = Math::floorFloat($this->source->y + $explosionSize + 1);
|
||||
$minZ = Math::floorFloat($this->source->z - $explosionSize - 1);
|
||||
$maxZ = Math::floorFloat($this->source->z + $explosionSize + 1);
|
||||
|
||||
$explosionBB = new AxisAlignedBB($minX, $minY, $minZ, $maxX, $maxY, $maxZ);
|
||||
|
||||
$list = $this->level->getNearbyEntities($explosionBB, $this->what instanceof Entity ? $this->what : null);
|
||||
foreach($list as $entity){
|
||||
$distance = $entity->distance($this->source) / $explosionSize;
|
||||
|
||||
if($distance <= 1){
|
||||
$motion = $entity->subtract($this->source)->normalize();
|
||||
|
||||
$impact = (1 - $distance) * ($exposure = 1);
|
||||
|
||||
$damage = (int) ((($impact * $impact + $impact) / 2) * 8 * $explosionSize + 1);
|
||||
|
||||
$this->level->getServer()->getPluginManager()->callEvent($ev = new EntityDamageEvent($entity, $this->what instanceof Entity ? EntityDamageEvent::CAUSE_ENTITY_EXPLOSION : EntityDamageEvent::CAUSE_BLOCK_EXPLOSION, $damage));
|
||||
|
||||
if(!$ev->isCancelled()){
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
$entity->setMotion($motion->multiply($impact));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$air = Item::get(Item::AIR);
|
||||
|
||||
foreach($this->affectedBlocks as $block){
|
||||
$block->setDamage($this->level->getBlockDataAt($block->x, $block->y, $block->z));
|
||||
|
||||
if($block instanceof TNT){
|
||||
$data = [
|
||||
"x" => $block->x + 0.5,
|
||||
"y" => $block->y + 0.5,
|
||||
"z" => $block->z + 0.5,
|
||||
"power" => 4,
|
||||
"fuse" => mt_rand(10, 30), //0.5 to 1.5 seconds
|
||||
];
|
||||
//TODO
|
||||
//$e = $server->api->entity->add($this->level, ENTITY_OBJECT, OBJECT_PRIMEDTNT, $data);
|
||||
//$e->spawnToAll();
|
||||
$mot = (new Random())->nextSignedFloat() * M_PI * 2;
|
||||
$tnt = new PrimedTNT($this->level->getChunk($block->x >> 4, $block->z >> 4), new Compound("", [
|
||||
"Pos" => new Enum("Pos", [
|
||||
new Double("", $block->x + 0.5),
|
||||
new Double("", $block->y + 0.5),
|
||||
new Double("", $block->z + 0.5)
|
||||
]),
|
||||
"Motion" => new Enum("Motion", [
|
||||
new Double("", -sin($mot) * 0.02),
|
||||
new Double("", 0.2),
|
||||
new Double("", -cos($mot) * 0.02)
|
||||
]),
|
||||
"Rotation" => new Enum("Rotation", [
|
||||
new Float("", 0),
|
||||
new Float("", 0)
|
||||
]),
|
||||
"Fuse" => new Byte("Fuse", mt_rand(10, 30))
|
||||
]));
|
||||
$tnt->spawnToAll();
|
||||
}elseif(mt_rand(0, 100) < $yield){
|
||||
if(isset(self::$specialDrops[$block->getID()])){
|
||||
//TODO
|
||||
//$server->api->entity->drop(new Position($block->x + 0.5, $block->y, $block->z + 0.5, $this->level), Item::get(self::$specialDrops[$block->getID()], 0));
|
||||
}else{
|
||||
//TODO
|
||||
//$server->api->entity->drop(new Position($block->x + 0.5, $block->y, $block->z + 0.5, $this->level), Item::get($block->getID(), $this->level->level->getBlockDamage($block->x, $block->y, $block->z)));
|
||||
foreach($block->getDrops($air) as $drop){
|
||||
$this->level->dropItem($block, Item::get(...$drop));
|
||||
}
|
||||
}
|
||||
$this->level->setBlockIdAt($block->x, $block->y, $block->z, 0);
|
||||
@ -147,7 +191,9 @@ class Explosion{
|
||||
$pk->z = $this->source->z;
|
||||
$pk->radius = $this->size;
|
||||
$pk->records = $send;
|
||||
Server::broadcastPacket($this->level->getPlayers(), $pk);
|
||||
Server::broadcastPacket($this->level->getUsingChunk($source->x >> 4, $source->z >> 4), $pk);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ use pocketmine\block\Grass;
|
||||
use pocketmine\block\Ice;
|
||||
use pocketmine\block\Leaves;
|
||||
use pocketmine\block\Leaves2;
|
||||
use pocketmine\block\Liquid;
|
||||
use pocketmine\block\MelonStem;
|
||||
use pocketmine\block\Mycelium;
|
||||
use pocketmine\block\Potato;
|
||||
@ -98,7 +99,7 @@ use pocketmine\utils\TextFormat;
|
||||
class Level implements ChunkManager, Metadatable{
|
||||
|
||||
private static $levelIdCounter = 1;
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
public static $COMPRESSION_LEVEL = 8;
|
||||
|
||||
|
||||
const BLOCK_UPDATE_NORMAL = 1;
|
||||
@ -157,6 +158,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
/** @var ReversePriorityQueue */
|
||||
private $updateQueue;
|
||||
private $updateQueueIndex = [];
|
||||
|
||||
/** @var Player[][] */
|
||||
private $chunkSendQueue = [];
|
||||
@ -203,6 +205,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelTimings */
|
||||
public $timings;
|
||||
|
||||
protected $generator;
|
||||
|
||||
/**
|
||||
* Returns the chunk unique hash/key
|
||||
*
|
||||
@ -235,6 +239,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->levelId = static::$levelIdCounter++;
|
||||
$this->blockMetadata = new BlockMetadataStore($this);
|
||||
$this->server = $server;
|
||||
$this->autoSave = $server->getAutoSave();
|
||||
|
||||
/** @var LevelProvider $provider */
|
||||
|
||||
@ -244,8 +249,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
throw new \Exception("Provider is not a subclass of LevelProvider");
|
||||
}
|
||||
$this->server->getLogger()->info("Preparing level \"" . $this->provider->getName() . "\"");
|
||||
$generator = Generator::getGenerator($this->provider->getGenerator());
|
||||
$this->server->getGenerationManager()->openLevel($this, $generator, $this->provider->getGeneratorOptions());
|
||||
$this->generator = Generator::getGenerator($this->provider->getGenerator());
|
||||
|
||||
$this->blockOrder = $provider::getProviderOrder();
|
||||
$this->useSections = $provider::usesChunkSection();
|
||||
@ -260,7 +264,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->chunkTickList = [];
|
||||
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false);
|
||||
$this->timings = new LevelTimings($this);
|
||||
}
|
||||
|
||||
public function initLevel(){
|
||||
$this->server->getGenerationManager()->openLevel($this, $this->generator, $this->provider->getGeneratorOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -426,7 +433,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$pk->time = (int) $this->time;
|
||||
$pk->started = $this->stopTime == false;
|
||||
foreach($this->players as $player){
|
||||
$player->directDataPacket($pk);
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,6 +455,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->sendTime();
|
||||
}
|
||||
|
||||
$this->unloadChunks();
|
||||
|
||||
if(count($this->changedCount) > 0){
|
||||
if(count($this->players) > 0){
|
||||
foreach($this->changedCount as $index => $mini){
|
||||
@ -502,6 +511,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->doTickPending->startTiming();
|
||||
while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){
|
||||
$block = $this->getBlock($this->updateQueue->extract()["data"]);
|
||||
unset($this->updateQueueIndex["{$block->x}:{$block->y}:{$block->z}"]);
|
||||
$block->onUpdate(self::BLOCK_UPDATE_SCHEDULED);
|
||||
}
|
||||
$this->timings->doTickPending->stopTiming();
|
||||
@ -509,13 +519,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->entityTick->startTiming();
|
||||
//Update entities that need update
|
||||
//if(count($this->updateEntities) > 0){
|
||||
//Timings::$tickEntityTimer->startTiming();
|
||||
Timings::$tickEntityTimer->startTiming();
|
||||
foreach($this->entities as $id => $entity){
|
||||
if($entity->onUpdate() !== true){
|
||||
unset($this->updateEntities[$id]);
|
||||
if(!$entity->closed){
|
||||
$entity->onUpdate();
|
||||
}
|
||||
}
|
||||
//Timings::$tickEntityTimer->stopTiming();
|
||||
Timings::$tickEntityTimer->stopTiming();
|
||||
//}
|
||||
$this->timings->entityTick->stopTiming();
|
||||
|
||||
@ -576,7 +586,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
unset($this->chunkTickList[$index]);
|
||||
continue;
|
||||
}
|
||||
$chunk = $this->getChunkAt($chunkX, $chunkZ, true);
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ, true);
|
||||
|
||||
|
||||
if($this->useSections){
|
||||
@ -692,7 +702,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $delay
|
||||
*/
|
||||
public function scheduleUpdate(Vector3 $pos, $delay){
|
||||
$this->updateQueue->insert($pos, (int) $delay);
|
||||
$index = "{$pos->x}:{$pos->y}:{$pos->z}";
|
||||
if(isset($this->updateQueueIndex[$index]) and $this->updateQueueIndex[$index] <= $delay){
|
||||
return;
|
||||
}
|
||||
$this->updateQueueIndex[$index] = $delay;
|
||||
$this->updateQueue->insert(new Vector3((int) $pos->x, (int) $pos->y, (int) $pos->z), (int) $delay + $this->server->getTick());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -713,7 +728,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
for($z = $minZ; $z < $maxZ; ++$z){
|
||||
for($x = $minX; $x < $maxX; ++$x){
|
||||
for($y = $minY - 1; $y < $maxY; ++$y){
|
||||
$this->getBlock(new Vector3($x, $y, $z))->collidesWithBB($bb, $collides);
|
||||
$block = $this->getBlock(new Vector3($x, $y, $z));
|
||||
if(!($block instanceof Air)){
|
||||
$block->collidesWithBB($bb, $collides);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -727,7 +745,11 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool
|
||||
*/
|
||||
public function isFullBlock(Vector3 $pos){
|
||||
$bb = $this->getBlock($pos)->getBoundingBox();
|
||||
if($pos instanceof Block){
|
||||
$bb = $pos->getBoundingBox();
|
||||
}else{
|
||||
$bb = $this->getBlock($pos)->getBoundingBox();
|
||||
}
|
||||
|
||||
return $bb instanceof AxisAlignedBB and $bb->getAverageEdgeLength() >= 1;
|
||||
}
|
||||
@ -748,11 +770,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$collides = [];
|
||||
|
||||
//TODO: optimize this loop, check collision cube boundaries
|
||||
for($z = $minZ; $z < $maxZ; ++$z){
|
||||
for($x = $minX; $x < $maxX; ++$x){
|
||||
for($y = $minY - 1; $y < $maxY; ++$y){
|
||||
$this->getBlock(new Vector3($x, $y, $z))->collidesWithBB($bb, $collides);
|
||||
$block = $this->getBlock(new Vector3($x, $y, $z));
|
||||
if(!($block instanceof Air)){
|
||||
$block->collidesWithBB($bb, $collides);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -846,7 +870,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function getBlock(Vector3 $pos){
|
||||
$blockId = 0;
|
||||
$meta = 0;
|
||||
if($pos->y >= 0 and $pos->y < 128 and ($chunk = $this->getChunkAt($pos->x >> 4, $pos->z >> 4, true)) !== null){
|
||||
if($pos->y >= 0 and $pos->y < 128 and ($chunk = $this->getChunk($pos->x >> 4, $pos->z >> 4, true)) !== null){
|
||||
$chunk->getBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f, $blockId, $meta);
|
||||
}
|
||||
|
||||
@ -859,7 +883,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
return $air;
|
||||
}
|
||||
|
||||
return Block::get($blockId, $meta, Position::fromObject($pos, $this));
|
||||
return Block::get($blockId, $meta, new Position($pos->x, $pos->y, $pos->z, $this));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -885,7 +909,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->getChunkAt($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f, $block->getID(), $block->getDamage())){
|
||||
if($this->getChunk($pos->x >> 4, $pos->z >> 4, true)->setBlock($pos->x & 0x0f, $pos->y & 0x7f, $pos->z & 0x0f, $block->getID(), $block->getDamage())){
|
||||
if(!($pos instanceof Position)){
|
||||
$pos = new Position($pos->x, $pos->y, $pos->z, $this);
|
||||
}
|
||||
@ -905,7 +929,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
foreach($this->getUsingChunk($pos->x >> 4, $pos->z >> 4) as $player){
|
||||
/** @var Player $player */
|
||||
$player->directDataPacket($pk);
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}else{
|
||||
if(!($pos instanceof Position)){
|
||||
@ -935,11 +959,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param Vector3 $source
|
||||
* @param Item $item
|
||||
* @param Vector3 $motion
|
||||
* @param int $delay
|
||||
*/
|
||||
public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, $delay = 10){
|
||||
$motion = $motion === null ? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion;
|
||||
if($item->getID() !== Item::AIR and $item->getCount() > 0){
|
||||
$itemEntity = new DroppedItem($this->getChunkAt($source->getX() >> 4, $source->getZ() >> 4), new Compound("", [
|
||||
if($item->getID() > 0 and $item->getCount() > 0){
|
||||
$itemEntity = new DroppedItem($this->getChunk($source->getX() >> 4, $source->getZ() >> 4), new Compound("", [
|
||||
"Pos" => new Enum("Pos", [
|
||||
new Double("", $source->getX()),
|
||||
new Double("", $source->getY()),
|
||||
@ -1054,7 +1079,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!($player instanceof Player) or ($player->getGamemode() & 0x01) === 0){
|
||||
foreach($drops as $drop){
|
||||
if($drop[2] > 0){
|
||||
$this->dropItem($vector->add(0.5, 0.5, 0.5), Item::get($drop[0], $drop[1], $drop[2]));
|
||||
$this->dropItem($vector->add(0.5, 0.5, 0.5), Item::get(...$drop));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1172,7 +1197,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($hand->getID() === Item::SIGN_POST or $hand->getID() === Item::WALL_SIGN){
|
||||
$tile = new Sign($this->getChunkAt($block->x >> 4, $block->z >> 4), new Compound(false, [
|
||||
$tile = new Sign($this->getChunk($block->x >> 4, $block->z >> 4), new Compound(false, [
|
||||
"id" => new String("id", Tile::SIGN),
|
||||
"x" => new Int("x", $block->x),
|
||||
"y" => new Int("y", $block->y),
|
||||
@ -1213,7 +1238,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entities near the current one inside the AxisAlignedBB
|
||||
* Returns the entities colliding the current one inside the AxisAlignedBB
|
||||
*
|
||||
* @param AxisAlignedBB $bb
|
||||
* @param Entity $entity
|
||||
@ -1223,16 +1248,18 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function getCollidingEntities(AxisAlignedBB $bb, Entity $entity = null){
|
||||
$nearby = [];
|
||||
|
||||
$minX = ($bb->minX - 2) >> 4;
|
||||
$maxX = ($bb->maxX + 2) >> 4;
|
||||
$minZ = ($bb->minZ - 2) >> 4;
|
||||
$maxZ = ($bb->maxZ + 2) >> 4;
|
||||
if($entity === null or $entity->canCollide){
|
||||
$minX = Math::floorFloat(($bb->minX - 2) / 16);
|
||||
$maxX = Math::floorFloat(($bb->maxX - 2) / 16);
|
||||
$minZ = Math::floorFloat(($bb->minZ - 2) / 16);
|
||||
$maxZ = Math::floorFloat(($bb->maxZ - 2) / 16);
|
||||
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
foreach($this->getChunkEntities($x, $z) as $ent){
|
||||
if($ent !== $entity and ($entity === null or $entity->canCollideWith($ent)) and $ent->boundingBox->intersectsWith($bb)){
|
||||
$nearby[] = $ent;
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
foreach($this->getChunkEntities($x, $z) as $ent){
|
||||
if($ent !== $entity and ($entity === null or $entity->canCollideWith($ent)) and $ent->boundingBox->intersectsWith($bb)){
|
||||
$nearby[] = $ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1252,10 +1279,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function getNearbyEntities(AxisAlignedBB $bb, Entity $entity = null){
|
||||
$nearby = [];
|
||||
|
||||
$minX = ($bb->minX - 2) >> 4;
|
||||
$maxX = ($bb->maxX + 2) >> 4;
|
||||
$minZ = ($bb->minZ - 2) >> 4;
|
||||
$maxZ = ($bb->maxZ + 2) >> 4;
|
||||
$minX = Math::floorFloat(($bb->minX - 2) / 16);
|
||||
$maxX = Math::floorFloat(($bb->maxX - 2) / 16);
|
||||
$minZ = Math::floorFloat(($bb->minZ - 2) / 16);
|
||||
$maxZ = Math::floorFloat(($bb->maxZ - 2) / 16);
|
||||
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
@ -1331,7 +1358,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getChunkEntities($X, $Z){
|
||||
return ($chunk = $this->getChunkAt($X, $Z)) instanceof FullChunk ? $chunk->getEntities() : [];
|
||||
return ($chunk = $this->getChunk($X, $Z)) instanceof FullChunk ? $chunk->getEntities() : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1343,7 +1370,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return Tile[]
|
||||
*/
|
||||
public function getChunkTiles($X, $Z){
|
||||
return ($chunk = $this->getChunkAt($X, $Z)) instanceof FullChunk ? $chunk->getTiles() : [];
|
||||
return ($chunk = $this->getChunk($X, $Z)) instanceof FullChunk ? $chunk->getTiles() : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1356,7 +1383,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1368,7 +1395,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1381,7 +1408,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1393,7 +1420,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1406,7 +1433,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockSkyLightAt($x, $y, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1418,7 +1445,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockSkyLightAt($x, $y, $z, $level){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBlockSkyLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1431,7 +1458,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockLightAt($x, $y, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1443,7 +1470,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $level 0-15
|
||||
*/
|
||||
public function setBlockLightAt($x, $y, $z, $level){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBlockLight($x & 0x0f, $y & 0x7f, $z & 0x0f, $level & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1453,7 +1480,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int
|
||||
*/
|
||||
public function getBiomeId($x, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBiomeId($x & 0x0f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBiomeId($x & 0x0f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1463,7 +1490,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return int[]
|
||||
*/
|
||||
public function getBiomeColor($x, $z){
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getBiomeColor($x & 0x0f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getBiomeColor($x & 0x0f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1472,7 +1499,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $biomeId
|
||||
*/
|
||||
public function setBiomeId($x, $z, $biomeId){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBiomeId($x & 0x0f, $z & 0x0f, $biomeId);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBiomeId($x & 0x0f, $z & 0x0f, $biomeId);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1483,7 +1510,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @param int $B
|
||||
*/
|
||||
public function setBiomeColor($x, $z, $R, $G, $B){
|
||||
$this->getChunkAt($x >> 4, $z >> 4, true)->setBiomeColor($x & 0x0f, $z & 0x0f, $R, $G, $B);
|
||||
$this->getChunk($x >> 4, $z >> 4, true)->setBiomeColor($x & 0x0f, $z & 0x0f, $R, $G, $B);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1495,38 +1522,53 @@ class Level implements ChunkManager, Metadatable{
|
||||
*
|
||||
* @return Chunk
|
||||
*/
|
||||
public function getChunkAt($x, $z, $create = false){
|
||||
public function getChunk($x, $z, $create = false){
|
||||
if(isset($this->chunks[$index = "$x:$z"])){
|
||||
return $this->chunks[$index];
|
||||
}elseif(($chunk = $this->provider->getChunk($x, $z, $create)) instanceof FullChunk){
|
||||
$this->chunks[$index] = $chunk;
|
||||
|
||||
$chunk->initChunk();
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $z
|
||||
* @param bool $create
|
||||
*
|
||||
* @return Chunk
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function getChunkAt($x, $z, $create = false){
|
||||
return $this->getChunk($x, $z, $create);
|
||||
}
|
||||
|
||||
public function generateChunkCallback($x, $z, FullChunk $chunk){
|
||||
$oldChunk = $this->getChunkAt($x, $z);
|
||||
$oldChunk = $this->getChunk($x, $z);
|
||||
unset($this->chunkGenerationQueue["$x:$z"]);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
$chunk = $this->getChunkAt($x, $z);
|
||||
if($chunk instanceof FullChunk){
|
||||
if(!($oldChunk instanceof FullChunk) or ($oldChunk->isPopulated() === false and $chunk->isPopulated())){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
|
||||
}
|
||||
$chunk = $this->getChunk($x, $z);
|
||||
if($chunk instanceof FullChunk and (!($oldChunk instanceof FullChunk) or $oldChunk->isPopulated() === false) and $chunk->isPopulated()){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunk($x, $z, FullChunk $chunk){
|
||||
public function setChunk($x, $z, FullChunk $chunk, $unload = true){
|
||||
$index = Level::chunkHash($x, $z);
|
||||
foreach($this->getUsingChunk($x, $z) as $player){
|
||||
$player->unloadChunk($x, $z);
|
||||
if($unload){
|
||||
foreach($this->getUsingChunk($x, $z) as $player){
|
||||
$player->unloadChunk($x, $z);
|
||||
}
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->chunks[$index] = $chunk;
|
||||
}else{
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->chunks[$index] = $chunk;
|
||||
}
|
||||
unset($this->chunks[$index]);
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->loadChunk($x, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1542,7 +1584,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->loadChunk($x >> 4, $z >> 4);
|
||||
}
|
||||
|
||||
return $this->getChunkAt($x >> 4, $z >> 4, true)->getHighestBlockAt($x & 0x0f, $z & 0x0f);
|
||||
return $this->getChunk($x >> 4, $z >> 4, true)->getHighestBlockAt($x & 0x0f, $z & 0x0f);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1562,7 +1604,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkGenerated($x, $z){
|
||||
$chunk = $this->getChunkAt($x, $z);
|
||||
$chunk = $this->getChunk($x, $z);
|
||||
return $chunk instanceof FullChunk ? $chunk->isGenerated() : false;
|
||||
}
|
||||
|
||||
@ -1573,7 +1615,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool
|
||||
*/
|
||||
public function isChunkPopulated($x, $z){
|
||||
$chunk = $this->getChunkAt($x, $z);
|
||||
$chunk = $this->getChunk($x, $z);
|
||||
return $chunk instanceof FullChunk ? $chunk->isPopulated() : false;
|
||||
}
|
||||
|
||||
@ -1671,10 +1713,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
$entity->kill();
|
||||
}
|
||||
|
||||
if($this->isChunkLoaded($entity->chunkX, $entity->chunkZ)){
|
||||
$this->getChunkAt($entity->chunkX, $entity->chunkZ, true)->removeEntity($entity);
|
||||
}
|
||||
|
||||
unset($this->entities[$entity->getID()]);
|
||||
}
|
||||
|
||||
@ -1714,9 +1752,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
if($tile->getLevel() !== $this){
|
||||
throw new \RuntimeException("Invalid Tile level");
|
||||
}
|
||||
if($this->isChunkLoaded($tile->chunk->getX(), $tile->chunk->getZ())){
|
||||
$this->getChunkAt($tile->chunk->getX(), $tile->chunk->getZ(), true)->removeTile($tile);
|
||||
}
|
||||
|
||||
unset($this->tiles[$tile->getID()]);
|
||||
}
|
||||
|
||||
@ -1789,7 +1825,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$index = "$x:$z";
|
||||
|
||||
$chunk = $this->getChunkAt($x, $z);
|
||||
$chunk = $this->getChunk($x, $z);
|
||||
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk));
|
||||
@ -1800,10 +1836,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$this->timings->doChunkUnload->startTiming();
|
||||
|
||||
if($this->getAutoSave()){
|
||||
if(isset($this->chunks[$index])){
|
||||
$this->provider->setChunk($x, $z, $this->chunks[$index]);
|
||||
}
|
||||
if($chunk instanceof FullChunk and $this->getAutoSave()){
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->provider->saveChunk($x, $z);
|
||||
}
|
||||
|
||||
@ -1851,9 +1885,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$spawn = $this->getSpawn();
|
||||
}
|
||||
if($spawn instanceof Vector3){
|
||||
$x = (int) round($spawn->x);
|
||||
$y = (int) round($spawn->y);
|
||||
$z = (int) round($spawn->z);
|
||||
$x = Math::floorFloat($spawn->x);
|
||||
$y = Math::floorFloat($spawn->y);
|
||||
$z = Math::floorFloat($spawn->z);
|
||||
for(; $y > 0; --$y){
|
||||
$v = new Vector3($x, $y, $z);
|
||||
$b = $this->getBlock($v->getSide(0));
|
||||
@ -1867,14 +1901,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
$v = new Vector3($x, $y, $z);
|
||||
if($this->getBlock($v->getSide(1)) instanceof Air){
|
||||
if($this->getBlock($v) instanceof Air){
|
||||
return new Position($x, $y, $z, $this);
|
||||
return new Position($spawn->x, $y === Math::floorFloat($spawn->y) ? $spawn->y : $y, $spawn->z, $this);
|
||||
}
|
||||
}else{
|
||||
++$y;
|
||||
}
|
||||
}
|
||||
|
||||
return new Position($x, $y, $z, $this);
|
||||
return new Position($spawn->x, $y, $spawn->z, $this);
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1986,44 +2020,32 @@ class Level implements ChunkManager, Metadatable{
|
||||
$Z = null;
|
||||
|
||||
foreach($this->chunks as $index => $chunk){
|
||||
if(!isset($this->usedChunks[$index])){
|
||||
if(!isset($this->unloadQueue[$index]) and (!isset($this->usedChunks[$index]) or count($this->usedChunks[$index]) === 0)){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
$this->unloadChunkRequest($X, $Z, true);
|
||||
if(!$this->isSpawnChunk($X, $Z)){
|
||||
$this->unloadChunkRequest($X, $Z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->timings->doChunkGC->stopTiming();
|
||||
}
|
||||
|
||||
protected function unloadChunks(){
|
||||
if(count($this->unloadQueue) > 0){
|
||||
$X = null;
|
||||
$Z = null;
|
||||
foreach($this->unloadQueue as $index => $time){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
|
||||
if($this->getAutoSave()){
|
||||
$this->provider->saveChunk($X, $Z);
|
||||
}
|
||||
//If the chunk can't be unloaded, it stays on the queue
|
||||
if($this->unloadChunk($X, $Z, true)){
|
||||
unset($this->unloadQueue[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->usedChunks as $i => $c){
|
||||
if(count($c) === 0){
|
||||
Level::getXZ($i, $X, $Z);
|
||||
if(!$this->isSpawnChunk($X, $Z)){
|
||||
if($this->getAutoSave()){
|
||||
$this->provider->saveChunk($X, $Z);
|
||||
}
|
||||
|
||||
$this->unloadChunk($X, $Z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->timings->doChunkGC->stopTiming();
|
||||
}
|
||||
|
||||
|
||||
public function setMetadata($metadataKey, MetadataValue $metadataValue){
|
||||
$this->server->getPlayerMetadata()->setMetadata($this, $metadataKey, $metadataValue);
|
||||
}
|
||||
|
@ -248,6 +248,8 @@ interface FullChunk{
|
||||
*/
|
||||
public function unload($save = true, $safe = true);
|
||||
|
||||
public function initChunk();
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
|
@ -98,20 +98,12 @@ class Anvil extends McRegion{
|
||||
|
||||
$chunk->setProvider($this);
|
||||
|
||||
if($chunk->isPopulated() === false){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false);
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$region = $this->getRegion($regionX, $regionZ);
|
||||
$region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}else{
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
//$this->saveChunk($chunkX, $chunkZ);
|
||||
}
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
|
@ -182,10 +182,8 @@ class Chunk extends BaseChunk{
|
||||
|
||||
$tiles = [];
|
||||
foreach($this->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
|
@ -98,49 +98,8 @@ abstract class BaseChunk extends BaseFullChunk implements Chunk{
|
||||
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"));
|
||||
}
|
||||
|
||||
if($this->provider instanceof LevelProvider){
|
||||
$this->provider->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($entities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
|
||||
//TODO: add all entities
|
||||
if($nbt->id instanceof String){ //New format
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($tiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
switch($nbt["id"]){
|
||||
case Tile::CHEST:
|
||||
new Chest($this, $nbt);
|
||||
break;
|
||||
case Tile::FURNACE:
|
||||
new Furnace($this, $nbt);
|
||||
break;
|
||||
case Tile::SIGN:
|
||||
new Sign($this, $nbt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
}
|
||||
$this->NBTtiles = $tiles;
|
||||
$this->NBTentities = $entities;
|
||||
}
|
||||
|
||||
public function getBlock($x, $y, $z, &$blockId, &$meta = null){
|
||||
|
@ -21,12 +21,14 @@
|
||||
|
||||
namespace pocketmine\level\format\generic;
|
||||
|
||||
use pocketmine\entity\Arrow;
|
||||
use pocketmine\entity\DroppedItem;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\FallingBlock;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\tile\Chest;
|
||||
use pocketmine\tile\Furnace;
|
||||
use pocketmine\tile\Sign;
|
||||
@ -55,6 +57,10 @@ abstract class BaseFullChunk implements FullChunk{
|
||||
|
||||
protected $blockLight;
|
||||
|
||||
protected $NBTtiles;
|
||||
|
||||
protected $NBTentities;
|
||||
|
||||
/** @var LevelProvider */
|
||||
protected $provider;
|
||||
|
||||
@ -98,30 +104,40 @@ abstract class BaseFullChunk implements FullChunk{
|
||||
$this->biomeColors = array_fill(0, 256, Binary::readInt("\x00\x85\xb2\x4a"));
|
||||
}
|
||||
|
||||
if($this->getProvider() instanceof LevelProvider){
|
||||
$this->NBTtiles = $tiles;
|
||||
$this->NBTentities = $entities;
|
||||
}
|
||||
|
||||
public function initChunk(){
|
||||
if($this->getProvider() instanceof LevelProvider and $this->NBTentities !== null){
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->startTiming();
|
||||
foreach($entities as $nbt){
|
||||
foreach($this->NBTentities as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
}
|
||||
|
||||
//TODO: add all entities
|
||||
if($nbt->id instanceof String){ //New format
|
||||
switch($nbt["id"]){
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}else{ //Old format
|
||||
|
||||
switch($nbt["id"]){
|
||||
case DroppedItem::NETWORK_ID:
|
||||
case "Item":
|
||||
(new DroppedItem($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
case Arrow::NETWORK_ID:
|
||||
case "Arrow":
|
||||
(new Arrow($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
case FallingBlock::NETWORK_ID:
|
||||
case "FallingSand":
|
||||
(new FallingBlock($this, $nbt))->spawnToAll();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadEntitiesTimer->stopTiming();
|
||||
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->startTiming();
|
||||
foreach($tiles as $nbt){
|
||||
foreach($this->NBTtiles as $nbt){
|
||||
if($nbt instanceof Compound){
|
||||
if(!isset($nbt->id)){
|
||||
continue;
|
||||
@ -140,6 +156,8 @@ abstract class BaseFullChunk implements FullChunk{
|
||||
}
|
||||
}
|
||||
$this->getProvider()->getLevel()->timings->syncChunkLoadTileEntitiesTimer->stopTiming();
|
||||
$this->NBTentities = null;
|
||||
$this->NBTtiles = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -248,14 +266,24 @@ abstract class BaseFullChunk implements FullChunk{
|
||||
if($save === true){
|
||||
$level->saveChunk($this->getX(), $this->getZ());
|
||||
}
|
||||
if($this->getProvider()->unloadChunk($this->getX(), $this->getZ(), $safe)){
|
||||
if($safe === true){
|
||||
foreach($this->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
continue;
|
||||
}
|
||||
$entity->close();
|
||||
}
|
||||
foreach($this->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBlockIdArray(){
|
||||
|
@ -288,10 +288,8 @@ class Chunk extends BaseFullChunk{
|
||||
|
||||
$tiles = [];
|
||||
foreach($this->getTiles() as $tile){
|
||||
if($tile->closed !== true){
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
$tile->saveNBT();
|
||||
$tiles[] = $tile->namedtag;
|
||||
}
|
||||
|
||||
$nbt->Entities = new Enum("TileEntities", $tiles);
|
||||
|
@ -183,6 +183,7 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->chunks[$index] = $chunk;
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
@ -190,23 +191,7 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
public function unloadChunk($x, $z, $safe = true){
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk instanceof FullChunk){
|
||||
if($safe === true){
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
if($entity instanceof Player){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($chunk->getEntities() as $entity){
|
||||
$entity->close();
|
||||
}
|
||||
|
||||
foreach($chunk->getTiles() as $tile){
|
||||
$tile->close();
|
||||
}
|
||||
|
||||
if($chunk instanceof FullChunk and $chunk->unload(false, $safe)){
|
||||
$this->chunks[$index = Level::chunkHash($x, $z)] = null;
|
||||
|
||||
unset($this->chunks[$index]);
|
||||
@ -262,20 +247,19 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
$chunk->setProvider($this);
|
||||
|
||||
if($chunk->isPopulated() === false){
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
|
||||
|
||||
|
||||
if($this->getChunk($chunkX, $chunkZ, false) !== $chunk){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false);
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
$this->loadRegion($regionX, $regionZ);
|
||||
$region = $this->getRegion($regionX, $regionZ);
|
||||
$region->removeChunk($chunkX - $region->getX() * 32, $chunkZ - $region->getZ() * 32);
|
||||
$this->loadChunk($chunkX, $chunkZ);
|
||||
}else{
|
||||
$chunk->setX($chunkX);
|
||||
$chunk->setZ($chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
//$this->saveChunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}
|
||||
|
||||
public static function createChunkSection($Y){
|
||||
|
@ -81,7 +81,7 @@ class GenerationChunkManager implements ChunkManager{
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
$chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
|
||||
if($chunk === null){
|
||||
throw new \Exception("null chunk received");
|
||||
throw new \Exception("null Chunk received");
|
||||
}
|
||||
|
||||
return $chunk;
|
||||
|
99
src/pocketmine/level/generator/GenerationInstanceManager.php
Normal file
99
src/pocketmine/level/generator/GenerationInstanceManager.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationInstanceManager extends GenerationRequestManager{
|
||||
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
/** @var GenerationManager */
|
||||
protected $generationManager;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->generationManager = new GenerationLevelManager($this->server, $this);
|
||||
}
|
||||
|
||||
public function process(){
|
||||
$this->generationManager->process();
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
$this->generationManager->shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param string $generator
|
||||
* @param array $options
|
||||
*/
|
||||
public function openLevel(Level $level, $generator, array $options = []){
|
||||
$this->generationManager->openLevel($level->getID(), $level->getSeed(), $generator, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
*/
|
||||
public function closeLevel(Level $level){
|
||||
$this->generationManager->closeLevel($level->getID());
|
||||
}
|
||||
|
||||
public function addNamespace($namespace, $path){
|
||||
|
||||
}
|
||||
|
||||
public function requestChunk(Level $level, $chunkX, $chunkZ){
|
||||
$this->generationManager->enqueueChunk($level->getID(), $chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function getChunk($levelID, $chunkX, $chunkZ){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, true);
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
throw new \Exception("Invalid Chunk given");
|
||||
}
|
||||
}else{
|
||||
$this->generationManager->closeLevel($levelID);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function receiveChunk($levelID, FullChunk $chunk){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}else{
|
||||
$this->generationManager->closeLevel($levelID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
144
src/pocketmine/level/generator/GenerationLevelManager.php
Normal file
144
src/pocketmine/level/generator/GenerationLevelManager.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationLevelManager extends GenerationManager{
|
||||
|
||||
/** @var GenerationChunkManager[] */
|
||||
protected $levels = [];
|
||||
|
||||
/** @var array */
|
||||
protected $requestQueue = [];
|
||||
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
|
||||
/** @var GenerationInstanceManager */
|
||||
protected $manager;
|
||||
|
||||
protected $maxCount;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @param GenerationInstanceManager $manager
|
||||
*/
|
||||
public function __construct(Server $server, GenerationInstanceManager $manager){
|
||||
$this->server = $server;
|
||||
$this->manager = $manager;
|
||||
$this->maxCount = $this->server->getProperty("chunk-generation.per-tick", 1);
|
||||
}
|
||||
|
||||
public function openLevel($levelID, $seed, $class, array $options){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateChunk($levelID, $chunkX, $chunkZ){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
|
||||
if(isset($this->levels[$levelID])){
|
||||
foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
$this->levels[$levelID]->cleanChangedChunk($index);
|
||||
}
|
||||
|
||||
$this->levels[$levelID]->doGarbageCollection();
|
||||
$this->levels[$levelID]->cleanChangedChunks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function process(){
|
||||
if(count($this->requestQueue) > 0){
|
||||
$count = 0;
|
||||
foreach($this->requestQueue as $levelID => $chunks){
|
||||
if($count >= $this->maxCount){
|
||||
break;
|
||||
}
|
||||
|
||||
if(count($chunks) === 0){
|
||||
unset($this->requestQueue[$levelID]);
|
||||
}else{
|
||||
Level::getXZ($key = key($chunks), $chunkX, $chunkZ);
|
||||
unset($this->requestQueue[$levelID][$key]);
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
}
|
||||
$this->levels = [];
|
||||
}
|
||||
|
||||
public function closeLevel($levelID){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->shutdown();
|
||||
unset($this->levels[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueueChunk($levelID, $chunkX, $chunkZ){
|
||||
if(!isset($this->requestQueue[$levelID])){
|
||||
$this->requestQueue[$levelID] = [];
|
||||
}
|
||||
if(!isset($this->requestQueue[$levelID][$index = "$chunkX:$chunkZ"])){
|
||||
$this->requestQueue[$levelID][$index] = 1;
|
||||
}else{
|
||||
$this->requestQueue[$levelID][$index]++;
|
||||
arsort($this->requestQueue[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $levelID
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
return $this->manager->getChunk($levelID, $chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function sendChunk($levelID, FullChunk $chunk){
|
||||
$this->manager->receiveChunk($levelID, $chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Logger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->server->getLogger();
|
||||
}
|
||||
|
||||
}
|
@ -164,7 +164,7 @@ class GenerationManager{
|
||||
}
|
||||
|
||||
protected function closeLevel($levelID){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->shutdown();
|
||||
unset($this->levels[$levelID]);
|
||||
}
|
||||
|
@ -33,12 +33,17 @@ class GenerationRequestManager{
|
||||
/** @var GenerationThread */
|
||||
protected $generationThread;
|
||||
|
||||
private $internalThreaded;
|
||||
private $externalThreaded;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->generationThread = new GenerationThread($server->getLogger(), $server->getLoader());
|
||||
$this->internalThreaded = new \Threaded();
|
||||
$this->externalThreaded = new \Threaded();
|
||||
$this->generationThread = new GenerationThread($this->internalThreaded, $this->externalThreaded, $server->getLogger(), $server->getLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +83,7 @@ class GenerationRequestManager{
|
||||
|
||||
protected function handleRequest($levelID, $chunkX, $chunkZ){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$chunk = $level->getChunkAt($chunkX, $chunkZ, true);
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, true);
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
}else{
|
||||
@ -99,6 +104,10 @@ class GenerationRequestManager{
|
||||
}
|
||||
}
|
||||
|
||||
public function process(){
|
||||
$this->handlePackets();
|
||||
}
|
||||
|
||||
public function handlePackets(){
|
||||
while(strlen($packet = $this->generationThread->readThreadToMainPacket()) > 0){
|
||||
$pid = ord($packet{0});
|
||||
|
@ -77,15 +77,15 @@ class GenerationThread extends Thread{
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
public function __construct(\ThreadedLogger $logger, \ClassLoader $loader){
|
||||
public function __construct(\Threaded $internalThreaded, \Threaded $externalThreaded, \ThreadedLogger $logger, \ClassLoader $loader){
|
||||
$this->loader = $loader;
|
||||
$this->logger = $logger;
|
||||
$loadPaths = [];
|
||||
$this->addDependency($loadPaths, new \ReflectionClass($this->loader));
|
||||
$this->loadPaths = array_reverse($loadPaths);
|
||||
|
||||
$this->externalQueue = new \Threaded();
|
||||
$this->internalQueue = new \Threaded();
|
||||
$this->externalQueue = $internalThreaded;
|
||||
$this->internalQueue = $externalThreaded;
|
||||
|
||||
$this->start();
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ abstract class Generator{
|
||||
}
|
||||
|
||||
public static function fade($x){
|
||||
return $x * $x * $x * ($x * ($x * 6 - 15) + 10);
|
||||
return $x ** 3 * ($x * ($x * 6 - 15) + 10);
|
||||
}
|
||||
|
||||
public static function lerp($x, $y, $z){
|
||||
|
@ -25,11 +25,10 @@ use pocketmine\utils\Random;
|
||||
|
||||
/**
|
||||
* Generates simplex-based noise.
|
||||
* <p>
|
||||
*
|
||||
* This is a modified version of the freely published version in the paper by
|
||||
* Stefan Gustavson at
|
||||
* <a href="http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf">
|
||||
* http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf</a>
|
||||
* http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
|
||||
*/
|
||||
class Simplex extends Perlin{
|
||||
protected static $SQRT_3;
|
||||
@ -192,36 +191,36 @@ class Simplex extends Perlin{
|
||||
$gi3 = $this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12;
|
||||
|
||||
// Calculate the contribution from the four corners
|
||||
$t0 = 0.6 - $x0 * $x0 - $y0 * $y0 - $z0 * $z0;
|
||||
$t0 = 0.6 - $x0 ** 2 - $y0 ** 2 - $z0 ** 2;
|
||||
if($t0 < 0){
|
||||
$n0 = 0.0;
|
||||
}else{
|
||||
$t0 *= $t0;
|
||||
$n0 = $t0 * $t0 * self::dot3D(self::$grad3[$gi0], $x0, $y0, $z0);
|
||||
$t0 **= 2;
|
||||
$n0 = $t0 ** 2 * self::dot3D(self::$grad3[$gi0], $x0, $y0, $z0);
|
||||
}
|
||||
|
||||
$t1 = 0.6 - $x1 * $x1 - $y1 * $y1 - $z1 * $z1;
|
||||
$t1 = 0.6 - $x1 ** 2 - $y1 ** 2 - $z1 ** 2;
|
||||
if($t1 < 0){
|
||||
$n1 = 0.0;
|
||||
}else{
|
||||
$t1 *= $t1;
|
||||
$n1 = $t1 * $t1 * self::dot3D(self::$grad3[$gi1], $x1, $y1, $z1);
|
||||
$t1 **= 2;
|
||||
$n1 = $t1 ** 2 * self::dot3D(self::$grad3[$gi1], $x1, $y1, $z1);
|
||||
}
|
||||
|
||||
$t2 = 0.6 - $x2 * $x2 - $y2 * $y2 - $z2 * $z2;
|
||||
$t2 = 0.6 - $x2 ** 2 - $y2 ** 2 - $z2 ** 2;
|
||||
if($t2 < 0){
|
||||
$n2 = 0.0;
|
||||
}else{
|
||||
$t2 *= $t2;
|
||||
$n2 = $t2 * $t2 * self::dot3D(self::$grad3[$gi2], $x2, $y2, $z2);
|
||||
$t2 **= 2;
|
||||
$n2 = $t2 ** 2 * self::dot3D(self::$grad3[$gi2], $x2, $y2, $z2);
|
||||
}
|
||||
|
||||
$t3 = 0.6 - $x3 * $x3 - $y3 * $y3 - $z3 * $z3;
|
||||
$t3 = 0.6 - $x3 ** 2 - $y3 ** 2 - $z3 ** 2;
|
||||
if($t3 < 0){
|
||||
$n3 = 0.0;
|
||||
}else{
|
||||
$t3 *= $t3;
|
||||
$n3 = $t3 * $t3 * self::dot3D(self::$grad3[$gi3], $x3, $y3, $z3);
|
||||
$t3 **= 2;
|
||||
$n3 = $t3 ** 2 * self::dot3D(self::$grad3[$gi3], $x3, $y3, $z3);
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the noise value.
|
||||
@ -273,28 +272,28 @@ class Simplex extends Perlin{
|
||||
$gi2 = $this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12;
|
||||
|
||||
// Calculate the contribution from the three corners
|
||||
$t0 = 0.5 - $x0 * $x0 - $y0 * $y0;
|
||||
$t0 = 0.5 - $x0 ** 2 - $y0 ** 2;
|
||||
if($t0 < 0){
|
||||
$n0 = 0.0;
|
||||
}else{
|
||||
$t0 *= $t0;
|
||||
$n0 = $t0 * $t0 * self::dot2D(self::$grad3[$gi0], $x0, $y0); // (x,y) of grad3 used for 2D gradient
|
||||
$t0 **= 2;
|
||||
$n0 = $t0 ** 2 * self::dot2D(self::$grad3[$gi0], $x0, $y0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
|
||||
$t1 = 0.5 - $x1 * $x1 - $y1 * $y1;
|
||||
$t1 = 0.5 - $x1 ** 2 - $y1 ** 2;
|
||||
if($t1 < 0){
|
||||
$n1 = 0.0;
|
||||
}else{
|
||||
$t1 *= $t1;
|
||||
$n1 = $t1 * $t1 * self::dot2D(self::$grad3[$gi1], $x1, $y1);
|
||||
$t1 **= 2;
|
||||
$n1 = $t1 ** 2 * self::dot2D(self::$grad3[$gi1], $x1, $y1);
|
||||
}
|
||||
|
||||
$t2 = 0.5 - $x2 * $x2 - $y2 * $y2;
|
||||
$t2 = 0.5 - $x2 ** 2 - $y2 ** 2;
|
||||
if($t2 < 0){
|
||||
$n2 = 0.0;
|
||||
}else{
|
||||
$t2 *= $t2;
|
||||
$n2 = $t2 * $t2 * self::dot2D(self::$grad3[$gi2], $x2, $y2);
|
||||
$t2 **= 2;
|
||||
$n2 = $t2 ** 2 * self::dot2D(self::$grad3[$gi2], $x2, $y2);
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the noise value.
|
||||
|
@ -67,17 +67,17 @@ class Ore{
|
||||
|
||||
for($x = $startX; $x <= $endX; ++$x){
|
||||
$sizeX = ($x + 0.5 - $seedX) / $size;
|
||||
$sizeX *= $sizeX;
|
||||
$sizeX **= 2;
|
||||
|
||||
if($sizeX < 1){
|
||||
for($y = $startY; $y <= $endY; ++$y){
|
||||
$sizeY = ($y + 0.5 - $seedY) / $size;
|
||||
$sizeY *= $sizeY;
|
||||
$sizeY **= 2;
|
||||
|
||||
if($y > 0 and ($sizeX + $sizeY) < 1){
|
||||
for($z = $startZ; $z <= $endZ; ++$z){
|
||||
$sizeZ = ($z + 0.5 - $seedZ) / $size;
|
||||
$sizeZ *= $sizeZ;
|
||||
$sizeZ **= 2;
|
||||
|
||||
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){
|
||||
$level->setBlockIdAt($x, $y, $z, $this->type->material->getID());
|
||||
|
@ -79,7 +79,7 @@ class SmallTree extends Tree{
|
||||
$bRadius = 3;
|
||||
for($xx = -$bRadius; $xx <= $bRadius; ++$xx){
|
||||
for($zz = -$bRadius; $zz <= $bRadius; ++$zz){
|
||||
if(sqrt(($xx * $xx) + ($zz * $zz)) <= $radius){
|
||||
if(sqrt($xx ** 2 + $zz ** 2) <= $radius){
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
|
@ -176,9 +176,12 @@ class Vector3{
|
||||
}
|
||||
|
||||
public function lengthSquared(){
|
||||
return $this->x * $this->x + $this->y * $this->y + $this->z * $this->z;
|
||||
return $this->x ** 2 + $this->y ** 2 + $this->z ** 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector3
|
||||
*/
|
||||
public function normalize(){
|
||||
$len = $this->length();
|
||||
if($len != 0){
|
||||
|
@ -73,7 +73,6 @@ use pocketmine\network\protocol\UnloadChunkPacket;
|
||||
use pocketmine\network\protocol\UpdateBlockPacket;
|
||||
use pocketmine\network\protocol\UseItemPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\scheduler\CallbackTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use raklib\protocol\EncapsulatedPacket;
|
||||
@ -89,7 +88,7 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
private $players = [];
|
||||
|
||||
/** @var \SplObjectStorage */
|
||||
private $identifers;
|
||||
private $identifiers;
|
||||
|
||||
/** @var int[] */
|
||||
private $identifiersACK = [];
|
||||
@ -99,12 +98,18 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
|
||||
private $upload = 0;
|
||||
private $download = 0;
|
||||
|
||||
private $internalThreaded;
|
||||
private $externalThreaded;
|
||||
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->identifers = new \SplObjectStorage();
|
||||
$this->identifiers = new \SplObjectStorage();
|
||||
|
||||
$this->internalThreaded = new \Threaded();
|
||||
$this->externalThreaded = new \Threaded();
|
||||
|
||||
$server = new RakLibServer($this->server->getLogger(), $this->server->getLoader(), $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp());
|
||||
$server = new RakLibServer($this->internalThreaded, $this->externalThreaded, $this->server->getLogger(), $this->server->getLoader(), $this->server->getPort(), $this->server->getIp() === "" ? "0.0.0.0" : $this->server->getIp());
|
||||
$this->interface = new ServerHandler($server, $this);
|
||||
$this->setName($this->server->getMotd());
|
||||
}
|
||||
@ -129,7 +134,7 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
public function closeSession($identifier, $reason){
|
||||
if(isset($this->players[$identifier])){
|
||||
$player = $this->players[$identifier];
|
||||
$this->identifers->detach($player);
|
||||
$this->identifiers->detach($player);
|
||||
unset($this->players[$identifier]);
|
||||
unset($this->identifiersACK[$identifier]);
|
||||
$player->close(TextFormat::YELLOW . $player->getName() . " has left the game", $reason);
|
||||
@ -137,11 +142,11 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
}
|
||||
|
||||
public function close(Player $player, $reason = "unknown reason"){
|
||||
if(isset($this->identifers[$player])){
|
||||
unset($this->players[$this->identifers[$player]]);
|
||||
unset($this->identifiersACK[$this->identifers[$player]]);
|
||||
$this->interface->closeSession($this->identifers[$player], $reason);
|
||||
$this->identifers->detach($player);
|
||||
if(isset($this->identifiers[$player])){
|
||||
unset($this->players[$this->identifiers[$player]]);
|
||||
unset($this->identifiersACK[$this->identifiers[$player]]);
|
||||
$this->interface->closeSession($this->identifiers[$player], $reason);
|
||||
$this->identifiers->detach($player);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,7 +162,7 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
$player = new Player($this, null, $address, $port);
|
||||
$this->players[$identifier] = $player;
|
||||
$this->identifiersACK[$identifier] = 0;
|
||||
$this->identifers->attach($player, $identifier);
|
||||
$this->identifiers->attach($player, $identifier);
|
||||
$this->server->addPlayer($identifier, $player);
|
||||
}
|
||||
|
||||
@ -208,8 +213,8 @@ class RakLibInterface implements ServerInstance, SourceInterface{
|
||||
}
|
||||
|
||||
public function putPacket(Player $player, DataPacket $packet, $needACK = false, $immediate = false){
|
||||
if(isset($this->identifers[$player])){
|
||||
$identifier = $this->identifers[$player];
|
||||
if(isset($this->identifiers[$player])){
|
||||
$identifier = $this->identifiers[$player];
|
||||
$packet->encode();
|
||||
$pk = new EncapsulatedPacket();
|
||||
$pk->buffer = $packet->buffer;
|
||||
|
@ -25,12 +25,13 @@
|
||||
*/
|
||||
namespace pocketmine\network\query;
|
||||
|
||||
use pocketmine\event\server\QueryRegenerateEvent;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
class QueryHandler{
|
||||
private $server, $lastToken, $token, $longData, $timeout;
|
||||
private $server, $lastToken, $token, $longData, $shortData, $timeout;
|
||||
|
||||
const HANDSHAKE = 9;
|
||||
const STATISTICS = 0;
|
||||
@ -57,43 +58,10 @@ class QueryHandler{
|
||||
}
|
||||
|
||||
public function regenerateInfo(){
|
||||
$str = "";
|
||||
$plist = $this->server->getName() . " " . $this->server->getPocketMineVersion();
|
||||
$pl = $this->server->getPluginManager()->getPlugins();
|
||||
if(count($pl) > 0 and $this->server->getProperty("settings.query-plugins", true) === true){
|
||||
$plist .= ":";
|
||||
foreach($pl as $p){
|
||||
$d = $p->getDescription();
|
||||
$plist .= " " . str_replace([";", ":", " "], ["", "", "_"], $d->getName()) . " " . str_replace([";", ":", " "], ["", "", "_"], $d->getVersion()) . ";";
|
||||
}
|
||||
$plist = substr($plist, 0, -1);
|
||||
}
|
||||
$KVdata = [
|
||||
"splitnum" => chr(128),
|
||||
"hostname" => $this->server->getServerName(),
|
||||
"gametype" => ($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP",
|
||||
"game_id" => "MINECRAFTPE",
|
||||
"version" => $this->server->getVersion(),
|
||||
"server_engine" => $this->server->getName() . " " . $this->server->getPocketMineVersion(),
|
||||
"plugins" => $plist,
|
||||
"map" => $this->server->getDefaultLevel() === null ? "unknown" : $this->server->getDefaultLevel()->getName(),
|
||||
"numplayers" => count($this->server->getOnlinePlayers()),
|
||||
"maxplayers" => $this->server->getMaxPlayers(),
|
||||
"whitelist" => $this->server->hasWhitelist() === true ? "on" : "off",
|
||||
"hostport" => $this->server->getPort()
|
||||
];
|
||||
foreach($KVdata as $key => $value){
|
||||
$str .= $key . "\x00" . $value . "\x00";
|
||||
}
|
||||
$str .= "\x00\x01player_\x00\x00";
|
||||
foreach($this->server->getOnlinePlayers() as $player){
|
||||
if($player->getName() != ""){
|
||||
$str .= $player->getName() . "\x00";
|
||||
}
|
||||
}
|
||||
$str .= "\x00";
|
||||
$this->longData = $str;
|
||||
$this->timeout = microtime(true) + 5;
|
||||
$this->server->getPluginManager()->callEvent($ev = new QueryRegenerateEvent($this->server, 5));
|
||||
$this->longData = $ev->getLongQuery();
|
||||
$this->shortData = $ev->getShortQuery();
|
||||
$this->timeout = microtime(true) + $ev->getTimeout();
|
||||
}
|
||||
|
||||
public function regenerateToken(){
|
||||
@ -127,13 +95,15 @@ class QueryHandler{
|
||||
}
|
||||
$reply = chr(self::STATISTICS);
|
||||
$reply .= Binary::writeInt($sessionID);
|
||||
|
||||
if($this->timeout < microtime(true)){
|
||||
$this->regenerateInfo();
|
||||
}
|
||||
|
||||
if(strlen($payload) === 8){
|
||||
if($this->timeout < microtime(true)){
|
||||
$this->regenerateInfo();
|
||||
}
|
||||
$reply .= $this->longData;
|
||||
}else{
|
||||
$reply .= $this->server->getServerName() . "\x00" . (($this->server->getGamemode() & 0x01) === 0 ? "SMP" : "CMP") . "\x00" . ($this->server->getDefaultLevel() === null ? "unknown" : $this->server->getDefaultLevel()->getName()) . "\x00" . count($this->server->getOnlinePlayers()) . "\x00" . $this->server->getMaxPlayers() . "\x00" . Binary::writeLShort($this->server->getPort()) . $this->server->getIp() . "\x00";
|
||||
$reply .= $this->shortData;
|
||||
}
|
||||
$this->server->sendPacket($address, $port, $reply);
|
||||
break;
|
||||
|
@ -641,10 +641,6 @@ class PluginManager{
|
||||
* @param Event $event
|
||||
*/
|
||||
public function callEvent(Event $event){
|
||||
$this->fireEvent($event);
|
||||
}
|
||||
|
||||
private function fireEvent(Event $event){
|
||||
$handlers = $event->getHandlers();
|
||||
$listeners = $handlers->getRegisteredListeners();
|
||||
|
||||
|
@ -33,7 +33,7 @@ chunk-sending:
|
||||
#Amount of chunks sent to players per tick
|
||||
per-tick: 4
|
||||
#Compression level used when sending chunks. Higher = more CPU, less bandwidth usage
|
||||
compression-level: 7
|
||||
compression-level: 8
|
||||
#Amount of chunks loaded around a player by the server, min. 56 as MC: PE 0.9.5
|
||||
#Increasing this more than 96 (as of MC: PE 0.9.5) can cause issues with the client ignoring chunks
|
||||
max-chunks: 96
|
||||
@ -46,6 +46,15 @@ chunk-ticking:
|
||||
light-updates: false
|
||||
clear-tick-list: false
|
||||
|
||||
chunk-generation:
|
||||
#Whether to run the generation on a different thread or on the main thread
|
||||
#Generation will be less glitchy on the main thread, but will lag more
|
||||
#Using this with fast generators is recommended
|
||||
#If enabled, the dedicated generation thread may leak memory
|
||||
use-async: false
|
||||
#Max. amount of chunks to generate per tick, only for use-async: true
|
||||
per-tick: 1
|
||||
|
||||
chunk-gc:
|
||||
period-in-ticks: 600
|
||||
|
||||
|
@ -43,7 +43,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
protected $doubleInventory = null;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::CHEST;
|
||||
$nbt->id = new String("id", Tile::CHEST);
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new ChestInventory($this);
|
||||
|
||||
@ -55,6 +55,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
for($i = 0; $i < $this->getSize(); ++$i){
|
||||
$this->inventory->setItem($i, $this->getItem($i));
|
||||
}
|
||||
|
||||
$this->checkPairing();
|
||||
}
|
||||
|
||||
@ -169,13 +170,16 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
|
||||
protected function checkPairing(){
|
||||
if(($pair = $this->getPair()) instanceof Chest){
|
||||
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);
|
||||
if($this->doubleInventory === null){
|
||||
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{
|
||||
$this->doubleInventory = null;
|
||||
unset($this->namedtag->pairx, $this->namedtag->pairz);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,7 +196,7 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
*/
|
||||
public function getPair(){
|
||||
if($this->isPaired()){
|
||||
$tile = $this->getLevel()->getTile(new Vector3((int) $this->namedtag->pairx, $this->y, (int) $this->namedtag->pairz));
|
||||
$tile = $this->getLevel()->getTile(new Vector3((int) $this->namedtag["pairx"], $this->y, (int) $this->namedtag["pairz"]));
|
||||
if($tile instanceof Chest){
|
||||
return $tile;
|
||||
}
|
||||
@ -206,11 +210,11 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->namedtag->pairx = $tile->x;
|
||||
$this->namedtag->pairz = $tile->z;
|
||||
$this->namedtag->pairx = new Int("pairx", $tile->x);
|
||||
$this->namedtag->pairz = new Int("pairz", $tile->z);
|
||||
|
||||
$tile->namedtag->pairx = $this->x;
|
||||
$tile->namedtag->pairz = $this->z;
|
||||
$tile->namedtag->pairx = new Int("pairx", $this->x);
|
||||
$tile->namedtag->pairz = new Int("pairz", $this->z);
|
||||
|
||||
$this->spawnToAll();
|
||||
$tile->spawnToAll();
|
||||
@ -244,8 +248,8 @@ class Chest extends Spawnable implements InventoryHolder, Container{
|
||||
new Int("x", (int) $this->x),
|
||||
new Int("y", (int) $this->y),
|
||||
new Int("z", (int) $this->z),
|
||||
new Int("pairx", (int) $this->namedtag->pairx),
|
||||
new Int("pairz", (int) $this->namedtag->pairz)
|
||||
new Int("pairx", (int) $this->namedtag["pairx"]),
|
||||
new Int("pairz", (int) $this->namedtag["pairz"])
|
||||
]);
|
||||
}else{
|
||||
return new Compound("", [
|
||||
|
@ -32,6 +32,7 @@ use pocketmine\nbt\tag\Byte;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Short;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\network\protocol\ContainerSetDataPacket;
|
||||
|
||||
class Furnace extends Tile implements InventoryHolder, Container{
|
||||
@ -39,7 +40,7 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
protected $inventory;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::FURNACE;
|
||||
$nbt->id = new String("id", Tile::FURNACE);
|
||||
parent::__construct($chunk, $nbt);
|
||||
$this->inventory = new FurnaceInventory($this);
|
||||
|
||||
@ -170,6 +171,8 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->timings->startTiming();
|
||||
|
||||
$ret = false;
|
||||
|
||||
$fuel = $this->inventory->getFuel();
|
||||
@ -248,6 +251,8 @@ class Furnace extends Tile implements InventoryHolder, Container{
|
||||
|
||||
$this->lastUpdate = microtime(true);
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
return $ret;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use pocketmine\nbt\tag\String;
|
||||
class Sign extends Spawnable{
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt){
|
||||
$nbt["id"] = Tile::SIGN;
|
||||
$nbt->id = new String("id", Tile::SIGN);
|
||||
if(!isset($nbt->Text1)){
|
||||
$nbt->Text1 = new String("Text1", "");
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ abstract class Spawnable extends Tile{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($this->getLevel()->getPlayers() as $player){
|
||||
foreach($this->getLevel()->getUsingChunk($this->chunk->getX(), $this->chunk->getZ()) as $player){
|
||||
if($player->spawned === true){
|
||||
$this->spawnTo($player);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
|
||||
abstract class Tile extends Position{
|
||||
const SIGN = "Sign";
|
||||
@ -54,6 +55,7 @@ abstract class Tile extends Position{
|
||||
public $namedtag;
|
||||
protected $lastUpdate;
|
||||
protected $server;
|
||||
protected $timings;
|
||||
|
||||
/** @var \pocketmine\event\TimingsHandler */
|
||||
public $tickTimer;
|
||||
@ -63,6 +65,8 @@ abstract class Tile extends Position{
|
||||
throw new \Exception("Invalid garbage Chunk given to Tile");
|
||||
}
|
||||
|
||||
$this->timings = Timings::getTileEntityTimings($this);
|
||||
|
||||
$this->server = $chunk->getProvider()->getLevel()->getServer();
|
||||
$this->chunk = $chunk;
|
||||
$this->setLevel($chunk->getProvider()->getLevel());
|
||||
@ -85,9 +89,9 @@ abstract class Tile extends Position{
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
$this->namedtag["x"] = $this->x;
|
||||
$this->namedtag["y"] = $this->y;
|
||||
$this->namedtag["z"] = $this->z;
|
||||
$this->namedtag->x = new Int("x", $this->x);
|
||||
$this->namedtag->y = new Int("y", $this->y);
|
||||
$this->namedtag->z = new Int("z", $this->z);
|
||||
}
|
||||
|
||||
public function onUpdate(){
|
||||
|
Submodule src/raklib updated: bcb3efb03c...64a96a7e80
Reference in New Issue
Block a user