private function getOptimalFlowDirections() { if ($this->temporalVector === \null) { $this->temporalVector = new Vector3(0, 0, 0); } for ($j = 0; $j < 4; ++$j) { $this->flowCost[$j] = 1000; $x = $this->x; $y = $this->y; $z = $this->z; if ($j === 0) { --$x; } elseif ($j === 1) { ++$x; } elseif ($j === 2) { --$z; } elseif ($j === 3) { ++$z; } $block = $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $z)); if (!$block->canBeFlowedInto() and !$block instanceof Liquid) { continue; } elseif ($block instanceof Liquid and $block->getDamage() === 0) { continue; } elseif ($this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y - 1, $z))->canBeFlowedInto()) { $this->flowCost[$j] = 0; } else { $this->flowCost[$j] = $this->calculateFlowCost($block, 1, $j); } } $minCost = $this->flowCost[0]; for ($i = 1; $i < 4; ++$i) { if ($this->flowCost[$i] < $minCost) { $minCost = $this->flowCost[$i]; } } for ($i = 0; $i < 4; ++$i) { $this->isOptimalFlowDirection[$i] = $this->flowCost[$i] === $minCost; } return $this->isOptimalFlowDirection; }
public function isInside(Vector3 $v) { if (!$this->isValid()) { return false; } return min($this->x1, $this->x2) <= $v->x and min($this->y1, $this->y2) <= $v->y and min($this->z1, $this->z2) <= $v->z and max($this->x1, $this->x2) >= $v->x and max($this->y1, $this->y2) >= $v->y and max($this->z1, $this->z2) >= $v->z and !$v instanceof Position or $v->getLevel()->getName() === $this->levelName; }
public function calcWeather($currentTick) { if ($this->canCalculate()) { $tickDiff = $currentTick - $this->lastUpdate; $this->duration -= $tickDiff; if ($this->duration <= 0) { $duration = mt_rand(min($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax), max($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax)); if ($this->weatherNow === self::SUNNY) { $weather = $this->randomWeatherData[array_rand($this->randomWeatherData)]; $this->setWeather($weather, $duration); } else { $weather = self::SUNNY; $this->setWeather($weather, $duration); } } if ($this->weatherNow > 0 and $this->level->getServer()->lightningTime > 0 and is_int($this->duration / $this->level->getServer()->lightningTime)) { $players = $this->level->getPlayers(); if (count($players) > 0) { $p = $players[array_rand($players)]; $x = $p->x + mt_rand(-64, 64); $z = $p->z + mt_rand(-64, 64); $y = $this->level->getHighestBlockAt($x, $z); $this->level->spawnLightning($this->temporalVector->setComponents($x, $y, $z)); } } } $this->lastUpdate = $currentTick; }
/** * @param Vector3 $v * @param mixed $data * @param int $id * @param FullChunk $chunk */ public function __construct($v, $data, $id = -2, $chunk = null) { $this->data = $data; if (!$v instanceof Vector3) { if ($v instanceof FullChunk) { $v->removeEntity($this); } throw new \RuntimeException("BadConstructCustomHuman"); } $nbt = new Compound(); $nbt->Pos = new Enum("Motion", [new Double(0, $v->x), new Double(1, $v->y), new Double(2, $v->z)]); $nbt->Motion = new Enum("Motion", [new Double(0, 0), new Double(1, 0), new Double(2, 0)]); $nbt->Rotation = new Enum("Rotation", [new Float(0, $this->getDefaultYaw()), new Float(1, $this->getDefaultPitch())]); $nbt->FallDistance = new Float("FallDistance", 0); $nbt->Fire = new Short("Fire", 0); $nbt->Air = new Short("Air", 0); $nbt->OnGround = new Byte("OnGround", 1); $nbt->Invulnerable = new Byte("Invulnerable", 1); $nbt->Health = new Short("Health", 20); $nbt->NameTag = $this->getDefaultName(); $nbt->Inventory = new Enum("Inventory", []); $nbt->Inventory->setTagType(NBT::TAG_Compound); $this->inventory = new PlayerInventory($this); $this->setSkin($this->getDefaultSkin()); parent::__construct($chunk, $nbt); }
public function isInside(Vector3 $v) { $out = true; $out = ($out and $v->distance($this->centre) <= $this->radius); if ($v instanceof Position) { $out = ($out and $v->getLevel()->getName() === $this->centre->getLevel()->getName()); } return $out; }
public function isInside(Vector3 $v) { if (!$this->isValid()) { return false; } if ($v instanceof Position and $v->getLevel()->getName() !== $this->levelName) { return false; } return $v->distanceSquared($this->center) <= $this->radiusSquared; }
public function onUpdate($currentTick) { if ($this->closed !== false) { return false; } if (++$this->switchDirectionTicker === 100) { $this->switchDirectionTicker = 0; if (mt_rand(0, 100) < 50) { $this->swimDirection = null; } } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if ($this->isAlive()) { if ($this->y > 62 and $this->swimDirection !== null) { $this->swimDirection->y = -0.5; } /*$inWater = $this->isInsideOfAir(); if(!$inWater){ //$this->motionY -= $this->gravity; $this->swimDirection = null; }else*/ if ($this->swimDirection !== null) { if ($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()) { $this->motionX = $this->swimDirection->x * $this->swimSpeed; $this->motionY = $this->swimDirection->y * $this->swimSpeed; $this->motionZ = $this->swimDirection->z * $this->swimSpeed; } } else { $this->swimDirection = $this->generateRandomDirection(); $this->swimSpeed = mt_rand(50, 100); } $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); $this->move($this->motionX, $this->motionY, $this->motionZ); if ($expectedPos->distanceSquared($this) > 0) { $this->swimDirection = $this->generateRandomDirection(); $this->swimSpeed = mt_rand(50, 100); } $friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; $f = sqrt($this->motionX ** 2 + $this->motionZ ** 2); $this->yaw = -atan2($this->motionX, $this->motionZ) * 180 / M_PI; $this->pitch = -atan2($f, $this->motionY) * 180 / M_PI; if ($this->onGround) { $this->motionY *= -0.5; } $this->updateMovement(); } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 1.0E-5 or abs($this->motionY) > 1.0E-5 or abs($this->motionZ) > 1.0E-5; }
/** * Checks whether the passed {@link Vector3} is included in this {@link Space}.<br> * Floating point vectors are accepted.<br> * <br> * The contents of this function are based on <a * href="http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html">Wolfram|MathWorld: Point-Lne Distance * (3-Dimensional)</a>, whereas {@code X0 = $v, X1 = $this->baseCenter, X2 = $this->topCenter}. * * @param Vector3 $v the coordinates to check. * * @return bool whether <code> $v</code> is inside the space. */ public function isInside(Vector3 $v) { if (!$this->isValid()) { return false; } if ($v instanceof Position and $v->getLevel()->getName() !== $this->levelName) { return false; } $distSquared = $v->subtract($this->baseCenter)->cross($v->subtract($this->topCenter))->lengthSquared() / $this->topCenter->subtract($this->baseCenter)->lengthSquared(); // |---| return $distSquared <= $this->radiusSquared; // |(X0 - X1) x (X0 - X2)| / |X2 - X1| }
public static function createEffectArrow(Player $player, Vector3 $position, Vector3 $speed, $yaw, $pitch, $r, $g, $b, $critical) { $nbtTag = new Compound("", ["Pos" => new Enum("Pos", [new Double("", $position->getX()), new Double("", $position->getY()), new Double("", $position->getZ())]), "Rotation" => new Enum("Rotation", [new Float("", $yaw), new Float("", $pitch)])]); $arrow = new EffectArrow($player->chunk, $nbtTag, $r, $g, $b, $player, $critical); $arrow->setMotion($speed); $launchEvent = new ProjectileLaunchEvent($arrow); Server::getInstance()->getPluginManager()->callEvent($launchEvent); if ($launchEvent->isCancelled()) { $arrow->kill(); return null; } else { return $arrow; } }
public function onPlayerInteract(PlayerInteractEvent $e) { // Implement the CompassTP thingie... $pl = $e->getPlayer(); if (!$pl->hasPermission("toybox.compasstp")) { return; } $hand = $pl->getInventory()->getItemInHand(); if ($hand->getID() != $this->item) { return; } $pos = $pl->getPosition()->add(0, $pl->getEyeHeight(), 0); $start = new Vector3($pos->getX(), $pos->getY(), $pos->getZ()); $lv = $pl->getLevel(); for ($start = new Vector3($pos->getX(), $pos->getY(), $pos->getZ()); $start->distance($pos) < 120; $pos = $pos->add($pl->getDirectionVector())) { $block = $lv->getBlock($pos->floor()); if ($block->getId() != 0) { break; } } if ($block->getId() == 0) { $pl->sendMessage(mc::_("Can not teleport to the void!")); return; } $pos = $pos->subtract($pl->getDirectionVector()); $dist = $start->distance($pos); if ($dist < 2.8) { $pl->sendMessage(mc::_("Not teleporting...")); $pl->sendMessage(mc::_("You could easily walk there!")); return; } //echo "Block: ".$block->getName()." (".$block->getId().")\n"; //print_r($pos); //echo "Distance: ".$start->distance($pos)."\n"; $pl->sendMessage(mc::_("Teleporting... %1%", intval($dist))); $pos = $pos->add(0, 1, 0); /*$cb = new CallbackTask([$this,"delayedTP"],[$pl->getName(), $pos->getX(), $pos->getY(), $pos->getZ()]); $this->owner->getServer()->getScheduler()->scheduleDelayedTask($cb,20); //$pl->teleport($pos); return;*/ $m = 5.0; for ($f = 1.0; $f <= $m; $f++) { $ticks = intval($f) * 5; $x = ($pos->getX() - $start->getX()) * $f / $m + $start->getX(); $y = ($pos->getY() - $start->getY()) * $f / $m + $start->getY(); $z = ($pos->getZ() - $start->getZ()) * $f / $m + $start->getZ(); $c = new PluginCallbackTask($this->owner, [$this, "delayedTP"], [$pl->getName(), $x, $y, $z]); $this->owner->getServer()->getScheduler()->scheduleDelayedTask($c, $ticks); } }
public function onUpdate($type) { if ($type === Level::BLOCK_UPDATE_NORMAL) { $faces = array_flip(array(2 => 2, 3 => 4, 4 => 5, 5 => 3)); if ($this->getSide(Vector3::getOppositeSide($faces[$this->meta % 4 + 2]))->isTransparent() === true) { $this->getLevel()->useBreakOn($this); return Level::BLOCK_UPDATE_NORMAL; } } elseif ($type === Level::BLOCK_UPDATE_RANDOM) { if (mt_rand(0, 2) === 1) { if ($this->meta < 8) { $block = clone $this; $block->meta += 4; Server::getInstance()->getPluginManager()->callEvent($ev = new BlockGrowEvent($this, $block)); if (!$ev->isCancelled()) { $this->getLevel()->setBlock($this, $ev->getNewState(), true, true); } else { return Level::BLOCK_UPDATE_RANDOM; } } } else { return Level::BLOCK_UPDATE_RANDOM; } } return false; }
public function __construct($x, $y, $z, $id, $class, $data) { parent::__construct($x, $y, $z); $this->id = $id; $this->class = $class; $this->data = $data; }
public function onUpdate($currentTick) { if ($this->closed !== false) { return false; } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = true; //parent::onUpdate($currentTick); if ($this->isAlive()) { $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); $expBlock0 = $this->getLevel()->getBlock($expectedPos->add(0, -1, 0)->round()); $expBlock1 = $this->getLevel()->getBlock($expectedPos->add(0, 0, 0)->round()); if ($expBlock0->getId() == 0) { $this->motionY -= $this->gravity; //重力计算 $this->motionX = 0; $this->motionZ = 0; } else { $this->motionY = 0; } if ($expBlock1->getId() != 0) { $this->motionY += 0.1; } $this->move($this->motionX, $this->motionY, $this->motionZ); if ($this->isFreeMoving) { $this->motionX = 0; $this->motionZ = 0; $this->isFreeMoving = false; } /*$friction = 1 - $this->drag; $this->motionX *= $friction; $this->motionY *= 1 - $this->drag; $this->motionZ *= $friction;*/ $f = sqrt($this->motionX ** 2 + $this->motionZ ** 2); $this->yaw = -atan2($this->motionX, $this->motionZ) * 180 / M_PI; //视角计算 //$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); $this->updateMovement(); } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 1.0E-5 or abs($this->motionY) > 1.0E-5 or abs($this->motionZ) > 1.0E-5; }
public function __construct($x, $y, $z, $nameTag, $yaw, $pitch, Item $item, $skin, $slim = false) { parent::__construct($x, $y, $z); $this->nameTag = $nameTag; $this->yaw = $yaw; $this->pitch = $pitch; $this->item = $item; $this->skin = $skin; $this->slim = $slim; $this->metadata = [Entity::DATA_FLAGS => [Entity::DATA_TYPE_BYTE, 0], Entity::DATA_AIR => [Entity::DATA_TYPE_SHORT, 300], Entity::DATA_NAMETAG => [Entity::DATA_TYPE_STRING, $this->nameTag], Entity::DATA_SHOW_NAMETAG => [Entity::DATA_TYPE_BYTE, 1], Entity::DATA_SILENT => [Entity::DATA_TYPE_BYTE, 1], Entity::DATA_NO_AI => [Entity::DATA_TYPE_BYTE, 1]]; }
public function __construct(Level $level, Vector3 $pos) { if (!($pos instanceof Vector3 && $level instanceof Level)) { echo "this is not Floor!\n"; } $this->vector = $pos; $this->x = $pos->getX(); $this->y = $pos->getY(); $this->z = $pos->getZ(); $this->level = $level; $this->block = $level->getBlock($pos); $this->id = $this->block->getId(); $this->data = $this->block->getDamage(); $this->height = 128; //$this->level->getHeightMap($this->x, $this->z); echo "max height: {$this->height}\n"; if (!($this->isElevatorBlock($this->block) || $this->isExtensionFloorBlock($this->block))) { echo "THIS IS NOT FLOOR!\n"; } }
public function calcWeather($currentTick) { if ($this->canCalculate()) { $tickDiff = $currentTick - $this->lastUpdate; $this->duration -= $tickDiff; if ($this->duration <= 0) { //0晴天1下雨2雷雨3阴天雷 if ($this->weatherNow == self::SUNNY) { $weather = $this->randomWeatherData[array_rand($this->randomWeatherData)]; $duration = mt_rand(min($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax), max($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax)); $this->level->getServer()->getPluginManager()->callEvent($ev = new WeatherChangeEvent($this->level, $weather, $duration)); if (!$ev->isCancelled()) { $this->weatherNow = $ev->getWeather(); $this->strength1 = mt_rand(90000, 110000); $this->strength2 = mt_rand(30000, 40000); $this->duration = $ev->getDuration(); $this->changeWeather($this->weatherNow, $this->strength1, $this->strength2); } } else { $weather = self::SUNNY; $duration = mt_rand(min($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax), max($this->level->getServer()->weatherRandomDurationMin, $this->level->getServer()->weatherRandomDurationMax)); $this->level->getServer()->getPluginManager()->callEvent($ev = new WeatherChangeEvent($this->level, $weather, $duration)); if (!$ev->isCancelled()) { $this->weatherNow = $ev->getWeather(); $this->strength1 = 0; $this->strength2 = 0; $this->duration = $ev->getDuration(); $this->changeWeather($this->weatherNow, $this->strength1, $this->strength2); } } } if ($this->weatherNow > 0 and $this->level->getServer()->lightningTime > 0 and is_int($this->duration / $this->level->getServer()->lightningTime)) { $players = $this->level->getPlayers(); if (count($players) > 0) { $p = $players[array_rand($players)]; $x = $p->x + mt_rand(-64, 64); $z = $p->z + mt_rand(-64, 64); $y = $this->level->getHighestBlockAt($x, $z); $this->level->spawnLightning($this->temporalVector->setComponents($x, $y, $z)); } /*foreach($this->level->getPlayers() as $p){ if(mt_rand(0, 1) == 1){ $x = $p->getX() + rand(-100, 100); $y = $p->getY() + rand(20, 50); $z = $p->getZ() + rand(-100, 100); $this->level->sendLighting($x, $y, $z, $p); } }*/ } } $this->lastUpdate = $currentTick; }
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null) { if ($face !== 0 && $face !== 1) { $faces = [3 => 3, 2 => 4, 4 => 2, 5 => 1]; $this->meta = $faces[$face]; if ($this->getSide(Vector3::getOppositeSide($face))->getId() === Block::TRIPWIRE) { $this->meta & 0x1; } $this->getLevel()->setBlock($block, Block::get(Block::TRIPWIRE_HOOK, $this->meta), true); return true; } return false; }
public function onBreak(Item $item) { $sound = new EndermanTeleportSound($this); $this->getLevel()->addSound($sound); $particle = new PortalParticle($this); $this->getLevel()->addParticle($particle); $block = $this; //$this->getLevel()->setBlock($block, new Block(Block::PORTAL, 0));//在破坏处放置一个方块防止计算出错 if ($this->getLevel()->getBlock($this->temporalVector->setComponents($block->x - 1, $block->y, $block->z))->getId() == Block::PORTAL or $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x + 1, $block->y, $block->z))->getId() == Block::PORTAL) { //x方向 for ($x = $block->x; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL; $x++) { for ($y = $block->y; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL; $y++) { $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for ($y = $block->y - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL; $y--) { $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } for ($x = $block->x - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $block->y, $block->z))->getId() == Block::PORTAL; $x--) { for ($y = $block->y; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL; $y++) { $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } for ($y = $block->y - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($x, $y, $block->z))->getId() == Block::PORTAL; $y--) { $this->getLevel()->setBlock($this->temporalVector->setComponents($x, $y, $block->z), new Air()); } } } else { //z方向 for ($z = $block->z; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL; $z++) { for ($y = $block->y; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL; $y++) { $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for ($y = $block->y - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL; $y--) { $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } for ($z = $block->z - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $block->y, $z))->getId() == Block::PORTAL; $z--) { for ($y = $block->y; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL; $y++) { $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } for ($y = $block->y - 1; $this->getLevel()->getBlock($this->temporalVector->setComponents($block->x, $y, $z))->getId() == Block::PORTAL; $y--) { $this->getLevel()->setBlock($this->temporalVector->setComponents($block->x, $y, $z), new Air()); } } } parent::onBreak($item); }
/** * @return bool */ public function explodeA() { if ($this->size < 0.1) { return false; } $vector = new Vector3(0, 0, 0); $vBlock = new Vector3(0, 0, 0); $mRays = intval($this->rays - 1); for ($i = 0; $i < $this->rays; ++$i) { for ($j = 0; $j < $this->rays; ++$j) { for ($k = 0; $k < $this->rays; ++$k) { if ($i === 0 or $i === $mRays or $j === 0 or $j === $mRays or $k === 0 or $k === $mRays) { $vector->setComponents($i / $mRays * 2 - 1, $j / $mRays * 2 - 1, $k / $mRays * 2 - 1); $vector->setComponents($vector->x / ($len = $vector->length()) * $this->stepLen, $vector->y / $len * $this->stepLen, $vector->z / $len * $this->stepLen); $pointerX = $this->source->x; $pointerY = $this->source->y; $pointerZ = $this->source->z; for ($blastForce = $this->size * (mt_rand(700, 1300) / 1000); $blastForce > 0; $blastForce -= $this->stepLen * 0.75) { $x = (int) $pointerX; $y = (int) $pointerY; $z = (int) $pointerZ; $vBlock->x = $pointerX >= $x ? $x : $x - 1; $vBlock->y = $pointerY >= $y ? $y : $y - 1; $vBlock->z = $pointerZ >= $z ? $z : $z - 1; if ($vBlock->y < 0 or $vBlock->y > 127) { break; } $block = $this->level->getBlock($vBlock); if ($block->getId() !== 0) { $blastForce -= ($block->getHardness() / 5 + 0.3) * $this->stepLen; if ($blastForce > 0) { if (!isset($this->affectedBlocks[$index = PHP_INT_SIZE === 8 ? ($block->x & 0xfffffff) << 35 | ($block->y & 0x7f) << 28 | $block->z & 0xfffffff : $block->x . ":" . $block->y . ":" . $block->z])) { $this->affectedBlocks[$index] = $block; } } } $pointerX += $vector->x; $pointerY += $vector->y; $pointerZ += $vector->z; } } } } } return true; }
public function onUpdate($currentTick) { if ($this->closed !== false) { return false; } if ($this->getLevel()->getServer()->aiConfig["zombie"] != 2) { return parent::onUpdate($currentTick); } $this->lastUpdate = $currentTick; $this->timings->startTiming(); $hasUpdate = parent::onUpdate($currentTick); if ($this->isAlive()) { /* Don't use time directly * Instead, get remainder of current time divided by 24,000 * This tells us the time of day, which is what we really need */ $timeOfDay = abs($this->getLevel()->getTime() % 24000); if (0 < $timeOfDay and $timeOfDay < 13000) { $this->setOnFire(2); } //僵尸起火 $p = $this->getNearestPlayer(); //找到最近的可以被仇恨的玩家 if (!$p) { $this->hated = false; if (++$this->moveTicker >= 100) { $this->moveDirection = $this->generateRandomDirection(); $this->moveTicker = 0; } } else { $this->hated = $p; if ($p->distance($this) <= $this->fire_r) { $p->setOnFire(2); } //点燃玩家 if (!$this->tempTicking) { if (++$this->hateTicker >= 10 or $this->moveDirection == null) { //每0.5秒获取僵尸前进的新方向 $this->moveDirection = $this->generateDirection($p); $this->hateTicker = 0; } } } if ($this->tempTicking) { //帮助僵尸寻找新的方向走出困境 if (++$this->tempTicker >= 20) { $this->tempTicking = false; $this->tempTicker = 0; } } if ($this->hated instanceof Player) { //攻击玩家 if ($this->hated->distance($this) < $this->attack_r) { $this->hated->attack(2, new EntityDamageByEntityEvent($this, $this->hated, EntityDamageEvent::CAUSE_ENTITY_ATTACK, 2)); } } if ($this->moveDirection != null) { if ($this->motionX ** 2 + $this->motionZ ** 2 <= $this->moveDirection->lengthSquared()) { $motionY = $this->getVelY(); //僵尸运动计算 if ($motionY >= 0) { $this->motionX = $this->moveDirection->x * $this->moveSpeed; $this->motionZ = $this->moveDirection->z * $this->moveSpeed; $this->motionY = $motionY; } else { $this->moveDirection = $this->generateRandomDirection(); //生成随机运动方向 $this->moveTicker = 0; $this->tempTicking = true; } } } else { $this->moveDirection = $this->generateRandomDirection(); $this->moveTicker = 0; } //var_dump($this->moveDirection,$this->motionX,$this->motionZ); $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); if ($this->motionY == 0) { $this->motionY -= $this->gravity; } //重力计算 $this->move($this->motionX, $this->motionY, $this->motionZ); if ($expectedPos->distanceSquared($this) > 0) { $this->moveDirection = $this->generateRandomDirection(); } $friction = 1 - $this->drag; $this->motionX *= $friction; //$this->motionY *= 1 - $this->drag; $this->motionZ *= $friction; $f = sqrt($this->motionX ** 2 + $this->motionZ ** 2); $this->yaw = -atan2($this->motionX, $this->motionZ) * 180 / M_PI; //视角计算 //$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); $this->updateMovement(); } $this->timings->stopTiming(); return $hasUpdate or !$this->onGround or abs($this->motionX) > 1.0E-5 or abs($this->motionY) > 1.0E-5 or abs($this->motionZ) > 1.0E-5; }
public function __construct(Level $level, Vector3 $start, Vector3 $direction, $yOffset = 0, $maxDistance = 0) { $this->level = $level; $this->maxDistance = (int) $maxDistance; $this->blockQueue = new \SplFixedArray(3); $startClone = new Vector3($start->x, $start->y, $start->z); $startClone->y += $yOffset; $this->currentDistance = 0; $mainDirection = 0; $secondDirection = 0; $thirdDirection = 0; $mainPosition = 0; $secondPosition = 0; $thirdPosition = 0; $pos = new Vector3($startClone->x, $startClone->y, $startClone->z); $startBlock = $this->level->getBlock(new Vector3(floor($pos->x), floor($pos->y), floor($pos->z))); if ($this->getXLength($direction) > $mainDirection) { $this->mainFace = $this->getXFace($direction); $mainDirection = $this->getXLength($direction); $mainPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getYFace($direction); $secondDirection = $this->getYLength($direction); $secondPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getZFace($direction); $thirdDirection = $this->getZLength($direction); $thirdPosition = $this->getZPosition($direction, $startClone, $startBlock); } if ($this->getYLength($direction) > $mainDirection) { $this->mainFace = $this->getYFace($direction); $mainDirection = $this->getYLength($direction); $mainPosition = $this->getYPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getZFace($direction); $secondDirection = $this->getZLength($direction); $secondPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getXFace($direction); $thirdDirection = $this->getXLength($direction); $thirdPosition = $this->getXPosition($direction, $startClone, $startBlock); } if ($this->getZLength($direction) > $mainDirection) { $this->mainFace = $this->getZFace($direction); $mainDirection = $this->getZLength($direction); $mainPosition = $this->getZPosition($direction, $startClone, $startBlock); $this->secondFace = $this->getXFace($direction); $secondDirection = $this->getXLength($direction); $secondPosition = $this->getXPosition($direction, $startClone, $startBlock); $this->thirdFace = $this->getYFace($direction); $thirdDirection = $this->getYLength($direction); $thirdPosition = $this->getYPosition($direction, $startClone, $startBlock); } $d = $mainPosition / $mainDirection; $secondd = $secondPosition - $secondDirection * $d; $thirdd = $thirdPosition - $thirdDirection * $d; $this->secondError = floor($secondd * self::$gridSize); $this->secondStep = round($secondDirection / $mainDirection * self::$gridSize); $this->thirdError = floor($thirdd * self::$gridSize); $this->thirdStep = round($thirdDirection / $mainDirection * self::$gridSize); if ($this->secondError + $this->secondStep <= 0) { $this->secondError = -$this->secondStep + 1; } if ($this->thirdError + $this->thirdStep <= 0) { $this->thirdError = -$this->thirdStep + 1; } $lastBlock = $startBlock->getSide(Vector3::getOppositeSide($this->mainFace)); if ($this->secondError < 0) { $this->secondError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->secondFace)); } if ($this->thirdError < 0) { $this->thirdError += self::$gridSize; $lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->thirdFace)); } $this->secondError -= self::$gridSize; $this->thirdError -= self::$gridSize; $this->blockQueue[0] = $lastBlock; $this->currentBlock = -1; $this->scan(); $startBlockFound = false; for ($cnt = $this->currentBlock; $cnt >= 0; --$cnt) { if ($this->blockEquals($this->blockQueue[$cnt], $startBlock)) { $this->currentBlock = $cnt; $startBlockFound = true; break; } } if (!$startBlockFound) { throw new \InvalidStateException("Start block missed in BlockIterator"); } $this->maxDistanceInt = round($maxDistance / (sqrt($mainDirection ** 2 + $secondDirection ** 2 + $thirdDirection ** 2) / $mainDirection)); }
/** * Returns a side Vector * * @param int $side * @param int $step * * @return Position * * @throws LevelException */ public function getSide($side, $step = 1) { if (!$this->isValid()) { throw new LevelException("Undefined Level reference"); } return Position::fromObject(parent::getSide($side, $step), $this->level); }
/** * Zombie initialization routine and freely walking mode cycle timer * Timer:20 ticks */ public function ZombieRandomWalkCalc() { $this->dif = Server::getInstance()->getDifficulty(); //$this->getLogger()->info(count($this->plugin->zombie)); $zo = $this->entity; if ($this->willMove()) { if (!isset($this->data)) { $this->data = array('ID' => $zo->getId(), 'IsChasing' => false, 'motionx' => 0, 'motiony' => 0, 'motionz' => 0, 'hurt' => 10, 'time' => 10, 'x' => 0, 'y' => 0, 'z' => 0, 'oldv3' => $zo->getLocation(), 'yup' => 20, 'up' => 0, 'yaw' => $zo->yaw, 'pitch' => 0, 'level' => $zo->getLevel()->getName(), 'xxx' => 0, 'zzz' => 0, 'gotimer' => 10, 'swim' => 0, 'jump' => 0.01, 'canjump' => true, 'drop' => false, 'canAttack' => 0, 'knockBack' => false); $zom =& $this->data; $zom['x'] = $zo->getX(); $zom['y'] = $zo->getY(); $zom['z'] = $zo->getZ(); } $zom =& $this->data; if ($zom['IsChasing'] === false) { //Walk mode if ($zom['gotimer'] == 0 or $zom['gotimer'] == 10) { //Limit rotation rate $newmx = mt_rand(-5, 5) / 10; while (abs($newmx - $zom['motionx']) >= 0.7) { $newmx = mt_rand(-5, 5) / 10; } $zom['motionx'] = $newmx; $newmz = mt_rand(-5, 5) / 10; while (abs($newmz - $zom['motionz']) >= 0.7) { $newmz = mt_rand(-5, 5) / 10; } $zom['motionz'] = $newmz; } elseif ($zom['gotimer'] >= 20 and $zom['gotimer'] <= 24) { $zom['motionx'] = 0; $zom['motionz'] = 0; //Zombie stop } $zom['gotimer'] += 0.5; if ($zom['gotimer'] >= 22) { $zom['gotimer'] = 0; } //Reset timer walk //$zom['motionx'] = mt_rand(-10,10)/10; //$zom['motionz'] = mt_rand(-10,10)/10; $zom['yup'] = 0; $zom['up'] = 0; //$width = $this->width; $pos = new Vector3($zom['x'] + $zom['motionx'], floor($zo->getY()) + 1, $zom['z'] + $zom['motionz']); //目标坐标 $zy = $this->ifjump($zo->getLevel(), $pos); if ($zy === false) { //if can not move forward $pos2 = new Vector3($zom['x'], $zom['y'], $zom['z']); //Target coordinates if ($this->ifjump($zo->getLevel(), $pos2) === false) { //Original coordinate $pos2 = new Vector3($zom['x'], $zom['y'] - 1, $zom['z']); //decline $zom['up'] = 1; $zom['yup'] = 0; } else { $zom['motionx'] = -$zom['motionx']; $zom['motionz'] = -$zom['motionz']; //He turned 180 degrees $zom['up'] = 0; } } else { $pos2 = new Vector3($zom['x'] + $zom['motionx'], $zy - 1, $zom['z'] + $zom['motionz']); //Target coordinates if ($pos2->y - $zom['y'] < 0) { $zom['up'] = 1; } else { $zom['up'] = 0; } } if ($zom['motionx'] == 0 and $zom['motionz'] == 0) { //Zombie stop } else { //Steering computing $yaw = $this->getyaw($zom['motionx'], $zom['motionz']); //$zo->setRotation($yaw,0); $zom['yaw'] = $yaw; $zom['pitch'] = 0; } //Update zombie coordinates if (!$zom['knockBack']) { $zom['x'] = $pos2->getX(); $zom['z'] = $pos2->getZ(); $zom['y'] = $pos2->getY(); } $zom['motiony'] = $pos2->getY() - $zo->getY(); //echo($zo->getY()."\n"); //var_dump($pos2); //var_dump($zom['motiony']); $zo->setPosition($pos2); //echo "SetPosition \n"; } } }
/** * @param Vector3|Position|Location $pos * @param float $yaw * @param float $pitch * * @return bool */ public function teleport(Vector3 $pos, $yaw = null, $pitch = null) { if ($pos instanceof Location) { $yaw = $yaw === null ? $pos->yaw : $yaw; $pitch = $pitch === null ? $pos->pitch : $pitch; } $from = Position::fromObject($this, $this->level); $to = Position::fromObject($pos, $pos instanceof Position ? $pos->getLevel() : $this->level); $this->server->getPluginManager()->callEvent($ev = new EntityTeleportEvent($this, $from, $to)); if ($ev->isCancelled()) { return false; } $this->ySize = 0; $pos = $ev->getTo(); $this->setMotion($this->temporalVector->setComponents(0, 0, 0)); if ($this->setPositionAndRotation($pos, $yaw === null ? $this->yaw : $yaw, $pitch === null ? $this->pitch : $pitch) !== false) { $this->resetFallDistance(); $this->onGround = true; $this->lastX = $this->x; $this->lastY = $this->y; $this->lastZ = $this->z; $this->lastYaw = $this->yaw; $this->lastPitch = $this->pitch; $this->updateMovement(); return true; } return false; }
public function SkeletonRandomWalkCalc() { $this->dif = $this->plugin->getServer()->getDifficulty(); //$this->getLogger()->info("僵尸数量:".count($this->plugin->Skeleton)); foreach ($this->plugin->getServer()->getLevels() as $level) { foreach ($level->getEntities() as $zo) { if ($zo instanceof Skeleton) { if ($this->plugin->willMove($zo)) { if (!isset($this->plugin->Skeleton[$zo->getId()])) { $this->plugin->Skeleton[$zo->getId()] = array('ID' => $zo->getId(), 'IsChasing' => false, 'motionx' => 0, 'motiony' => 0, 'motionz' => 0, 'hurt' => 10, 'time' => 10, 'x' => 0, 'y' => 0, 'z' => 0, 'oldv3' => $zo->getLocation(), 'yup' => 20, 'up' => 0, 'yaw' => $zo->yaw, 'pitch' => 0, 'level' => $zo->getLevel()->getName(), 'xxx' => 0, 'zzz' => 0, 'gotimer' => 10, 'swim' => 0, 'jump' => 0.01, 'canjump' => true, 'drop' => false, 'canAttack' => 0, 'shoot' => 20, 'knockBack' => false); $zom =& $this->plugin->Skeleton[$zo->getId()]; $zom['x'] = $zo->getX(); $zom['y'] = $zo->getY(); $zom['z'] = $zo->getZ(); } $zom =& $this->plugin->Skeleton[$zo->getId()]; if ($zom['IsChasing'] === false) { //自由行走模式 if ($zom['gotimer'] == 0 or $zom['gotimer'] == 10) { //限制转动幅度 $newmx = mt_rand(-5, 5) / 10; while (abs($newmx - $zom['motionx']) >= 0.7) { $newmx = mt_rand(-5, 5) / 10; } $zom['motionx'] = $newmx; $newmz = mt_rand(-5, 5) / 10; while (abs($newmz - $zom['motionz']) >= 0.7) { $newmz = mt_rand(-5, 5) / 10; } $zom['motionz'] = $newmz; } elseif ($zom['gotimer'] >= 20 and $zom['gotimer'] <= 24) { $zom['motionx'] = 0; $zom['motionz'] = 0; //僵尸停止 } $zom['gotimer'] += 0.5; if ($zom['gotimer'] >= 22) { $zom['gotimer'] = 0; } //重置走路计时器 //$zom['motionx'] = mt_rand(-10,10)/10; //$zom['motionz'] = mt_rand(-10,10)/10; $zom['yup'] = 0; $zom['up'] = 0; //boybook的y轴判断法 //$width = $this->width; $pos = new Vector3($zom['x'] + $zom['motionx'], floor($zo->getY()) + 1, $zom['z'] + $zom['motionz']); //目标坐标 $zy = $this->plugin->ifjump($zo->getLevel(), $pos); if ($zy === false) { //前方不可前进 $pos2 = new Vector3($zom['x'], $zom['y'], $zom['z']); //目标坐标 if ($this->plugin->ifjump($zo->getLevel(), $pos2) === false) { //原坐标依然是悬空 $pos2 = new Vector3($zom['x'], $zom['y'] - 1, $zom['z']); //下降 $zom['up'] = 1; $zom['yup'] = 0; } else { $zom['motionx'] = -$zom['motionx']; $zom['motionz'] = -$zom['motionz']; //转向180度,向身后走 $zom['up'] = 0; } } else { $pos2 = new Vector3($zom['x'] + $zom['motionx'], $zy - 1, $zom['z'] + $zom['motionz']); //目标坐标 if ($pos2->y - $zom['y'] < 0) { $zom['up'] = 1; } else { $zom['up'] = 0; } } if ($zom['motionx'] == 0 and $zom['motionz'] == 0) { //僵尸停止 } else { //转向计算 $yaw = $this->plugin->getyaw($zom['motionx'], $zom['motionz']); //$zo->setRotation($yaw,0); $zom['yaw'] = $yaw; $zom['pitch'] = 0; } //更新僵尸坐标 if (!$zom['knockBack']) { $zom['x'] = $pos2->getX(); $zom['z'] = $pos2->getZ(); $zom['y'] = $pos2->getY(); } $zom['motiony'] = $pos2->getY() - $zo->getY(); //echo($zo->getY()."\n"); //var_dump($pos2); //var_dump($zom['motiony']); $zo->setPosition($pos2); //echo "SetPosition \n"; } } } } } }
/** * @param Vector3 $source * @param Item $item * @param Vector3 $motion * @param int $delay */ public function dropItem(Vector3 $source, Item $item, Vector3 $motion = null, $delay = 10) { $motion = $motion === null ? new Vector3(lcg_value() * 0.2 - 0.1, 0.2, lcg_value() * 0.2 - 0.1) : $motion; if ($item->getId() > 0 and $item->getCount() > 0) { $itemEntity = Entity::createEntity("Item", $this->getChunk($source->getX() >> 4, $source->getZ() >> 4), new Compound("", ["Pos" => new Enum("Pos", [new Double("", $source->getX()), new Double("", $source->getY()), new Double("", $source->getZ())]), "Motion" => new Enum("Motion", [new Double("", $motion->x), new Double("", $motion->y), new Double("", $motion->z)]), "Rotation" => new Enum("Rotation", [new Float("", lcg_value() * 360), new Float("", 0)]), "Health" => new Short("Health", 5), "Item" => new Compound("Item", ["id" => new Short("id", $item->getId()), "Damage" => new Short("Damage", $item->getDamage()), "Count" => new Byte("Count", $item->getCount())]), "PickupDelay" => new Short("PickupDelay", $delay)])); $itemEntity->spawnToAll(); } }
/** * @param Level $level * @param Vector3 $v3 * @param bool $hate * @param bool $reason * @return bool|float|string * 判断某坐标是否可以行走 * 并给出原因 */ public function ifjump(Level $level, Vector3 $v3, $hate = false, $reason = false) { //boybook Y轴算法核心函数 $x = floor($v3->getX()); $y = floor($v3->getY()); $z = floor($v3->getZ()); //echo ($y." "); if ($this->whatBlock($level, new Vector3($x, $y, $z)) == "air") { //echo "前方空气 "; if ($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "block" or new Vector3($x, $y - 1, $z) == "climb") { //方块 //echo "考虑向前 "; if ($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "high") { //上方一格被堵住了 //echo "上方卡住 \n"; if ($reason) { return 'up!'; } return false; //上方卡住 } else { //echo "GO向前走 \n"; if ($reason) { return 'GO'; } return $y; //向前走 } } elseif ($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "half") { //半砖 //echo "下到半砖 \n"; if ($reason) { return 'half'; } return $y - 0.5; //向下跳0.5格 } elseif ($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "lava") { //岩浆 //echo "前方岩浆 \n"; if ($reason) { return 'lava'; } return false; //前方岩浆 } elseif ($this->whatBlock($level, new Vector3($x, $y - 1, $z)) == "air") { //空气 //echo "考虑向下跳 "; if ($this->whatBlock($level, new Vector3($x, $y - 2, $z)) == "block") { //echo "GO向下跳 \n"; if ($reason) { return 'down'; } return $y - 1; //向下跳 } else { //前方悬崖 //echo "前方悬崖 \n"; if ($reason) { return 'fall'; } /* if ($hate === false) { return false; } else { return $y-1; //向下跳 }*/ } } } elseif ($this->whatBlock($level, new Vector3($x, $y, $z)) == "half") { //半砖 //echo "前方半砖 \n"; if ($this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 1, $z)) == "high") { //上方一格被堵住了 //return false; //上方卡住 } else { if ($reason) { return 'halfGO'; } return $y + 0.5; } } elseif ($this->whatBlock($level, new Vector3($x, $y, $z)) == "lava") { //岩浆 //echo "前方岩浆 \n"; if ($reason) { return 'lava'; } return false; } elseif ($this->whatBlock($level, new Vector3($x, $y, $z)) == "high") { //1.5格高方块 //echo "前方栅栏 \n"; if ($reason) { return 'high'; } return false; } elseif ($this->whatBlock($level, new Vector3($x, $y, $z)) == "climb") { //梯子 //echo "前方梯子 \n"; //return $y; if ($reason) { return 'climb'; } if ($hate) { return $y + 0.7; } else { return $y + 0.5; } } else { //考虑向上 //echo "考虑向上 "; if ($this->whatBlock($level, new Vector3($x, $y + 1, $z)) != "air") { //前方是面墙 //echo "前方是墙 \n"; if ($reason) { return 'wall'; } return false; } else { if ($this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "block" or $this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "half" or $this->whatBlock($level, new Vector3($x, $y + 2, $z)) == "high") { //上方两格被堵住了 //echo "2格处被堵 \n"; if ($reason) { return 'up2!'; } return false; } else { //echo "GO向上跳 \n"; if ($reason) { return 'upGO'; } return $y + 1; //向上跳 } } } return false; }
/** * @param Vector3 $spawn default null * * @return bool|Position */ public function getSafeSpawn($spawn = null) { if (!$spawn instanceof Vector3) { $spawn = $this->getSpawn(); } if ($spawn instanceof Vector3) { $x = (int) round($spawn->x); $y = (int) round($spawn->y); $z = (int) round($spawn->z); for (; $y > 0; --$y) { $v = new Vector3($x, $y, $z); $b = $this->getBlock($v->getSide(0)); if ($b === false) { return $spawn; } elseif (!$b instanceof Air) { break; } } for (; $y < 128; ++$y) { $v = new Vector3($x, $y, $z); if ($this->getBlock($v->getSide(1)) instanceof Air) { if ($this->getBlock($v) instanceof Air) { return new Position($x, $y, $z, $this); } } else { ++$y; } } return new Position($x, $y, $z, $this); } return false; }
public function setPosition(Vector3 $pos) { if ($this->closed) { return false; } if ($pos instanceof Position and $pos->level !== null and $pos->level !== $this->level) { if ($this->switchLevel($pos->getLevel()) === false) { return false; } } $this->x = $pos->x; $this->y = $pos->y; $this->z = $pos->z; $radius = $this->width / 2; $this->boundingBox->setBounds($pos->x - $radius, $pos->y, $pos->z - $radius, $pos->x + $radius, $pos->y + $this->height, $pos->z + $radius); $this->checkChunks(); return true; }
/** * Handles a Minecraft packet * TODO: Separate all of this in handlers * * WARNING: Do not use this, it's only for internal use. * Changes to this function won't be recorded on the version. * * @param DataPacket $packet */ public function handleDataPacket(DataPacket $packet) { if ($this->connected === false) { return; } if ($packet::NETWORK_ID === ProtocolInfo::BATCH_PACKET) { /** @var BatchPacket $packet */ $this->server->getNetwork()->processBatch($packet, $this); return; } $timings = Timings::getReceiveDataPacketTimings($packet); $timings->startTiming(); $this->server->getPluginManager()->callEvent($ev = new DataPacketReceiveEvent($this, $packet)); if ($ev->isCancelled()) { $timings->stopTiming(); return; } switch ($packet::NETWORK_ID) { case ProtocolInfo::LOGIN_PACKET: if ($this->loggedIn) { break; } $this->username = TextFormat::clean($packet->username); $this->displayName = $this->username; $this->setNameTag($this->username); $this->iusername = strtolower($this->username); if (count($this->server->getOnlinePlayers()) >= $this->server->getMaxPlayers() and $this->kick("disconnectionScreen.serverFull", false)) { break; } if ($packet->protocol1 !== ProtocolInfo::CURRENT_PROTOCOL) { if ($packet->protocol1 < ProtocolInfo::CURRENT_PROTOCOL) { $message = "disconnectionScreen.outdatedClient"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_CLIENT; $this->directDataPacket($pk); } else { $message = "disconnectionScreen.outdatedServer"; $pk = new PlayStatusPacket(); $pk->status = PlayStatusPacket::LOGIN_FAILED_SERVER; $this->directDataPacket($pk); } $this->close("", $message, false); break; } $this->randomClientId = $packet->clientId; $this->loginData = ["clientId" => $packet->clientId, "loginData" => null]; $this->uuid = $packet->clientUUID; $this->rawUUID = $this->uuid->toBinary(); $this->clientSecret = $packet->clientSecret; $valid = true; $len = strlen($packet->username); if ($len > 16 or $len < 3) { $valid = false; } for ($i = 0; $i < $len and $valid; ++$i) { $c = ord($packet->username[$i]); if ($c >= ord("a") and $c <= ord("z") or $c >= ord("A") and $c <= ord("Z") or $c >= ord("0") and $c <= ord("9") or $c === ord("_")) { continue; } $valid = false; break; } if (!$valid or $this->iusername === "rcon" or $this->iusername === "console") { $this->close("", "disconnectionScreen.invalidName"); break; } if (strlen($packet->skin) !== 64 * 32 * 4 and strlen($packet->skin) !== 64 * 64 * 4) { $this->close("", "disconnectionScreen.invalidSkin"); break; } $this->setSkin($packet->skin, $packet->skinname, $packet->oldclient, $packet->slim, $packet->transparent); $this->server->getPluginManager()->callEvent($ev = new PlayerPreLoginEvent($this, "Plugin reason")); if ($ev->isCancelled()) { $this->close("", $ev->getKickMessage()); break; } $this->onPlayerPreLogin(); break; case ProtocolInfo::MOVE_PLAYER_PACKET: $newPos = new Vector3($packet->x, $packet->y - $this->getEyeHeight(), $packet->z); $revert = false; if (!$this->isAlive() or $this->spawned !== true) { $revert = true; $this->forceMovement = new Vector3($this->x, $this->y, $this->z); } if ($this->teleportPosition !== null or $this->forceMovement instanceof Vector3 and (($dist = $newPos->distanceSquared($this->forceMovement)) > 0.1 or $revert)) { $this->sendPosition($this->forceMovement, $packet->yaw, $packet->pitch); } else { $packet->yaw %= 360; $packet->pitch %= 360; if ($packet->yaw < 0) { $packet->yaw += 360; } $this->setRotation($packet->yaw, $packet->pitch); $this->newPosition = $newPos; $this->forceMovement = null; } break; case ProtocolInfo::MOB_EQUIPMENT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } if ($packet->slot === 0x28 or $packet->slot === 0 or $packet->slot === 255) { //0 for 0.8.0 compatibility $packet->slot = -1; //Air } else { $packet->slot -= 9; //Get real block slot } /** @var Item $item */ $item = null; if ($this->isCreative()) { //Creative mode match $item = $packet->item; $slot = Item::getCreativeItemIndex($item); } else { $item = $this->inventory->getItem($packet->slot); $slot = $packet->slot; } if ($packet->slot === -1) { //Air if ($this->isCreative()) { $found = false; for ($i = 0; $i < $this->inventory->getHotbarSize(); ++$i) { if ($this->inventory->getHotbarSlotIndex($i) === -1) { $this->inventory->setHeldItemIndex($i); $found = true; break; } } if (!$found) { //couldn't find a empty slot (error) $this->inventory->sendContents($this); break; } } else { if ($packet->selectedSlot >= 0 and $packet->selectedSlot < 9) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setHeldItemSlot($packet->slot); } else { $this->inventory->sendContents($this); break; } } } elseif ($item === null or $slot === -1 or !$item->deepEquals($packet->item)) { // packet error or not implemented $this->inventory->sendContents($this); break; } elseif ($this->isCreative()) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setItem($packet->selectedSlot, $item); $this->inventory->setHeldItemSlot($packet->selectedSlot); } else { if ($packet->selectedSlot >= 0 and $packet->selectedSlot < $this->inventory->getHotbarSize()) { $this->inventory->setHeldItemIndex($packet->selectedSlot); $this->inventory->setHeldItemSlot($slot); } else { $this->inventory->sendContents($this); break; } } $this->inventory->sendHeldItem($this->hasSpawned); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::USE_ITEM_PACKET: if ($this->spawned === false or !$this->isAlive() or $this->blocked) { break; } $blockVector = new Vector3($packet->x, $packet->y, $packet->z); $this->craftingType = 0; if ($packet->face >= 0 and $packet->face <= 5) { //Use Block, place $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); if (!$this->canInteract($blockVector->add(0.5, 0.5, 0.5), 13) or $this->isSpectator()) { } elseif ($this->isCreative()) { $item = $this->inventory->getItemInHand(); if ($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this) === true) { break; } } elseif (!$this->inventory->getItemInHand()->deepEquals($packet->item)) { $this->inventory->sendHeldItem($this); } else { $item = $this->inventory->getItemInHand(); $oldItem = clone $item; //TODO: Implement adventure mode checks if ($this->level->useItemOn($blockVector, $item, $packet->face, $packet->fx, $packet->fy, $packet->fz, $this)) { if (!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()) { $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } break; } } $this->inventory->sendHeldItem($this); if ($blockVector->distanceSquared($this) > 10000) { break; } $target = $this->level->getBlock($blockVector); $block = $target->getSide($packet->face); $this->level->sendBlocks([$this], [$target, $block], UpdateBlockPacket::FLAG_ALL_PRIORITY); break; } elseif ($packet->face === 0xff) { $aimPos = (new Vector3($packet->x / 32768, $packet->y / 32768, $packet->z / 32768))->normalize(); if ($this->isCreative()) { $item = $this->inventory->getItemInHand(); } elseif (!$this->inventory->getItemInHand()->deepEquals($packet->item)) { $this->inventory->sendHeldItem($this); break; } else { $item = $this->inventory->getItemInHand(); } $ev = new PlayerInteractEvent($this, $item, $aimPos, $packet->face, PlayerInteractEvent::RIGHT_CLICK_AIR); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendHeldItem($this); break; } if ($item->getId() === Item::SNOWBALL) { $nbt = new Compound("", ["Pos" => new Enum("Pos", [new Double("", $this->x), new Double("", $this->y + $this->getEyeHeight()), new Double("", $this->z)]), "Motion" => new Enum("Motion", [new Double("", $aimPos->x), new Double("", $aimPos->y), new Double("", $aimPos->z)]), "Rotation" => new Enum("Rotation", [new Float("", $this->yaw), new Float("", $this->pitch)])]); $f = 1.5; $snowball = Entity::createEntity("Snowball", $this->chunk, $nbt, $this); $snowball->setMotion($snowball->getMotion()->multiply($f)); if ($this->isSurvival()) { $item->setCount($item->getCount() - 1); $this->inventory->setItemInHand($item->getCount() > 0 ? $item : Item::get(Item::AIR)); } if ($snowball instanceof Projectile) { $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($snowball)); if ($projectileEv->isCancelled()) { $snowball->kill(); } else { $snowball->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } } else { $snowball->spawnToAll(); } } $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, true); $this->startAction = $this->server->getTick(); } break; case ProtocolInfo::PLAYER_ACTION_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive() and $packet->action !== PlayerActionPacket::ACTION_RESPAWN and $packet->action !== PlayerActionPacket::ACTION_DIMENSION_CHANGE) { break; } $packet->eid = $this->id; $pos = new Vector3($packet->x, $packet->y, $packet->z); switch ($packet->action) { case PlayerActionPacket::ACTION_START_BREAK: if ($this->lastBreak !== PHP_INT_MAX or $pos->distanceSquared($this) > 10000) { break; } $target = $this->level->getBlock($pos); $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, $packet->face, $target->getId() === 0 ? PlayerInteractEvent::LEFT_CLICK_AIR : PlayerInteractEvent::LEFT_CLICK_BLOCK); $this->getServer()->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendHeldItem($this); break; } $this->lastBreak = microtime(true); break; case PlayerActionPacket::ACTION_ABORT_BREAK: $this->lastBreak = PHP_INT_MAX; break; case PlayerActionPacket::ACTION_RELEASE_ITEM: if ($this->startAction > -1 and $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION)) { if ($this->inventory->getItemInHand()->getId() === Item::BOW) { $bow = $this->inventory->getItemInHand(); if ($this->isSurvival() and !$this->inventory->contains(Item::get(Item::ARROW, 0, 1))) { $this->inventory->sendContents($this); break; } $nbt = new Compound("", ["Pos" => new Enum("Pos", [new Double("", $this->x), new Double("", $this->y + $this->getEyeHeight()), new Double("", $this->z)]), "Motion" => new Enum("Motion", [new Double("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), new Double("", -sin($this->pitch / 180 * M_PI)), new Double("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI))]), "Rotation" => new Enum("Rotation", [new Float("", $this->yaw), new Float("", $this->pitch)]), "Fire" => new Short("Fire", $this->isOnFire() ? 45 * 60 : 0)]); $diff = $this->server->getTick() - $this->startAction; $p = $diff / 20; $f = min(($p ** 2 + $p * 2) / 3, 1) * 2; $ev = new EntityShootBowEvent($this, $bow, Entity::createEntity("Arrow", $this->chunk, $nbt, $this, $f == 2 ? true : false), $f); if ($f < 0.1 or $diff < 5) { $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $ev->getProjectile()->kill(); $this->inventory->sendContents($this); } else { $ev->getProjectile()->setMotion($ev->getProjectile()->getMotion()->multiply($ev->getForce())); if ($this->isSurvival()) { $this->inventory->removeItem(Item::get(Item::ARROW, 0, 1)); $bow->setDamage($bow->getDamage() + 1); if ($bow->getDamage() >= 385) { $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 0)); } else { $this->inventory->setItemInHand($bow); } } if ($ev->getProjectile() instanceof Projectile) { $this->server->getPluginManager()->callEvent($projectileEv = new ProjectileLaunchEvent($ev->getProjectile())); if ($projectileEv->isCancelled()) { $ev->getProjectile()->kill(); } else { $ev->getProjectile()->spawnToAll(); $this->level->addSound(new LaunchSound($this), $this->getViewers()); } } else { $ev->getProjectile()->spawnToAll(); } } } } elseif ($this->inventory->getItemInHand()->getId() === Item::BUCKET and $this->inventory->getItemInHand()->getDamage() === 1) { //Milk! $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $this->inventory->getItemInHand())); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); if ($this->isSurvival()) { $slot = $this->inventory->getItemInHand(); --$slot->count; $this->inventory->setItemInHand($slot); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 1)); } $this->removeAllEffects(); } else { $this->inventory->sendContents($this); } break; case PlayerActionPacket::ACTION_STOP_SLEEPING: $this->stopSleep(); break; case PlayerActionPacket::ACTION_RESPAWN: if ($this->spawned === false or $this->isAlive() or !$this->isOnline()) { break; } if ($this->server->isHardcore()) { $this->setBanned(true); break; } $this->craftingType = 0; $this->server->getPluginManager()->callEvent($ev = new PlayerRespawnEvent($this, $this->getSpawn())); $this->teleport($ev->getRespawnPosition()); $this->setSprinting(false); $this->setSneaking(false); $this->extinguish(); $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); $this->deadTicks = 0; $this->noDamageTicks = 60; $this->setHealth($this->getMaxHealth()); $this->removeAllEffects(); $this->sendData($this); $this->sendSettings(); $this->inventory->sendContents($this); $this->inventory->sendArmorContents($this); $this->blocked = false; $this->spawnToAll(); $this->scheduleUpdate(); break; case PlayerActionPacket::ACTION_START_SPRINT: $ev = new PlayerToggleSprintEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSprinting(true); } break; case PlayerActionPacket::ACTION_STOP_SPRINT: $ev = new PlayerToggleSprintEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSprinting(false); } break; case PlayerActionPacket::ACTION_START_SNEAK: $ev = new PlayerToggleSneakEvent($this, true); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSneaking(true); } break; case PlayerActionPacket::ACTION_STOP_SNEAK: $ev = new PlayerToggleSneakEvent($this, false); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->sendData($this); } else { $this->setSneaking(false); } break; } $this->startAction = -1; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::REMOVE_BLOCK_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $vector = new Vector3($packet->x, $packet->y, $packet->z); if ($this->isCreative()) { $item = $this->inventory->getItemInHand(); } else { $item = $this->inventory->getItemInHand(); } $oldItem = clone $item; if ($this->canInteract($vector->add(0.5, 0.5, 0.5), $this->isCreative() ? 13 : 6) and $this->level->useBreakOn($vector, $item, $this)) { if ($this->isSurvival()) { if (!$item->deepEquals($oldItem) or $item->getCount() !== $oldItem->getCount()) { $this->inventory->setItemInHand($item); $this->inventory->sendHeldItem($this->hasSpawned); } } break; } $this->inventory->sendContents($this); $target = $this->level->getBlock($vector); $tile = $this->level->getTile($vector); $this->level->sendBlocks([$this], [$target], UpdateBlockPacket::FLAG_ALL_PRIORITY); $this->inventory->sendHeldItem($this); if ($tile instanceof Spawnable) { $tile->spawnTo($this); } break; case ProtocolInfo::MOB_ARMOR_EQUIPMENT_PACKET: break; case ProtocolInfo::INTERACT_PACKET: if ($this->spawned === false or !$this->isAlive() or $this->blocked) { break; } $this->craftingType = 0; $target = $this->level->getEntity($packet->target); $cancelled = false; if ($target instanceof Player and $this->server->getConfigBoolean("pvp", true) === false) { $cancelled = true; } if ($target instanceof Entity and $this->getGamemode() !== Player::VIEW and $this->isAlive() and $target->isAlive()) { if ($target instanceof DroppedItem or $target instanceof Arrow) { $this->kick("Attempting to attack an invalid entity"); $this->server->getLogger()->warning($this->getServer()->getLanguage()->translateString("pocketmine.player.invalidEntity", [$this->getName()])); break; } $item = $this->inventory->getItemInHand(); $damageTable = [Item::WOODEN_SWORD => 4, Item::GOLD_SWORD => 4, Item::STONE_SWORD => 5, Item::IRON_SWORD => 6, Item::DIAMOND_SWORD => 7, Item::WOODEN_AXE => 3, Item::GOLD_AXE => 3, Item::STONE_AXE => 3, Item::IRON_AXE => 5, Item::DIAMOND_AXE => 6, Item::WOODEN_PICKAXE => 2, Item::GOLD_PICKAXE => 2, Item::STONE_PICKAXE => 3, Item::IRON_PICKAXE => 4, Item::DIAMOND_PICKAXE => 5, Item::WOODEN_SHOVEL => 1, Item::GOLD_SHOVEL => 1, Item::STONE_SHOVEL => 2, Item::IRON_SHOVEL => 3, Item::DIAMOND_SHOVEL => 4]; $damage = [EntityDamageEvent::MODIFIER_BASE => isset($damageTable[$item->getId()]) ? $damageTable[$item->getId()] : 1]; if (!$this->canInteract($target, 8)) { $cancelled = true; } elseif ($target instanceof Player) { if (($target->getGamemode() & 0x1) > 0) { break; } elseif ($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0) { $cancelled = true; } $armorValues = [Item::LEATHER_CAP => 1, Item::LEATHER_TUNIC => 3, Item::LEATHER_PANTS => 2, Item::LEATHER_BOOTS => 1, Item::CHAIN_HELMET => 1, Item::CHAIN_CHESTPLATE => 5, Item::CHAIN_LEGGINGS => 4, Item::CHAIN_BOOTS => 1, Item::GOLD_HELMET => 1, Item::GOLD_CHESTPLATE => 5, Item::GOLD_LEGGINGS => 3, Item::GOLD_BOOTS => 1, Item::IRON_HELMET => 2, Item::IRON_CHESTPLATE => 6, Item::IRON_LEGGINGS => 5, Item::IRON_BOOTS => 2, Item::DIAMOND_HELMET => 3, Item::DIAMOND_CHESTPLATE => 8, Item::DIAMOND_LEGGINGS => 6, Item::DIAMOND_BOOTS => 3]; $points = 0; foreach ($target->getInventory()->getArmorContents() as $index => $i) { if (isset($armorValues[$i->getId()])) { $points += $armorValues[$i->getId()]; } } $damage[EntityDamageEvent::MODIFIER_ARMOR] = -floor($damage[EntityDamageEvent::MODIFIER_BASE] * $points * 0.04); } $ev = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $damage); if ($cancelled) { $ev->setCancelled(); } $target->attack($ev->getFinalDamage(), $ev); if ($ev->isCancelled()) { if ($item->isTool() and $this->isSurvival()) { $this->inventory->sendContents($this); } break; } if ($item->isTool() and $this->isSurvival()) { if ($item->useOn($target) and $item->getDamage() >= $item->getMaxDurability()) { $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); } else { $this->inventory->setItemInHand($item); } } } break; case ProtocolInfo::ANIMATE_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } $this->server->getPluginManager()->callEvent($ev = new PlayerAnimationEvent($this, $packet->action)); if ($ev->isCancelled()) { break; } $pk = new AnimatePacket(); $pk->eid = $this->getId(); $pk->action = $ev->getAnimationType(); Server::broadcastPacket($this->getViewers(), $pk); break; case ProtocolInfo::SET_HEALTH_PACKET: //Not used break; case ProtocolInfo::ENTITY_EVENT_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); //TODO: check if this should be true switch ($packet->event) { case 9: //Eating $items = [Item::APPLE => 4, Item::MUSHROOM_STEW => 10, Item::BEETROOT_SOUP => 10, Item::BREAD => 5, Item::RAW_PORKCHOP => 3, Item::COOKED_PORKCHOP => 8, Item::RAW_BEEF => 3, Item::STEAK => 8, Item::COOKED_CHICKEN => 6, Item::RAW_CHICKEN => 2, Item::MELON_SLICE => 2, Item::GOLDEN_APPLE => 10, Item::PUMPKIN_PIE => 8, Item::CARROT => 4, Item::POTATO => 1, Item::BAKED_POTATO => 6, Item::COOKIE => 2, Item::COOKED_FISH => 5, Item::COOKED_SALMON => 6, Item::RAW_FISH => 2, Item::RAW_SALMON => 2, Item::CLOWN_FISH => 1, Item::PUFFER_FISH => 1]; $slot = $this->inventory->getItemInHand(); if ($this->getHealth() < $this->getMaxHealth() and isset($items[$slot->getId()])) { $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $slot)); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $pk = new EntityEventPacket(); $pk->eid = $this->getId(); $pk->event = EntityEventPacket::USE_ITEM; $this->dataPacket($pk); Server::broadcastPacket($this->getViewers(), $pk); $amount = $items[$slot->getId()]; $ev = new EntityRegainHealthEvent($this, $amount, EntityRegainHealthEvent::CAUSE_EATING); $this->heal($ev->getAmount(), $ev); --$slot->count; $this->inventory->setItemInHand($slot); if ($slot->getId() === Item::MUSHROOM_STEW or $slot->getId() === Item::BEETROOT_SOUP) { $this->inventory->addItem(Item::get(Item::BOWL, 0, 1)); } elseif ($slot->getId() === Item::PUFFER_FISH) { //Pufferfish //$this->addEffect(Effect::getEffect(Effect::HUNGER)->setAmplifier(2)->setDuration(15 * 20)); $this->addEffect(Effect::getEffect(Effect::NAUSEA)->setAmplifier(1)->setDuration(15 * 20)); $this->addEffect(Effect::getEffect(Effect::POISON)->setAmplifier(3)->setDuration(60 * 20)); } } break; } break; case ProtocolInfo::DROP_ITEM_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $item = $this->inventory->getItemInHand(); $ev = new PlayerDropItemEvent($this, $item); $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } $this->inventory->setItemInHand(Item::get(Item::AIR, 0, 1)); $motion = $this->getDirectionVector()->multiply(0.4); $this->level->dropItem($this->add(0, 1.3, 0), $item, $motion, 40); $this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ACTION, false); break; case ProtocolInfo::TEXT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } $this->craftingType = 0; if ($packet->type === TextPacket::TYPE_CHAT) { $packet->message = TextFormat::clean($packet->message, $this->removeFormat); foreach (explode("\n", $packet->message) as $message) { if (trim($message) != "" and strlen($message) <= 255 and $this->messageCounter-- > 0) { $ev = new PlayerCommandPreprocessEvent($this, $message); if (mb_strlen($ev->getMessage(), "UTF-8") > 320) { $ev->setCancelled(); } $this->server->getPluginManager()->callEvent($ev); if ($ev->isCancelled()) { break; } if (substr($ev->getMessage(), 0, 1) === "/") { //Command Timings::$playerCommandTimer->startTiming(); $this->server->dispatchCommand($ev->getPlayer(), substr($ev->getMessage(), 1)); Timings::$playerCommandTimer->stopTiming(); } else { $this->server->getPluginManager()->callEvent($ev = new PlayerChatEvent($this, $ev->getMessage())); if (!$ev->isCancelled()) { $this->server->broadcastMessage($this->getServer()->getLanguage()->translateString($ev->getFormat(), [$ev->getPlayer()->getDisplayName(), $ev->getMessage()]), $ev->getRecipients()); } } } } } break; case ProtocolInfo::CONTAINER_CLOSE_PACKET: if ($this->spawned === false or $packet->windowid === 0) { break; } $this->craftingType = 0; $this->currentTransaction = null; if (isset($this->windowIndex[$packet->windowid])) { $this->server->getPluginManager()->callEvent(new InventoryCloseEvent($this->windowIndex[$packet->windowid], $this)); $this->removeWindow($this->windowIndex[$packet->windowid]); } else { unset($this->windowIndex[$packet->windowid]); } break; case ProtocolInfo::CRAFTING_EVENT_PACKET: if ($this->spawned === false or !$this->isAlive()) { break; } elseif (!isset($this->windowIndex[$packet->windowId])) { $this->inventory->sendContents($this); $pk = new ContainerClosePacket(); $pk->windowid = $packet->windowId; $this->dataPacket($pk); break; } $recipe = $this->server->getCraftingManager()->getRecipe($packet->id); if ($recipe === null or ($recipe instanceof BigShapelessRecipe or $recipe instanceof BigShapedRecipe) and $this->craftingType === 0) { $this->inventory->sendContents($this); break; } foreach ($packet->input as $i => $item) { if ($item->getDamage() === -1 or $item->getDamage() === 0xffff) { $item->setDamage(null); } if ($i < 9 and $item->getId() > 0) { $item->setCount(1); } } $canCraft = true; if ($recipe instanceof ShapedRecipe) { for ($x = 0; $x < 3 and $canCraft; ++$x) { for ($y = 0; $y < 3; ++$y) { $item = $packet->input[$y * 3 + $x]; $ingredient = $recipe->getIngredient($x, $y); if ($item->getCount() > 0) { if ($ingredient === null or !$ingredient->deepEquals($item, $ingredient->getDamage() !== null, $ingredient->getCompoundTag() !== null)) { $canCraft = false; break; } } } } } elseif ($recipe instanceof ShapelessRecipe) { $needed = $recipe->getIngredientList(); for ($x = 0; $x < 3 and $canCraft; ++$x) { for ($y = 0; $y < 3; ++$y) { $item = clone $packet->input[$y * 3 + $x]; foreach ($needed as $k => $n) { if ($n->deepEquals($item, $n->getDamage() !== null, $n->getCompoundTag() !== null)) { $remove = min($n->getCount(), $item->getCount()); $n->setCount($n->getCount() - $remove); $item->setCount($item->getCount() - $remove); if ($n->getCount() === 0) { unset($needed[$k]); } } } if ($item->getCount() > 0) { $canCraft = false; break; } } } if (count($needed) > 0) { $canCraft = false; } } else { $canCraft = false; } /** @var Item[] $ingredients */ $ingredients = $packet->input; $result = $packet->output[0]; if (!$canCraft or !$recipe->getResult()->deepEquals($result)) { $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": expected " . $recipe->getResult() . ", got " . $result . ", using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $used = array_fill(0, $this->inventory->getSize(), 0); foreach ($ingredients as $ingredient) { $slot = -1; foreach ($this->inventory->getContents() as $index => $i) { if ($ingredient->getId() !== 0 and $ingredient->deepEquals($i, $i->getDamage() !== null) and $i->getCount() - $used[$index] >= 1) { $slot = $index; $used[$index]++; break; } } if ($ingredient->getId() !== 0 and $slot === -1) { $canCraft = false; break; } } if (!$canCraft) { $this->server->getLogger()->debug("Unmatched recipe " . $recipe->getId() . " from player " . $this->getName() . ": client does not have enough items, using: " . implode(", ", $ingredients)); $this->inventory->sendContents($this); break; } $this->server->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $ingredients, $recipe)); if ($ev->isCancelled()) { $this->inventory->sendContents($this); break; } foreach ($used as $slot => $count) { if ($count === 0) { continue; } $item = $this->inventory->getItem($slot); if ($item->getCount() > $count) { $newItem = clone $item; $newItem->setCount($item->getCount() - $count); } else { $newItem = Item::get(Item::AIR, 0, 0); } $this->inventory->setItem($slot, $newItem); } $extraItem = $this->inventory->addItem($recipe->getResult()); if (count($extraItem) > 0) { foreach ($extraItem as $item) { $this->level->dropItem($this, $item); } } switch ($recipe->getResult()->getId()) { case Item::WORKBENCH: $this->awardAchievement("buildWorkBench"); break; case Item::WOODEN_PICKAXE: $this->awardAchievement("buildPickaxe"); break; case Item::FURNACE: $this->awardAchievement("buildFurnace"); break; case Item::WOODEN_HOE: $this->awardAchievement("buildHoe"); break; case Item::BREAD: $this->awardAchievement("makeBread"); break; case Item::CAKE: //TODO: detect complex recipes like cake that leave remains $this->awardAchievement("bakeCake"); $this->inventory->addItem(Item::get(Item::BUCKET, 0, 3)); break; case Item::STONE_PICKAXE: case Item::GOLD_PICKAXE: case Item::IRON_PICKAXE: case Item::DIAMOND_PICKAXE: $this->awardAchievement("buildBetterPickaxe"); break; case Item::WOODEN_SWORD: $this->awardAchievement("buildSword"); break; case Item::DIAMOND: $this->awardAchievement("diamond"); break; } break; case ProtocolInfo::CONTAINER_SET_SLOT_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } if ($packet->slot < 0) { break; } if ($packet->windowid === 0) { //Our inventory if ($packet->slot >= $this->inventory->getSize()) { break; } if ($this->isCreative()) { if (Item::getCreativeItemIndex($packet->item) !== -1) { $this->inventory->setItem($packet->slot, $packet->item); $this->inventory->setHotbarSlotIndex($packet->slot, $packet->slot); //links $hotbar[$packet->slot] to $slots[$packet->slot] } } $transaction = new BaseTransaction($this->inventory, $packet->slot, $this->inventory->getItem($packet->slot), $packet->item); } elseif ($packet->windowid === ContainerSetContentPacket::SPECIAL_ARMOR) { //Our armor if ($packet->slot >= 4) { break; } $transaction = new BaseTransaction($this->inventory, $packet->slot + $this->inventory->getSize(), $this->inventory->getArmorItem($packet->slot), $packet->item); } elseif (isset($this->windowIndex[$packet->windowid])) { $this->craftingType = 0; $inv = $this->windowIndex[$packet->windowid]; $transaction = new BaseTransaction($inv, $packet->slot, $inv->getItem($packet->slot), $packet->item); } else { break; } if ($transaction->getSourceItem()->deepEquals($transaction->getTargetItem()) and $transaction->getTargetItem()->getCount() === $transaction->getSourceItem()->getCount()) { //No changes! //No changes, just a local inventory update sent by the server break; } if ($this->currentTransaction === null or $this->currentTransaction->getCreationTime() < microtime(true) - 8) { if ($this->currentTransaction !== null) { foreach ($this->currentTransaction->getInventories() as $inventory) { if ($inventory instanceof PlayerInventory) { $inventory->sendArmorContents($this); } $inventory->sendContents($this); } } $this->currentTransaction = new SimpleTransactionGroup($this); } $this->currentTransaction->addTransaction($transaction); if ($this->currentTransaction->canExecute()) { $achievements = []; foreach ($this->currentTransaction->getTransactions() as $ts) { $inv = $ts->getInventory(); if ($inv instanceof FurnaceInventory) { if ($ts->getSlot() === 2) { switch ($inv->getResult()->getId()) { case Item::IRON_INGOT: $achievements[] = "acquireIron"; break; } } } } if ($this->currentTransaction->execute()) { foreach ($achievements as $a) { $this->awardAchievement($a); } } $this->currentTransaction = null; } break; case ProtocolInfo::BLOCK_ENTITY_DATA_PACKET: if ($this->spawned === false or $this->blocked === true or !$this->isAlive()) { break; } $this->craftingType = 0; $pos = new Vector3($packet->x, $packet->y, $packet->z); if ($pos->distanceSquared($this) > 10000) { break; } $t = $this->level->getTile($pos); if ($t instanceof Sign) { $nbt = new NBT(NBT::LITTLE_ENDIAN); $nbt->read($packet->namedtag); $nbt = $nbt->getData(); if ($nbt["id"] !== Tile::SIGN) { $t->spawnTo($this); } else { $ev = new SignChangeEvent($t->getBlock(), $this, [TextFormat::clean($nbt["Text1"], $this->removeFormat), TextFormat::clean($nbt["Text2"], $this->removeFormat), TextFormat::clean($nbt["Text3"], $this->removeFormat), TextFormat::clean($nbt["Text4"], $this->removeFormat)]); if (!isset($t->namedtag->Creator) or $t->namedtag["Creator"] !== $this->getRawUniqueId()) { $ev->setCancelled(); } else { foreach ($ev->getLines() as $line) { if (mb_strlen($line, "UTF-8") > 16) { $ev->setCancelled(); } } } $this->server->getPluginManager()->callEvent($ev); if (!$ev->isCancelled()) { $t->setText($ev->getLine(0), $ev->getLine(1), $ev->getLine(2), $ev->getLine(3)); } else { $t->spawnTo($this); } } } break; default: break; } $timings->stopTiming(); }