mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-04-13 09:24:44 +02:00
Use UpdateSubChunkBlocksPacket
This commit is contained in:
parent
7801e357fb
commit
02b2ed1db4
2 changed files with 178 additions and 1 deletions
core/src/main/java/org/geysermc/geyser
|
@ -168,6 +168,12 @@ public final class WorldCache {
|
|||
ChunkUtils.updateBlock(session, blockState, position);
|
||||
}
|
||||
|
||||
public void removePrediction(Vector3i position) {
|
||||
if (!this.unverifiedPredictions.isEmpty()) {
|
||||
this.unverifiedPredictions.removeInt(position);
|
||||
}
|
||||
}
|
||||
|
||||
public void endPredictionsUpTo(int sequence) {
|
||||
if (this.unverifiedPredictions.isEmpty()) {
|
||||
return;
|
||||
|
|
|
@ -25,19 +25,190 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java.level;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateSubChunkBlocksPacket;
|
||||
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.cache.SkullCache;
|
||||
import org.geysermc.geyser.translator.level.block.entity.BedrockOnlyBlockEntity;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockChangeEntry;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundSectionBlocksUpdatePacket;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import static org.geysermc.geyser.level.block.BlockStateValues.JAVA_AIR_ID;
|
||||
|
||||
@Translator(packet = ClientboundSectionBlocksUpdatePacket.class)
|
||||
public class JavaSectionBlocksUpdateTranslator extends PacketTranslator<ClientboundSectionBlocksUpdatePacket> {
|
||||
private static final int NEIGHBORS_NETWORK_FLAG = (1 << UpdateBlockPacket.Flag.NEIGHBORS.ordinal()) | (1 << UpdateBlockPacket.Flag.NETWORK.ordinal());
|
||||
private static final int NETWORK_FLAG = (1 << UpdateBlockPacket.Flag.NETWORK.ordinal());
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundSectionBlocksUpdatePacket packet) {
|
||||
// Send normal block updates if not many changes
|
||||
if (packet.getEntries().length < 32) {
|
||||
for (BlockChangeEntry entry : packet.getEntries()) {
|
||||
session.getWorldCache().updateServerCorrectBlockState(entry.getPosition(), entry.getBlock());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateSubChunkBlocksPacket subChunkBlocksPacket = new UpdateSubChunkBlocksPacket();
|
||||
subChunkBlocksPacket.setChunkX(packet.getChunkX());
|
||||
subChunkBlocksPacket.setChunkY(packet.getChunkY());
|
||||
subChunkBlocksPacket.setChunkZ(packet.getChunkZ());
|
||||
|
||||
// If the entire section is updated, this might be a legacy non-full chunk update
|
||||
// which can contain thousands of unchanged blocks
|
||||
if (packet.getEntries().length == 4096) {
|
||||
// hack - bedrock might ignore the block updates if the chunk was still loading.
|
||||
// sending an UpdateBlockPacket seems to force it
|
||||
BlockChangeEntry firstEntry = packet.getEntries()[0];
|
||||
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
|
||||
blockPacket.setBlockPosition(firstEntry.getPosition());
|
||||
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(firstEntry.getBlock()));
|
||||
blockPacket.setDataLayer(0);
|
||||
session.sendUpstreamPacket(blockPacket);
|
||||
|
||||
// Filter out unchanged blocks
|
||||
Vector3i offset = Vector3i.from(packet.getChunkX() << 4, packet.getChunkY() << 4, packet.getChunkZ() << 4);
|
||||
BlockPositionIterator blockIter = BlockPositionIterator.fromMinMax(
|
||||
offset.getX(), offset.getY(), offset.getZ(),
|
||||
offset.getX() + 15, offset.getY() + 15, offset.getZ() + 15
|
||||
);
|
||||
|
||||
int[] sectionBlocks = session.getGeyser().getWorldManager().getBlocksAt(session, blockIter);
|
||||
BitSet waterlogged = BlockRegistries.WATERLOGGED.get();
|
||||
for (BlockChangeEntry entry : packet.getEntries()) {
|
||||
Vector3i pos = entry.getPosition().sub(offset);
|
||||
int index = pos.getZ() + pos.getX() * 16 + pos.getY() * 256;
|
||||
int oldBlockState = sectionBlocks[index];
|
||||
if (oldBlockState != entry.getBlock()) {
|
||||
// Avoid sending unnecessary waterlogged updates
|
||||
boolean updateWaterlogged = waterlogged.get(oldBlockState) != waterlogged.get(entry.getBlock());
|
||||
applyEntry(session, entry, subChunkBlocksPacket, updateWaterlogged);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (BlockChangeEntry entry : packet.getEntries()) {
|
||||
applyEntry(session, entry, subChunkBlocksPacket, true);
|
||||
}
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(subChunkBlocksPacket);
|
||||
|
||||
// Post block update
|
||||
for (BlockChangeEntry entry : packet.getEntries()) {
|
||||
session.getWorldCache().updateServerCorrectBlockState(entry.getPosition(), entry.getBlock());
|
||||
session.getWorldCache().removePrediction(entry.getPosition());
|
||||
BlockStateValues.getLecternBookStates().handleBlockChange(session, entry.getBlock(), entry.getPosition());
|
||||
|
||||
// Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet
|
||||
// needs to be sent
|
||||
for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityUtils.BEDROCK_ONLY_BLOCK_ENTITIES) {
|
||||
if (bedrockOnlyBlockEntity.isBlock(entry.getBlock())) {
|
||||
// Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks
|
||||
bedrockOnlyBlockEntity.updateBlock(session, entry.getBlock(), entry.getPosition());
|
||||
break; //No block will be a part of two classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modified version of ChunkUtils#updateBlockClientSide
|
||||
private static void applyEntry(GeyserSession session, BlockChangeEntry entry, UpdateSubChunkBlocksPacket subChunkBlocksPacket, boolean updateWaterlogged) {
|
||||
Vector3i position = entry.getPosition();
|
||||
int blockState = entry.getBlock();
|
||||
|
||||
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
||||
|
||||
// Checks for item frames so they aren't tripped up and removed
|
||||
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
||||
if (itemFrameEntity != null) {
|
||||
if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it
|
||||
itemFrameEntity.updateBlock(true);
|
||||
// Still update the chunk cache with the new block if updateBlock is called
|
||||
return;
|
||||
}
|
||||
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
||||
}
|
||||
|
||||
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(blockState);
|
||||
|
||||
int skullVariant = BlockStateValues.getSkullVariant(blockState);
|
||||
if (skullVariant == -1) {
|
||||
// Skull is gone
|
||||
session.getSkullCache().removeSkull(position);
|
||||
} else if (skullVariant == 3) {
|
||||
// The changed block was a player skull so check if a custom block was defined for this skull
|
||||
SkullCache.Skull skull = session.getSkullCache().updateSkull(position, blockState);
|
||||
if (skull != null && skull.getBlockDefinition() != null) {
|
||||
definition = skull.getBlockDefinition();
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent moving_piston from being placed
|
||||
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
|
||||
if (!BlockStateValues.isMovingPiston(blockState)) {
|
||||
subChunkBlocksPacket.getStandardBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
|
||||
position,
|
||||
definition,
|
||||
NEIGHBORS_NETWORK_FLAG,
|
||||
-1,
|
||||
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
|
||||
));
|
||||
|
||||
if (updateWaterlogged) {
|
||||
BlockDefinition waterDefinition = BlockRegistries.WATERLOGGED.get().get(blockState) ?
|
||||
session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir();
|
||||
subChunkBlocksPacket.getExtraBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
|
||||
position,
|
||||
waterDefinition,
|
||||
0,
|
||||
-1,
|
||||
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Extended collision boxes for custom blocks
|
||||
if (!session.getBlockMappings().getExtendedCollisionBoxes().isEmpty()) {
|
||||
int aboveBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() + 1, position.getZ());
|
||||
BlockDefinition aboveBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(blockState);
|
||||
int belowBlock = session.getGeyser().getWorldManager().getBlockAt(session, position.getX(), position.getY() - 1, position.getZ());
|
||||
BlockDefinition belowBedrockExtendedCollisionDefinition = session.getBlockMappings().getExtendedCollisionBoxes().get(belowBlock);
|
||||
if (belowBedrockExtendedCollisionDefinition != null && blockState == BlockStateValues.JAVA_AIR_ID) {
|
||||
subChunkBlocksPacket.getStandardBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
|
||||
position,
|
||||
belowBedrockExtendedCollisionDefinition,
|
||||
NETWORK_FLAG,
|
||||
-1,
|
||||
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
|
||||
));
|
||||
} else if (aboveBedrockExtendedCollisionDefinition != null && aboveBlock == BlockStateValues.JAVA_AIR_ID) {
|
||||
subChunkBlocksPacket.getStandardBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
|
||||
position.add(0, 1, 0),
|
||||
aboveBedrockExtendedCollisionDefinition,
|
||||
NETWORK_FLAG,
|
||||
-1,
|
||||
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
|
||||
));
|
||||
} else if (aboveBlock == BlockStateValues.JAVA_AIR_ID) {
|
||||
subChunkBlocksPacket.getStandardBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
|
||||
position.add(0, 1, 0),
|
||||
session.getBlockMappings().getBedrockAir(),
|
||||
NETWORK_FLAG,
|
||||
-1,
|
||||
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue