mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-06 11:57:10 +00:00
Merge branch 'next-minor' into next-major
This commit is contained in:
commit
6e2685cbbb
@ -557,7 +557,7 @@ class Server{
|
||||
$ev->call();
|
||||
$class = $ev->getPlayerClass();
|
||||
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString("Level", ""))) !== null){
|
||||
if($offlinePlayerData !== null && ($world = $this->worldManager->getWorldByName($offlinePlayerData->getString(Player::TAG_LEVEL, ""))) !== null){
|
||||
$playerPos = EntityDataHelper::parseLocation($offlinePlayerData, $world);
|
||||
$spawn = $playerPos->asVector3();
|
||||
}else{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block\tile;
|
||||
|
||||
use pocketmine\data\bedrock\item\SavedItemStackData;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\inventory\Inventory;
|
||||
use pocketmine\item\Item;
|
||||
@ -51,7 +52,7 @@ trait ContainerTrait{
|
||||
/** @var CompoundTag $itemNBT */
|
||||
foreach($inventoryTag as $itemNBT){
|
||||
try{
|
||||
$newContents[$itemNBT->getByte("Slot")] = Item::nbtDeserialize($itemNBT);
|
||||
$newContents[$itemNBT->getByte(SavedItemStackData::TAG_SLOT)] = Item::nbtDeserialize($itemNBT);
|
||||
}catch(SavedDataLoadingException $e){
|
||||
//TODO: not the best solution
|
||||
\GlobalLogger::get()->logException($e);
|
||||
|
@ -81,6 +81,15 @@ abstract class Entity{
|
||||
public const MOTION_THRESHOLD = 0.00001;
|
||||
protected const STEP_CLIP_MULTIPLIER = 0.4;
|
||||
|
||||
private const TAG_FIRE = "Fire"; //TAG_Short
|
||||
private const TAG_ON_GROUND = "OnGround"; //TAG_Byte
|
||||
private const TAG_FALL_DISTANCE = "FallDistance"; //TAG_Float
|
||||
private const TAG_CUSTOM_NAME = "CustomName"; //TAG_String
|
||||
private const TAG_CUSTOM_NAME_VISIBLE = "CustomNameVisible"; //TAG_Byte
|
||||
public const TAG_POS = "Pos"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
|
||||
public const TAG_MOTION = "Motion"; //TAG_List<TAG_Double>|TAG_List<TAG_Float>
|
||||
public const TAG_ROTATION = "Rotation"; //TAG_List<TAG_Float>
|
||||
|
||||
private static int $entityCount = 1;
|
||||
|
||||
/**
|
||||
@ -189,7 +198,7 @@ abstract class Entity{
|
||||
$this->recalculateBoundingBox();
|
||||
|
||||
if($nbt !== null){
|
||||
$this->motion = EntityDataHelper::parseVec3($nbt, "Motion", true);
|
||||
$this->motion = EntityDataHelper::parseVec3($nbt, self::TAG_MOTION, true);
|
||||
}else{
|
||||
$this->motion = new Vector3(0, 0, 0);
|
||||
}
|
||||
@ -437,17 +446,17 @@ abstract class Entity{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = CompoundTag::create()
|
||||
->setTag("Pos", new ListTag([
|
||||
->setTag(self::TAG_POS, new ListTag([
|
||||
new DoubleTag($this->location->x),
|
||||
new DoubleTag($this->location->y),
|
||||
new DoubleTag($this->location->z)
|
||||
]))
|
||||
->setTag("Motion", new ListTag([
|
||||
->setTag(self::TAG_MOTION, new ListTag([
|
||||
new DoubleTag($this->motion->x),
|
||||
new DoubleTag($this->motion->y),
|
||||
new DoubleTag($this->motion->z)
|
||||
]))
|
||||
->setTag("Rotation", new ListTag([
|
||||
->setTag(self::TAG_ROTATION, new ListTag([
|
||||
new FloatTag($this->location->yaw),
|
||||
new FloatTag($this->location->pitch)
|
||||
]));
|
||||
@ -456,33 +465,33 @@ abstract class Entity{
|
||||
EntityFactory::getInstance()->injectSaveId(get_class($this), $nbt);
|
||||
|
||||
if($this->getNameTag() !== ""){
|
||||
$nbt->setString("CustomName", $this->getNameTag());
|
||||
$nbt->setByte("CustomNameVisible", $this->isNameTagVisible() ? 1 : 0);
|
||||
$nbt->setString(self::TAG_CUSTOM_NAME, $this->getNameTag());
|
||||
$nbt->setByte(self::TAG_CUSTOM_NAME_VISIBLE, $this->isNameTagVisible() ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->setFloat("FallDistance", $this->fallDistance);
|
||||
$nbt->setShort("Fire", $this->fireTicks);
|
||||
$nbt->setByte("OnGround", $this->onGround ? 1 : 0);
|
||||
$nbt->setFloat(self::TAG_FALL_DISTANCE, $this->fallDistance);
|
||||
$nbt->setShort(self::TAG_FIRE, $this->fireTicks);
|
||||
$nbt->setByte(self::TAG_ON_GROUND, $this->onGround ? 1 : 0);
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
$this->fireTicks = $nbt->getShort("Fire", 0);
|
||||
$this->fireTicks = $nbt->getShort(self::TAG_FIRE, 0);
|
||||
|
||||
$this->onGround = $nbt->getByte("OnGround", 0) !== 0;
|
||||
$this->onGround = $nbt->getByte(self::TAG_ON_GROUND, 0) !== 0;
|
||||
|
||||
$this->fallDistance = $nbt->getFloat("FallDistance", 0.0);
|
||||
$this->fallDistance = $nbt->getFloat(self::TAG_FALL_DISTANCE, 0.0);
|
||||
|
||||
if(($customNameTag = $nbt->getTag("CustomName")) instanceof StringTag){
|
||||
if(($customNameTag = $nbt->getTag(self::TAG_CUSTOM_NAME)) instanceof StringTag){
|
||||
$this->setNameTag($customNameTag->getValue());
|
||||
|
||||
if(($customNameVisibleTag = $nbt->getTag("CustomNameVisible")) instanceof StringTag){
|
||||
if(($customNameVisibleTag = $nbt->getTag(self::TAG_CUSTOM_NAME_VISIBLE)) instanceof StringTag){
|
||||
//Older versions incorrectly saved this as a string (see 890f72dbf23a77f294169b79590770470041adc4)
|
||||
$this->setNameTagVisible($customNameVisibleTag->getValue() !== "");
|
||||
}else{
|
||||
$this->setNameTagVisible($nbt->getByte("CustomNameVisible", 1) !== 0);
|
||||
$this->setNameTagVisible($nbt->getByte(self::TAG_CUSTOM_NAME_VISIBLE, 1) !== 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,19 +57,19 @@ final class EntityDataHelper{
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function parseLocation(CompoundTag $nbt, World $world) : Location{
|
||||
$pos = self::parseVec3($nbt, "Pos", false);
|
||||
$pos = self::parseVec3($nbt, Entity::TAG_POS, false);
|
||||
|
||||
$yawPitch = $nbt->getTag("Rotation");
|
||||
$yawPitch = $nbt->getTag(Entity::TAG_ROTATION);
|
||||
if(!($yawPitch instanceof ListTag) || $yawPitch->getTagType() !== NBT::TAG_Float){
|
||||
throw new SavedDataLoadingException("'Rotation' should be a List<Float>");
|
||||
throw new SavedDataLoadingException("'" . Entity::TAG_ROTATION . "' should be a List<Float>");
|
||||
}
|
||||
/** @var FloatTag[] $values */
|
||||
$values = $yawPitch->getValue();
|
||||
if(count($values) !== 2){
|
||||
throw new SavedDataLoadingException("Expected exactly 2 entries for 'Rotation'");
|
||||
}
|
||||
self::validateFloat("Rotation", "yaw", $values[0]->getValue());
|
||||
self::validateFloat("Rotation", "pitch", $values[1]->getValue());
|
||||
self::validateFloat(Entity::TAG_ROTATION, "yaw", $values[0]->getValue());
|
||||
self::validateFloat(Entity::TAG_ROTATION, "pitch", $values[1]->getValue());
|
||||
|
||||
return Location::fromObject($pos, $world, $values[0]->getValue(), $values[1]->getValue());
|
||||
}
|
||||
|
@ -66,6 +66,9 @@ use function reset;
|
||||
final class EntityFactory{
|
||||
use SingletonTrait;
|
||||
|
||||
public const TAG_IDENTIFIER = "identifier"; //TAG_String
|
||||
public const TAG_LEGACY_ID = "id"; //TAG_Int
|
||||
|
||||
/**
|
||||
* @var \Closure[] save ID => creator function
|
||||
* @phpstan-var array<int|string, \Closure(World, CompoundTag) : Entity>
|
||||
@ -113,9 +116,9 @@ final class EntityFactory{
|
||||
}, ['FallingSand', 'minecraft:falling_block'], LegacyIds::FALLING_BLOCK);
|
||||
|
||||
$this->register(ItemEntity::class, function(World $world, CompoundTag $nbt) : ItemEntity{
|
||||
$itemTag = $nbt->getCompoundTag("Item");
|
||||
$itemTag = $nbt->getCompoundTag(ItemEntity::TAG_ITEM);
|
||||
if($itemTag === null){
|
||||
throw new SavedDataLoadingException("Expected \"Item\" NBT tag not found");
|
||||
throw new SavedDataLoadingException("Expected \"" . ItemEntity::TAG_ITEM . "\" NBT tag not found");
|
||||
}
|
||||
|
||||
$item = Item::nbtDeserialize($itemTag);
|
||||
@ -126,14 +129,14 @@ final class EntityFactory{
|
||||
}, ['Item', 'minecraft:item'], LegacyIds::ITEM);
|
||||
|
||||
$this->register(Painting::class, function(World $world, CompoundTag $nbt) : Painting{
|
||||
$motive = PaintingMotive::getMotiveByName($nbt->getString("Motive"));
|
||||
$motive = PaintingMotive::getMotiveByName($nbt->getString(Painting::TAG_MOTIVE));
|
||||
if($motive === null){
|
||||
throw new SavedDataLoadingException("Unknown painting motive");
|
||||
}
|
||||
$blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
|
||||
if(($directionTag = $nbt->getTag("Direction")) instanceof ByteTag){
|
||||
$blockIn = new Vector3($nbt->getInt(Painting::TAG_TILE_X), $nbt->getInt(Painting::TAG_TILE_Y), $nbt->getInt(Painting::TAG_TILE_Z));
|
||||
if(($directionTag = $nbt->getTag(Painting::TAG_DIRECTION_BE)) instanceof ByteTag){
|
||||
$facing = Painting::DATA_TO_FACING[$directionTag->getValue()] ?? Facing::NORTH;
|
||||
}elseif(($facingTag = $nbt->getTag("Facing")) instanceof ByteTag){
|
||||
}elseif(($facingTag = $nbt->getTag(Painting::TAG_FACING_JE)) instanceof ByteTag){
|
||||
$facing = Painting::DATA_TO_FACING[$facingTag->getValue()] ?? Facing::NORTH;
|
||||
}else{
|
||||
throw new SavedDataLoadingException("Missing facing info");
|
||||
@ -217,7 +220,7 @@ final class EntityFactory{
|
||||
*/
|
||||
public function createFromData(World $world, CompoundTag $nbt) : ?Entity{
|
||||
try{
|
||||
$saveId = $nbt->getTag("identifier") ?? $nbt->getTag("id");
|
||||
$saveId = $nbt->getTag(self::TAG_IDENTIFIER) ?? $nbt->getTag(self::TAG_LEGACY_ID);
|
||||
$func = null;
|
||||
if($saveId instanceof StringTag){
|
||||
$func = $this->creationFuncs[$saveId->getValue()] ?? null;
|
||||
@ -238,7 +241,7 @@ final class EntityFactory{
|
||||
|
||||
public function injectSaveId(string $class, CompoundTag $saveData) : void{
|
||||
if(isset($this->saveNames[$class])){
|
||||
$saveData->setTag("identifier", new StringTag($this->saveNames[$class]));
|
||||
$saveData->setTag(self::TAG_IDENTIFIER, new StringTag($this->saveNames[$class]));
|
||||
}else{
|
||||
throw new \InvalidArgumentException("Entity $class is not registered");
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\data\bedrock\item\SavedItemStackData;
|
||||
use pocketmine\data\SavedDataLoadingException;
|
||||
use pocketmine\entity\animation\TotemUseAnimation;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
@ -77,6 +78,26 @@ use function random_int;
|
||||
|
||||
class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
|
||||
private const TAG_INVENTORY = "Inventory"; //TAG_List<TAG_Compound>
|
||||
private const TAG_OFF_HAND_ITEM = "OffHandItem"; //TAG_Compound
|
||||
private const TAG_ENDER_CHEST_INVENTORY = "EnderChestInventory"; //TAG_List<TAG_Compound>
|
||||
private const TAG_SELECTED_INVENTORY_SLOT = "SelectedInventorySlot"; //TAG_Int
|
||||
private const TAG_FOOD_LEVEL = "foodLevel"; //TAG_Int
|
||||
private const TAG_FOOD_EXHAUSTION_LEVEL = "foodExhaustionLevel"; //TAG_Float
|
||||
private const TAG_FOOD_SATURATION_LEVEL = "foodSaturationLevel"; //TAG_Float
|
||||
private const TAG_FOOD_TICK_TIMER = "foodTickTimer"; //TAG_Int
|
||||
private const TAG_XP_LEVEL = "XpLevel"; //TAG_Int
|
||||
private const TAG_XP_PROGRESS = "XpP"; //TAG_Float
|
||||
private const TAG_LIFETIME_XP_TOTAL = "XpTotal"; //TAG_Int
|
||||
private const TAG_XP_SEED = "XpSeed"; //TAG_Int
|
||||
private const TAG_NAME_TAG = "NameTag"; //TAG_String
|
||||
private const TAG_SKIN = "Skin"; //TAG_Compound
|
||||
private const TAG_SKIN_NAME = "Name"; //TAG_String
|
||||
private const TAG_SKIN_DATA = "Data"; //TAG_ByteArray
|
||||
private const TAG_SKIN_CAPE_DATA = "CapeData"; //TAG_ByteArray
|
||||
private const TAG_SKIN_GEOMETRY_NAME = "GeometryName"; //TAG_String
|
||||
private const TAG_SKIN_GEOMETRY_DATA = "GeometryData"; //TAG_ByteArray
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::PLAYER; }
|
||||
|
||||
protected PlayerInventory $inventory;
|
||||
@ -104,16 +125,16 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
* @throws SavedDataLoadingException
|
||||
*/
|
||||
public static function parseSkinNBT(CompoundTag $nbt) : Skin{
|
||||
$skinTag = $nbt->getCompoundTag("Skin");
|
||||
$skinTag = $nbt->getCompoundTag(self::TAG_SKIN);
|
||||
if($skinTag === null){
|
||||
throw new SavedDataLoadingException("Missing skin data");
|
||||
}
|
||||
return new Skin( //this throws if the skin is invalid
|
||||
$skinTag->getString("Name"),
|
||||
($skinDataTag = $skinTag->getTag("Data")) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray("Data"), //old data (this used to be saved as a StringTag in older versions of PM)
|
||||
$skinTag->getByteArray("CapeData", ""),
|
||||
$skinTag->getString("GeometryName", ""),
|
||||
$skinTag->getByteArray("GeometryData", "")
|
||||
$skinTag->getString(self::TAG_SKIN_NAME),
|
||||
($skinDataTag = $skinTag->getTag(self::TAG_SKIN_DATA)) instanceof StringTag ? $skinDataTag->getValue() : $skinTag->getByteArray(self::TAG_SKIN_DATA), //old data (this used to be saved as a StringTag in older versions of PM)
|
||||
$skinTag->getByteArray(self::TAG_SKIN_CAPE_DATA, ""),
|
||||
$skinTag->getString(self::TAG_SKIN_GEOMETRY_NAME, ""),
|
||||
$skinTag->getByteArray(self::TAG_SKIN_GEOMETRY_DATA, "")
|
||||
);
|
||||
}
|
||||
|
||||
@ -208,7 +229,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
* For Human entities which are not players, sets their properties such as nametag, skin and UUID from NBT.
|
||||
*/
|
||||
protected function initHumanData(CompoundTag $nbt) : void{
|
||||
if(($nameTagTag = $nbt->getTag("NameTag")) instanceof StringTag){
|
||||
if(($nameTagTag = $nbt->getTag(self::TAG_NAME_TAG)) instanceof StringTag){
|
||||
$this->setNameTag($nameTagTag->getValue());
|
||||
}
|
||||
|
||||
@ -257,14 +278,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
$this->enderInventory = new PlayerEnderInventory($this);
|
||||
$this->initHumanData($nbt);
|
||||
|
||||
$inventoryTag = $nbt->getListTag("Inventory");
|
||||
$inventoryTag = $nbt->getListTag(self::TAG_INVENTORY);
|
||||
if($inventoryTag !== null){
|
||||
$inventoryItems = [];
|
||||
$armorInventoryItems = [];
|
||||
|
||||
/** @var CompoundTag $item */
|
||||
foreach($inventoryTag as $i => $item){
|
||||
$slot = $item->getByte("Slot");
|
||||
$slot = $item->getByte(SavedItemStackData::TAG_SLOT);
|
||||
if($slot >= 0 && $slot < 9){ //Hotbar
|
||||
//Old hotbar saving stuff, ignore it
|
||||
}elseif($slot >= 100 && $slot < 104){ //Armor
|
||||
@ -277,7 +298,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
self::populateInventoryFromListTag($this->inventory, $inventoryItems);
|
||||
self::populateInventoryFromListTag($this->armorInventory, $armorInventoryItems);
|
||||
}
|
||||
$offHand = $nbt->getCompoundTag("OffHandItem");
|
||||
$offHand = $nbt->getCompoundTag(self::TAG_OFF_HAND_ITEM);
|
||||
if($offHand !== null){
|
||||
$this->offHandInventory->setItem(0, Item::nbtDeserialize($offHand));
|
||||
}
|
||||
@ -287,35 +308,35 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}));
|
||||
|
||||
$enderChestInventoryTag = $nbt->getListTag("EnderChestInventory");
|
||||
$enderChestInventoryTag = $nbt->getListTag(self::TAG_ENDER_CHEST_INVENTORY);
|
||||
if($enderChestInventoryTag !== null){
|
||||
$enderChestInventoryItems = [];
|
||||
|
||||
/** @var CompoundTag $item */
|
||||
foreach($enderChestInventoryTag as $i => $item){
|
||||
$enderChestInventoryItems[$item->getByte("Slot")] = Item::nbtDeserialize($item);
|
||||
$enderChestInventoryItems[$item->getByte(SavedItemStackData::TAG_SLOT)] = Item::nbtDeserialize($item);
|
||||
}
|
||||
self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems);
|
||||
}
|
||||
|
||||
$this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0));
|
||||
$this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0));
|
||||
$this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{
|
||||
foreach($this->getViewers() as $viewer){
|
||||
$viewer->getNetworkSession()->onMobMainHandItemChange($this);
|
||||
}
|
||||
});
|
||||
|
||||
$this->hungerManager->setFood((float) $nbt->getInt("foodLevel", (int) $this->hungerManager->getFood()));
|
||||
$this->hungerManager->setExhaustion($nbt->getFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion()));
|
||||
$this->hungerManager->setSaturation($nbt->getFloat("foodSaturationLevel", $this->hungerManager->getSaturation()));
|
||||
$this->hungerManager->setFoodTickTimer($nbt->getInt("foodTickTimer", $this->hungerManager->getFoodTickTimer()));
|
||||
$this->hungerManager->setFood((float) $nbt->getInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood()));
|
||||
$this->hungerManager->setExhaustion($nbt->getFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion()));
|
||||
$this->hungerManager->setSaturation($nbt->getFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation()));
|
||||
$this->hungerManager->setFoodTickTimer($nbt->getInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer()));
|
||||
|
||||
$this->xpManager->setXpAndProgressNoEvent(
|
||||
$nbt->getInt("XpLevel", 0),
|
||||
$nbt->getFloat("XpP", 0.0));
|
||||
$this->xpManager->setLifetimeTotalXp($nbt->getInt("XpTotal", 0));
|
||||
$nbt->getInt(self::TAG_XP_LEVEL, 0),
|
||||
$nbt->getFloat(self::TAG_XP_PROGRESS, 0.0));
|
||||
$this->xpManager->setLifetimeTotalXp($nbt->getInt(self::TAG_LIFETIME_XP_TOTAL, 0));
|
||||
|
||||
if(($xpSeedTag = $nbt->getTag("XpSeed")) instanceof IntTag){
|
||||
if(($xpSeedTag = $nbt->getTag(self::TAG_XP_SEED)) instanceof IntTag){
|
||||
$this->xpSeed = $xpSeedTag->getValue();
|
||||
}else{
|
||||
$this->xpSeed = random_int(Limits::INT32_MIN, Limits::INT32_MAX);
|
||||
@ -384,18 +405,18 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
|
||||
$nbt->setInt("foodLevel", (int) $this->hungerManager->getFood());
|
||||
$nbt->setFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion());
|
||||
$nbt->setFloat("foodSaturationLevel", $this->hungerManager->getSaturation());
|
||||
$nbt->setInt("foodTickTimer", $this->hungerManager->getFoodTickTimer());
|
||||
$nbt->setInt(self::TAG_FOOD_LEVEL, (int) $this->hungerManager->getFood());
|
||||
$nbt->setFloat(self::TAG_FOOD_EXHAUSTION_LEVEL, $this->hungerManager->getExhaustion());
|
||||
$nbt->setFloat(self::TAG_FOOD_SATURATION_LEVEL, $this->hungerManager->getSaturation());
|
||||
$nbt->setInt(self::TAG_FOOD_TICK_TIMER, $this->hungerManager->getFoodTickTimer());
|
||||
|
||||
$nbt->setInt("XpLevel", $this->xpManager->getXpLevel());
|
||||
$nbt->setFloat("XpP", $this->xpManager->getXpProgress());
|
||||
$nbt->setInt("XpTotal", $this->xpManager->getLifetimeTotalXp());
|
||||
$nbt->setInt("XpSeed", $this->xpSeed);
|
||||
$nbt->setInt(self::TAG_XP_LEVEL, $this->xpManager->getXpLevel());
|
||||
$nbt->setFloat(self::TAG_XP_PROGRESS, $this->xpManager->getXpProgress());
|
||||
$nbt->setInt(self::TAG_LIFETIME_XP_TOTAL, $this->xpManager->getLifetimeTotalXp());
|
||||
$nbt->setInt(self::TAG_XP_SEED, $this->xpSeed);
|
||||
|
||||
$inventoryTag = new ListTag([], NBT::TAG_Compound);
|
||||
$nbt->setTag("Inventory", $inventoryTag);
|
||||
$nbt->setTag(self::TAG_INVENTORY, $inventoryTag);
|
||||
|
||||
//Normal inventory
|
||||
$slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize();
|
||||
@ -414,11 +435,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->setInt("SelectedInventorySlot", $this->inventory->getHeldItemIndex());
|
||||
$nbt->setInt(self::TAG_SELECTED_INVENTORY_SLOT, $this->inventory->getHeldItemIndex());
|
||||
|
||||
$offHandItem = $this->offHandInventory->getItem(0);
|
||||
if(!$offHandItem->isNull()){
|
||||
$nbt->setTag("OffHandItem", $offHandItem->nbtSerialize());
|
||||
$nbt->setTag(self::TAG_OFF_HAND_ITEM, $offHandItem->nbtSerialize());
|
||||
}
|
||||
|
||||
/** @var CompoundTag[] $items */
|
||||
@ -432,14 +453,14 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
$nbt->setTag("EnderChestInventory", new ListTag($items, NBT::TAG_Compound));
|
||||
$nbt->setTag(self::TAG_ENDER_CHEST_INVENTORY, new ListTag($items, NBT::TAG_Compound));
|
||||
|
||||
$nbt->setTag("Skin", CompoundTag::create()
|
||||
->setString("Name", $this->skin->getSkinId())
|
||||
->setByteArray("Data", $this->skin->getSkinData())
|
||||
->setByteArray("CapeData", $this->skin->getCapeData())
|
||||
->setString("GeometryName", $this->skin->getGeometryName())
|
||||
->setByteArray("GeometryData", $this->skin->getGeometryData())
|
||||
$nbt->setTag(self::TAG_SKIN, CompoundTag::create()
|
||||
->setString(self::TAG_SKIN_NAME, $this->skin->getSkinId())
|
||||
->setByteArray(self::TAG_SKIN_DATA, $this->skin->getSkinData())
|
||||
->setByteArray(self::TAG_SKIN_CAPE_DATA, $this->skin->getCapeData())
|
||||
->setString(self::TAG_SKIN_GEOMETRY_NAME, $this->skin->getGeometryName())
|
||||
->setByteArray(self::TAG_SKIN_GEOMETRY_DATA, $this->skin->getGeometryData())
|
||||
);
|
||||
|
||||
return $nbt;
|
||||
|
@ -77,6 +77,16 @@ use const M_PI;
|
||||
abstract class Living extends Entity{
|
||||
protected const DEFAULT_BREATH_TICKS = 300;
|
||||
|
||||
private const TAG_LEGACY_HEALTH = "HealF"; //TAG_Float
|
||||
private const TAG_HEALTH = "Health"; //TAG_Float
|
||||
private const TAG_BREATH_TICKS = "Air"; //TAG_Short
|
||||
private const TAG_ACTIVE_EFFECTS = "ActiveEffects"; //TAG_List<TAG_Compound>
|
||||
private const TAG_EFFECT_ID = "Id"; //TAG_Byte
|
||||
private const TAG_EFFECT_DURATION = "Duration"; //TAG_Int
|
||||
private const TAG_EFFECT_AMPLIFIER = "Amplifier"; //TAG_Byte
|
||||
private const TAG_EFFECT_SHOW_PARTICLES = "ShowParticles"; //TAG_Byte
|
||||
private const TAG_EFFECT_AMBIENT = "Ambient"; //TAG_Byte
|
||||
|
||||
protected int $attackTime = 0;
|
||||
|
||||
public int $deadTicks = 0;
|
||||
@ -127,9 +137,9 @@ abstract class Living extends Entity{
|
||||
|
||||
$health = $this->getMaxHealth();
|
||||
|
||||
if(($healFTag = $nbt->getTag("HealF")) instanceof FloatTag){
|
||||
if(($healFTag = $nbt->getTag(self::TAG_LEGACY_HEALTH)) instanceof FloatTag){
|
||||
$health = $healFTag->getValue();
|
||||
}elseif(($healthTag = $nbt->getTag("Health")) instanceof ShortTag){
|
||||
}elseif(($healthTag = $nbt->getTag(self::TAG_HEALTH)) instanceof ShortTag){
|
||||
$health = $healthTag->getValue(); //Older versions of PocketMine-MP incorrectly saved this as a short instead of a float
|
||||
}elseif($healthTag instanceof FloatTag){
|
||||
$health = $healthTag->getValue();
|
||||
@ -137,23 +147,23 @@ abstract class Living extends Entity{
|
||||
|
||||
$this->setHealth($health);
|
||||
|
||||
$this->setAirSupplyTicks($nbt->getShort("Air", self::DEFAULT_BREATH_TICKS));
|
||||
$this->setAirSupplyTicks($nbt->getShort(self::TAG_BREATH_TICKS, self::DEFAULT_BREATH_TICKS));
|
||||
|
||||
/** @var CompoundTag[]|ListTag|null $activeEffectsTag */
|
||||
$activeEffectsTag = $nbt->getListTag("ActiveEffects");
|
||||
$activeEffectsTag = $nbt->getListTag(self::TAG_ACTIVE_EFFECTS);
|
||||
if($activeEffectsTag !== null){
|
||||
foreach($activeEffectsTag as $e){
|
||||
$effect = EffectIdMap::getInstance()->fromId($e->getByte("Id"));
|
||||
$effect = EffectIdMap::getInstance()->fromId($e->getByte(self::TAG_EFFECT_ID));
|
||||
if($effect === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->effectManager->add(new EffectInstance(
|
||||
$effect,
|
||||
$e->getInt("Duration"),
|
||||
Binary::unsignByte($e->getByte("Amplifier")),
|
||||
$e->getByte("ShowParticles", 1) !== 0,
|
||||
$e->getByte("Ambient", 0) !== 0
|
||||
$e->getInt(self::TAG_EFFECT_DURATION),
|
||||
Binary::unsignByte($e->getByte(self::TAG_EFFECT_AMPLIFIER)),
|
||||
$e->getByte(self::TAG_EFFECT_SHOW_PARTICLES, 1) !== 0,
|
||||
$e->getByte(self::TAG_EFFECT_AMBIENT, 0) !== 0
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -263,22 +273,22 @@ abstract class Living extends Entity{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setFloat("Health", $this->getHealth());
|
||||
$nbt->setFloat(self::TAG_HEALTH, $this->getHealth());
|
||||
|
||||
$nbt->setShort("Air", $this->getAirSupplyTicks());
|
||||
$nbt->setShort(self::TAG_BREATH_TICKS, $this->getAirSupplyTicks());
|
||||
|
||||
if(count($this->effectManager->all()) > 0){
|
||||
$effects = [];
|
||||
foreach($this->effectManager->all() as $effect){
|
||||
$effects[] = CompoundTag::create()
|
||||
->setByte("Id", EffectIdMap::getInstance()->toId($effect->getType()))
|
||||
->setByte("Amplifier", Binary::signByte($effect->getAmplifier()))
|
||||
->setInt("Duration", $effect->getDuration())
|
||||
->setByte("Ambient", $effect->isAmbient() ? 1 : 0)
|
||||
->setByte("ShowParticles", $effect->isVisible() ? 1 : 0);
|
||||
->setByte(self::TAG_EFFECT_ID, EffectIdMap::getInstance()->toId($effect->getType()))
|
||||
->setByte(self::TAG_EFFECT_AMPLIFIER, Binary::signByte($effect->getAmplifier()))
|
||||
->setInt(self::TAG_EFFECT_DURATION, $effect->getDuration())
|
||||
->setByte(self::TAG_EFFECT_AMBIENT, $effect->isAmbient() ? 1 : 0)
|
||||
->setByte(self::TAG_EFFECT_SHOW_PARTICLES, $effect->isVisible() ? 1 : 0);
|
||||
}
|
||||
|
||||
$nbt->setTag("ActiveEffects", new ListTag($effects));
|
||||
$nbt->setTag(self::TAG_ACTIVE_EFFECTS, new ListTag($effects));
|
||||
}
|
||||
|
||||
return $nbt;
|
||||
|
@ -36,6 +36,8 @@ class Villager extends Living implements Ageable{
|
||||
public const PROFESSION_BLACKSMITH = 3;
|
||||
public const PROFESSION_BUTCHER = 4;
|
||||
|
||||
private const TAG_PROFESSION = "Profession"; //TAG_Int
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::VILLAGER; }
|
||||
|
||||
private bool $baby = false;
|
||||
@ -53,7 +55,7 @@ class Villager extends Living implements Ageable{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
/** @var int $profession */
|
||||
$profession = $nbt->getInt("Profession", self::PROFESSION_FARMER);
|
||||
$profession = $nbt->getInt(self::TAG_PROFESSION, self::PROFESSION_FARMER);
|
||||
|
||||
if($profession > 4 || $profession < 0){
|
||||
$profession = self::PROFESSION_FARMER;
|
||||
@ -64,7 +66,7 @@ class Villager extends Living implements Ageable{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setInt("Profession", $this->getProfession());
|
||||
$nbt->setInt(self::TAG_PROFESSION, $this->getProfession());
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ class ExperienceOrb extends Entity{
|
||||
|
||||
public const TAG_VALUE_PC = "Value"; //short
|
||||
public const TAG_VALUE_PE = "experience value"; //int (WTF?)
|
||||
private const TAG_AGE = "Age"; //TAG_Short
|
||||
|
||||
/** Max distance an orb will follow a player across. */
|
||||
public const MAX_TARGET_DISTANCE = 8.0;
|
||||
@ -102,13 +103,13 @@ class ExperienceOrb extends Entity{
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
$this->age = $nbt->getShort("Age", 0);
|
||||
$this->age = $nbt->getShort(self::TAG_AGE, 0);
|
||||
}
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
|
||||
$nbt->setShort("Age", $this->age);
|
||||
$nbt->setShort(self::TAG_AGE, $this->age);
|
||||
|
||||
$nbt->setShort(self::TAG_VALUE_PC, $this->getXpValue());
|
||||
$nbt->setInt(self::TAG_VALUE_PE, $this->getXpValue());
|
||||
|
@ -47,6 +47,9 @@ use function abs;
|
||||
|
||||
class FallingBlock extends Entity{
|
||||
private const TAG_FALLING_BLOCK = "FallingBlock"; //TAG_Compound
|
||||
private const TAG_TILE_ID = "TileID"; //TAG_Int
|
||||
private const TAG_TILE = "Tile"; //TAG_Byte
|
||||
private const TAG_DATA = "Data"; //TAG_Byte
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
|
||||
|
||||
@ -70,14 +73,14 @@ class FallingBlock extends Entity{
|
||||
if(($fallingBlockTag = $nbt->getCompoundTag(self::TAG_FALLING_BLOCK)) !== null){
|
||||
$blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
|
||||
}else{
|
||||
if(($tileIdTag = $nbt->getTag("TileID")) instanceof IntTag){
|
||||
if(($tileIdTag = $nbt->getTag(self::TAG_TILE_ID)) instanceof IntTag){
|
||||
$blockId = $tileIdTag->getValue();
|
||||
}elseif(($tileTag = $nbt->getTag("Tile")) instanceof ByteTag){
|
||||
}elseif(($tileTag = $nbt->getTag(self::TAG_TILE)) instanceof ByteTag){
|
||||
$blockId = $tileTag->getValue();
|
||||
}else{
|
||||
throw new SavedDataLoadingException("Missing legacy falling block info");
|
||||
}
|
||||
$damage = $nbt->getByte("Data", 0);
|
||||
$damage = $nbt->getByte(self::TAG_DATA, 0);
|
||||
|
||||
$blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
|
||||
}
|
||||
|
@ -43,6 +43,13 @@ use function max;
|
||||
|
||||
class ItemEntity extends Entity{
|
||||
|
||||
private const TAG_HEALTH = "Health"; //TAG_Short
|
||||
private const TAG_AGE = "Age"; //TAG_Short
|
||||
private const TAG_PICKUP_DELAY = "PickupDelay"; //TAG_Short
|
||||
private const TAG_OWNER = "Owner"; //TAG_String
|
||||
private const TAG_THROWER = "Thrower"; //TAG_String
|
||||
public const TAG_ITEM = "Item"; //TAG_Compound
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::ITEM; }
|
||||
|
||||
public const MERGE_CHECK_PERIOD = 2; //0.1 seconds
|
||||
@ -74,17 +81,17 @@ class ItemEntity extends Entity{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
$this->setMaxHealth(5);
|
||||
$this->setHealth($nbt->getShort("Health", (int) $this->getHealth()));
|
||||
$this->setHealth($nbt->getShort(self::TAG_HEALTH, (int) $this->getHealth()));
|
||||
|
||||
$age = $nbt->getShort("Age", 0);
|
||||
$age = $nbt->getShort(self::TAG_AGE, 0);
|
||||
if($age === -32768){
|
||||
$this->despawnDelay = self::NEVER_DESPAWN;
|
||||
}else{
|
||||
$this->despawnDelay = max(0, self::DEFAULT_DESPAWN_DELAY - $age);
|
||||
}
|
||||
$this->pickupDelay = $nbt->getShort("PickupDelay", $this->pickupDelay);
|
||||
$this->owner = $nbt->getString("Owner", $this->owner);
|
||||
$this->thrower = $nbt->getString("Thrower", $this->thrower);
|
||||
$this->pickupDelay = $nbt->getShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
|
||||
$this->owner = $nbt->getString(self::TAG_OWNER, $this->owner);
|
||||
$this->thrower = $nbt->getString(self::TAG_THROWER, $this->thrower);
|
||||
}
|
||||
|
||||
protected function onFirstUpdate(int $currentTick) : void{
|
||||
@ -188,17 +195,17 @@ class ItemEntity extends Entity{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setTag("Item", $this->item->nbtSerialize());
|
||||
$nbt->setShort("Health", (int) $this->getHealth());
|
||||
$nbt->setTag(self::TAG_ITEM, $this->item->nbtSerialize());
|
||||
$nbt->setShort(self::TAG_HEALTH, (int) $this->getHealth());
|
||||
if($this->despawnDelay === self::NEVER_DESPAWN){
|
||||
$age = -32768;
|
||||
}else{
|
||||
$age = self::DEFAULT_DESPAWN_DELAY - $this->despawnDelay;
|
||||
}
|
||||
$nbt->setShort("Age", $age);
|
||||
$nbt->setShort("PickupDelay", $this->pickupDelay);
|
||||
$nbt->setString("Owner", $this->owner);
|
||||
$nbt->setString("Thrower", $this->thrower);
|
||||
$nbt->setShort(self::TAG_AGE, $age);
|
||||
$nbt->setShort(self::TAG_PICKUP_DELAY, $this->pickupDelay);
|
||||
$nbt->setString(self::TAG_OWNER, $this->owner);
|
||||
$nbt->setString(self::TAG_THROWER, $this->thrower);
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
@ -41,6 +41,13 @@ use pocketmine\world\World;
|
||||
use function ceil;
|
||||
|
||||
class Painting extends Entity{
|
||||
public const TAG_TILE_X = "TileX"; //TAG_Int
|
||||
public const TAG_TILE_Y = "TileY"; //TAG_Int
|
||||
public const TAG_TILE_Z = "TileZ"; //TAG_Int
|
||||
public const TAG_FACING_JE = "Facing"; //TAG_Byte
|
||||
public const TAG_DIRECTION_BE = "Direction"; //TAG_Byte
|
||||
public const TAG_MOTIVE = "Motive"; //TAG_String
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::PAINTING; }
|
||||
|
||||
public const DATA_TO_FACING = [
|
||||
@ -84,14 +91,14 @@ class Painting extends Entity{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setInt("TileX", (int) $this->blockIn->x);
|
||||
$nbt->setInt("TileY", (int) $this->blockIn->y);
|
||||
$nbt->setInt("TileZ", (int) $this->blockIn->z);
|
||||
$nbt->setInt(self::TAG_TILE_X, (int) $this->blockIn->x);
|
||||
$nbt->setInt(self::TAG_TILE_Y, (int) $this->blockIn->y);
|
||||
$nbt->setInt(self::TAG_TILE_Z, (int) $this->blockIn->z);
|
||||
|
||||
$nbt->setByte("Facing", self::FACING_TO_DATA[$this->facing]);
|
||||
$nbt->setByte("Direction", self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
|
||||
$nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing]);
|
||||
$nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
|
||||
|
||||
$nbt->setString("Motive", $this->motive->getName());
|
||||
$nbt->setString(self::TAG_MOTIVE, $this->motive->getName());
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
@ -39,6 +39,8 @@ use pocketmine\world\Position;
|
||||
|
||||
class PrimedTNT extends Entity implements Explosive{
|
||||
|
||||
private const TAG_FUSE = "Fuse"; //TAG_Short
|
||||
|
||||
public static function getNetworkTypeId() : string{ return EntityIds::TNT; }
|
||||
|
||||
protected int $fuse;
|
||||
@ -78,7 +80,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
protected function initEntity(CompoundTag $nbt) : void{
|
||||
parent::initEntity($nbt);
|
||||
|
||||
$this->fuse = $nbt->getShort("Fuse", 80);
|
||||
$this->fuse = $nbt->getShort(self::TAG_FUSE, 80);
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity) : bool{
|
||||
@ -87,7 +89,7 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setShort("Fuse", $this->fuse);
|
||||
$nbt->setShort(self::TAG_FUSE, $this->fuse);
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
@ -52,6 +52,7 @@ class Arrow extends Projectile{
|
||||
|
||||
private const TAG_PICKUP = "pickup"; //TAG_Byte
|
||||
public const TAG_CRIT = "crit"; //TAG_Byte
|
||||
private const TAG_LIFE = "life"; //TAG_Short
|
||||
|
||||
protected float $damage = 2.0;
|
||||
protected int $pickupMode = self::PICKUP_ANY;
|
||||
@ -75,14 +76,14 @@ class Arrow extends Projectile{
|
||||
|
||||
$this->pickupMode = $nbt->getByte(self::TAG_PICKUP, self::PICKUP_ANY);
|
||||
$this->critical = $nbt->getByte(self::TAG_CRIT, 0) === 1;
|
||||
$this->collideTicks = $nbt->getShort("life", $this->collideTicks);
|
||||
$this->collideTicks = $nbt->getShort(self::TAG_LIFE, $this->collideTicks);
|
||||
}
|
||||
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
$nbt->setByte(self::TAG_PICKUP, $this->pickupMode);
|
||||
$nbt->setByte(self::TAG_CRIT, $this->critical ? 1 : 0);
|
||||
$nbt->setShort("life", $this->collideTicks);
|
||||
$nbt->setShort(self::TAG_LIFE, $this->collideTicks);
|
||||
return $nbt;
|
||||
}
|
||||
|
||||
|
@ -53,6 +53,10 @@ use const PHP_INT_MAX;
|
||||
|
||||
abstract class Projectile extends Entity{
|
||||
private const TAG_STUCK_ON_BLOCK_POS = "StuckToBlockPos";
|
||||
private const TAG_DAMAGE = "damage"; //TAG_Double
|
||||
private const TAG_TILE_X = "tileX"; //TAG_Int
|
||||
private const TAG_TILE_Y = "tileY"; //TAG_Int
|
||||
private const TAG_TILE_Z = "tileZ"; //TAG_Int
|
||||
|
||||
protected float $damage = 0.0;
|
||||
protected ?Vector3 $blockHit = null;
|
||||
@ -75,7 +79,7 @@ abstract class Projectile extends Entity{
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
$this->damage = $nbt->getDouble("damage", $this->damage);
|
||||
$this->damage = $nbt->getDouble(self::TAG_DAMAGE, $this->damage);
|
||||
|
||||
if(($stuckOnBlockPosTag = $nbt->getListTag(self::TAG_STUCK_ON_BLOCK_POS)) !== null){
|
||||
if($stuckOnBlockPosTag->getTagType() !== NBT::TAG_Int || count($stuckOnBlockPosTag) !== 3){
|
||||
@ -86,7 +90,7 @@ abstract class Projectile extends Entity{
|
||||
$values = $stuckOnBlockPosTag->getValue();
|
||||
|
||||
$this->blockHit = new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue());
|
||||
}elseif(($tileXTag = $nbt->getTag("tileX")) instanceof IntTag && ($tileYTag = $nbt->getTag("tileY")) instanceof IntTag && ($tileZTag = $nbt->getTag("tileZ")) instanceof IntTag){
|
||||
}elseif(($tileXTag = $nbt->getTag(self::TAG_TILE_X)) instanceof IntTag && ($tileYTag = $nbt->getTag(self::TAG_TILE_Y)) instanceof IntTag && ($tileZTag = $nbt->getTag(self::TAG_TILE_Z)) instanceof IntTag){
|
||||
$this->blockHit = new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
|
||||
}
|
||||
}
|
||||
@ -124,7 +128,7 @@ abstract class Projectile extends Entity{
|
||||
public function saveNBT() : CompoundTag{
|
||||
$nbt = parent::saveNBT();
|
||||
|
||||
$nbt->setDouble("damage", $this->damage);
|
||||
$nbt->setDouble(self::TAG_DAMAGE, $this->damage);
|
||||
|
||||
if($this->blockHit !== null){
|
||||
$nbt->setTag(self::TAG_STUCK_ON_BLOCK_POS, new ListTag([
|
||||
|
@ -60,6 +60,9 @@ class Item implements \JsonSerializable{
|
||||
use ItemEnchantmentHandlingTrait;
|
||||
|
||||
public const TAG_ENCH = "ench";
|
||||
private const TAG_ENCH_ID = "id"; //TAG_Short
|
||||
private const TAG_ENCH_LVL = "lvl"; //TAG_Short
|
||||
|
||||
public const TAG_DISPLAY = "display";
|
||||
public const TAG_BLOCK_ENTITY_TAG = "BlockEntityTag";
|
||||
|
||||
@ -68,6 +71,9 @@ class Item implements \JsonSerializable{
|
||||
|
||||
public const TAG_KEEP_ON_DEATH = "minecraft:keep_on_death";
|
||||
|
||||
private const TAG_CAN_PLACE_ON = "CanPlaceOn"; //TAG_List<TAG_String>
|
||||
private const TAG_CAN_DESTROY = "CanDestroy"; //TAG_List<TAG_String>
|
||||
|
||||
private CompoundTag $nbt;
|
||||
|
||||
protected int $count = 1;
|
||||
@ -296,8 +302,8 @@ class Item implements \JsonSerializable{
|
||||
if($enchantments !== null && $enchantments->getTagType() === NBT::TAG_Compound){
|
||||
/** @var CompoundTag $enchantment */
|
||||
foreach($enchantments as $enchantment){
|
||||
$magicNumber = $enchantment->getShort("id", -1);
|
||||
$level = $enchantment->getShort("lvl", 0);
|
||||
$magicNumber = $enchantment->getShort(self::TAG_ENCH_ID, -1);
|
||||
$level = $enchantment->getShort(self::TAG_ENCH_LVL, 0);
|
||||
if($level <= 0){
|
||||
continue;
|
||||
}
|
||||
@ -311,7 +317,7 @@ class Item implements \JsonSerializable{
|
||||
$this->blockEntityTag = $tag->getCompoundTag(self::TAG_BLOCK_ENTITY_TAG);
|
||||
|
||||
$this->canPlaceOn = [];
|
||||
$canPlaceOn = $tag->getListTag("CanPlaceOn");
|
||||
$canPlaceOn = $tag->getListTag(self::TAG_CAN_PLACE_ON);
|
||||
if($canPlaceOn !== null && $canPlaceOn->getTagType() === NBT::TAG_String){
|
||||
/** @var StringTag $entry */
|
||||
foreach($canPlaceOn as $entry){
|
||||
@ -319,7 +325,7 @@ class Item implements \JsonSerializable{
|
||||
}
|
||||
}
|
||||
$this->canDestroy = [];
|
||||
$canDestroy = $tag->getListTag("CanDestroy");
|
||||
$canDestroy = $tag->getListTag(self::TAG_CAN_DESTROY);
|
||||
if($canDestroy !== null && $canDestroy->getTagType() === NBT::TAG_String){
|
||||
/** @var StringTag $entry */
|
||||
foreach($canDestroy as $entry){
|
||||
@ -354,8 +360,8 @@ class Item implements \JsonSerializable{
|
||||
$ench = new ListTag();
|
||||
foreach($this->getEnchantments() as $enchantmentInstance){
|
||||
$ench->push(CompoundTag::create()
|
||||
->setShort("id", EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType()))
|
||||
->setShort("lvl", $enchantmentInstance->getLevel())
|
||||
->setShort(self::TAG_ENCH_ID, EnchantmentIdMap::getInstance()->toId($enchantmentInstance->getType()))
|
||||
->setShort(self::TAG_ENCH_LVL, $enchantmentInstance->getLevel())
|
||||
);
|
||||
}
|
||||
$tag->setTag(self::TAG_ENCH, $ench);
|
||||
@ -372,18 +378,18 @@ class Item implements \JsonSerializable{
|
||||
foreach($this->canPlaceOn as $item){
|
||||
$canPlaceOn->push(new StringTag($item));
|
||||
}
|
||||
$tag->setTag("CanPlaceOn", $canPlaceOn);
|
||||
$tag->setTag(self::TAG_CAN_PLACE_ON, $canPlaceOn);
|
||||
}else{
|
||||
$tag->removeTag("CanPlaceOn");
|
||||
$tag->removeTag(self::TAG_CAN_PLACE_ON);
|
||||
}
|
||||
if(count($this->canDestroy) > 0){
|
||||
$canDestroy = new ListTag();
|
||||
foreach($this->canDestroy as $item){
|
||||
$canDestroy->push(new StringTag($item));
|
||||
}
|
||||
$tag->setTag("CanDestroy", $canDestroy);
|
||||
$tag->setTag(self::TAG_CAN_DESTROY, $canDestroy);
|
||||
}else{
|
||||
$tag->removeTag("CanDestroy");
|
||||
$tag->removeTag(self::TAG_CAN_DESTROY);
|
||||
}
|
||||
|
||||
if($this->keepOnDeath){
|
||||
|
@ -685,7 +685,7 @@ class NetworkSession{
|
||||
//TODO: we shouldn't be loading player data here at all, but right now we don't have any choice :(
|
||||
$this->cachedOfflinePlayerData = $this->server->getOfflinePlayerData($this->info->getUsername());
|
||||
if($checkXUID){
|
||||
$recordedXUID = $this->cachedOfflinePlayerData !== null ? $this->cachedOfflinePlayerData->getTag("LastKnownXUID") : null;
|
||||
$recordedXUID = $this->cachedOfflinePlayerData !== null ? $this->cachedOfflinePlayerData->getTag(Player::TAG_LAST_KNOWN_XUID) : null;
|
||||
if(!($recordedXUID instanceof StringTag)){
|
||||
$this->logger->debug("No previous XUID recorded, no choice but to trust this player");
|
||||
}elseif(!$kickForXUIDMismatch($recordedXUID->getValue())){
|
||||
|
@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\handler;
|
||||
use pocketmine\block\BaseSign;
|
||||
use pocketmine\block\ItemFrame;
|
||||
use pocketmine\block\Lectern;
|
||||
use pocketmine\block\tile\Sign;
|
||||
use pocketmine\block\utils\SignText;
|
||||
use pocketmine\entity\animation\ConsumingItemAnimation;
|
||||
use pocketmine\entity\Attribute;
|
||||
@ -650,7 +651,7 @@ class InGamePacketHandler extends PacketHandler{
|
||||
if(!($nbt instanceof CompoundTag)) throw new AssumptionFailedError("PHPStan should ensure this is a CompoundTag"); //for phpstorm's benefit
|
||||
|
||||
if($block instanceof BaseSign){
|
||||
if(($textBlobTag = $nbt->getTag("Text")) instanceof StringTag){
|
||||
if(($textBlobTag = $nbt->getTag(Sign::TAG_TEXT_BLOB)) instanceof StringTag){
|
||||
try{
|
||||
$text = SignText::fromBlob($textBlobTag->getValue());
|
||||
}catch(\InvalidArgumentException $e){
|
||||
|
@ -37,11 +37,11 @@ class OfflinePlayer implements IPlayer{
|
||||
}
|
||||
|
||||
public function getFirstPlayed() : ?int{
|
||||
return ($this->namedtag !== null && ($firstPlayedTag = $this->namedtag->getTag("firstPlayed")) instanceof LongTag) ? $firstPlayedTag->getValue() : null;
|
||||
return ($this->namedtag !== null && ($firstPlayedTag = $this->namedtag->getTag(Player::TAG_FIRST_PLAYED)) instanceof LongTag) ? $firstPlayedTag->getValue() : null;
|
||||
}
|
||||
|
||||
public function getLastPlayed() : ?int{
|
||||
return ($this->namedtag !== null && ($lastPlayedTag = $this->namedtag->getTag("lastPlayed")) instanceof LongTag) ? $lastPlayedTag->getValue() : null;
|
||||
return ($this->namedtag !== null && ($lastPlayedTag = $this->namedtag->getTag(Player::TAG_LAST_PLAYED)) instanceof LongTag) ? $lastPlayedTag->getValue() : null;
|
||||
}
|
||||
|
||||
public function hasPlayedBefore() : bool{
|
||||
|
@ -178,6 +178,16 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
private const MAX_REACH_DISTANCE_SURVIVAL = 7;
|
||||
private const MAX_REACH_DISTANCE_ENTITY_INTERACTION = 8;
|
||||
|
||||
public const TAG_FIRST_PLAYED = "firstPlayed"; //TAG_Long
|
||||
public const TAG_LAST_PLAYED = "lastPlayed"; //TAG_Long
|
||||
private const TAG_GAME_MODE = "playerGameType"; //TAG_Int
|
||||
private const TAG_SPAWN_WORLD = "SpawnLevel"; //TAG_String
|
||||
private const TAG_SPAWN_X = "SpawnX"; //TAG_Int
|
||||
private const TAG_SPAWN_Y = "SpawnY"; //TAG_Int
|
||||
private const TAG_SPAWN_Z = "SpawnZ"; //TAG_Int
|
||||
public const TAG_LEVEL = "Level"; //TAG_String
|
||||
public const TAG_LAST_KNOWN_XUID = "LastKnownXUID"; //TAG_String
|
||||
|
||||
/**
|
||||
* Validates the given username.
|
||||
*/
|
||||
@ -346,10 +356,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
}
|
||||
));
|
||||
|
||||
$this->firstPlayed = $nbt->getLong("firstPlayed", $now = (int) (microtime(true) * 1000));
|
||||
$this->lastPlayed = $nbt->getLong("lastPlayed", $now);
|
||||
$this->firstPlayed = $nbt->getLong(self::TAG_FIRST_PLAYED, $now = (int) (microtime(true) * 1000));
|
||||
$this->lastPlayed = $nbt->getLong(self::TAG_LAST_PLAYED, $now);
|
||||
|
||||
if(!$this->server->getForceGamemode() && ($gameModeTag = $nbt->getTag("playerGameType")) instanceof IntTag){
|
||||
if(!$this->server->getForceGamemode() && ($gameModeTag = $nbt->getTag(self::TAG_GAME_MODE)) instanceof IntTag){
|
||||
$this->internalSetGameMode(GameModeIdMap::getInstance()->fromId($gameModeTag->getValue()) ?? GameMode::SURVIVAL()); //TODO: bad hack here to avoid crashes on corrupted data
|
||||
}else{
|
||||
$this->internalSetGameMode($this->server->getGamemode());
|
||||
@ -361,8 +371,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
$this->setNameTagAlwaysVisible();
|
||||
$this->setCanClimb();
|
||||
|
||||
if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString("SpawnLevel", ""))) instanceof World){
|
||||
$this->spawnPosition = new Position($nbt->getInt("SpawnX"), $nbt->getInt("SpawnY"), $nbt->getInt("SpawnZ"), $world);
|
||||
if(($world = $this->server->getWorldManager()->getWorldByName($nbt->getString(self::TAG_SPAWN_WORLD, ""))) instanceof World){
|
||||
$this->spawnPosition = new Position($nbt->getInt(self::TAG_SPAWN_X), $nbt->getInt(self::TAG_SPAWN_Y), $nbt->getInt(self::TAG_SPAWN_Z), $world);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2240,23 +2250,23 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{
|
||||
public function getSaveData() : CompoundTag{
|
||||
$nbt = $this->saveNBT();
|
||||
|
||||
$nbt->setString("LastKnownXUID", $this->xuid);
|
||||
$nbt->setString(self::TAG_LAST_KNOWN_XUID, $this->xuid);
|
||||
|
||||
if($this->location->isValid()){
|
||||
$nbt->setString("Level", $this->getWorld()->getFolderName());
|
||||
$nbt->setString(self::TAG_LEVEL, $this->getWorld()->getFolderName());
|
||||
}
|
||||
|
||||
if($this->hasValidCustomSpawn()){
|
||||
$spawn = $this->getSpawn();
|
||||
$nbt->setString("SpawnLevel", $spawn->getWorld()->getFolderName());
|
||||
$nbt->setInt("SpawnX", $spawn->getFloorX());
|
||||
$nbt->setInt("SpawnY", $spawn->getFloorY());
|
||||
$nbt->setInt("SpawnZ", $spawn->getFloorZ());
|
||||
$nbt->setString(self::TAG_SPAWN_WORLD, $spawn->getWorld()->getFolderName());
|
||||
$nbt->setInt(self::TAG_SPAWN_X, $spawn->getFloorX());
|
||||
$nbt->setInt(self::TAG_SPAWN_Y, $spawn->getFloorY());
|
||||
$nbt->setInt(self::TAG_SPAWN_Z, $spawn->getFloorZ());
|
||||
}
|
||||
|
||||
$nbt->setInt("playerGameType", GameModeIdMap::getInstance()->toId($this->gamemode));
|
||||
$nbt->setLong("firstPlayed", $this->firstPlayed);
|
||||
$nbt->setLong("lastPlayed", (int) floor(microtime(true) * 1000));
|
||||
$nbt->setInt(self::TAG_GAME_MODE, GameModeIdMap::getInstance()->toId($this->gamemode));
|
||||
$nbt->setLong(self::TAG_FIRST_PLAYED, $this->firstPlayed);
|
||||
$nbt->setLong(self::TAG_LAST_PLAYED, (int) floor(microtime(true) * 1000));
|
||||
|
||||
return $nbt;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user