Created
July 30, 2020 01:53
-
-
Save PJZ9n/b0218bbe854c277d6f6bd7589a681059 to your computer and use it in GitHub Desktop.
クライアントによるスパムバグを防ぐ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @name AntiSpamBug | |
* @version 1.0.0 | |
* @main pjz9n\antispambug\AntiSpamBug | |
* @api 3.11.0 | |
*/ | |
declare(strict_types=1); | |
namespace pjz9n\antispambug; | |
use pocketmine\block\Block; | |
use pocketmine\entity\Entity; | |
use pocketmine\event\block\BlockPlaceEvent; | |
use pocketmine\event\Listener; | |
use pocketmine\event\player\PlayerInteractEvent; | |
use pocketmine\event\server\DataPacketReceiveEvent; | |
use pocketmine\item\Item; | |
use pocketmine\item\ItemFactory; | |
use pocketmine\math\Vector3; | |
use pocketmine\nbt\tag\ListTag; | |
use pocketmine\nbt\tag\StringTag; | |
use pocketmine\network\mcpe\protocol\InventoryTransactionPacket; | |
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket; | |
use pocketmine\network\mcpe\protocol\UpdateBlockPacket; | |
use pocketmine\Player; | |
use pocketmine\plugin\PluginBase; | |
use ReflectionException; | |
use ReflectionObject; | |
class AntiSpamBug extends PluginBase implements Listener | |
{ | |
public function onEnable(): void | |
{ | |
$this->getServer()->getPluginManager()->registerEvents($this, $this); | |
} | |
public function interact(PlayerInteractEvent $event): void | |
{ | |
if ($event->getAction() !== PlayerInteractEvent::RIGHT_CLICK_BLOCK) return; | |
$event->getPlayer()->sendMessage("Interact! " . uniqid()); | |
} | |
/** @var Vector3[] */ | |
private $lastRightClickPos = []; | |
/** @var int[]|float[] */ | |
private $lastRightClickTime = []; | |
/** | |
* @param DataPacketReceiveEvent $event | |
* | |
* @throws ReflectionException | |
*/ | |
public function onDataPacketReceive(DataPacketReceiveEvent $event): void | |
{ | |
$packet = $event->getPacket(); | |
$player = $event->getPlayer(); | |
$playerName = $player->getName(); | |
if (!isset($this->lastRightClickPos[$playerName], $this->lastRightClickTime[$playerName])) { | |
$this->lastRightClickPos[$playerName] = null; | |
$this->lastRightClickTime[$playerName] = 0.0; | |
} | |
if (!($packet instanceof InventoryTransactionPacket)) return; | |
if (!$player->spawned || !$player->isAlive()) { | |
return; | |
} | |
if ($packet->transactionType === InventoryTransactionPacket::TYPE_USE_ITEM && | |
$packet->trData->actionType === InventoryTransactionPacket::USE_ITEM_ACTION_CLICK_BLOCK) { | |
$event->setCancelled(); | |
$blockVector = new Vector3($packet->trData->x, $packet->trData->y, $packet->trData->z); | |
$face = $packet->trData->face; | |
//TODO: start hack for client spam bug | |
$spamBug = ($this->lastRightClickPos[$playerName] !== null && | |
microtime(true) - $this->lastRightClickTime[$playerName] < 0.1 && | |
$this->lastRightClickPos[$playerName]->distanceSquared($packet->trData->clickPos) < 0.00001 | |
); | |
$this->lastRightClickPos[$playerName] = clone $packet->trData->clickPos; | |
$this->lastRightClickTime[$playerName] = microtime(true); | |
if ($spamBug) { | |
return; | |
} | |
//TODO: end hack for client spam bug | |
$player->setUsingItem(false); | |
if ($player->canInteract($blockVector->add(0.5, 0.5, 0.5), 13)) { | |
if ($player->isCreative()) { | |
$item = $player->getInventory()->getItemInHand(); | |
if ($this->useItemOnWrapper($blockVector, $item, $face, $packet->trData->clickPos, $player, true)) { | |
return; | |
} | |
} else if (!$player->getInventory()->getItemInHand()->equals($packet->trData->itemInHand)) { | |
$player->getInventory()->sendHeldItem($player); | |
} else { | |
$item = $player->getInventory()->getItemInHand(); | |
$oldItem = clone $item; | |
if ($this->useItemOnWrapper($blockVector, $item, $face, $packet->trData->clickPos, $player, true)) { | |
if (!$item->equalsExact($oldItem) && $oldItem->equalsExact($player->getInventory()->getItemInHand())) { | |
$player->getInventory()->setItemInHand($item); | |
$player->getInventory()->sendHeldItem($this->hasSpawned($player)); | |
} | |
return; | |
} | |
} | |
} | |
$player->getInventory()->sendHeldItem($player); | |
if ($blockVector->distanceSquared($player) > 10000) { | |
return; | |
} | |
$target = $player->getLevel()->getBlock($blockVector); | |
$block = $target->getSide($face); | |
/** @var Block[] $blocks */ | |
$blocks = array_merge($target->getAllSides(), $block->getAllSides()); | |
$player->getLevel()->sendBlocks([$player], $blocks, UpdateBlockPacket::FLAG_ALL_PRIORITY); | |
} | |
} | |
private $lastTime = []; | |
private function useItemOnWrapper(Vector3 $vector, Item &$item, int $face, Vector3 $clickVector = null, Player $player = null, bool $playSound = false): bool | |
{ | |
$playerName = $player->getName(); | |
if (!isset($this->lastTime[$playerName])) { | |
$this->lastTime[$playerName] = 0; | |
} | |
$level = $player->getLevel();//TODO: $playerはnullable | |
$blockClicked = $level->getBlock($vector); | |
$blockReplace = $blockClicked->getSide($face); | |
if ($clickVector === null) { | |
$clickVector = new Vector3(0.0, 0.0, 0.0); | |
} | |
if (!$level->isInWorld($blockReplace->x, $blockReplace->y, $blockReplace->z)) { | |
return false; | |
} | |
if ($blockClicked->getId() === Block::AIR) { | |
return false; | |
} | |
if (microtime(true) - $this->lastTime[$playerName] > 0.1/*100ms*/) { | |
$this->lastTime[$playerName] = microtime(true); | |
if ($player !== null) { | |
$ev = new PlayerInteractEvent($player, $item, $blockClicked, $clickVector, $face, PlayerInteractEvent::RIGHT_CLICK_BLOCK); | |
if ($level->checkSpawnProtection($player, $blockClicked) || $player->isSpectator()) { | |
$ev->setCancelled(); | |
} | |
$ev->call(); | |
if (!$ev->isCancelled()) { | |
if ((!$player->isSneaking() || $item->isNull()) && $blockClicked->onActivate($item, $player)) { | |
return true; | |
} | |
if ($item->onActivate($player, $blockReplace, $blockClicked, $face, $clickVector)) { | |
return true; | |
} | |
} else { | |
return false; | |
} | |
} else if ($blockClicked->onActivate($item, $player)) { | |
return true; | |
} | |
} | |
if ($item->canBePlaced()) { | |
$hand = $item->getBlock(); | |
$hand->position($blockReplace); | |
} else { | |
return false; | |
} | |
if ($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)) { | |
$blockReplace = $blockClicked; | |
$hand->position($blockReplace); | |
} else if (!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)) { | |
return false; | |
} | |
if ($hand->isSolid()) { | |
foreach ($hand->getCollisionBoxes() as $collisionBox) { | |
if (count($level->getCollidingEntities($collisionBox)) > 0) { | |
return false; | |
} | |
} | |
} | |
if ($player !== null) { | |
$ev = new BlockPlaceEvent($player, $hand, $blockReplace, $blockClicked, $item); | |
if ($level->checkSpawnProtection($player, $blockReplace) || $player->isSpectator()) { | |
$ev->setCancelled(); | |
} | |
if ($player->isAdventure(true) && !$ev->isCancelled()) { | |
$canPlace = false; | |
$tag = $item->getNamedTagEntry("CanPlaceOn"); | |
if ($tag instanceof ListTag) { | |
foreach ($tag as $v) { | |
if ($v instanceof StringTag) { | |
$entry = ItemFactory::fromStringSingle($v->getValue()); | |
if ($entry->getId() > 0 && $entry->getBlock()->getId() === $blockClicked->getId()) { | |
$canPlace = true; | |
break; | |
} | |
} | |
} | |
} | |
$ev->setCancelled(!$canPlace); | |
} | |
$ev->call(); | |
if ($ev->isCancelled()) { | |
return false; | |
} | |
} | |
if (!$hand->place($item, $blockReplace, $blockClicked, $face, $clickVector, $player)) { | |
return false; | |
} | |
if ($playSound) { | |
$level->broadcastLevelSoundEvent($hand, LevelSoundEventPacket::SOUND_PLACE, $hand->getRuntimeId()); | |
} | |
$item->pop(); | |
return true; | |
} | |
/** | |
* @param Entity $entity | |
* | |
* @return Player[] | |
* @throws ReflectionException | |
*/ | |
private function hasSpawned(Entity $entity): array | |
{ | |
$reflection = new ReflectionObject($entity); | |
$hasSpawnedProperty = $reflection->getProperty("hasSpawned"); | |
$hasSpawnedProperty->setAccessible(true); | |
$hasSpawned = $hasSpawnedProperty->getValue($entity); | |
return $hasSpawned; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment