Skip to content

Instantly share code, notes, and snippets.

@PJZ9n
Created July 30, 2020 01:53
Show Gist options
  • Save PJZ9n/b0218bbe854c277d6f6bd7589a681059 to your computer and use it in GitHub Desktop.
Save PJZ9n/b0218bbe854c277d6f6bd7589a681059 to your computer and use it in GitHub Desktop.
クライアントによるスパムバグを防ぐ
<?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