Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@

import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.chunk.GeyserChunk;
import org.geysermc.geyser.registry.BlockRegistries;
Expand All @@ -37,6 +37,7 @@
import org.geysermc.mcprotocollib.protocol.data.game.chunk.DataPalette;

public class ChunkCache {
@Getter
private final boolean cache;
private final Long2ObjectMap<GeyserChunk> chunks;

Expand Down Expand Up @@ -68,41 +69,40 @@ private GeyserChunk getChunk(int chunkX, int chunkZ) {
return chunks.getOrDefault(chunkPosition, null);
}

public void updateBlock(int x, int y, int z, int block) {
if (!cache) {
return;
/**
* Doesn't check for cache enabled, so don't use this without checking that first!
*/
@Deprecated
Comment thread
valaphee marked this conversation as resolved.
public DataPalette getChunkSection(int chunkX, int chunkY, int chunkZ) {
GeyserChunk chunk = this.getChunk(chunkX, chunkZ);
if (chunk == null) {
return null;
}

GeyserChunk chunk = this.getChunk(x >> 4, z >> 4);
if (chunk == null) {
return;
if (chunkY < getChunkMinY() || chunkY - getChunkMinY() > chunk.sections().length - 1) {
return null;
}

if (y < minY || ((y - minY) >> 4) > chunk.sections().length - 1) {
// Y likely goes above or below the height limit of this world
DataPalette palette = chunk.sections()[chunkY - getChunkMinY()];
if (palette == null) {
palette = DataPalette.createForBlockState(Block.JAVA_AIR_ID, BlockRegistries.BLOCK_STATES.get().size());
chunk.sections()[chunkY - getChunkMinY()] = palette;
}
Comment thread
valaphee marked this conversation as resolved.

return palette;
}

public void updateBlock(int x, int y, int z, int block) {
if (!cache) {
return;
}

boolean previouslyEmpty = false;
try {
DataPalette palette = chunk.sections()[(y - minY) >> 4];
if (palette == null) {
previouslyEmpty = true;
if (block != Block.JAVA_AIR_ID) {
// A previously empty chunk, which is no longer empty as a block has been added to it
palette = DataPalette.createForBlockState(Block.JAVA_AIR_ID, BlockRegistries.BLOCK_STATES.get().size());
chunk.sections()[(y - minY) >> 4] = palette;
} else {
// Nothing to update
return;
}
}

palette.set(x & 0xF, y & 0xF, z & 0xF, block);
} catch (Throwable e) {
GeyserImpl.getInstance().getLogger().error("Failed to update block in chunk cache! ", e);
GeyserImpl.getInstance().getLogger().error("Info: newChunk=%s, block=%s, pos=%s,%s,%s".formatted(previouslyEmpty, block, x, y, z));
DataPalette palette = this.getChunkSection(x >> 4, y >> 4, z >> 4);
if (palette == null) {
return;
Comment thread
valaphee marked this conversation as resolved.
Outdated
}

palette.set(x & 0xF, y & 0xF, z & 0xF, block);
}
Comment thread
valaphee marked this conversation as resolved.

public int getBlockAt(int x, int y, int z) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,84 @@

package org.geysermc.geyser.translator.protocol.java.level;

import org.cloudburstmc.protocol.bedrock.packet.UpdateSubChunkBlocksPacket;
import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.SkullBlock;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.mcprotocollib.protocol.data.game.chunk.DataPalette;
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;

@Translator(packet = ClientboundSectionBlocksUpdatePacket.class)
public class JavaSectionBlocksUpdateTranslator extends PacketTranslator<ClientboundSectionBlocksUpdatePacket> {

@Override
public void translate(GeyserSession session, ClientboundSectionBlocksUpdatePacket packet) {
DataPalette palette = null;
if (session.getChunkCache().isCache()) {
palette = session.getChunkCache().getChunkSection(packet.getChunkX(), packet.getChunkY(), packet.getChunkZ());
if (palette == null) {
Comment thread
valaphee marked this conversation as resolved.
return;
}
Comment thread
valaphee marked this conversation as resolved.
Comment thread
valaphee marked this conversation as resolved.
}
Comment thread
valaphee marked this conversation as resolved.

BitSet waterlogged = BlockRegistries.WATERLOGGED.get();

UpdateSubChunkBlocksPacket updateSubChunkBlocksPacket = new UpdateSubChunkBlocksPacket();

for (BlockChangeEntry entry : packet.getEntries()) {
session.getWorldCache().updateServerCorrectBlockState(entry.getPosition(), entry.getBlock());
int oldBlock = palette != null
? palette.get(entry.getPosition().getX() & 0xF, entry.getPosition().getY() & 0xF, entry.getPosition().getZ() & 0xF)
: session.getGeyser().getWorldManager().getBlockAt(session, entry.getPosition());
if (entry.getBlock() == oldBlock) {
Comment thread
valaphee marked this conversation as resolved.
// Skip unchanged blocks which may occur with older versions of Minecraft
continue;
}
Comment thread
valaphee marked this conversation as resolved.
Comment thread
valaphee marked this conversation as resolved.

if (palette != null) {
palette.set(entry.getPosition().getX() & 0xF, entry.getPosition().getY() & 0xF, entry.getPosition().getZ() & 0xF, entry.getBlock());
}
Comment thread
onebeastchris marked this conversation as resolved.

BlockState blockState = BlockState.of(entry.getBlock());
if (blockState.is(Blocks.AIR)) {
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, entry.getPosition());
if (itemFrameEntity != null) { // Item frame is still present and no block overrides that; refresh it
itemFrameEntity.updateBlock(true);
continue;
}
}
if (!(blockState.block() instanceof SkullBlock)) {
// Skull is gone
session.getSkullCache().removeSkull(entry.getPosition());
}
Comment thread
valaphee marked this conversation as resolved.

updateSubChunkBlocksPacket.getStandardBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
entry.getPosition(),
session.getBlockMappings().getBedrockBlock(blockState),
3,
Comment thread
valaphee marked this conversation as resolved.
Outdated
-1,
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
Comment thread
valaphee marked this conversation as resolved.
Outdated
));
Comment thread
valaphee marked this conversation as resolved.
Comment thread
valaphee marked this conversation as resolved.

boolean isWaterlogged = waterlogged.get(entry.getBlock());
if (waterlogged.get(oldBlock) != isWaterlogged) {
updateSubChunkBlocksPacket.getExtraBlocks().add(new org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry(
entry.getPosition(),
isWaterlogged ? session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir(),
0,
-1,
org.cloudburstmc.protocol.bedrock.data.BlockChangeEntry.MessageType.NONE
));
}
}

Comment thread
valaphee marked this conversation as resolved.
session.sendUpstreamPacket(updateSubChunkBlocksPacket);
Comment thread
valaphee marked this conversation as resolved.
Outdated
}
}
Loading