Compare commits

..

97 Commits

Author SHA1 Message Date
582c165479 Implemented Explosion and PrimedTNT, closes #2139 2014-10-07 17:46:01 +02:00
5fb205493a Spawnable->spawnToAll() now uses the actual chunk instead of the entire level to spawn 2014-10-07 12:50:53 +02:00
e346d245e2 Merge pull request #2166 from PocketMine/query-event-implementation
Query event implementation
2014-10-07 11:46:55 +02:00
4fece32ca8 API 1.5.0 2014-10-07 11:02:22 +02:00
0b79d74a2f Implemented QueryRegenerateEvent 2014-10-07 11:02:05 +02:00
b83c6fbfa3 Added Vine blocks, closes #2162 2014-10-07 10:18:02 +02:00
dda9c598f1 Added new Tool type selection constants 2014-10-07 10:16:05 +02:00
8769d2bcd1 Unload entities from chunks, possible fix for #2157, fixes #2165 2014-10-07 09:27:18 +02:00
3b7ece3363 Do not spawn dead entities to players, fixes #2157, possible fix for #2165 2014-10-07 09:24:19 +02:00
b6025e3f2b Removed selectors on Player->sendMessage() 2014-10-06 16:45:11 +02:00
35de331b74 Fixed Level->getCollidingEntities() when called with a null Entity 2014-10-06 13:18:05 +02:00
de11cce154 Improved entity ticking 2014-10-06 13:10:59 +02:00
d53ba52d32 Removed parse errors from AutoReporting 2014-10-06 12:12:54 +02:00
9db2fe40eb Fixed player datta not getting saved, use fall distance on fly calculation 2014-10-06 12:02:36 +02:00
57bb8f14fa Fixed Player movement collision checks 2014-10-06 11:47:28 +02:00
761cd59514 Implemented flying protection 2014-10-06 11:22:59 +02:00
4c2a1c8684 Implement crop growth levels properly, fixes #2002, closes #2160 2014-10-06 09:34:40 +02:00
31bb6d1a68 Changed base block classes to abstract, closes #2159 2014-10-06 09:27:44 +02:00
cd65179aef Fixes #2155 2014-10-06 12:07:48 +10:30
9abd2c63f4 Improved initial chunk loading, do not skip near chunks, fixes world loading getting stuck 2014-10-06 00:45:02 +02:00
376e359577 Moved connected flag up in the Player disconnect process 2014-10-05 22:51:20 +02:00
571e2f8895 Revert "Option to disable hitbox calculation"
This reverts commit fae330d499.
2014-10-05 16:32:49 +02:00
7106ea87e6 Merge branch 'master' of github.com:PocketMine/PocketMine-MP 2014-10-05 16:32:27 +02:00
6b65b68ebc Possible fix for #1661 2014-10-05 16:32:15 +02:00
d4c75ce68a Trim system timezone output 2014-10-05 17:40:17 +10:30
fae330d499 Option to disable hitbox calculation 2014-10-04 20:07:37 -05:00
5e03e157ad Fixed #2153 2014-10-04 23:16:35 +02:00
b0c40dc1ab Fixed Server->unloadLevel() not removing objects from memory 2014-10-04 20:56:27 +02:00
6840589f4e Fix trailing newline 2014-10-04 18:28:42 +02:00
19e4aaa16a Merge pull request #2151 from sekjun9878/master
Timezone for Windows and IP Geolocation TZdata
2014-10-05 01:51:44 +09:30
e0a7944faa Re-use timezone offset parsing 2014-10-05 01:43:33 +09:30
eeda22d0ba Add portable linux timezone detection for incompatible linux distributions. 2014-10-05 01:29:07 +09:30
769f1effb0 Cleaner timezone parsing for Linux and add support for Macs 2014-10-05 01:05:00 +09:30
42c7322273 Reliable timezone detection using systeminfo for Windows and additional IP Geolocation-based detection. Added a warning message for when auto-detection fails. Fixes #2015. 2014-10-05 00:32:01 +09:30
c8cf6b715e Merge pull request #2150 from PEMapModder/patch-2
Fixed PlayerChatEvent::setRecipients() being ignored
2014-10-04 14:49:14 +02:00
41f94f7385 Fixed PlayerChatEvent::setRecipients() being useless 2014-10-04 20:10:12 +08:00
602bdf27a5 Compatibility with pthreads > 2.0.8 2014-10-02 16:58:37 +02:00
539fa232f8 Added individual object timings to Entities / Tile Entities 2014-09-30 16:09:21 +02:00
f8378c09ba Fixed entities getting OnGround default to true 2014-09-30 13:13:31 +02:00
69fb7ae525 Update RakLib, fixes server locking when shutting down 2014-09-30 09:52:50 +02:00
ee8ad6f92a Implement Armor changes as Transactions 2014-09-30 09:37:58 +02:00
f888acbd7c Send PlayerArmorEquipmentPacket instead of ContainerSetContentPacket with id 0x78 2014-09-29 23:43:52 +02:00
215691f1c4 Update RakLib 2014-09-29 23:23:11 +02:00
1252dd65a9 Improved PlayerInventory->setItemInHand() $source call, fixed Tool durability 2014-09-29 17:53:53 +02:00
762c27affe Added armor change $source parameter 2014-09-29 17:50:48 +02:00
706e1099a1 Update raklib 2014-09-29 16:46:19 +02:00
9cd66dc969 Some changes 2014-09-29 16:37:56 +02:00
5b6b789ab3 Improved exponentiation 2014-09-29 13:24:25 +02:00
85ff696ae5 Improved Chunk entity loading 2014-09-29 13:14:14 +02:00
25f8e8318b Fixed Anvil levels duplicating saved entities 2014-09-29 13:09:13 +02:00
61d84c73d0 Implemented Arrow pick up 2014-09-29 13:05:18 +02:00
f5822c6de8 Improved entity movement updates 2014-09-29 12:59:01 +02:00
b0bd927545 Improved entity base ticks 2014-09-29 12:55:10 +02:00
fde61b7d21 Improved single-threaded chunk generation efficiency 2014-09-29 12:48:11 +02:00
886ad8442c Fixed #2055 Extra packet data sent on armor change 2014-09-28 17:31:44 +02:00
7b5869bea8 RakLib submodule update 2014-09-28 16:35:24 +02:00
3063863c65 RakLib update 2014-09-28 16:06:27 +02:00
0dfaa19380 Fixed invalid spawn position setting for plugins 2014-09-28 15:19:09 +02:00
7fea29e874 Updated pthreads version on Travis-CI 2014-09-28 12:50:03 +02:00
2ded2013bf Fixed auto-save configuration, made it global 2014-09-28 10:50:43 +02:00
aa27c28e65 Force new player position on spawn 2014-09-28 01:14:03 +02:00
05a81bebf4 Fixed server crash when Tile Entities / Entities were loaded and requested the same chunk 2014-09-28 00:39:49 +02:00
ce91f2943a Fixed Double Chests 2014-09-28 00:39:11 +02:00
1d8562fb8c Allow setting the player spawnpoint via events before PlayerJoinEvent 2014-09-28 00:14:08 +02:00
1dfb17b932 Fixed #2126 Items drop twice from tile entities 2014-09-27 14:31:41 +02:00
16384c2b20 Improved Player generation queue 2014-09-27 10:31:02 +02:00
48041b2f19 Basic entity motion on water 2014-09-27 00:09:38 +02:00
529bf743db Added torch drop 2014-09-26 23:07:43 +02:00
48bc919a33 Added Liquid flow 2014-09-26 16:56:10 +02:00
474091c013 Improved Level block update scheduling for repeated updates 2014-09-26 16:55:34 +02:00
666e5553c2 Fixed Level->scheduleUpdate() tick calculation 2014-09-26 16:04:51 +02:00
ae6f532b1d Partial Liquid flow 2014-09-26 13:25:52 +02:00
b231eba803 Fixed E_NOTICE error on player movement 2014-09-26 13:23:47 +02:00
82d903733d Fixed #2121 2014-09-26 11:34:06 +02:00
8e2903da86 API version 1.4.1 2014-09-26 11:19:30 +02:00
d720113ac9 Added non-threaded chunk generation, toggleable on pocketmine.yml 2014-09-26 11:18:46 +02:00
5db45222c6 Deprecated Level->getChunkAt() in favor of Level->getChunk() 2014-09-26 10:31:32 +02:00
2975509d0f Improved chunk unload queue, possible fix for #1661 2014-09-25 23:03:56 +02:00
7e49d073fa Improved player movement, check once per tick 2014-09-24 16:58:22 +02:00
ef3674a296 Possible fix for #1661 2014-09-24 16:47:53 +02:00
6cb7e36f8a Improved knockback motion 2014-09-24 16:17:04 +02:00
b88f19bb74 Improved Level::getSafeSpawn() 2014-09-23 20:16:16 +02:00
8c3fcf0798 Send player metadata on respawn 2014-09-23 20:14:01 +02:00
6b312a7826 Fixed players getting Suffocation damage inside transparent blocks 2014-09-23 19:50:44 +02:00
9907e84eee Torches can now be crafted using charcoal 2014-09-23 19:36:30 +02:00
01f299a7f1 Merge branch 'master' of github.com:PocketMine/PocketMine-MP 2014-09-23 15:54:59 +02:00
91f20f6789 Add valid position difference back to Player entities 2014-09-23 15:54:48 +02:00
4c1edc3f7e Re-order RFC section 2014-09-23 20:32:39 +09:30
5fd1e271c5 Merge pull request #2112 from PocketMine/rfc-rfc-and-voting
RFC and Voting
2014-09-22 15:46:03 +02:00
b3ae6eae04 Added RFC ready status, added new label to label list 2014-09-22 09:30:14 +02:00
3b56b536b6 Changed RFC label 2014-09-22 08:46:03 +02:00
9258281546 Fixed missing whitespace 2014-09-21 21:18:37 +02:00
e7897be7cd Merge pull request #2113 from tnpxxsheepdog/rfc-rfc-and-voting
Update CONTRIBUTING.md
2014-09-21 21:08:18 +02:00
5ff01de413 Update CONTRIBUTING.md
Smoothed out wording, fix grammar.
2014-09-21 15:05:08 -04:00
f191ada405 Fixed text typo 2014-09-21 18:33:34 +02:00
addd74d09e Fix typo, added PR: Request for Comments label 2014-09-21 17:51:35 +02:00
4e9d2d0a7f Added 'RFC and Voting' to CONTRIBUTING.md 2014-09-21 17:45:58 +02:00
99 changed files with 2347 additions and 1115 deletions

View File

@ -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

View File

@ -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.

View File

@ -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;

View File

@ -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();

View File

@ -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();

View File

@ -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--;

View File

@ -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
*/

View File

@ -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;
}

View File

@ -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--;

View File

@ -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;
}
}

View File

@ -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),

View File

@ -22,7 +22,7 @@
namespace pocketmine\block;
class Flowable extends Transparent{
abstract class Flowable extends Transparent{
public $isFlowable = true;
public $isFullBlock = false;

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}
}

View File

@ -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--;

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View 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 [];
}
}
}

View File

@ -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;
}
}

View File

@ -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--;

View File

@ -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;

View File

@ -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");

View File

@ -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");

View File

@ -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);
}

View File

@ -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()),

View File

@ -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);

View File

@ -24,4 +24,5 @@ namespace pocketmine\entity;
interface Explosive{
public function explode();
}

View File

@ -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);
}

View File

@ -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()){

View File

@ -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);
}
}

View File

@ -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;

View 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";
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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";
}

View File

@ -27,4 +27,8 @@ class DiamondAxe extends Tool{
parent::__construct(self::DIAMOND_AXE, $meta, $count, "Diamond Axe");
}
public function isAxe(){
return Tool::TIER_DIAMOND;
}
}

View File

@ -27,4 +27,7 @@ class DiamondHoe extends Tool{
parent::__construct(self::DIAMOND_HOE, $meta, $count, "Diamond Hoe");
}
public function isHoe(){
return Tool::TIER_DIAMOND;
}
}

View File

@ -27,4 +27,7 @@ class DiamondPickaxe extends Tool{
parent::__construct(self::DIAMOND_PICKAXE, $meta, $count, "Diamond Pickaxe");
}
public function isPickaxe(){
return Tool::TIER_DIAMOND;
}
}

View File

@ -27,4 +27,7 @@ class DiamondShovel extends Tool{
parent::__construct(self::DIAMOND_SHOVEL, $meta, $count, "Diamond Shovel");
}
public function isShovel(){
return Tool::TIER_DIAMOND;
}
}

View File

@ -27,4 +27,7 @@ class DiamondSword extends Tool{
parent::__construct(self::DIAMOND_SWORD, $meta, $count, "Diamond Sword");
}
public function isSword(){
return Tool::TIER_DIAMOND;
}
}

View File

@ -27,4 +27,7 @@ class GoldAxe extends Tool{
parent::__construct(self::GOLD_AXE, $meta, $count, "Gold Axe");
}
public function isAxe(){
return Tool::TIER_GOLD;
}
}

View File

@ -27,4 +27,7 @@ class GoldHoe extends Tool{
parent::__construct(self::GOLD_HOE, $meta, $count, "Gold Hoe");
}
public function isHoe(){
return Tool::TIER_GOLD;
}
}

View File

@ -27,4 +27,7 @@ class GoldPickaxe extends Tool{
parent::__construct(self::GOLD_PICKAXE, $meta, $count, "Gold Pickaxe");
}
public function isPickaxe(){
return Tool::TIER_GOLD;
}
}

View File

@ -27,4 +27,7 @@ class GoldShovel extends Tool{
parent::__construct(self::GOLD_SHOVEL, $meta, $count, "Gold Shovel");
}
public function isShovel(){
return Tool::TIER_GOLD;
}
}

View File

@ -27,4 +27,7 @@ class GoldSword extends Tool{
parent::__construct(self::GOLD_SWORD, $meta, $count, "Gold Sword");
}
public function isSword(){
return Tool::TIER_GOLD;
}
}

View File

@ -27,4 +27,7 @@ class IronAxe extends Tool{
parent::__construct(self::IRON_AXE, $meta, $count, "Iron Axe");
}
public function isAxe(){
return Tool::TIER_IRON;
}
}

View File

@ -27,4 +27,7 @@ class IronHoe extends Tool{
parent::__construct(self::IRON_HOE, $meta, $count, "Iron Hoe");
}
public function isHoe(){
return Tool::TIER_IRON;
}
}

View File

@ -27,4 +27,7 @@ class IronPickaxe extends Tool{
parent::__construct(self::IRON_PICKAXE, $meta, $count, "Iron Pickaxe");
}
public function isPickaxe(){
return Tool::TIER_IRON;
}
}

View File

@ -27,4 +27,7 @@ class IronShovel extends Tool{
parent::__construct(self::IRON_SHOVEL, $meta, $count, "Iron Shovel");
}
public function isShovel(){
return Tool::TIER_IRON;
}
}

View File

@ -27,4 +27,7 @@ class IronSword extends Tool{
parent::__construct(self::IRON_SWORD, $meta, $count, "Iron Sword");
}
public function isSword(){
return Tool::TIER_IRON;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -27,4 +27,8 @@ class StoneAxe extends Tool{
parent::__construct(self::STONE_AXE, $meta, $count, "Stone Axe");
}
public function isAxe(){
return Tool::TIER_STONE;
}
}

View File

@ -27,4 +27,7 @@ class StoneHoe extends Tool{
parent::__construct(self::STONE_HOE, $meta, $count, "Stone Hoe");
}
public function isHoe(){
return Tool::TIER_STONE;
}
}

View File

@ -27,4 +27,7 @@ class StonePickaxe extends Tool{
parent::__construct(self::STONE_PICKAXE, $meta, $count, "Stone Pickaxe");
}
public function isPickaxe(){
return Tool::TIER_IRON;
}
}

View File

@ -27,4 +27,7 @@ class StoneShovel extends Tool{
parent::__construct(self::STONE_SHOVEL, $meta, $count, "Stone Shovel");
}
public function isShovel(){
return Tool::TIER_STONE;
}
}

View File

@ -27,4 +27,7 @@ class StoneSword extends Tool{
parent::__construct(self::STONE_SWORD, $meta, $count, "Stone Sword");
}
public function isSword(){
return Tool::TIER_STONE;
}
}

View File

@ -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);
}
}

View File

@ -27,4 +27,7 @@ class WoodenAxe extends Tool{
parent::__construct(self::WOODEN_AXE, $meta, $count, "Wooden Axe");
}
public function isAxe(){
return Tool::TIER_WOODEN;
}
}

View File

@ -27,4 +27,7 @@ class WoodenHoe extends Tool{
parent::__construct(self::WOODEN_HOE, $meta, $count, "Wooden Hoe");
}
public function isHoe(){
return Tool::TIER_WOODEN;
}
}

View File

@ -27,4 +27,7 @@ class WoodenPickaxe extends Tool{
parent::__construct(self::WOODEN_PICKAXE, $meta, $count, "Wooden Pickaxe");
}
public function isPickaxe(){
return Tool::TIER_WOODEN;
}
}

View File

@ -27,4 +27,7 @@ class WoodenShovel extends Tool{
parent::__construct(self::WOODEN_SHOVEL, $meta, $count, "Wooden Shovel");
}
public function isShovel(){
return Tool::TIER_WOODEN;
}
}

View File

@ -27,4 +27,7 @@ class WoodenSword extends Tool{
parent::__construct(self::WOODEN_SWORD, $meta, $count, "Wooden Sword");
}
public function isSword(){
return Tool::TIER_WOODEN;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -248,6 +248,8 @@ interface FullChunk{
*/
public function unload($save = true, $safe = true);
public function initChunk();
/**
* @return string[]
*/

View File

@ -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){

View File

@ -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);

View File

@ -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){

View File

@ -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(){

View File

@ -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);

View File

@ -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){

View File

@ -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;

View 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);
}
}
}

View 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();
}
}

View File

@ -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]);
}

View File

@ -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});

View File

@ -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();
}

View File

@ -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){

View File

@ -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.

View File

@ -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());

View File

@ -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);
}

View File

@ -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){

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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("", [

View File

@ -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;
}
}

View File

@ -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", "");
}

View File

@ -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);
}

View File

@ -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(){