Yeet cache chunks

So many features require this config option, and we don't intend on supporting it being both disabled and enabled.
This commit is contained in:
Camotoy 2021-06-01 15:36:33 -04:00
parent 7b0099e869
commit ebf726ce9e
No known key found for this signature in database
GPG Key ID: 7EEFB66FE798081F
31 changed files with 92 additions and 290 deletions

View File

@ -48,9 +48,4 @@ public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger());
}
@Override
public boolean isCacheChunks() {
return true; // We override this as with Bukkit, we have direct access to the server implementation
}
}

View File

@ -87,8 +87,6 @@ public interface GeyserConfiguration {
boolean isAboveBedrockNetherBuilding();
boolean isCacheChunks();
boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled();

View File

@ -111,9 +111,6 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("default-locale")
private String defaultLocale = null; // is null by default so system language takes priority
@JsonProperty("cache-chunks")
private boolean cacheChunks = false;
@JsonProperty("cache-images")
private int cacheImages = 0;

View File

@ -174,11 +174,8 @@ public class FishingHookEntity extends ThrowableEntity {
* @return true if this entity is currently in air.
*/
protected boolean isInAir(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockTranslator.JAVA_AIR_ID;
}
return false;
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return block == BlockTranslator.JAVA_AIR_ID;
}
@Override

View File

@ -94,11 +94,9 @@ public class LivingEntity extends Entity {
Position bedPosition = (Position) entityMetadata.getValue();
if (bedPosition != null) {
metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ()));
if (session.getConnector().getConfig().isCacheChunks()) {
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, bedPosition);
}
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
// Bed has to be updated, or else player is floating in the air
ChunkUtils.updateBlock(session, bed, bedPosition);
// Indicate that the player should enter the sleep cycle
// Has to be a byte or it does not work
// (Bed position is what actually triggers sleep - "pose" is only optional)

View File

@ -166,11 +166,8 @@ public class ThrowableEntity extends Entity implements Tickable {
* @return true if this entity is currently in water.
*/
protected boolean isInWater(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) {
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return BlockStateValues.getWaterLevel(block) != -1;
}
return false;
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
return BlockStateValues.getWaterLevel(block) != -1;
}
@Override

View File

@ -111,7 +111,7 @@ public class VillagerEntity extends AbstractMerchantEntity {
float bedPositionSubtractorW = 0;
float bedPositionSubtractorN = 0;
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION, null);
if (session.getConnector().getConfig().isCacheChunks() && bedPosition != null) {
if (bedPosition != null) {
bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
}
String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId);

View File

@ -233,12 +233,6 @@ public class GeyserSession implements CommandSender {
@Setter
private boolean sprinting;
/**
* Not updated if cache chunks is enabled.
*/
@Setter
private boolean jumping;
/**
* Whether the player is swimming in water.
* Used to update speed when crawling.

View File

@ -42,11 +42,7 @@ public class ChunkCache {
private int minY;
public ChunkCache(GeyserSession session) {
if (session.getConnector().getWorldManager().hasOwnChunkCache()) {
this.cache = false; // To prevent Spigot from initializing
} else {
this.cache = session.getConnector().getConfig().isCacheChunks();
}
this.cache = !session.getConnector().getWorldManager().hasOwnChunkCache(); // To prevent Spigot from initializing
chunks = cache ? new Long2ObjectOpenHashMap<>() : null;
}

View File

@ -51,8 +51,6 @@ import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockUtils;
import java.util.concurrent.TimeUnit;
@Translator(packet = PlayerActionPacket.class)
public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket> {
@ -162,43 +160,42 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
// Otherwise handled in BedrockInventoryTransactionTranslator
break;
case START_BREAK:
if (session.getConnector().getConfig().isCacheChunks()) {
// Start the block breaking animation
if (session.getGameMode() != GameMode.CREATIVE) {
int blockState = session.getConnector().getWorldManager().getBlockAt(session, vector);
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
PlayerInventory inventory = session.getPlayerInventory();
GeyserItemStack item = inventory.getItemInHand();
ItemEntry itemEntry;
CompoundTag nbtData;
if (item != null) {
itemEntry = item.getItemEntry();
nbtData = item.getNbt();
} else {
itemEntry = null;
nbtData = new CompoundTag("");
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(session, BlockTranslator.getBlockMapping(blockState), itemEntry, nbtData, true) * 20);
startBreak.setData((int) (65535 / breakTime));
session.setBreakingBlock(blockState);
session.sendUpstreamPacket(startBreak);
// Start the block breaking animation
if (session.getGameMode() != GameMode.CREATIVE) {
int blockState = session.getConnector().getWorldManager().getBlockAt(session, vector);
LevelEventPacket startBreak = new LevelEventPacket();
startBreak.setType(LevelEventType.BLOCK_START_BREAK);
startBreak.setPosition(vector.toFloat());
PlayerInventory inventory = session.getPlayerInventory();
GeyserItemStack item = inventory.getItemInHand();
ItemEntry itemEntry;
CompoundTag nbtData;
if (item != null) {
itemEntry = item.getItemEntry();
nbtData = item.getNbt();
} else {
itemEntry = null;
nbtData = new CompoundTag("");
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(session, BlockTranslator.getBlockMapping(blockState), itemEntry, nbtData, true) * 20);
startBreak.setData((int) (65535 / breakTime));
session.setBreakingBlock(blockState);
session.sendUpstreamPacket(startBreak);
}
// Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getFace());
int blockUp = session.getConnector().getWorldManager().getBlockAt(session, fireBlockPos);
String identifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockUp);
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(fireBlockPos.getX(),
fireBlockPos.getY(), fireBlockPos.getZ()), BlockFace.values()[packet.getFace()]);
session.sendDownstreamPacket(startBreakingPacket);
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
// Account for fire - the client likes to hit the block behind.
Vector3i fireBlockPos = BlockUtils.getBlockPosition(packet.getBlockPosition(), packet.getFace());
int blockUp = session.getConnector().getWorldManager().getBlockAt(session, fireBlockPos);
String identifier = BlockTranslator.getJavaIdBlockMap().inverse().get(blockUp);
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(fireBlockPos.getX(),
fireBlockPos.getY(), fireBlockPos.getZ()), BlockFace.values()[packet.getFace()]);
session.sendDownstreamPacket(startBreakingPacket);
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
}
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, position, BlockFace.values()[packet.getFace()]);
session.sendDownstreamPacket(startBreakingPacket);
break;
@ -246,11 +243,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
session.getEntityCache().updateBossBars();
break;
case JUMP:
if (!session.getConnector().getConfig().isCacheChunks()) {
// Save the jumping status for determining teleport status
session.setJumping(true);
session.getConnector().getGeneralThreadPool().schedule(() -> session.setJumping(false), 1, TimeUnit.SECONDS);
}
// Leaving as a potential placeholder for an event or soul sand fixing
break;
}
}

View File

@ -141,32 +141,22 @@ public class CollisionManager {
Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY,
Double.parseDouble(Float.toString(bedrockPosition.getZ())));
if (session.getConnector().getConfig().isCacheChunks()) {
// With chunk caching, we can do some proper collision checks
updatePlayerBoundingBox(position);
updatePlayerBoundingBox(position);
// Correct player position
if (!correctPlayerPosition()) {
// Cancel the movement if it needs to be cancelled
recalculatePosition();
return null;
}
// Correct player position
if (!correctPlayerPosition()) {
// Cancel the movement if it needs to be cancelled
recalculatePosition();
return null;
}
position = Vector3d.from(playerBoundingBox.getMiddleX(),
playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2),
playerBoundingBox.getMiddleZ());
position = Vector3d.from(playerBoundingBox.getMiddleX(),
playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2),
playerBoundingBox.getMiddleZ());
if (!onGround) {
// Trim the position to prevent rounding errors that make Java think we are clipping into a block
position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ());
}
} else {
// When chunk caching is off, we have to rely on this
// It rounds the Y position up to the nearest 0.5
// This snaps players to snap to the top of stairs and slabs like on Java Edition
// However, it causes issues such as the player floating on carpets
if (onGround) javaY = Math.ceil(javaY * 2) / 2;
position = position.up(javaY - position.getY());
if (!onGround) {
// Trim the position to prevent rounding errors that make Java think we are clipping into a block
position = Vector3d.from(position.getX(), Double.parseDouble(DECIMAL_FORMAT.format(position.getY())), position.getZ());
}
return position;
@ -268,10 +258,6 @@ public class CollisionManager {
* were they not sneaking
*/
public boolean isUnderSlab() {
if (!session.getConnector().getConfig().isCacheChunks()) {
// We can't reliably determine this
return false;
}
Vector3i position = session.getPlayerEntity().getPosition().toInt();
BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ());
if (collision != null) {
@ -289,8 +275,7 @@ public class CollisionManager {
* @return if the player is currently in a water block
*/
public boolean isPlayerInWater() {
return session.getConnector().getConfig().isCacheChunks()
&& session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID;
return session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID;
}
/**

View File

@ -48,11 +48,6 @@ public class CollisionTranslator {
private static final Int2ObjectMap<BlockCollision> COLLISION_MAP = new Int2ObjectOpenHashMap<>();
public static void init() {
// If chunk caching is off then don't initialize
if (!GeyserConnector.getInstance().getConfig().isCacheChunks()) {
return;
}
List<Class<?>> collisionTypes = new ArrayList<>();
Map<Class<?>, CollisionRemapper> annotationMap = new HashMap<>();

View File

@ -54,15 +54,6 @@ import java.util.Collections;
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
public BeaconInventoryTranslator() {
super(1, new BlockInventoryHolder("minecraft:beacon", ContainerType.BEACON) {
@Override
public void prepareInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
if (!session.getConnector().getConfig().isCacheChunks()) {
// Beacons cannot work without knowing their physical location
return;
}
super.prepareInventory(translator, session, inventory);
}
@Override
protected boolean checkInteractionPosition(GeyserSession session) {
// Since we can't fall back to a virtual inventory, let's make opening one easier
@ -71,7 +62,7 @@ public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator
@Override
public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) {
if (!session.getConnector().getConfig().isCacheChunks() || !((BeaconContainer) inventory).isUsingRealBlock()) {
if (!((BeaconContainer) inventory).isUsingRealBlock()) {
InventoryUtils.closeInventory(session, inventory.getId(), false);
return;
}

View File

@ -64,9 +64,7 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
passenger = session.getPlayerEntity();
session.setRidingVehicleEntity(entity);
// We need to confirm teleports before entering a vehicle, or else we will likely exit right out
if (session.getConnector().getConfig().isCacheChunks()) {
session.confirmTeleport(passenger.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0).toDouble());
}
session.confirmTeleport(passenger.getPosition().sub(0, EntityType.PLAYER.getOffset(), 0).toDouble());
}
// Passenger hasn't loaded in (likely since we're waiting for a skin response)
// and entity link needs to be set later

View File

@ -25,21 +25,15 @@
package org.geysermc.connector.network.translators.java.entity.player;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
import org.geysermc.connector.inventory.GeyserItemStack;
import org.geysermc.connector.inventory.PlayerInventory;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.item.ItemEntry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockUtils;
import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerPlayerActionAckPacket.class)
@ -56,47 +50,5 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
session.sendUpstreamPacket(stopBreak);
}
if (!session.getConnector().getConfig().isCacheChunks()) {
LevelEventPacket levelEvent = new LevelEventPacket();
switch (packet.getAction()) {
case START_DIGGING:
if (session.getGameMode() == GameMode.CREATIVE) {
break;
}
levelEvent.setType(LevelEventType.BLOCK_START_BREAK);
levelEvent.setPosition(Vector3f.from(
packet.getPosition().getX(),
packet.getPosition().getY(),
packet.getPosition().getZ()
));
PlayerInventory inventory = session.getPlayerInventory();
GeyserItemStack item = inventory.getItemInHand();
ItemEntry itemEntry;
CompoundTag nbtData;
if (item != null) {
itemEntry = item.getItemEntry();
nbtData = item.getNbt();
} else {
itemEntry = null;
nbtData = new CompoundTag("");
}
double breakTime = Math.ceil(BlockUtils.getBreakTime(session, BlockTranslator.getBlockMapping(packet.getNewState()), itemEntry, nbtData, true) * 20);
levelEvent.setData((int) (65535 / breakTime));
session.setBreakingBlock(packet.getNewState());
session.sendUpstreamPacket(levelEvent);
break;
case CANCEL_DIGGING:
levelEvent.setType(LevelEventType.BLOCK_STOP_BREAK);
levelEvent.setPosition(Vector3f.from(
packet.getPosition().getX(),
packet.getPosition().getY(),
packet.getPosition().getZ()
));
levelEvent.setData(0);
session.setBreakingBlock(0);
session.sendUpstreamPacket(levelEvent);
break;
}
}
}
}

View File

@ -87,21 +87,6 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
session.setSpawned(true);
// Ignore certain move correction packets for smoother movement
// These are never relative
// When chunk caching is enabled this isn't needed as we shouldn't get these
if (!session.getConnector().getConfig().isCacheChunks() && packet.getRelative().isEmpty()) {
double xDis = Math.abs(entity.getPosition().getX() - packet.getX());
double yDis = entity.getPosition().getY() - packet.getY();
double zDis = Math.abs(entity.getPosition().getZ() - packet.getZ());
if (!(xDis > 1.5 || (yDis < 1.45 || yDis > (session.isJumping() ? 4.3 : (session.isSprinting() ? 2.5 : 1.9))) || zDis > 1.5)) {
// Fake confirm the teleport but don't send it to the client
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
session.sendDownstreamPacket(teleportConfirmPacket);
return;
}
}
// If coordinates are relative, then add to the existing coordinate
double newX = packet.getX() +
(packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0);

View File

@ -45,8 +45,7 @@ public class JavaBlockChangeTranslator extends PacketTranslator<ServerBlockChang
public void translate(ServerBlockChangePacket packet, GeyserSession session) {
Position pos = packet.getRecord().getPosition();
boolean updatePlacement = session.getConnector().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
!(session.getConnector().getConfig().isCacheChunks() &&
session.getConnector().getWorldManager().getBlockAt(session, pos) == packet.getRecord().getBlock());
session.getConnector().getWorldManager().getBlockAt(session, pos) != packet.getRecord().getBlock();
ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), pos);
if (updatePlacement) {
this.checkPlace(session, packet);

View File

@ -31,27 +31,22 @@ import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdate
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
import org.geysermc.connector.network.translators.world.block.entity.RequiresBlockState;
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.utils.BlockEntityUtils;
import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerUpdateTileEntityPacket.class)
public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> {
private final boolean cacheChunks;
public JavaUpdateTileEntityTranslator() {
cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
}
@Override
public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name());
if (packet.getNbt().isEmpty()) { // Fixes errors in CubeCraft sending empty NBT
if (packet.getNbt().isEmpty()) { // Fixes errors in servers sending empty NBT
BlockEntityUtils.updateBlockEntity(session, null, packet.getPosition());
return;
}
@ -59,11 +54,12 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdat
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
// The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies
// between Java block states and Bedrock block entity data
int blockState = cacheChunks ?
// Cache chunks is enabled; use chunk cache
session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition()) :
// Cache chunks is not enabled; use block entity cache
ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(packet.getPosition());
int blockState;
if (translator instanceof RequiresBlockState) {
blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition());
} else {
blockState = BlockTranslator.JAVA_AIR_ID;
}
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), blockState), packet.getPosition());
// Check for custom skulls.
if (SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS && packet.getNbt().contains("SkullOwner")) {

View File

@ -33,11 +33,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
@BlockEntity(name = "Banner")
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getBannerColor(blockState) != -1;
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
int bannerColor = BlockStateValues.getBannerColor(blockState);

View File

@ -31,11 +31,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
@BlockEntity(name = "Bed")
public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getBedColor(blockState) != -1;
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
byte bedcolor = BlockStateValues.getBedColor(blockState);

View File

@ -32,7 +32,14 @@ import org.geysermc.connector.network.session.GeyserSession;
/**
* Implemented only if a block is a block entity in Bedrock and not Java Edition.
*/
public interface BedrockOnlyBlockEntity {
public interface BedrockOnlyBlockEntity extends RequiresBlockState {
/**
* Determines if block is part of class
* @param blockState BlockState to be compared
* @return true if part of the class
*/
boolean isBlock(int blockState);
/**
* Update the block on Bedrock Edition.
* @param session GeyserSession.

View File

@ -47,10 +47,9 @@ import java.util.Map;
public abstract class BlockEntityTranslator {
public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>();
/**
* A list of all block entities that require the Java block state in order to fill out their block entity information.
* This list will be smaller with cache chunks on as we don't need to double-cache data
* A list of all block entities that only exist on Bedrock
*/
public static final ObjectArrayList<RequiresBlockState> REQUIRES_BLOCK_STATE_LIST = new ObjectArrayList<>();
public static final ObjectArrayList<BedrockOnlyBlockEntity> BEDROCK_ONLY_BLOCK_ENTITIES = new ObjectArrayList<>();
/**
* Contains a list of irregular block entity name translations that can't be fit into the regex
@ -84,18 +83,12 @@ public abstract class BlockEntityTranslator {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_entity.failed", clazz.getCanonicalName()));
}
}
boolean cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
for (Class<?> clazz : ref.getSubTypesOf(RequiresBlockState.class)) {
GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
for (Class<?> clazz : ref.getSubTypesOf(BedrockOnlyBlockEntity.class)) {
GeyserConnector.getInstance().getLogger().debug("Found Bedrock-only block entity: " + clazz.getCanonicalName());
try {
RequiresBlockState requiresBlockState = (RequiresBlockState) clazz.newInstance();
if (cacheChunks && !(requiresBlockState instanceof BedrockOnlyBlockEntity)) {
// Not needed to put this one in the map; cache chunks takes care of that for us
GeyserConnector.getInstance().getLogger().debug("Not adding because cache chunks is enabled.");
continue;
}
REQUIRES_BLOCK_STATE_LIST.add(requiresBlockState);
BedrockOnlyBlockEntity bedrockOnlyBlockEntity = (BedrockOnlyBlockEntity) clazz.newInstance();
BEDROCK_ONLY_BLOCK_ENTITIES.add(bedrockOnlyBlockEntity);
} catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName()));
}

View File

@ -54,9 +54,4 @@ public class CommandBlockBlockEntityTranslator extends BlockEntityTranslator imp
builder.put("LastExecution", (long) 0);
}
}
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getCommandBlockValues().containsKey(blockState);
}
}

View File

@ -37,7 +37,7 @@ import org.geysermc.connector.utils.BlockEntityUtils;
* Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity
*/
@BlockEntity(name = "Chest")
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getDoubleChestValues().containsKey(blockState);

View File

@ -33,7 +33,7 @@ import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.BlockEntityUtils;
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState {
public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
/**
* @param blockState the Java block state of a potential flower pot block
* @return true if the block is a flower pot

View File

@ -30,21 +30,14 @@ import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.ChunkUtils;
/**
* Does not implement BlockEntityTranslator because it's only a block entity in Bedrock
*/
public class NoteblockBlockEntityTranslator implements RequiresBlockState {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getNoteblockPitch(blockState) != -1;
}
public class NoteblockBlockEntityTranslator {
public static void translate(GeyserSession session, Position position) {
int blockState = session.getConnector().getConfig().isCacheChunks() ?
session.getConnector().getWorldManager().getBlockAt(session, position) :
ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position);
int blockState = session.getConnector().getWorldManager().getBlockAt(session, position);
BlockEventPacket blockEventPacket = new BlockEventPacket();
blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEventPacket.setEventType(0);

View File

@ -29,12 +29,4 @@ package org.geysermc.connector.network.translators.world.block.entity;
* Implemented in block entities if their Java block state is required for additional values in Bedrock
*/
public interface RequiresBlockState {
/**
* Determines if block is part of class
* @param blockState BlockState to be compared
* @return true if part of the class
*/
boolean isBlock(int blockState);
}

View File

@ -32,7 +32,7 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import javax.annotation.Nullable;
@BlockEntity(name = "ShulkerBox")
public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator {
public class ShulkerBoxBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
/**
* Also used in {@link org.geysermc.connector.network.translators.inventory.translators.ShulkerInventoryTranslator}
* where {@code tag} is passed as null.

View File

@ -50,11 +50,6 @@ import java.util.concurrent.TimeUnit;
public class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
public static boolean ALLOW_CUSTOM_SKULLS;
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getSkullVariant(blockState) != -1;
}
@Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
byte skullVariant = BlockStateValues.getSkullVariant(blockState);

View File

@ -42,8 +42,6 @@ import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Data;
import lombok.experimental.UtilityClass;
import org.geysermc.connector.GeyserConnector;
@ -55,7 +53,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity;
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
import org.geysermc.connector.network.translators.world.block.entity.RequiresBlockState;
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.network.translators.world.chunk.BlockStorage;
import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
@ -70,11 +67,6 @@ import static org.geysermc.connector.network.translators.world.block.BlockTransl
@UtilityClass
public class ChunkUtils {
/**
* Temporarily stores positions of BlockState values that are needed for certain block entities actively.
* Not used if cache chunks is enabled
*/
public static final Object2IntMap<Position> CACHED_BLOCK_ENTITIES = new Object2IntOpenHashMap<>();
private static int indexYZXtoXZY(int yzx) {
return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8);
@ -364,20 +356,12 @@ public class ChunkUtils {
return newLecternHasBook;
});
// Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag
// This is the only place I could find that interacts with the Java block state and block updates
// Iterates through all block entity translators and determines if the block state needs to be saved
for (RequiresBlockState requiresBlockState : BlockEntityTranslator.REQUIRES_BLOCK_STATE_LIST) {
if (requiresBlockState.isBlock(blockState)) {
// Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet
// needs to be sent
for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityTranslator.BEDROCK_ONLY_BLOCK_ENTITIES) {
if (bedrockOnlyBlockEntity.isBlock(blockState)) {
// Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks
if (requiresBlockState instanceof BedrockOnlyBlockEntity) {
((BedrockOnlyBlockEntity) requiresBlockState).updateBlock(session, blockState, position);
break;
}
if (!session.getConnector().getConfig().isCacheChunks()) {
// Blocks aren't saved to a chunk cache; resort to this smaller cache
CACHED_BLOCK_ENTITIES.put(new Position(position.getX(), position.getY(), position.getZ()), blockState);
}
bedrockOnlyBlockEntity.updateBlock(session, blockState, position);
break; //No block will be a part of two classes
}
}
@ -412,7 +396,6 @@ public class ChunkUtils {
@Data
public static final class ChunkData {
private final ChunkSection[] sections;
private final NbtMap[] blockEntities;
}
}

View File

@ -134,18 +134,6 @@ emote-offhand-workaround: "disabled"
# The default locale if we dont have the one the client requested. Uncomment to not use the default system language.
# default-locale: en_us
# Configures if chunk caching should be enabled or not. This keeps an individual
# record of each block the client loads in. This feature does allow for a few things
# such as more accurate movement that causes less problems with anticheat (meaning
# you're less likely to be banned) and allows block break animations to show up in
# creative mode (and other features). Although this increases RAM usage, it likely
# won't have much of an effect for the vast majority of people. However, if you're
# running out of RAM or are in a RAM-sensitive environment, you may want to disable
# this. When using the Spigot version of Geyser, support for features or
# implementations this allows is automatically enabled without the additional caching
# as Geyser has direct access to the server itself.
cache-chunks: true
# Specify how many days images will be cached to disk to save downloading them from the internet.
# A value of 0 is disabled. (Default: 0)
cache-images: 0