mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-09 03:06:55 +00:00
Compare commits
67 Commits
Author | SHA1 | Date | |
---|---|---|---|
9abaa42cd7 | |||
77b9feb3c0 | |||
e0e2e1775f | |||
d2d65ce6cc | |||
ff2e982f22 | |||
daf56e990b | |||
3f5e83a322 | |||
5ecc5ed7e0 | |||
cd80ae00d4 | |||
beb5d72299 | |||
0eef634aab | |||
0ea166a551 | |||
6417cff618 | |||
a71af952ba | |||
93dd05a03e | |||
98f903783c | |||
5d47ea4337 | |||
c242d6213a | |||
4ad1093fd7 | |||
fc0782df02 | |||
bfaa224f6b | |||
de88f0fce1 | |||
9b078854c4 | |||
42f8e061a5 | |||
1455c38dbe | |||
75df6973df | |||
4763360e9e | |||
0299191e64 | |||
2664a1b4d8 | |||
4249c00c3e | |||
517c4e5143 | |||
69c343bb9b | |||
70df1579a8 | |||
ea9f9aa250 | |||
34a899e28b | |||
b80868040e | |||
a7f1181335 | |||
bf8a8b386e | |||
4b518f2a58 | |||
60b1f0a6e9 | |||
5934399a0d | |||
b42132a7c3 | |||
c05697f506 | |||
d4fe1b8ece | |||
9b2653fb6f | |||
ed88684e71 | |||
cbb9c4f298 | |||
660d42e8d1 | |||
dbeceb02f9 | |||
fd77dd0066 | |||
87ce87112b | |||
1d71f5edb3 | |||
0f620157e8 | |||
2323601f98 | |||
d34b94302f | |||
ec4c61e113 | |||
231e491bb9 | |||
69cdc6f13a | |||
dfeb62491a | |||
178eedb536 | |||
4975da2aae | |||
5946ec8819 | |||
abf0dee426 | |||
30f5a8fac6 | |||
f704061618 | |||
23dc6e09d8 | |||
15b7fc978e |
@ -30,7 +30,8 @@
|
||||
"pocketmine/nbt": "^0.2.1",
|
||||
"pocketmine/math": "^0.2.0",
|
||||
"pocketmine/snooze": "^0.1.0",
|
||||
"daverandom/callback-validator": "dev-master"
|
||||
"daverandom/callback-validator": "dev-master",
|
||||
"adhocore/json-comment": "^0.0.7"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
58
composer.lock
generated
58
composer.lock
generated
@ -4,8 +4,52 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2d120a3dd7d68958809c3662d1cb7c99",
|
||||
"content-hash": "d3fb809caf4d5a5c99054f47f28ff271",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
"version": "v0.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adhocore/php-json-comment.git",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/adhocore/php-json-comment/zipball/135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"reference": "135356c7e7336ef59924f1d921c770045f937a76",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8 || ^5.7 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ahc\\Json\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jitendra Adhikari",
|
||||
"email": "jiten.adhikary@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Lightweight JSON comment stripper library for PHP",
|
||||
"keywords": [
|
||||
"comment",
|
||||
"json",
|
||||
"strip-comment"
|
||||
],
|
||||
"time": "2018-08-01T12:27:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "daverandom/callback-validator",
|
||||
"version": "dev-master",
|
||||
@ -48,16 +92,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/binaryutils",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/BinaryUtils.git",
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be"
|
||||
"reference": "925001c8eff97a36519b16bbd095964b0fc52772"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"reference": "54efeb978be0ff9335022729fe63c1e2077bf1be",
|
||||
"url": "https://api.github.com/repos/pmmp/BinaryUtils/zipball/925001c8eff97a36519b16bbd095964b0fc52772",
|
||||
"reference": "925001c8eff97a36519b16bbd095964b0fc52772",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -75,10 +119,10 @@
|
||||
],
|
||||
"description": "Classes and methods for conveniently handling binary data",
|
||||
"support": {
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/master",
|
||||
"source": "https://github.com/pmmp/BinaryUtils/tree/0.1.3",
|
||||
"issues": "https://github.com/pmmp/BinaryUtils/issues"
|
||||
},
|
||||
"time": "2018-08-26T18:11:05+00:00"
|
||||
"time": "2018-12-30T12:17:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/math",
|
||||
|
@ -159,7 +159,7 @@ class CrashDump{
|
||||
$error = $lastExceptionError;
|
||||
}else{
|
||||
$error = (array) error_get_last();
|
||||
$error["trace"] = Utils::getTrace(4); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$error["trace"] = Utils::printableCurrentTrace(3); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump
|
||||
$errorConversion = [
|
||||
E_ERROR => "E_ERROR",
|
||||
E_WARNING => "E_WARNING",
|
||||
|
@ -101,6 +101,7 @@ use pocketmine\network\mcpe\PlayerNetworkSessionAdapter;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
@ -120,6 +121,7 @@ use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
|
||||
use pocketmine\network\mcpe\protocol\ModalFormRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
|
||||
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ProtocolInfo;
|
||||
@ -276,7 +278,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
/** @var int */
|
||||
protected $spawnThreshold;
|
||||
/** @var int */
|
||||
protected $chunkLoadCount = 0;
|
||||
protected $spawnChunkLoadCount = 0;
|
||||
/** @var int */
|
||||
protected $chunksPerTick;
|
||||
|
||||
@ -962,8 +964,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->usedChunks[Level::chunkHash($x, $z)] = true;
|
||||
$this->chunkLoadCount++;
|
||||
|
||||
$this->dataPacket($payload);
|
||||
|
||||
if($this->spawned){
|
||||
@ -974,8 +974,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->chunkLoadCount >= $this->spawnThreshold and !$this->spawned){
|
||||
$this->doFirstSpawn();
|
||||
if($this->spawnChunkLoadCount !== -1 and ++$this->spawnChunkLoadCount >= $this->spawnThreshold){
|
||||
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
|
||||
$this->spawnChunkLoadCount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1013,11 +1014,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
Timings::$playerChunkSendTimer->stopTiming();
|
||||
}
|
||||
|
||||
protected function doFirstSpawn(){
|
||||
public function doFirstSpawn(){
|
||||
if($this->spawned){
|
||||
return; //avoid player spawning twice (this can only happen on 3.x with a custom malicious client)
|
||||
}
|
||||
$this->spawned = true;
|
||||
|
||||
$this->sendPlayStatus(PlayStatusPacket::PLAYER_SPAWN);
|
||||
|
||||
if($this->hasPermission(Server::BROADCAST_CHANNEL_USERS)){
|
||||
PermissionManager::getInstance()->subscribeToPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
}
|
||||
@ -1071,8 +1073,6 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
Timings::$playerChunkOrderTimer->startTiming();
|
||||
|
||||
$this->nextChunkOrderRun = 200;
|
||||
|
||||
$radius = $this->server->getAllowedViewDistance($this->viewDistance);
|
||||
$radiusSquared = $radius ** 2;
|
||||
|
||||
@ -1149,6 +1149,14 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}
|
||||
|
||||
$this->loadQueue = $newOrder;
|
||||
if(!empty($this->loadQueue) or !empty($unloadChunks)){
|
||||
$pk = new NetworkChunkPublisherUpdatePacket();
|
||||
$pk->x = $this->getFloorX();
|
||||
$pk->y = $this->getFloorY();
|
||||
$pk->z = $this->getFloorZ();
|
||||
$pk->radius = $this->viewDistance * 16; //blocks, not chunks >.>
|
||||
$this->dataPacket($pk);
|
||||
}
|
||||
|
||||
Timings::$playerChunkOrderTimer->stopTiming();
|
||||
}
|
||||
@ -1762,7 +1770,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->nextChunkOrderRun-- <= 0){
|
||||
if($this->nextChunkOrderRun !== PHP_INT_MAX and $this->nextChunkOrderRun-- <= 0){
|
||||
$this->nextChunkOrderRun = PHP_INT_MAX;
|
||||
$this->orderChunks();
|
||||
}
|
||||
|
||||
@ -2087,6 +2096,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->worldName = $this->server->getMotd();
|
||||
$this->dataPacket($pk);
|
||||
|
||||
$this->sendDataPacket(new AvailableEntityIdentifiersPacket());
|
||||
|
||||
$this->level->sendTime($this);
|
||||
|
||||
$this->sendAttributes(true);
|
||||
@ -2619,6 +2630,13 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
if(!$this->spawned or !$this->isAlive()){
|
||||
return true;
|
||||
}
|
||||
if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){
|
||||
//TODO HACK: silence useless spam (MCPE 1.8)
|
||||
//this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang
|
||||
//hacks, it also sends it when changing the held item now, which causes us to think the inventory was closed
|
||||
//when it wasn't.
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->doCloseInventory();
|
||||
|
||||
@ -3379,92 +3397,82 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
*/
|
||||
final public function close($message = "", string $reason = "generic reason", bool $notify = true) : void{
|
||||
if($this->isConnected() and !$this->closed){
|
||||
if($notify and strlen($reason) > 0){
|
||||
$pk = new DisconnectPacket();
|
||||
$pk->message = $reason;
|
||||
$this->directDataPacket($pk);
|
||||
}
|
||||
$this->interface->close($this, $notify ? $reason : "");
|
||||
$this->sessionAdapter = null;
|
||||
|
||||
try{
|
||||
if($notify and strlen($reason) > 0){
|
||||
$pk = new DisconnectPacket();
|
||||
$pk->message = $reason;
|
||||
$this->directDataPacket($pk);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
|
||||
$this->stopSleep();
|
||||
|
||||
if($this->spawned){
|
||||
$ev = new PlayerQuitEvent($this, $message, $reason);
|
||||
$ev->call();
|
||||
if($ev->getQuitMessage() != ""){
|
||||
$this->server->broadcastMessage($ev->getQuitMessage());
|
||||
}
|
||||
$this->interface->close($this, $notify ? $reason : "");
|
||||
$this->sessionAdapter = null;
|
||||
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_USERS, $this);
|
||||
PermissionManager::getInstance()->unsubscribeFromPermission(Server::BROADCAST_CHANNEL_ADMINISTRATIVE, $this);
|
||||
$this->save();
|
||||
}
|
||||
|
||||
$this->stopSleep();
|
||||
|
||||
if($this->spawned){
|
||||
$ev = new PlayerQuitEvent($this, $message, $reason);
|
||||
$ev->call();
|
||||
if($ev->getQuitMessage() != ""){
|
||||
$this->server->broadcastMessage($ev->getQuitMessage());
|
||||
if($this->isValid()){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
|
||||
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
|
||||
$entity->despawnFrom($this);
|
||||
}
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
}
|
||||
$this->usedChunks = [];
|
||||
$this->loadQueue = [];
|
||||
|
||||
try{
|
||||
$this->save();
|
||||
}catch(\Throwable $e){
|
||||
$this->server->getLogger()->critical("Failed to save player data for " . $this->getName());
|
||||
$this->server->getLogger()->logException($e);
|
||||
if($this->loggedIn){
|
||||
$this->server->onPlayerLogout($this);
|
||||
foreach($this->server->getOnlinePlayers() as $player){
|
||||
if(!$player->canSee($this)){
|
||||
$player->showPlayer($this);
|
||||
}
|
||||
}
|
||||
$this->hiddenPlayers = [];
|
||||
}
|
||||
|
||||
if($this->isValid()){
|
||||
foreach($this->usedChunks as $index => $d){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$this->level->unregisterChunkLoader($this, $chunkX, $chunkZ);
|
||||
foreach($this->level->getChunkEntities($chunkX, $chunkZ) as $entity){
|
||||
$entity->despawnFrom($this);
|
||||
}
|
||||
unset($this->usedChunks[$index]);
|
||||
}
|
||||
}
|
||||
$this->usedChunks = [];
|
||||
$this->loadQueue = [];
|
||||
$this->removeAllWindows(true);
|
||||
$this->windows = [];
|
||||
$this->windowIndex = [];
|
||||
$this->cursorInventory = null;
|
||||
$this->craftingGrid = null;
|
||||
|
||||
if($this->loggedIn){
|
||||
$this->server->onPlayerLogout($this);
|
||||
foreach($this->server->getOnlinePlayers() as $player){
|
||||
if(!$player->canSee($this)){
|
||||
$player->showPlayer($this);
|
||||
}
|
||||
}
|
||||
$this->hiddenPlayers = [];
|
||||
}
|
||||
if($this->constructed){
|
||||
parent::close();
|
||||
}
|
||||
$this->spawned = false;
|
||||
|
||||
$this->removeAllWindows(true);
|
||||
$this->windows = [];
|
||||
$this->windowIndex = [];
|
||||
$this->cursorInventory = null;
|
||||
$this->craftingGrid = null;
|
||||
if($this->loggedIn){
|
||||
$this->loggedIn = false;
|
||||
$this->server->removeOnlinePlayer($this);
|
||||
}
|
||||
|
||||
if($this->constructed){
|
||||
parent::close();
|
||||
}
|
||||
$this->spawned = false;
|
||||
$this->server->removePlayer($this);
|
||||
|
||||
if($this->loggedIn){
|
||||
$this->loggedIn = false;
|
||||
$this->server->removeOnlinePlayer($this);
|
||||
}
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [
|
||||
TextFormat::AQUA . $this->getName() . TextFormat::WHITE,
|
||||
$this->ip,
|
||||
$this->port,
|
||||
$this->getServer()->getLanguage()->translateString($reason)
|
||||
]));
|
||||
|
||||
$this->server->getLogger()->info($this->getServer()->getLanguage()->translateString("pocketmine.player.logOut", [
|
||||
TextFormat::AQUA . $this->getName() . TextFormat::WHITE,
|
||||
$this->ip,
|
||||
$this->port,
|
||||
$this->getServer()->getLanguage()->translateString($reason)
|
||||
]));
|
||||
$this->spawnPosition = null;
|
||||
|
||||
$this->spawnPosition = null;
|
||||
|
||||
if($this->perm !== null){
|
||||
$this->perm->clearPermissions();
|
||||
$this->perm = null;
|
||||
}
|
||||
}catch(\Throwable $e){
|
||||
$this->server->getLogger()->logException($e);
|
||||
}finally{
|
||||
$this->server->removePlayer($this);
|
||||
if($this->perm !== null){
|
||||
$this->perm->clearPermissions();
|
||||
$this->perm = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3877,9 +3885,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
public function onChunkChanged(Chunk $chunk){
|
||||
if(isset($this->usedChunks[$hash = Level::chunkHash($chunk->getX(), $chunk->getZ())])){
|
||||
$this->usedChunks[$hash] = false;
|
||||
if(!$this->spawned){
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
$this->nextChunkOrderRun = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace pocketmine {
|
||||
use pocketmine\wizard\SetupWizard;
|
||||
|
||||
const NAME = "PocketMine-MP";
|
||||
const BASE_VERSION = "3.4.1";
|
||||
const BASE_VERSION = "3.5.3";
|
||||
const IS_DEVELOPMENT_BUILD = false;
|
||||
const BUILD_NUMBER = 0;
|
||||
|
||||
|
@ -2173,7 +2173,7 @@ class Server{
|
||||
"fullFile" => $e->getFile(),
|
||||
"file" => $errfile,
|
||||
"line" => $errline,
|
||||
"trace" => Utils::getTrace(0, $trace)
|
||||
"trace" => Utils::printableTrace($trace)
|
||||
];
|
||||
|
||||
global $lastExceptionError, $lastError;
|
||||
|
@ -28,9 +28,9 @@ use pocketmine\event\block\BlockFormEvent;
|
||||
use pocketmine\event\block\BlockSpreadEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\sound\FizzSound;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
|
||||
abstract class Liquid extends Transparent{
|
||||
|
||||
@ -435,7 +435,7 @@ abstract class Liquid extends Transparent{
|
||||
$ev->call();
|
||||
if(!$ev->isCancelled()){
|
||||
$this->level->setBlock($this, $ev->getNewState(), true, true);
|
||||
$this->level->broadcastLevelSoundEvent($this->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_FIZZ, (int) ((2.6 + (lcg_value() - lcg_value()) * 0.8) * 1000));
|
||||
$this->level->addSound(new FizzSound($this->add(0.5, 0.5, 0.5), 2.6 + (lcg_value() - lcg_value()) * 0.8));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -31,6 +31,10 @@ class UnknownBlock extends Transparent{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function canBePlaced() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\lang\TranslationContainer;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\particle\AngryVillagerParticle;
|
||||
use pocketmine\level\particle\BlockForceFieldParticle;
|
||||
use pocketmine\level\particle\BubbleParticle;
|
||||
@ -84,14 +85,18 @@ class ParticleCommand extends VanillaCommand{
|
||||
|
||||
if($sender instanceof Player){
|
||||
$level = $sender->getLevel();
|
||||
$pos = new Vector3(
|
||||
$this->getRelativeDouble($sender->getX(), $sender, $args[1]),
|
||||
$this->getRelativeDouble($sender->getY(), $sender, $args[2], 0, Level::Y_MAX),
|
||||
$this->getRelativeDouble($sender->getZ(), $sender, $args[3])
|
||||
);
|
||||
}else{
|
||||
$level = $sender->getServer()->getDefaultLevel();
|
||||
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]);
|
||||
}
|
||||
|
||||
$name = strtolower($args[0]);
|
||||
|
||||
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]);
|
||||
|
||||
$xd = (float) $args[4];
|
||||
$yd = (float) $args[5];
|
||||
$zd = (float) $args[6];
|
||||
|
@ -127,8 +127,8 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function setMinValue(float $minValue){
|
||||
if($minValue > $this->getMaxValue()){
|
||||
throw new \InvalidArgumentException("Value $minValue is bigger than the maxValue!");
|
||||
if($minValue > ($max = $this->getMaxValue())){
|
||||
throw new \InvalidArgumentException("Minimum $minValue is greater than the maximum $max");
|
||||
}
|
||||
|
||||
if($this->minValue != $minValue){
|
||||
@ -143,8 +143,8 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function setMaxValue(float $maxValue){
|
||||
if($maxValue < $this->getMinValue()){
|
||||
throw new \InvalidArgumentException("Value $maxValue is bigger than the minValue!");
|
||||
if($maxValue < ($min = $this->getMinValue())){
|
||||
throw new \InvalidArgumentException("Maximum $maxValue is less than the minimum $min");
|
||||
}
|
||||
|
||||
if($this->maxValue != $maxValue){
|
||||
@ -160,7 +160,7 @@ class Attribute{
|
||||
|
||||
public function setDefaultValue(float $defaultValue){
|
||||
if($defaultValue > $this->getMaxValue() or $defaultValue < $this->getMinValue()){
|
||||
throw new \InvalidArgumentException("Value $defaultValue exceeds the range!");
|
||||
throw new \InvalidArgumentException("Default $defaultValue is outside the range " . $this->getMinValue() . " - " . $this->getMaxValue());
|
||||
}
|
||||
|
||||
if($this->defaultValue !== $defaultValue){
|
||||
@ -171,7 +171,7 @@ class Attribute{
|
||||
}
|
||||
|
||||
public function resetToDefault() : void{
|
||||
$this->setValue($this->getDefaultValue());
|
||||
$this->setValue($this->getDefaultValue(), true);
|
||||
}
|
||||
|
||||
public function getValue() : float{
|
||||
@ -188,7 +188,7 @@ class Attribute{
|
||||
public function setValue(float $value, bool $fit = false, bool $forceSend = false){
|
||||
if($value > $this->getMaxValue() or $value < $this->getMinValue()){
|
||||
if(!$fit){
|
||||
throw new \InvalidArgumentException("Value $value exceeds the range!");
|
||||
throw new \InvalidArgumentException("Value $value is outside the range " . $this->getMinValue() . " - " . $this->getMaxValue());
|
||||
}
|
||||
$value = min(max($value, $this->getMinValue()), $this->getMaxValue());
|
||||
}
|
||||
|
@ -175,6 +175,14 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_SCORE_TAG = 83; //string
|
||||
public const DATA_BALLOON_ATTACHED_ENTITY = 84; //int64, entity unique ID of owner
|
||||
public const DATA_PUFFERFISH_SIZE = 85; //byte
|
||||
public const DATA_BOAT_BUBBLE_TIME = 86; //int (time in bubble column)
|
||||
public const DATA_PLAYER_AGENT_EID = 87; //long
|
||||
/* 88 (float) related to panda sitting
|
||||
* 89 (float) related to panda sitting
|
||||
* 90 (unknown) */
|
||||
public const DATA_FLAGS2 = 91; //long (extended data flags)
|
||||
/* 92 (float) related to panda lying down
|
||||
* 93 (float) related to panda lying down */
|
||||
|
||||
|
||||
public const DATA_FLAG_ONFIRE = 0;
|
||||
@ -231,13 +239,23 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
public const DATA_FLAG_ENCHANTED = 51;
|
||||
public const DATA_FLAG_SHOW_TRIDENT_ROPE = 52; // tridents show an animated rope when enchanted with loyalty after they are thrown and return to their owner. To be combined with DATA_OWNER_EID
|
||||
public const DATA_FLAG_CONTAINER_PRIVATE = 53; //inventory is private, doesn't drop contents when killed if true
|
||||
//54 TransformationComponent
|
||||
public const DATA_FLAG_TRANSFORMING = 54;
|
||||
public const DATA_FLAG_SPIN_ATTACK = 55;
|
||||
public const DATA_FLAG_SWIMMING = 56;
|
||||
public const DATA_FLAG_BRIBED = 57; //dolphins have this set when they go to find treasure for the player
|
||||
public const DATA_FLAG_PREGNANT = 58;
|
||||
public const DATA_FLAG_LAYING_EGG = 59;
|
||||
//60 ??
|
||||
public const DATA_FLAG_RIDER_CAN_PICK = 60; //???
|
||||
public const DATA_FLAG_TRANSITION_SITTING = 61;
|
||||
public const DATA_FLAG_EATING = 62;
|
||||
public const DATA_FLAG_LAYING_DOWN = 63;
|
||||
public const DATA_FLAG_SNEEZING = 64;
|
||||
public const DATA_FLAG_TRUSTING = 65;
|
||||
public const DATA_FLAG_ROLLING = 66;
|
||||
public const DATA_FLAG_SCARED = 67;
|
||||
public const DATA_FLAG_IN_SCAFFOLDING = 68;
|
||||
public const DATA_FLAG_OVER_SCAFFOLDING = 69;
|
||||
public const DATA_FLAG_FALL_THROUGH_SCAFFOLDING = 70;
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
@ -637,6 +655,9 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param float $value
|
||||
*/
|
||||
public function setScale(float $value) : void{
|
||||
if($value <= 0){
|
||||
throw new \InvalidArgumentException("Scale must be greater than 0");
|
||||
}
|
||||
$multiplier = $value / $this->getScale();
|
||||
|
||||
$this->width *= $multiplier;
|
||||
@ -2078,7 +2099,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @return bool
|
||||
*/
|
||||
public function getGenericFlag(int $flagId) : bool{
|
||||
return $this->getDataFlag(self::DATA_FLAGS, $flagId);
|
||||
return $this->getDataFlag($flagId >= 64 ? self::DATA_FLAGS2 : self::DATA_FLAGS, $flagId % 64);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2088,7 +2109,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setGenericFlag(int $flagId, bool $value = true) : void{
|
||||
$this->setDataFlag(self::DATA_FLAGS, $flagId, $value, self::DATA_TYPE_LONG);
|
||||
$this->setDataFlag($flagId >= 64 ? self::DATA_FLAGS2 : self::DATA_FLAGS, $flagId % 64, $value, self::DATA_TYPE_LONG);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,8 +71,9 @@ interface EntityIds{
|
||||
public const ENDER_DRAGON = 53;
|
||||
public const SHULKER = 54;
|
||||
public const ENDERMITE = 55;
|
||||
public const LEARN_TO_CODE_MASCOT = 56;
|
||||
public const AGENT = 56, LEARN_TO_CODE_MASCOT = 56;
|
||||
public const VINDICATOR = 57;
|
||||
public const PHANTOM = 58;
|
||||
|
||||
public const ARMOR_STAND = 61;
|
||||
public const TRIPOD_CAMERA = 62;
|
||||
@ -86,8 +87,9 @@ interface EntityIds{
|
||||
public const EYE_OF_ENDER_SIGNAL = 70;
|
||||
public const ENDER_CRYSTAL = 71;
|
||||
public const FIREWORKS_ROCKET = 72;
|
||||
public const TRIDENT = 73;
|
||||
|
||||
public const THROWN_TRIDENT = 73, TRIDENT = 73;
|
||||
public const TURTLE = 74;
|
||||
public const CAT = 75;
|
||||
public const SHULKER_BULLET = 76;
|
||||
public const FISHING_HOOK = 77;
|
||||
public const CHALKBOARD = 78;
|
||||
@ -97,7 +99,7 @@ interface EntityIds{
|
||||
public const EGG = 82;
|
||||
public const PAINTING = 83;
|
||||
public const MINECART = 84;
|
||||
public const LARGE_FIREBALL = 85;
|
||||
public const FIREBALL = 85, LARGE_FIREBALL = 85;
|
||||
public const SPLASH_POTION = 86;
|
||||
public const ENDER_PEARL = 87;
|
||||
public const LEASH_KNOT = 88;
|
||||
@ -122,6 +124,7 @@ interface EntityIds{
|
||||
public const PUFFERFISH = 108;
|
||||
public const SALMON = 109;
|
||||
public const DROWNED = 110;
|
||||
public const TROPICAL_FISH = 111;
|
||||
public const FISH = 112;
|
||||
public const TROPICALFISH = 111, TROPICAL_FISH = 111;
|
||||
public const COD = 112, FISH = 112;
|
||||
public const PANDA = 113;
|
||||
}
|
||||
|
@ -450,7 +450,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
private function playLevelUpSound(int $newLevel) : void{
|
||||
$volume = 0x10000000 * (min(30, $newLevel) / 5); //No idea why such odd numbers, but this works...
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_LEVELUP, 1, (int) $volume);
|
||||
$this->level->broadcastLevelSoundEvent($this, LevelSoundEventPacket::SOUND_LEVELUP, (int) $volume);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -571,7 +571,10 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
if($e !== null){
|
||||
if($e->isOnFire()){
|
||||
if((
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_PROJECTILE or
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_ENTITY_ATTACK
|
||||
) and $e->isOnFire()){
|
||||
$this->setOnFire(2 * $this->level->getDifficulty());
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,11 @@ use pocketmine\lang\TextContainer;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when a player joins the server, after sending all the spawn packets
|
||||
* Called when the player spawns in the world after logging in, when they first see the terrain.
|
||||
*
|
||||
* Note: A lot of data is sent to the player between login and this event. Disconnecting the player during this event
|
||||
* will cause this data to be wasted. Prefer disconnecting at login-time if possible to minimize bandwidth wastage.
|
||||
* @see PlayerLoginEvent
|
||||
*/
|
||||
class PlayerJoinEvent extends PlayerEvent{
|
||||
/** @var string|TextContainer */
|
||||
|
@ -27,7 +27,9 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when a player joins, after things have been correctly set up (you can change anything now)
|
||||
* Called after the player has successfully authenticated, before it spawns. The player is on the loading screen when
|
||||
* this is called.
|
||||
* Cancelling this event will cause the player to be disconnected with the kick message set.
|
||||
*/
|
||||
class PlayerLoginEvent extends PlayerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
|
@ -27,7 +27,17 @@ use pocketmine\event\Cancellable;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
* Called when the player logs in, before things have been set up
|
||||
* Called when a player connects to the server, prior to authentication taking place.
|
||||
* Cancelling this event will cause the player to be disconnected with the kick message set.
|
||||
*
|
||||
* This event should be used to decide if the player may continue to login to the server. Do things like checking
|
||||
* bans, whitelisting, server-full etc here.
|
||||
*
|
||||
* WARNING: Any information about the player CANNOT be trusted at this stage, because they are not authenticated and
|
||||
* could be a hacker posing as another player.
|
||||
*
|
||||
* WARNING: Due to internal bad architecture, the player is not fully constructed at this stage, and errors might occur
|
||||
* when calling API methods on the player. Tread with caution.
|
||||
*/
|
||||
class PlayerPreLoginEvent extends PlayerEvent implements Cancellable{
|
||||
/** @var string */
|
||||
|
@ -61,19 +61,29 @@ class ChestInventory extends ContainerInventory{
|
||||
return $this->holder;
|
||||
}
|
||||
|
||||
protected function getOpenSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_CHEST_OPEN;
|
||||
}
|
||||
|
||||
protected function getCloseSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_CHEST_CLOSED;
|
||||
}
|
||||
|
||||
public function onOpen(Player $who) : void{
|
||||
parent::onOpen($who);
|
||||
|
||||
if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->broadcastBlockEventPacket(true);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_CHEST_OPEN);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getOpenSound());
|
||||
}
|
||||
}
|
||||
|
||||
public function onClose(Player $who) : void{
|
||||
if(count($this->getViewers()) === 1 and $this->getHolder()->isValid()){
|
||||
//TODO: this crap really shouldn't be managed by the inventory
|
||||
$this->broadcastBlockEventPacket(false);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), LevelSoundEventPacket::SOUND_CHEST_CLOSED);
|
||||
$this->getHolder()->getLevel()->broadcastLevelSoundEvent($this->getHolder()->add(0.5, 0.5, 0.5), $this->getCloseSound());
|
||||
}
|
||||
parent::onClose($who);
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\WindowTypes;
|
||||
use pocketmine\tile\EnderChest;
|
||||
|
||||
@ -58,6 +59,14 @@ class EnderChestInventory extends ChestInventory{
|
||||
$this->holder->setLevel($enderChest->getLevel());
|
||||
}
|
||||
|
||||
protected function getOpenSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_ENDERCHEST_OPEN;
|
||||
}
|
||||
|
||||
protected function getCloseSound() : int{
|
||||
return LevelSoundEventPacket::SOUND_ENDERCHEST_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* This override is here for documentation and code completion purposes only.
|
||||
* @return Position
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\EntityIds;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\event\entity\ProjectileLaunchEvent;
|
||||
use pocketmine\math\Vector3;
|
||||
@ -65,9 +66,7 @@ abstract class ProjectileItem extends Item{
|
||||
}else{
|
||||
$projectile->spawnToAll();
|
||||
|
||||
//319 is the Player's entity type ID in MCPE, with all its flags (which we don't know)
|
||||
//without this, it doesn't work at all.
|
||||
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 319);
|
||||
$player->getLevel()->broadcastLevelSoundEvent($player, LevelSoundEventPacket::SOUND_THROW, 0, EntityIds::PLAYER);
|
||||
}
|
||||
}elseif($projectile !== null){
|
||||
$projectile->spawnToAll();
|
||||
|
@ -49,6 +49,8 @@ use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\EmptySubChunk;
|
||||
use pocketmine\level\format\io\BaseLevelProvider;
|
||||
use pocketmine\level\format\io\ChunkRequestTask;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\format\io\LevelProvider;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
@ -69,6 +71,7 @@ use pocketmine\metadata\Metadatable;
|
||||
use pocketmine\metadata\MetadataValue;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\AddEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
@ -450,13 +453,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!is_array($pk)){
|
||||
$pk = [$pk];
|
||||
}
|
||||
|
||||
if($players === null){
|
||||
foreach($pk as $e){
|
||||
$this->broadcastPacketToViewers($sound, $e);
|
||||
if(!empty($pk)){
|
||||
if($players === null){
|
||||
foreach($pk as $e){
|
||||
$this->broadcastPacketToViewers($sound, $e);
|
||||
}
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk, false);
|
||||
}
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,13 +469,14 @@ class Level implements ChunkManager, Metadatable{
|
||||
if(!is_array($pk)){
|
||||
$pk = [$pk];
|
||||
}
|
||||
|
||||
if($players === null){
|
||||
foreach($pk as $e){
|
||||
$this->broadcastPacketToViewers($particle, $e);
|
||||
if(!empty($pk)){
|
||||
if($players === null){
|
||||
foreach($pk as $e){
|
||||
$this->broadcastPacketToViewers($particle, $e);
|
||||
}
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk, false);
|
||||
}
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,16 +505,16 @@ class Level implements ChunkManager, Metadatable{
|
||||
*
|
||||
* @param Vector3 $pos
|
||||
* @param int $soundId
|
||||
* @param int $pitch
|
||||
* @param int $extraData
|
||||
* @param int $entityTypeId
|
||||
* @param bool $isBabyMob
|
||||
* @param bool $disableRelativeVolume If true, all players receiving this sound-event will hear the sound at full volume regardless of distance
|
||||
*/
|
||||
public function broadcastLevelSoundEvent(Vector3 $pos, int $soundId, int $pitch = 1, int $extraData = -1, bool $isBabyMob = false, bool $disableRelativeVolume = false){
|
||||
public function broadcastLevelSoundEvent(Vector3 $pos, int $soundId, int $extraData = -1, int $entityTypeId = -1, bool $isBabyMob = false, bool $disableRelativeVolume = false){
|
||||
$pk = new LevelSoundEventPacket();
|
||||
$pk->sound = $soundId;
|
||||
$pk->pitch = $pitch;
|
||||
$pk->extraData = $extraData;
|
||||
$pk->entityType = AddEntityPacket::LEGACY_ID_MAP_BC[$entityTypeId] ?? ":";
|
||||
$pk->isBabyMob = $isBabyMob;
|
||||
$pk->disableRelativeVolume = $disableRelativeVolume;
|
||||
$pk->position = $pos->asVector3();
|
||||
@ -1935,7 +1940,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($playSound){
|
||||
$this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, 1, $hand->getRuntimeId());
|
||||
$this->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, $hand->getRuntimeId());
|
||||
}
|
||||
|
||||
$item->pop();
|
||||
@ -2438,7 +2443,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$chunkHash = Level::chunkHash($chunkX, $chunkZ);
|
||||
$oldChunk = $this->getChunk($chunkX, $chunkZ, false);
|
||||
if($oldChunk !== null){
|
||||
if($oldChunk !== null and $oldChunk !== $chunk){
|
||||
if($unload){
|
||||
$this->unloadChunk($chunkX, $chunkZ, false, false);
|
||||
}else{
|
||||
@ -2460,6 +2465,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
unset($this->blockCache[$chunkHash]);
|
||||
unset($this->chunkCache[$chunkHash]);
|
||||
unset($this->changedBlocks[$chunkHash]);
|
||||
if(isset($this->chunkSendTasks[$chunkHash])){ //invalidate pending caches
|
||||
$this->chunkSendTasks[$chunkHash]->cancelRun();
|
||||
unset($this->chunkSendTasks[$chunkHash]);
|
||||
}
|
||||
$chunk->setChanged();
|
||||
|
||||
if(!$this->isChunkInUse($chunkX, $chunkZ)){
|
||||
@ -2743,10 +2752,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
try{
|
||||
$chunk = $this->provider->loadChunk($x, $z);
|
||||
}catch(\Exception $e){
|
||||
}catch(CorruptedChunkException | UnsupportedChunkFormatException $e){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->critical("An error occurred while loading chunk x=$x z=$z: " . $e->getMessage());
|
||||
$logger->logException($e);
|
||||
$logger->critical("Failed to load chunk x=$x z=$z: " . $e->getMessage());
|
||||
}
|
||||
|
||||
if($chunk === null and $create){
|
||||
@ -2827,23 +2835,17 @@ class Level implements ChunkManager, Metadatable{
|
||||
return false;
|
||||
}
|
||||
|
||||
try{
|
||||
if($trySave and $this->getAutoSave() and $chunk->isGenerated()){
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0){
|
||||
$this->provider->saveChunk($chunk);
|
||||
}
|
||||
if($trySave and $this->getAutoSave() and $chunk->isGenerated()){
|
||||
if($chunk->hasChanged() or count($chunk->getTiles()) > 0 or count($chunk->getSavableEntities()) > 0){
|
||||
$this->provider->saveChunk($chunk);
|
||||
}
|
||||
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkUnloaded($chunk);
|
||||
}
|
||||
|
||||
$chunk->onUnload();
|
||||
}catch(\Throwable $e){
|
||||
$logger = $this->server->getLogger();
|
||||
$logger->error($this->server->getLanguage()->translateString("pocketmine.level.chunkUnloadError", [$e->getMessage()]));
|
||||
$logger->logException($e);
|
||||
}
|
||||
|
||||
foreach($this->getChunkLoaders($x, $z) as $loader){
|
||||
$loader->onChunkUnloaded($chunk);
|
||||
}
|
||||
|
||||
$chunk->onUnload();
|
||||
}
|
||||
|
||||
unset($this->chunks[$chunkHash]);
|
||||
|
@ -24,6 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\level\LevelException;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\BigEndianNBTStream;
|
||||
@ -152,6 +154,14 @@ abstract class BaseLevelProvider implements LevelProvider{
|
||||
file_put_contents($this->getPath() . "level.dat", $buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Chunk|null
|
||||
* @throws CorruptedChunkException
|
||||
* @throws UnsupportedChunkFormatException
|
||||
*/
|
||||
public function loadChunk(int $chunkX, int $chunkZ) : ?Chunk{
|
||||
return $this->readChunk($chunkX, $chunkZ);
|
||||
}
|
||||
@ -163,6 +173,14 @@ abstract class BaseLevelProvider implements LevelProvider{
|
||||
$this->writeChunk($chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Chunk|null
|
||||
* @throws UnsupportedChunkFormatException
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
abstract protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk;
|
||||
|
||||
abstract protected function writeChunk(Chunk $chunk) : void;
|
||||
|
@ -24,6 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\io\exception\UnsupportedChunkFormatException;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
interface LevelProvider{
|
||||
@ -100,8 +102,8 @@ interface LevelProvider{
|
||||
*
|
||||
* @return null|Chunk
|
||||
*
|
||||
* @throws \Exception any of a range of exceptions that could be thrown while reading chunks. See individual
|
||||
* implementations for details.
|
||||
* @throws CorruptedChunkException
|
||||
* @throws UnsupportedChunkFormatException
|
||||
*/
|
||||
public function loadChunk(int $chunkX, int $chunkZ) : ?Chunk;
|
||||
|
||||
|
@ -283,6 +283,11 @@ class LevelDB extends BaseLevelProvider{
|
||||
/** @var SubChunk[] $subChunks */
|
||||
$subChunks = [];
|
||||
|
||||
/** @var int[] $heightMap */
|
||||
$heightMap = [];
|
||||
/** @var string $biomeIds */
|
||||
$biomeIds = "";
|
||||
|
||||
/** @var bool $lightPopulated */
|
||||
$lightPopulated = true;
|
||||
|
||||
@ -325,10 +330,12 @@ class LevelDB extends BaseLevelProvider{
|
||||
}
|
||||
}
|
||||
|
||||
$binaryStream->setBuffer($this->db->get($index . self::TAG_DATA_2D), 0);
|
||||
if(($maps2d = $this->db->get($index . self::TAG_DATA_2D)) !== false){
|
||||
$binaryStream->setBuffer($maps2d, 0);
|
||||
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
$heightMap = array_values(unpack("v*", $binaryStream->get(512)));
|
||||
$biomeIds = $binaryStream->get(256);
|
||||
}
|
||||
break;
|
||||
case 2: // < MCPE 1.0
|
||||
$binaryStream->setBuffer($this->db->get($index . self::TAG_LEGACY_TERRAIN));
|
||||
|
@ -24,8 +24,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io\region;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\ChunkUtils;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\nbt\BigEndianNBTStream;
|
||||
use pocketmine\nbt\NBT;
|
||||
@ -99,7 +99,7 @@ class Anvil extends McRegion{
|
||||
$nbt = new BigEndianNBTStream();
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
if(!($chunk instanceof CompoundTag) or !$chunk->hasTag("Level")){
|
||||
throw new ChunkException("Invalid NBT format");
|
||||
throw new CorruptedChunkException("'Level' key is missing from chunk NBT");
|
||||
}
|
||||
|
||||
$chunk = $chunk->getCompoundTag("Level");
|
||||
|
@ -24,9 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\level\format\io\region;
|
||||
|
||||
use pocketmine\level\format\Chunk;
|
||||
use pocketmine\level\format\ChunkException;
|
||||
use pocketmine\level\format\io\BaseLevelProvider;
|
||||
use pocketmine\level\format\io\ChunkUtils;
|
||||
use pocketmine\level\format\io\exception\CorruptedChunkException;
|
||||
use pocketmine\level\format\SubChunk;
|
||||
use pocketmine\level\generator\GeneratorManager;
|
||||
use pocketmine\level\Level;
|
||||
@ -107,12 +107,13 @@ class McRegion extends BaseLevelProvider{
|
||||
* @param string $data
|
||||
*
|
||||
* @return Chunk
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
protected function nbtDeserialize(string $data) : Chunk{
|
||||
$nbt = new BigEndianNBTStream();
|
||||
$chunk = $nbt->readCompressed($data);
|
||||
if(!($chunk instanceof CompoundTag) or !$chunk->hasTag("Level")){
|
||||
throw new ChunkException("Invalid NBT format");
|
||||
throw new CorruptedChunkException("'Level' key is missing from chunk NBT");
|
||||
}
|
||||
|
||||
$chunk = $chunk->getCompoundTag("Level");
|
||||
@ -348,6 +349,14 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return Chunk|null
|
||||
*
|
||||
* @throws CorruptedChunkException
|
||||
*/
|
||||
protected function readChunk(int $chunkX, int $chunkZ) : ?Chunk{
|
||||
$regionX = $regionZ = null;
|
||||
self::getRegionIndex($chunkX, $chunkZ, $regionX, $regionZ);
|
||||
|
@ -33,7 +33,7 @@ class RegionLoader{
|
||||
public const COMPRESSION_GZIP = 1;
|
||||
public const COMPRESSION_ZLIB = 2;
|
||||
|
||||
public const MAX_SECTOR_LENGTH = 256 << 12; //256 sectors, (1 MiB)
|
||||
public const MAX_SECTOR_LENGTH = 255 << 12; //255 sectors (~0.996 MiB)
|
||||
public const REGION_HEADER_LENGTH = 8192; //4096 location table + 4096 timestamps
|
||||
|
||||
public static $COMPRESSION_LEVEL = 7;
|
||||
@ -198,81 +198,6 @@ class RegionLoader{
|
||||
}
|
||||
}
|
||||
|
||||
public function doSlowCleanUp() : int{
|
||||
for($i = 0; $i < 1024; ++$i){
|
||||
if($this->locationTable[$i][0] === 0 or $this->locationTable[$i][1] === 0){
|
||||
continue;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
$chunk = fread($this->filePointer, $this->locationTable[$i][1] << 12);
|
||||
$length = Binary::readInt(substr($chunk, 0, 4));
|
||||
if($length <= 1){
|
||||
$this->locationTable[$i] = [0, 0, 0]; //Non-generated chunk, remove it from index
|
||||
}
|
||||
|
||||
try{
|
||||
$chunk = zlib_decode(substr($chunk, 5));
|
||||
}catch(\Throwable $e){
|
||||
$this->locationTable[$i] = [0, 0, 0]; //Corrupted chunk, remove it
|
||||
continue;
|
||||
}
|
||||
|
||||
$chunk = chr(self::COMPRESSION_ZLIB) . zlib_encode($chunk, ZLIB_ENCODING_DEFLATE, 9);
|
||||
$chunk = Binary::writeInt(strlen($chunk)) . $chunk;
|
||||
$sectors = (int) ceil(strlen($chunk) / 4096);
|
||||
if($sectors > $this->locationTable[$i][1]){
|
||||
$this->locationTable[$i][0] = $this->lastSector + 1;
|
||||
$this->lastSector += $sectors;
|
||||
}
|
||||
fseek($this->filePointer, $this->locationTable[$i][0] << 12);
|
||||
fwrite($this->filePointer, str_pad($chunk, $sectors << 12, "\x00", STR_PAD_RIGHT));
|
||||
}
|
||||
$this->writeLocationTable();
|
||||
$n = $this->cleanGarbage();
|
||||
$this->writeLocationTable();
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
private function cleanGarbage() : int{
|
||||
$sectors = [];
|
||||
foreach($this->locationTable as $index => $data){ //Calculate file usage
|
||||
if($data[0] === 0 or $data[1] === 0){
|
||||
$this->locationTable[$index] = [0, 0, 0];
|
||||
continue;
|
||||
}
|
||||
for($i = 0; $i < $data[1]; ++$i){
|
||||
$sectors[$data[0]] = $index;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($sectors) === ($this->lastSector - 2)){ //No collection needed
|
||||
return 0;
|
||||
}
|
||||
|
||||
ksort($sectors);
|
||||
$shift = 0;
|
||||
$lastSector = 1; //First chunk - 1
|
||||
|
||||
fseek($this->filePointer, 8192);
|
||||
$sector = 2;
|
||||
foreach($sectors as $sector => $index){
|
||||
if(($sector - $lastSector) > 1){
|
||||
$shift += $sector - $lastSector - 1;
|
||||
}
|
||||
if($shift > 0){
|
||||
fseek($this->filePointer, $sector << 12);
|
||||
$old = fread($this->filePointer, 4096);
|
||||
fseek($this->filePointer, ($sector - $shift) << 12);
|
||||
fwrite($this->filePointer, $old, 4096);
|
||||
}
|
||||
$this->locationTable[$index][0] -= $shift;
|
||||
$lastSector = $sector;
|
||||
}
|
||||
ftruncate($this->filePointer, ($sector + 1) << 12); //Truncate to the end of file written
|
||||
return $shift;
|
||||
}
|
||||
|
||||
protected function loadLocationTable(){
|
||||
fseek($this->filePointer, 0);
|
||||
$this->lastSector = 1;
|
||||
|
@ -33,10 +33,8 @@ abstract class Tree{
|
||||
public $overridable = [
|
||||
Block::AIR => true,
|
||||
Block::SAPLING => true,
|
||||
Block::LOG => true,
|
||||
Block::LEAVES => true,
|
||||
Block::SNOW_LAYER => true,
|
||||
Block::LOG2 => true,
|
||||
Block::LEAVES2 => true
|
||||
];
|
||||
|
||||
|
@ -72,6 +72,16 @@ abstract class Particle extends Vector3{
|
||||
public const TYPE_SPIT = 42;
|
||||
public const TYPE_TOTEM = 43;
|
||||
public const TYPE_FOOD = 44;
|
||||
public const TYPE_FIREWORKS_STARTER = 45;
|
||||
public const TYPE_FIREWORKS_SPARK = 46;
|
||||
public const TYPE_FIREWORKS_OVERLAY = 47;
|
||||
public const TYPE_BALLOON_GAS = 48;
|
||||
public const TYPE_COLORED_FLAME = 49;
|
||||
public const TYPE_SPARKLER = 50;
|
||||
public const TYPE_CONDUIT = 51;
|
||||
public const TYPE_BUBBLE_COLUMN_UP = 52;
|
||||
public const TYPE_BUBBLE_COLUMN_DOWN = 53;
|
||||
public const TYPE_SNEEZE = 54;
|
||||
|
||||
/**
|
||||
* @return DataPacket|DataPacket[]
|
||||
|
@ -32,6 +32,8 @@ use pocketmine\network\mcpe\protocol\AddPlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\AdventureSettingsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AnimatePacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableCommandsPacket;
|
||||
use pocketmine\network\mcpe\protocol\AvailableEntityIdentifiersPacket;
|
||||
use pocketmine\network\mcpe\protocol\BiomeDefinitionListPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEntityDataPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\BlockPickRequestPacket;
|
||||
@ -69,6 +71,7 @@ use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\LabTablePacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacketV1;
|
||||
use pocketmine\network\mcpe\protocol\LoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\MapInfoRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
@ -79,6 +82,7 @@ use pocketmine\network\mcpe\protocol\ModalFormResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityAbsolutePacket;
|
||||
use pocketmine\network\mcpe\protocol\MoveEntityDeltaPacket;
|
||||
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkChunkPublisherUpdatePacket;
|
||||
use pocketmine\network\mcpe\protocol\NetworkStackLatencyPacket;
|
||||
use pocketmine\network\mcpe\protocol\NpcRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\PhotoTransferPacket;
|
||||
@ -126,6 +130,7 @@ use pocketmine\network\mcpe\protocol\ShowProfilePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowStoreOfferPacket;
|
||||
use pocketmine\network\mcpe\protocol\SimpleEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnParticleEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\StartGamePacket;
|
||||
use pocketmine\network\mcpe\protocol\StopSoundPacket;
|
||||
use pocketmine\network\mcpe\protocol\StructureBlockUpdatePacket;
|
||||
@ -237,7 +242,7 @@ abstract class NetworkSession{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -608,4 +613,25 @@ abstract class NetworkSession{
|
||||
public function handleScriptCustomEvent(ScriptCustomEventPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleSpawnParticleEffect(SpawnParticleEffectPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleAvailableEntityIdentifiers(AvailableEntityIdentifiersPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleNetworkChunkPublisherUpdate(NetworkChunkPublisherUpdatePacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function handleBiomeDefinitionList(BiomeDefinitionListPacket $packet) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ use pocketmine\network\mcpe\protocol\InteractPacket;
|
||||
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket;
|
||||
use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacketV1;
|
||||
use pocketmine\network\mcpe\protocol\LoginPacket;
|
||||
use pocketmine\network\mcpe\protocol\MapInfoRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket;
|
||||
@ -58,6 +59,7 @@ use pocketmine\network\mcpe\protocol\RequestChunkRadiusPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackChunkRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\ResourcePackClientResponsePacket;
|
||||
use pocketmine\network\mcpe\protocol\ServerSettingsRequestPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetLocalPlayerAsInitializedPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetPlayerGameTypePacket;
|
||||
use pocketmine\network\mcpe\protocol\ShowCreditsPacket;
|
||||
use pocketmine\network\mcpe\protocol\SpawnExperienceOrbPacket;
|
||||
@ -125,8 +127,8 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
return $this->player->handleMovePlayer($packet);
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
return $this->player->handleLevelSoundEvent($packet);
|
||||
public function handleLevelSoundEventPacketV1(LevelSoundEventPacketV1 $packet) : bool{
|
||||
return true; //useless leftover from 1.8
|
||||
}
|
||||
|
||||
public function handleEntityEvent(EntityEventPacket $packet) : bool{
|
||||
@ -280,4 +282,13 @@ class PlayerNetworkSessionAdapter extends NetworkSession{
|
||||
public function handleServerSettingsRequest(ServerSettingsRequestPacket $packet) : bool{
|
||||
return false; //TODO: GUI stuff
|
||||
}
|
||||
|
||||
public function handleSetLocalPlayerAsInitialized(SetLocalPlayerAsInitializedPacket $packet) : bool{
|
||||
$this->player->doFirstSpawn();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleLevelSoundEvent(LevelSoundEventPacket $packet) : bool{
|
||||
return $this->player->handleLevelSoundEvent($packet);
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\entity\Attribute;
|
||||
use pocketmine\entity\EntityIds;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
@ -33,6 +34,114 @@ use pocketmine\network\mcpe\protocol\types\EntityLink;
|
||||
class AddEntityPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::ADD_ENTITY_PACKET;
|
||||
|
||||
/*
|
||||
* Really really really really really nasty hack, to preserve backwards compatibility.
|
||||
* We can't transition to string IDs within 3.x because the network IDs (the integer ones) are exposed
|
||||
* to the API in some places (for god's sake shoghi).
|
||||
*
|
||||
* TODO: remove this on 4.0
|
||||
*/
|
||||
public const LEGACY_ID_MAP_BC = [
|
||||
EntityIds::NPC => "minecraft:npc",
|
||||
EntityIds::PLAYER => "minecraft:player",
|
||||
EntityIds::WITHER_SKELETON => "minecraft:wither_skeleton",
|
||||
EntityIds::HUSK => "minecraft:husk",
|
||||
EntityIds::STRAY => "minecraft:stray",
|
||||
EntityIds::WITCH => "minecraft:witch",
|
||||
EntityIds::ZOMBIE_VILLAGER => "minecraft:zombie_villager",
|
||||
EntityIds::BLAZE => "minecraft:blaze",
|
||||
EntityIds::MAGMA_CUBE => "minecraft:magma_cube",
|
||||
EntityIds::GHAST => "minecraft:ghast",
|
||||
EntityIds::CAVE_SPIDER => "minecraft:cave_spider",
|
||||
EntityIds::SILVERFISH => "minecraft:silverfish",
|
||||
EntityIds::ENDERMAN => "minecraft:enderman",
|
||||
EntityIds::SLIME => "minecraft:slime",
|
||||
EntityIds::ZOMBIE_PIGMAN => "minecraft:zombie_pigman",
|
||||
EntityIds::SPIDER => "minecraft:spider",
|
||||
EntityIds::SKELETON => "minecraft:skeleton",
|
||||
EntityIds::CREEPER => "minecraft:creeper",
|
||||
EntityIds::ZOMBIE => "minecraft:zombie",
|
||||
EntityIds::SKELETON_HORSE => "minecraft:skeleton_horse",
|
||||
EntityIds::MULE => "minecraft:mule",
|
||||
EntityIds::DONKEY => "minecraft:donkey",
|
||||
EntityIds::DOLPHIN => "minecraft:dolphin",
|
||||
EntityIds::TROPICALFISH => "minecraft:tropicalfish",
|
||||
EntityIds::WOLF => "minecraft:wolf",
|
||||
EntityIds::SQUID => "minecraft:squid",
|
||||
EntityIds::DROWNED => "minecraft:drowned",
|
||||
EntityIds::SHEEP => "minecraft:sheep",
|
||||
EntityIds::MOOSHROOM => "minecraft:mooshroom",
|
||||
EntityIds::PANDA => "minecraft:panda",
|
||||
EntityIds::SALMON => "minecraft:salmon",
|
||||
EntityIds::PIG => "minecraft:pig",
|
||||
EntityIds::VILLAGER => "minecraft:villager",
|
||||
EntityIds::COD => "minecraft:cod",
|
||||
EntityIds::PUFFERFISH => "minecraft:pufferfish",
|
||||
EntityIds::COW => "minecraft:cow",
|
||||
EntityIds::CHICKEN => "minecraft:chicken",
|
||||
EntityIds::BALLOON => "minecraft:balloon",
|
||||
EntityIds::LLAMA => "minecraft:llama",
|
||||
EntityIds::IRON_GOLEM => "minecraft:iron_golem",
|
||||
EntityIds::RABBIT => "minecraft:rabbit",
|
||||
EntityIds::SNOW_GOLEM => "minecraft:snow_golem",
|
||||
EntityIds::BAT => "minecraft:bat",
|
||||
EntityIds::OCELOT => "minecraft:ocelot",
|
||||
EntityIds::HORSE => "minecraft:horse",
|
||||
EntityIds::CAT => "minecraft:cat",
|
||||
EntityIds::POLAR_BEAR => "minecraft:polar_bear",
|
||||
EntityIds::ZOMBIE_HORSE => "minecraft:zombie_horse",
|
||||
EntityIds::TURTLE => "minecraft:turtle",
|
||||
EntityIds::PARROT => "minecraft:parrot",
|
||||
EntityIds::GUARDIAN => "minecraft:guardian",
|
||||
EntityIds::ELDER_GUARDIAN => "minecraft:elder_guardian",
|
||||
EntityIds::VINDICATOR => "minecraft:vindicator",
|
||||
EntityIds::WITHER => "minecraft:wither",
|
||||
EntityIds::ENDER_DRAGON => "minecraft:ender_dragon",
|
||||
EntityIds::SHULKER => "minecraft:shulker",
|
||||
EntityIds::ENDERMITE => "minecraft:endermite",
|
||||
EntityIds::MINECART => "minecraft:minecart",
|
||||
EntityIds::HOPPER_MINECART => "minecraft:hopper_minecart",
|
||||
EntityIds::TNT_MINECART => "minecraft:tnt_minecart",
|
||||
EntityIds::CHEST_MINECART => "minecraft:chest_minecart",
|
||||
EntityIds::COMMAND_BLOCK_MINECART => "minecraft:command_block_minecart",
|
||||
EntityIds::ARMOR_STAND => "minecraft:armor_stand",
|
||||
EntityIds::ITEM => "minecraft:item",
|
||||
EntityIds::TNT => "minecraft:tnt",
|
||||
EntityIds::FALLING_BLOCK => "minecraft:falling_block",
|
||||
EntityIds::XP_BOTTLE => "minecraft:xp_bottle",
|
||||
EntityIds::XP_ORB => "minecraft:xp_orb",
|
||||
EntityIds::EYE_OF_ENDER_SIGNAL => "minecraft:eye_of_ender_signal",
|
||||
EntityIds::ENDER_CRYSTAL => "minecraft:ender_crystal",
|
||||
EntityIds::SHULKER_BULLET => "minecraft:shulker_bullet",
|
||||
EntityIds::FISHING_HOOK => "minecraft:fishing_hook",
|
||||
EntityIds::DRAGON_FIREBALL => "minecraft:dragon_fireball",
|
||||
EntityIds::ARROW => "minecraft:arrow",
|
||||
EntityIds::SNOWBALL => "minecraft:snowball",
|
||||
EntityIds::EGG => "minecraft:egg",
|
||||
EntityIds::PAINTING => "minecraft:painting",
|
||||
EntityIds::THROWN_TRIDENT => "minecraft:thrown_trident",
|
||||
EntityIds::FIREBALL => "minecraft:fireball",
|
||||
EntityIds::SPLASH_POTION => "minecraft:splash_potion",
|
||||
EntityIds::ENDER_PEARL => "minecraft:ender_pearl",
|
||||
EntityIds::LEASH_KNOT => "minecraft:leash_knot",
|
||||
EntityIds::WITHER_SKULL => "minecraft:wither_skull",
|
||||
EntityIds::WITHER_SKULL_DANGEROUS => "minecraft:wither_skull_dangerous",
|
||||
EntityIds::BOAT => "minecraft:boat",
|
||||
EntityIds::LIGHTNING_BOLT => "minecraft:lightning_bolt",
|
||||
EntityIds::SMALL_FIREBALL => "minecraft:small_fireball",
|
||||
EntityIds::LLAMA_SPIT => "minecraft:llama_spit",
|
||||
EntityIds::AREA_EFFECT_CLOUD => "minecraft:area_effect_cloud",
|
||||
EntityIds::LINGERING_POTION => "minecraft:lingering_potion",
|
||||
EntityIds::FIREWORKS_ROCKET => "minecraft:fireworks_rocket",
|
||||
EntityIds::EVOCATION_FANG => "minecraft:evocation_fang",
|
||||
EntityIds::EVOCATION_ILLAGER => "minecraft:evocation_illager",
|
||||
EntityIds::VEX => "minecraft:vex",
|
||||
EntityIds::AGENT => "minecraft:agent",
|
||||
EntityIds::ICE_BOMB => "minecraft:ice_bomb",
|
||||
EntityIds::PHANTOM => "minecraft:phantom",
|
||||
EntityIds::TRIPOD_CAMERA => "minecraft:tripod_camera"
|
||||
];
|
||||
|
||||
/** @var int|null */
|
||||
public $entityUniqueId = null; //TODO
|
||||
/** @var int */
|
||||
@ -60,7 +169,10 @@ class AddEntityPacket extends DataPacket{
|
||||
protected function decodePayload(){
|
||||
$this->entityUniqueId = $this->getEntityUniqueId();
|
||||
$this->entityRuntimeId = $this->getEntityRuntimeId();
|
||||
$this->type = $this->getUnsignedVarInt();
|
||||
$this->type = array_search($t = $this->getString(), self::LEGACY_ID_MAP_BC);
|
||||
if($this->type === false){
|
||||
throw new \UnexpectedValueException("Can't map ID $t to legacy ID");
|
||||
}
|
||||
$this->position = $this->getVector3();
|
||||
$this->motion = $this->getVector3();
|
||||
$this->pitch = $this->getLFloat();
|
||||
@ -95,7 +207,7 @@ class AddEntityPacket extends DataPacket{
|
||||
protected function encodePayload(){
|
||||
$this->putEntityUniqueId($this->entityUniqueId ?? $this->entityRuntimeId);
|
||||
$this->putEntityRuntimeId($this->entityRuntimeId);
|
||||
$this->putUnsignedVarInt($this->type);
|
||||
$this->putString(self::LEGACY_ID_MAP_BC[$this->type]);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVector3Nullable($this->motion);
|
||||
$this->putLFloat($this->pitch);
|
||||
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class BiomeDefinitionListPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::BIOME_DEFINITION_LIST_PACKET;
|
||||
|
||||
/** @var string */
|
||||
public $namedtag;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->namedtag = $this->getRemaining();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->put($this->namedtag);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleBiomeDefinitionList($this);
|
||||
}
|
||||
}
|
@ -114,7 +114,9 @@ class ClientboundMapItemDataPacket extends DataPacket{
|
||||
$this->yOffset = $this->getVarInt();
|
||||
|
||||
$count = $this->getUnsignedVarInt();
|
||||
assert($count === $this->width * $this->height);
|
||||
if($count !== $this->width * $this->height){
|
||||
throw new \UnexpectedValueException("Expected colour count of " . ($this->height * $this->width) . " (height $this->height * width $this->width), got $count");
|
||||
}
|
||||
|
||||
for($y = 0; $y < $this->height; ++$y){
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
|
@ -76,7 +76,9 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
|
||||
protected function decodeHeader(){
|
||||
$pid = $this->getUnsignedVarInt();
|
||||
assert($pid === static::NETWORK_ID);
|
||||
if($pid !== static::NETWORK_ID){
|
||||
throw new \UnexpectedValueException("Expected " . static::NETWORK_ID . " for packet ID, got $pid");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,10 +141,10 @@ abstract class DataPacket extends NetworkBinaryStream{
|
||||
}
|
||||
|
||||
public function __get($name){
|
||||
throw new \Error("Cannot read non-existing field \"$name\"");
|
||||
throw new \Error("Undefined property: " . get_class($this) . "::\$" . $name);
|
||||
}
|
||||
|
||||
public function __set($name, $value){
|
||||
throw new \Error("Cannot write non-existing field \"$name\"");
|
||||
throw new \Error("Undefined property: " . get_class($this) . "::\$" . $name);
|
||||
}
|
||||
}
|
||||
|
@ -242,7 +242,48 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public const SOUND_CONVERT_TO_DROWNED = 211;
|
||||
public const SOUND_BUCKET_FILL_FISH = 212;
|
||||
public const SOUND_BUCKET_EMPTY_FISH = 213;
|
||||
public const SOUND_UNDEFINED = 214;
|
||||
public const SOUND_BUBBLE_UP = 214;
|
||||
public const SOUND_BUBBLE_DOWN = 215;
|
||||
public const SOUND_BUBBLE_POP = 216;
|
||||
public const SOUND_BUBBLE_UPINSIDE = 217;
|
||||
public const SOUND_BUBBLE_DOWNINSIDE = 218;
|
||||
public const SOUND_HURT_BABY = 219;
|
||||
public const SOUND_DEATH_BABY = 220;
|
||||
public const SOUND_STEP_BABY = 221;
|
||||
|
||||
public const SOUND_BORN = 223;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_BREAK = 224;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_CRACK = 225;
|
||||
public const SOUND_BLOCK_TURTLE_EGG_HATCH = 226;
|
||||
|
||||
public const SOUND_BLOCK_TURTLE_EGG_ATTACK = 228;
|
||||
public const SOUND_BEACON_ACTIVATE = 229;
|
||||
public const SOUND_BEACON_AMBIENT = 230;
|
||||
public const SOUND_BEACON_DEACTIVATE = 231;
|
||||
public const SOUND_BEACON_POWER = 232;
|
||||
public const SOUND_CONDUIT_ACTIVATE = 233;
|
||||
public const SOUND_CONDUIT_AMBIENT = 234;
|
||||
public const SOUND_CONDUIT_ATTACK = 235;
|
||||
public const SOUND_CONDUIT_DEACTIVATE = 236;
|
||||
public const SOUND_CONDUIT_SHORT = 237;
|
||||
public const SOUND_SWOOP = 238;
|
||||
public const SOUND_BLOCK_BAMBOO_SAPLING_PLACE = 239;
|
||||
public const SOUND_PRESNEEZE = 240;
|
||||
public const SOUND_SNEEZE = 241;
|
||||
public const SOUND_AMBIENT_TAME = 242;
|
||||
public const SOUND_SCARED = 243;
|
||||
public const SOUND_BLOCK_SCAFFOLDING_CLIMB = 244;
|
||||
public const SOUND_CROSSBOW_LOADING_START = 245;
|
||||
public const SOUND_CROSSBOW_LOADING_MIDDLE = 246;
|
||||
public const SOUND_CROSSBOW_LOADING_END = 247;
|
||||
public const SOUND_CROSSBOW_SHOOT = 248;
|
||||
public const SOUND_CROSSBOW_QUICK_CHARGE_START = 249;
|
||||
public const SOUND_CROSSBOW_QUICK_CHARGE_MIDDLE = 250;
|
||||
public const SOUND_CROSSBOW_QUICK_CHARGE_END = 251;
|
||||
public const SOUND_AMBIENT_AGGRESSIVE = 252;
|
||||
public const SOUND_AMBIENT_WORRIED = 253;
|
||||
public const SOUND_CANT_BREED = 254;
|
||||
public const SOUND_UNDEFINED = 255;
|
||||
|
||||
/** @var int */
|
||||
public $sound;
|
||||
@ -250,8 +291,8 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
public $position;
|
||||
/** @var int */
|
||||
public $extraData = -1;
|
||||
/** @var int */
|
||||
public $pitch = 1;
|
||||
/** @var string */
|
||||
public $entityType = ":"; //???
|
||||
/** @var bool */
|
||||
public $isBabyMob = false; //...
|
||||
/** @var bool */
|
||||
@ -261,7 +302,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
$this->sound = $this->getByte();
|
||||
$this->position = $this->getVector3();
|
||||
$this->extraData = $this->getVarInt();
|
||||
$this->pitch = $this->getVarInt();
|
||||
$this->entityType = $this->getString();
|
||||
$this->isBabyMob = $this->getBool();
|
||||
$this->disableRelativeVolume = $this->getBool();
|
||||
}
|
||||
@ -270,7 +311,7 @@ class LevelSoundEventPacket extends DataPacket{
|
||||
$this->putByte($this->sound);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVarInt($this->extraData);
|
||||
$this->putVarInt($this->pitch);
|
||||
$this->putString($this->entityType);
|
||||
$this->putBool($this->isBabyMob);
|
||||
$this->putBool($this->disableRelativeVolume);
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
/**
|
||||
* Useless leftover from a 1.8 refactor, does nothing
|
||||
*/
|
||||
class LevelSoundEventPacketV1 extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::LEVEL_SOUND_EVENT_PACKET_V1;
|
||||
|
||||
/** @var int */
|
||||
public $sound;
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var int */
|
||||
public $extraData = 0;
|
||||
/** @var int */
|
||||
public $entityType = 1;
|
||||
/** @var bool */
|
||||
public $isBabyMob = false; //...
|
||||
/** @var bool */
|
||||
public $disableRelativeVolume = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->sound = $this->getByte();
|
||||
$this->position = $this->getVector3();
|
||||
$this->extraData = $this->getVarInt();
|
||||
$this->entityType = $this->getVarInt();
|
||||
$this->isBabyMob = $this->getBool();
|
||||
$this->disableRelativeVolume = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->sound);
|
||||
$this->putVector3($this->position);
|
||||
$this->putVarInt($this->extraData);
|
||||
$this->putVarInt($this->entityType);
|
||||
$this->putBool($this->isBabyMob);
|
||||
$this->putBool($this->disableRelativeVolume);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleLevelSoundEventPacketV1($this);
|
||||
}
|
||||
}
|
@ -88,7 +88,7 @@ class LoginPacket extends DataPacket{
|
||||
|
||||
$logger = MainLogger::getLogger();
|
||||
$logger->debug(get_class($e) . " was thrown while decoding connection request in login (protocol version " . ($this->protocol ?? "unknown") . "): " . $e->getMessage());
|
||||
foreach(Utils::getTrace(0, $e->getTrace()) as $line){
|
||||
foreach(Utils::printableTrace($e->getTrace()) as $line){
|
||||
$logger->debug($line);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
|
||||
class NetworkChunkPublisherUpdatePacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::NETWORK_CHUNK_PUBLISHER_UPDATE_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $x;
|
||||
/** @var int */
|
||||
public $y;
|
||||
/** @var int */
|
||||
public $z;
|
||||
/** @var int */
|
||||
public $radius;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->getSignedBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->radius = $this->getUnsignedVarInt();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putSignedBlockPosition($this->x, $this->y, $this->z);
|
||||
$this->putUnsignedVarInt($this->radius);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleNetworkChunkPublisherUpdate($this);
|
||||
}
|
||||
}
|
@ -54,7 +54,7 @@ class PacketPool{
|
||||
static::registerPacket(new UpdateBlockPacket());
|
||||
static::registerPacket(new AddPaintingPacket());
|
||||
static::registerPacket(new ExplodePacket());
|
||||
static::registerPacket(new LevelSoundEventPacket());
|
||||
static::registerPacket(new LevelSoundEventPacketV1());
|
||||
static::registerPacket(new LevelEventPacket());
|
||||
static::registerPacket(new BlockEventPacket());
|
||||
static::registerPacket(new EntityEventPacket());
|
||||
@ -147,6 +147,11 @@ class PacketPool{
|
||||
static::registerPacket(new UpdateSoftEnumPacket());
|
||||
static::registerPacket(new NetworkStackLatencyPacket());
|
||||
static::registerPacket(new ScriptCustomEventPacket());
|
||||
static::registerPacket(new SpawnParticleEffectPacket());
|
||||
static::registerPacket(new AvailableEntityIdentifiersPacket());
|
||||
static::registerPacket(new LevelSoundEventPacket());
|
||||
static::registerPacket(new NetworkChunkPublisherUpdatePacket());
|
||||
static::registerPacket(new BiomeDefinitionListPacket());
|
||||
|
||||
static::registerPacket(new BatchPacket());
|
||||
}
|
||||
@ -178,4 +183,5 @@ class PacketPool{
|
||||
|
||||
return $pk;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,15 +39,15 @@ interface ProtocolInfo{
|
||||
/**
|
||||
* Actual Minecraft: PE protocol version
|
||||
*/
|
||||
public const CURRENT_PROTOCOL = 291;
|
||||
public const CURRENT_PROTOCOL = 313;
|
||||
/**
|
||||
* Current Minecraft PE version reported by the server. This is usually the earliest currently supported version.
|
||||
*/
|
||||
public const MINECRAFT_VERSION = 'v1.7.0';
|
||||
public const MINECRAFT_VERSION = 'v1.8.0';
|
||||
/**
|
||||
* Version number sent to clients in ping responses.
|
||||
*/
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.7.0';
|
||||
public const MINECRAFT_VERSION_NETWORK = '1.8.0';
|
||||
|
||||
public const LOGIN_PACKET = 0x01;
|
||||
public const PLAY_STATUS_PACKET = 0x02;
|
||||
@ -72,7 +72,7 @@ interface ProtocolInfo{
|
||||
public const UPDATE_BLOCK_PACKET = 0x15;
|
||||
public const ADD_PAINTING_PACKET = 0x16;
|
||||
public const EXPLODE_PACKET = 0x17;
|
||||
public const LEVEL_SOUND_EVENT_PACKET = 0x18;
|
||||
public const LEVEL_SOUND_EVENT_PACKET_V1 = 0x18;
|
||||
public const LEVEL_EVENT_PACKET = 0x19;
|
||||
public const BLOCK_EVENT_PACKET = 0x1a;
|
||||
public const ENTITY_EVENT_PACKET = 0x1b;
|
||||
@ -164,6 +164,12 @@ interface ProtocolInfo{
|
||||
public const SET_LOCAL_PLAYER_AS_INITIALIZED_PACKET = 0x71;
|
||||
public const UPDATE_SOFT_ENUM_PACKET = 0x72;
|
||||
public const NETWORK_STACK_LATENCY_PACKET = 0x73;
|
||||
|
||||
public const SCRIPT_CUSTOM_EVENT_PACKET = 0x75;
|
||||
public const SPAWN_PARTICLE_EFFECT_PACKET = 0x76;
|
||||
public const AVAILABLE_ENTITY_IDENTIFIERS_PACKET = 0x77;
|
||||
public const LEVEL_SOUND_EVENT_PACKET = 0x78;
|
||||
public const NETWORK_CHUNK_PUBLISHER_UPDATE_PACKET = 0x79;
|
||||
public const BIOME_DEFINITION_LIST_PACKET = 0x7a;
|
||||
|
||||
}
|
||||
|
@ -41,6 +41,9 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
/** @var ResourcePack[] */
|
||||
public $resourcePackStack = [];
|
||||
|
||||
/** @var bool */
|
||||
public $isExperimental = false;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->mustAccept = $this->getBool();
|
||||
$behaviorPackCount = $this->getUnsignedVarInt();
|
||||
@ -56,6 +59,8 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
$this->getString();
|
||||
$this->getString();
|
||||
}
|
||||
|
||||
$this->isExperimental = $this->getBool();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
@ -74,6 +79,8 @@ class ResourcePackStackPacket extends DataPacket{
|
||||
$this->putString($entry->getPackVersion());
|
||||
$this->putString(""); //TODO: subpack name
|
||||
}
|
||||
|
||||
$this->putBool($this->isExperimental);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
|
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\network\mcpe\protocol;
|
||||
|
||||
#include <rules/DataPacket.h>
|
||||
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\NetworkSession;
|
||||
use pocketmine\network\mcpe\protocol\types\DimensionIds;
|
||||
|
||||
class SpawnParticleEffectPacket extends DataPacket{
|
||||
public const NETWORK_ID = ProtocolInfo::SPAWN_PARTICLE_EFFECT_PACKET;
|
||||
|
||||
/** @var int */
|
||||
public $dimensionId = DimensionIds::OVERWORLD; //wtf mojang
|
||||
/** @var Vector3 */
|
||||
public $position;
|
||||
/** @var string */
|
||||
public $particleName;
|
||||
|
||||
protected function decodePayload(){
|
||||
$this->dimensionId = $this->getByte();
|
||||
$this->position = $this->getVector3();
|
||||
$this->particleName = $this->getString();
|
||||
}
|
||||
|
||||
protected function encodePayload(){
|
||||
$this->putByte($this->dimensionId);
|
||||
$this->putVector3($this->position);
|
||||
$this->putString($this->particleName);
|
||||
}
|
||||
|
||||
public function handle(NetworkSession $session) : bool{
|
||||
return $session->handleSpawnParticleEffect($this);
|
||||
}
|
||||
}
|
@ -118,6 +118,10 @@ class StartGamePacket extends DataPacket{
|
||||
public $isFromLockedWorldTemplate = false;
|
||||
/** @var bool */
|
||||
public $useMsaGamertagsOnly = false;
|
||||
/** @var bool */
|
||||
public $isFromWorldTemplate = false;
|
||||
/** @var bool */
|
||||
public $isWorldTemplateOptionLocked = false;
|
||||
|
||||
/** @var string */
|
||||
public $levelId = ""; //base64 string, usually the same as world folder name in vanilla
|
||||
@ -176,6 +180,8 @@ class StartGamePacket extends DataPacket{
|
||||
$this->hasLockedResourcePack = $this->getBool();
|
||||
$this->isFromLockedWorldTemplate = $this->getBool();
|
||||
$this->useMsaGamertagsOnly = $this->getBool();
|
||||
$this->isFromWorldTemplate = $this->getBool();
|
||||
$this->isWorldTemplateOptionLocked = $this->getBool();
|
||||
|
||||
$this->levelId = $this->getString();
|
||||
$this->worldName = $this->getString();
|
||||
@ -236,6 +242,8 @@ class StartGamePacket extends DataPacket{
|
||||
$this->putBool($this->hasLockedResourcePack);
|
||||
$this->putBool($this->isFromLockedWorldTemplate);
|
||||
$this->putBool($this->useMsaGamertagsOnly);
|
||||
$this->putBool($this->isFromWorldTemplate);
|
||||
$this->putBool($this->isWorldTemplateOptionLocked);
|
||||
|
||||
$this->putString($this->levelId);
|
||||
$this->putString($this->worldName);
|
||||
|
@ -43,6 +43,8 @@ class UpdateTradePacket extends DataPacket{
|
||||
public $varint1;
|
||||
/** @var int */
|
||||
public $varint2;
|
||||
/** @var int */
|
||||
public $varint3;
|
||||
/** @var bool */
|
||||
public $isWilling;
|
||||
/** @var int */
|
||||
@ -59,6 +61,7 @@ class UpdateTradePacket extends DataPacket{
|
||||
$this->windowType = $this->getByte();
|
||||
$this->varint1 = $this->getVarInt();
|
||||
$this->varint2 = $this->getVarInt();
|
||||
$this->varint3 = $this->getVarInt();
|
||||
$this->isWilling = $this->getBool();
|
||||
$this->traderEid = $this->getEntityUniqueId();
|
||||
$this->playerEid = $this->getEntityUniqueId();
|
||||
@ -71,6 +74,7 @@ class UpdateTradePacket extends DataPacket{
|
||||
$this->putByte($this->windowType);
|
||||
$this->putVarInt($this->varint1);
|
||||
$this->putVarInt($this->varint2);
|
||||
$this->putVarInt($this->varint3);
|
||||
$this->putBool($this->isWilling);
|
||||
$this->putEntityUniqueId($this->traderEid);
|
||||
$this->putEntityUniqueId($this->playerEid);
|
||||
|
@ -36,6 +36,7 @@ class NetworkInventoryAction{
|
||||
|
||||
public const SOURCE_WORLD = 2; //drop/pickup item entity
|
||||
public const SOURCE_CREATIVE = 3;
|
||||
public const SOURCE_CRAFTING_GRID = 100;
|
||||
public const SOURCE_TODO = 99999;
|
||||
|
||||
/**
|
||||
@ -80,7 +81,7 @@ class NetworkInventoryAction{
|
||||
/** @var int */
|
||||
public $sourceType;
|
||||
/** @var int */
|
||||
public $windowId = ContainerIds::NONE;
|
||||
public $windowId;
|
||||
/** @var int */
|
||||
public $sourceFlags = 0;
|
||||
/** @var int */
|
||||
@ -107,6 +108,7 @@ class NetworkInventoryAction{
|
||||
break;
|
||||
case self::SOURCE_CREATIVE:
|
||||
break;
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
$this->windowId = $packet->getVarInt();
|
||||
switch($this->windowId){
|
||||
@ -118,6 +120,8 @@ class NetworkInventoryAction{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType");
|
||||
}
|
||||
|
||||
$this->inventorySlot = $packet->getUnsignedVarInt();
|
||||
@ -142,9 +146,12 @@ class NetworkInventoryAction{
|
||||
break;
|
||||
case self::SOURCE_CREATIVE:
|
||||
break;
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
$packet->putVarInt($this->windowId);
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unknown inventory action source type $this->sourceType");
|
||||
}
|
||||
|
||||
$packet->putUnsignedVarInt($this->inventorySlot);
|
||||
@ -186,27 +193,17 @@ class NetworkInventoryAction{
|
||||
}
|
||||
|
||||
return new CreativeInventoryAction($this->oldItem, $this->newItem, $type);
|
||||
case self::SOURCE_CRAFTING_GRID:
|
||||
case self::SOURCE_TODO:
|
||||
//These types need special handling.
|
||||
switch($this->windowId){
|
||||
case self::SOURCE_TYPE_CRAFTING_ADD_INGREDIENT:
|
||||
case self::SOURCE_TYPE_CRAFTING_REMOVE_INGREDIENT:
|
||||
$window = $player->getCraftingGrid();
|
||||
return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem);
|
||||
case self::SOURCE_TYPE_CONTAINER_DROP_CONTENTS: //TODO: this type applies to all fake windows, not just crafting
|
||||
return new SlotChangeAction($player->getCraftingGrid(), $this->inventorySlot, $this->oldItem, $this->newItem);
|
||||
case self::SOURCE_TYPE_CRAFTING_RESULT:
|
||||
case self::SOURCE_TYPE_CRAFTING_USE_INGREDIENT:
|
||||
return null;
|
||||
|
||||
case self::SOURCE_TYPE_CONTAINER_DROP_CONTENTS:
|
||||
//TODO: this type applies to all fake windows, not just crafting
|
||||
$window = $player->getCraftingGrid();
|
||||
|
||||
//DROP_CONTENTS doesn't bother telling us what slot the item is in, so we find it ourselves
|
||||
$inventorySlot = $window->first($this->oldItem, true);
|
||||
if($inventorySlot === -1){
|
||||
throw new \InvalidStateException("Fake container " . get_class($window) . " for " . $player->getName() . " does not contain $this->oldItem");
|
||||
}
|
||||
return new SlotChangeAction($window, $inventorySlot, $this->oldItem, $this->newItem);
|
||||
}
|
||||
|
||||
//TODO: more stuff
|
||||
|
@ -25,6 +25,8 @@ declare(strict_types=1);
|
||||
namespace pocketmine\resourcepacks;
|
||||
|
||||
|
||||
use Ahc\Json\Comment as CommentedJsonDecoder;
|
||||
|
||||
class ZippedResourcePack implements ResourcePack{
|
||||
|
||||
/**
|
||||
@ -86,10 +88,13 @@ class ZippedResourcePack implements ResourcePack{
|
||||
|
||||
$archive->close();
|
||||
|
||||
$manifest = json_decode($manifestData);
|
||||
if($manifest === null){
|
||||
throw new ResourcePackException("Failed to parse manifest.json: " . json_last_error_msg());
|
||||
//maybe comments in the json, use stripped decoder (thanks mojang)
|
||||
try{
|
||||
$manifest = (new CommentedJsonDecoder())->decode($manifestData);
|
||||
}catch(\RuntimeException $e){
|
||||
throw new ResourcePackException("Failed to parse manifest.json: " . $e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
if(!self::verifyManifest($manifest)){
|
||||
throw new ResourcePackException("manifest.json is missing required fields");
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -70,6 +70,15 @@ class Chest extends Spawnable implements InventoryHolder, Container, Nameable{
|
||||
$this->saveItems($nbt);
|
||||
}
|
||||
|
||||
public function getCleanedNBT() : ?CompoundTag{
|
||||
$tag = parent::getCleanedNBT();
|
||||
if($tag !== null){
|
||||
//TODO: replace this with a purpose flag on writeSaveData()
|
||||
$tag->removeTag(self::TAG_PAIRX, self::TAG_PAIRZ);
|
||||
}
|
||||
return $tag;
|
||||
}
|
||||
|
||||
public function close() : void{
|
||||
if(!$this->closed){
|
||||
$this->inventory->removeAllViewers(true);
|
||||
|
@ -130,14 +130,9 @@ class Sign extends Spawnable{
|
||||
}
|
||||
|
||||
if($nbt->hasTag(self::TAG_TEXT_BLOB, StringTag::class)){
|
||||
$lines = array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, "");
|
||||
$lines = array_slice(array_pad(explode("\n", $nbt->getString(self::TAG_TEXT_BLOB)), 4, ""), 0, 4);
|
||||
}else{
|
||||
$lines = [
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 1)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 2)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 3)),
|
||||
$nbt->getString(sprintf(self::TAG_TEXT_LINE, 4))
|
||||
];
|
||||
return false;
|
||||
}
|
||||
|
||||
$removeFormat = $player->getRemoveFormat();
|
||||
|
@ -210,7 +210,7 @@ class MainLogger extends \AttachableThreadedLogger{
|
||||
$errfile = Utils::cleanPath($errfile);
|
||||
|
||||
$message = get_class($e) . ": \"$errstr\" ($errno) in \"$errfile\" at line $errline";
|
||||
$stack = Utils::getTrace(0, $trace);
|
||||
$stack = Utils::printableTrace($trace);
|
||||
|
||||
$this->synchronized(function() use ($type, $message, $stack) : void{
|
||||
$this->log($type, $message);
|
||||
|
@ -313,8 +313,8 @@ class Utils{
|
||||
++$processors;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim(@file_get_contents("/sys/devices/system/cpu/present")), $matches) > 0){
|
||||
}elseif(is_readable("/sys/devices/system/cpu/present")){
|
||||
if(preg_match("/^([0-9]+)\\-([0-9]+)$/", trim(file_get_contents("/sys/devices/system/cpu/present")), $matches) > 0){
|
||||
$processors = (int) ($matches[2] - $matches[1]);
|
||||
}
|
||||
}
|
||||
@ -525,24 +525,13 @@ class Utils{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $start
|
||||
* @param array|null $trace
|
||||
* @param array $trace
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getTrace($start = 0, $trace = null){
|
||||
if($trace === null){
|
||||
if(function_exists("xdebug_get_function_stack")){
|
||||
$trace = array_reverse(xdebug_get_function_stack());
|
||||
}else{
|
||||
$e = new \Exception();
|
||||
$trace = $e->getTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static function printableTrace(array $trace) : array{
|
||||
$messages = [];
|
||||
$j = 0;
|
||||
for($i = (int) $start; isset($trace[$i]); ++$i, ++$j){
|
||||
for($i = 0; isset($trace[$i]); ++$i){
|
||||
$params = "";
|
||||
if(isset($trace[$i]["args"]) or isset($trace[$i]["params"])){
|
||||
if(isset($trace[$i]["args"])){
|
||||
@ -555,12 +544,39 @@ class Utils{
|
||||
return (is_object($value) ? get_class($value) . " object" : gettype($value) . " " . (is_array($value) ? "Array()" : Utils::printable(@strval($value))));
|
||||
}, $args));
|
||||
}
|
||||
$messages[] = "#$j " . (isset($trace[$i]["file"]) ? self::cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable($params) . ")";
|
||||
$messages[] = "#$i " . (isset($trace[$i]["file"]) ? self::cleanPath($trace[$i]["file"]) : "") . "(" . (isset($trace[$i]["line"]) ? $trace[$i]["line"] : "") . "): " . (isset($trace[$i]["class"]) ? $trace[$i]["class"] . (($trace[$i]["type"] === "dynamic" or $trace[$i]["type"] === "->") ? "->" : "::") : "") . $trace[$i]["function"] . "(" . Utils::printable($params) . ")";
|
||||
}
|
||||
|
||||
return $messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $skipFrames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function currentTrace(int $skipFrames = 0) : array{
|
||||
++$skipFrames; //omit this frame from trace, in addition to other skipped frames
|
||||
if(function_exists("xdebug_get_function_stack")){
|
||||
$trace = array_reverse(xdebug_get_function_stack());
|
||||
}else{
|
||||
$e = new \Exception();
|
||||
$trace = $e->getTrace();
|
||||
}
|
||||
for($i = 0; $i < $skipFrames; ++$i){
|
||||
unset($trace[$i]);
|
||||
}
|
||||
return array_values($trace);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $skipFrames
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function printableCurrentTrace(int $skipFrames = 0) : array{
|
||||
return self::printableTrace(self::currentTrace(++$skipFrames));
|
||||
}
|
||||
|
||||
public static function cleanPath($path){
|
||||
return str_replace(["\\", ".php", "phar://", str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PATH), str_replace(["\\", "phar://"], ["/", ""], \pocketmine\PLUGIN_PATH)], ["/", "", "", "", ""], $path);
|
||||
}
|
||||
|
52
tests/phpunit/level/format/io/region/RegionLoaderTest.php
Normal file
52
tests/phpunit/level/format/io/region/RegionLoaderTest.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\level\format\io\region;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use pocketmine\level\format\ChunkException;
|
||||
|
||||
class RegionLoaderTest extends TestCase{
|
||||
|
||||
public function testChunkTooBig() : void{
|
||||
$r = new RegionLoader(sys_get_temp_dir() . '/chunk_too_big.testregion_' . bin2hex(random_bytes(4)), 0, 0);
|
||||
$r->open();
|
||||
|
||||
$this->expectException(ChunkException::class);
|
||||
$r->writeChunk(0, 0, str_repeat("a", 1044476));
|
||||
}
|
||||
|
||||
public function testChunkMaxSize() : void{
|
||||
$data = str_repeat("a", 1044475);
|
||||
$path = sys_get_temp_dir() . '/chunk_just_fits.testregion_' . bin2hex(random_bytes(4));
|
||||
$r = new RegionLoader($path, 0, 0);
|
||||
$r->open();
|
||||
|
||||
$r->writeChunk(0, 0, $data);
|
||||
$r->close();
|
||||
|
||||
$r = new RegionLoader($path, 0, 0);
|
||||
$r->open();
|
||||
self::assertSame($data, $r->readChunk(0, 0));
|
||||
}
|
||||
}
|
Submodule tests/plugins/PocketMine-DevTools updated: 638e44519a...3f14ab5bb7
Reference in New Issue
Block a user