-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix for "block lag" while placing blocks on higher latency clients #6803
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: stable
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2238,9 +2238,10 @@ private function destroyBlockInternal(Block $target, Item $item, ?Player $player | |
| * @param bool $playSound Whether to play a block-place sound if the block was placed successfully. | ||
| * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped if the inventory is full) | ||
| */ | ||
| public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false, array &$returnedItems = []) : bool{ | ||
| $blockClicked = $this->getBlock($vector); | ||
| public function useItemOn(Vector3 $clickedBlockPos, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false, array &$returnedItems = []) : bool{ | ||
| $blockClicked = $this->getBlock($clickedBlockPos); | ||
| $blockReplace = $blockClicked->getSide($face); | ||
| $mainBlocks = [$clickedBlockPos, ($sideClickPos = $clickedBlockPos->getSide($face))]; | ||
|
|
||
| if($clickVector === null){ | ||
| $clickVector = new Vector3(0.0, 0.0, 0.0); | ||
|
|
@@ -2254,15 +2255,18 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
|
|
||
| if(!$this->isInWorld($blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z)){ | ||
| //TODO: build height limit messages for custom world heights and mcregion cap | ||
| $player?->syncBlocks($mainBlocks); | ||
| return false; | ||
| } | ||
| $chunkX = $blockReplace->getPosition()->getFloorX() >> Chunk::COORD_BIT_SIZE; | ||
| $chunkZ = $blockReplace->getPosition()->getFloorZ() >> Chunk::COORD_BIT_SIZE; | ||
| if(!$this->isChunkLoaded($chunkX, $chunkZ) || $this->isChunkLocked($chunkX, $chunkZ)){ | ||
| $player?->syncBlocks($mainBlocks); | ||
| return false; | ||
| } | ||
|
|
||
| if($blockClicked->getTypeId() === BlockTypeIds::AIR){ | ||
| $player?->syncBlocks($mainBlocks); | ||
| return false; | ||
| } | ||
|
|
||
|
|
@@ -2279,6 +2283,18 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
| $ev->call(); | ||
| if(!$ev->isCancelled()){ | ||
| if($ev->useBlock() && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ | ||
| $aroundBlocks = []; | ||
| foreach ($clickedBlockPos->sidesArray() as $otherVector) { | ||
| if ($sideClickPos !== $otherVector) { | ||
| $aroundBlocks[] = $otherVector; | ||
| } | ||
| } | ||
| foreach ($sideClickPos->sidesArray() as $otherVector) { | ||
| if ($clickedBlockPos !== $otherVector) { | ||
| $aroundBlocks[] = $otherVector; | ||
| } | ||
| } | ||
| $player->syncBlocks($aroundBlocks); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the block interaction was disabled, then the client would wrongly see a door being openable when it's actually cancelled on the server side.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it should sync doors properly here, as it is also sending the blocks around the clicked block's position sides, similar to how
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a plugin does |
||
| return true; | ||
| } | ||
|
|
||
|
|
@@ -2289,13 +2305,15 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
| } | ||
| } | ||
| }else{ | ||
| $player->syncBlocks($mainBlocks); | ||
| return false; | ||
| } | ||
| }elseif($blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ | ||
| return true; | ||
| } | ||
|
|
||
| if($item->isNull() || !$item->canBePlaced()){ | ||
| $player?->syncBlocks($mainBlocks); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't remember too well how this code works. But I'm pretty sure it's possible for the client & server to disagree about the currently held item. So the client might think it's able to place a door while the server doesn't in network races. |
||
| return false; | ||
| } | ||
|
|
||
|
|
@@ -2307,24 +2325,43 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
| $item->getPlacementTransaction($blockReplace, $blockClicked, $face, $clickVector, $player); | ||
| if($tx === null){ | ||
| //no placement options available | ||
| $player?->syncBlocks($mainBlocks); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same again here - the client may think the placement will succeed if it doesn't have an up to date copy of the terrain |
||
| return false; | ||
| } | ||
|
|
||
| foreach($tx->getBlocks() as [$x, $y, $z, $block]){ | ||
| /** @var Vector3[] $placementSyncBlocks */ | ||
| $placementSyncBlocks = []; | ||
| $allowed = true; | ||
| $needsSync = false; | ||
| foreach($tx->getBlocks() as [$x, $y, $z, $block]) { | ||
| $blockPos = new Vector3($x, $y, $z); | ||
| $placementSyncBlocks[] = $blockPos; | ||
| $block->position($this, $x, $y, $z); | ||
| foreach($block->getCollisionBoxes() as $collisionBox){ | ||
| if(count($this->getCollidingEntities($collisionBox)) > 0){ | ||
| return false; //Entity in block | ||
| foreach($block->getCollisionBoxes() as $collisionBox) { | ||
| $playerCollision = false; | ||
| if ($player !== null && $player->getBoundingBox()->intersectsWith($collisionBox)) { | ||
| $allowed = false; | ||
| $playerCollision = true; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was a solution I didn't want to go for too, because it's possible the server and client may have different ideas about the block's bounding box (or the player's own). I think this is the workaround most people went for, but I'd still be happier if there was a better way to do it. |
||
| if (count($this->getCollidingEntities($collisionBox)) > 0) { | ||
| $allowed = false; | ||
| $needsSync = $needsSync || !$playerCollision; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (!$allowed) { | ||
| if ($needsSync) { | ||
| $player?->syncBlocks($placementSyncBlocks); | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| if($player !== null){ | ||
| $ev = new BlockPlaceEvent($player, $tx, $blockClicked, $item); | ||
| if($player->isSpectator()){ | ||
| $ev->cancel(); | ||
| } | ||
|
|
||
| if($player->isAdventure(true) && !$ev->isCancelled()){ | ||
| $canPlace = false; | ||
| $itemParser = LegacyStringToItemParser::getInstance(); | ||
|
|
@@ -2343,11 +2380,13 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
|
|
||
| $ev->call(); | ||
| if($ev->isCancelled()){ | ||
| $player->syncBlocks($placementSyncBlocks); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| if(!$tx->apply()){ | ||
| $player?->syncBlocks($placementSyncBlocks); | ||
| return false; | ||
| } | ||
| $first = true; | ||
|
|
@@ -2367,7 +2406,8 @@ public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $cli | |
| } | ||
|
|
||
| $item->pop(); | ||
|
|
||
| // As long as we are only syncing the relevant blocks to the actual placement, this should be fine. | ||
| $player?->syncBlocks($placementSyncBlocks); | ||
| return true; | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is BC breaking