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()); 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 isAboveBedrockNetherBuilding();
boolean isCacheChunks();
boolean isForceResourcePacks(); boolean isForceResourcePacks();
boolean isXboxAchievementsEnabled(); boolean isXboxAchievementsEnabled();

View File

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

View File

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

View File

@ -94,11 +94,9 @@ public class LivingEntity extends Entity {
Position bedPosition = (Position) entityMetadata.getValue(); Position bedPosition = (Position) entityMetadata.getValue();
if (bedPosition != null) { if (bedPosition != null) {
metadata.put(EntityData.BED_POSITION, Vector3i.from(bedPosition.getX(), bedPosition.getY(), bedPosition.getZ())); 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);
int bed = session.getConnector().getWorldManager().getBlockAt(session, bedPosition); // Bed has to be updated, or else player is floating in the air
// Bed has to be updated, or else player is floating in the air ChunkUtils.updateBlock(session, bed, bedPosition);
ChunkUtils.updateBlock(session, bed, bedPosition);
}
// Indicate that the player should enter the sleep cycle // Indicate that the player should enter the sleep cycle
// Has to be a byte or it does not work // Has to be a byte or it does not work
// (Bed position is what actually triggers sleep - "pose" is only optional) // (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. * @return true if this entity is currently in water.
*/ */
protected boolean isInWater(GeyserSession session) { protected boolean isInWater(GeyserSession session) {
if (session.getConnector().getConfig().isCacheChunks()) { int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt());
int block = session.getConnector().getWorldManager().getBlockAt(session, position.toInt()); return BlockStateValues.getWaterLevel(block) != -1;
return BlockStateValues.getWaterLevel(block) != -1;
}
return false;
} }
@Override @Override

View File

@ -111,7 +111,7 @@ public class VillagerEntity extends AbstractMerchantEntity {
float bedPositionSubtractorW = 0; float bedPositionSubtractorW = 0;
float bedPositionSubtractorN = 0; float bedPositionSubtractorN = 0;
Vector3i bedPosition = metadata.getPos(EntityData.BED_POSITION, null); 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); bedId = session.getConnector().getWorldManager().getBlockAt(session, bedPosition);
} }
String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId); String bedRotationZ = BlockTranslator.getJavaIdBlockMap().inverse().get(bedId);

View File

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

View File

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

View File

@ -141,32 +141,22 @@ public class CollisionManager {
Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY, Vector3d position = Vector3d.from(Double.parseDouble(Float.toString(bedrockPosition.getX())), javaY,
Double.parseDouble(Float.toString(bedrockPosition.getZ()))); Double.parseDouble(Float.toString(bedrockPosition.getZ())));
if (session.getConnector().getConfig().isCacheChunks()) { updatePlayerBoundingBox(position);
// With chunk caching, we can do some proper collision checks
updatePlayerBoundingBox(position);
// Correct player position // Correct player position
if (!correctPlayerPosition()) { if (!correctPlayerPosition()) {
// Cancel the movement if it needs to be cancelled // Cancel the movement if it needs to be cancelled
recalculatePosition(); recalculatePosition();
return null; return null;
} }
position = Vector3d.from(playerBoundingBox.getMiddleX(), position = Vector3d.from(playerBoundingBox.getMiddleX(),
playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2), playerBoundingBox.getMiddleY() - (playerBoundingBox.getSizeY() / 2),
playerBoundingBox.getMiddleZ()); playerBoundingBox.getMiddleZ());
if (!onGround) { if (!onGround) {
// Trim the position to prevent rounding errors that make Java think we are clipping into a block // 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()); 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());
} }
return position; return position;
@ -268,10 +258,6 @@ public class CollisionManager {
* were they not sneaking * were they not sneaking
*/ */
public boolean isUnderSlab() { public boolean isUnderSlab() {
if (!session.getConnector().getConfig().isCacheChunks()) {
// We can't reliably determine this
return false;
}
Vector3i position = session.getPlayerEntity().getPosition().toInt(); Vector3i position = session.getPlayerEntity().getPosition().toInt();
BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ()); BlockCollision collision = CollisionTranslator.getCollisionAt(session, position.getX(), position.getY(), position.getZ());
if (collision != null) { if (collision != null) {
@ -289,8 +275,7 @@ public class CollisionManager {
* @return if the player is currently in a water block * @return if the player is currently in a water block
*/ */
public boolean isPlayerInWater() { public boolean isPlayerInWater() {
return session.getConnector().getConfig().isCacheChunks() return session.getConnector().getWorldManager().getBlockAt(session, session.getPlayerEntity().getPosition().toInt()) == BlockTranslator.JAVA_WATER_ID;
&& 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<>(); private static final Int2ObjectMap<BlockCollision> COLLISION_MAP = new Int2ObjectOpenHashMap<>();
public static void init() { public static void init() {
// If chunk caching is off then don't initialize
if (!GeyserConnector.getInstance().getConfig().isCacheChunks()) {
return;
}
List<Class<?>> collisionTypes = new ArrayList<>(); List<Class<?>> collisionTypes = new ArrayList<>();
Map<Class<?>, CollisionRemapper> annotationMap = new HashMap<>(); Map<Class<?>, CollisionRemapper> annotationMap = new HashMap<>();

View File

@ -54,15 +54,6 @@ import java.util.Collections;
public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator { public class BeaconInventoryTranslator extends AbstractBlockInventoryTranslator {
public BeaconInventoryTranslator() { public BeaconInventoryTranslator() {
super(1, new BlockInventoryHolder("minecraft:beacon", ContainerType.BEACON) { 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 @Override
protected boolean checkInteractionPosition(GeyserSession session) { protected boolean checkInteractionPosition(GeyserSession session) {
// Since we can't fall back to a virtual inventory, let's make opening one easier // 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 @Override
public void openInventory(InventoryTranslator translator, GeyserSession session, Inventory inventory) { 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); InventoryUtils.closeInventory(session, inventory.getId(), false);
return; return;
} }

View File

@ -64,9 +64,7 @@ public class JavaEntitySetPassengersTranslator extends PacketTranslator<ServerEn
passenger = session.getPlayerEntity(); passenger = session.getPlayerEntity();
session.setRidingVehicleEntity(entity); session.setRidingVehicleEntity(entity);
// We need to confirm teleports before entering a vehicle, or else we will likely exit right out // 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) // Passenger hasn't loaded in (likely since we're waiting for a skin response)
// and entity link needs to be set later // and entity link needs to be set later

View File

@ -25,21 +25,15 @@
package org.geysermc.connector.network.translators.java.entity.player; 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.data.game.entity.player.PlayerAction;
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerActionAckPacket; 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.math.vector.Vector3f;
import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.LevelEventType;
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; 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.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; 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.network.translators.world.block.BlockTranslator;
import org.geysermc.connector.utils.BlockUtils;
import org.geysermc.connector.utils.ChunkUtils; import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerPlayerActionAckPacket.class) @Translator(packet = ServerPlayerActionAckPacket.class)
@ -56,47 +50,5 @@ public class JavaPlayerActionAckTranslator extends PacketTranslator<ServerPlayer
session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID); session.setBreakingBlock(BlockTranslator.JAVA_AIR_ID);
session.sendUpstreamPacket(stopBreak); 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); 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 // If coordinates are relative, then add to the existing coordinate
double newX = packet.getX() + double newX = packet.getX() +
(packet.getRelative().contains(PositionElement.X) ? entity.getPosition().getX() : 0); (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) { public void translate(ServerBlockChangePacket packet, GeyserSession session) {
Position pos = packet.getRecord().getPosition(); Position pos = packet.getRecord().getPosition();
boolean updatePlacement = session.getConnector().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event 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); ChunkUtils.updateBlock(session, packet.getRecord().getBlock(), pos);
if (updatePlacement) { if (updatePlacement) {
this.checkPlace(session, packet); 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.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.data.inventory.ContainerType; import com.nukkitx.protocol.bedrock.data.inventory.ContainerType;
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket; import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.PacketTranslator;
import org.geysermc.connector.network.translators.Translator; 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.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.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.utils.BlockEntityUtils; import org.geysermc.connector.utils.BlockEntityUtils;
import org.geysermc.connector.utils.ChunkUtils;
@Translator(packet = ServerUpdateTileEntityPacket.class) @Translator(packet = ServerUpdateTileEntityPacket.class)
public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> { public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdateTileEntityPacket> {
private final boolean cacheChunks;
public JavaUpdateTileEntityTranslator() {
cacheChunks = GeyserConnector.getInstance().getConfig().isCacheChunks();
}
@Override @Override
public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) { public void translate(ServerUpdateTileEntityPacket packet, GeyserSession session) {
String id = BlockEntityUtils.getBedrockBlockEntityId(packet.getType().name()); 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()); BlockEntityUtils.updateBlockEntity(session, null, packet.getPosition());
return; return;
} }
@ -59,11 +54,12 @@ public class JavaUpdateTileEntityTranslator extends PacketTranslator<ServerUpdat
BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id); BlockEntityTranslator translator = BlockEntityUtils.getBlockEntityTranslator(id);
// The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies // The Java block state is used in BlockEntityTranslator.translateTag() to make up for some inconsistencies
// between Java block states and Bedrock block entity data // between Java block states and Bedrock block entity data
int blockState = cacheChunks ? int blockState;
// Cache chunks is enabled; use chunk cache if (translator instanceof RequiresBlockState) {
session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition()) : blockState = session.getConnector().getWorldManager().getBlockAt(session, packet.getPosition());
// Cache chunks is not enabled; use block entity cache } else {
ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(packet.getPosition()); blockState = BlockTranslator.JAVA_AIR_ID;
}
BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), blockState), packet.getPosition()); BlockEntityUtils.updateBlockEntity(session, translator.getBlockEntityTag(id, packet.getNbt(), blockState), packet.getPosition());
// Check for custom skulls. // Check for custom skulls.
if (SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS && packet.getNbt().contains("SkullOwner")) { 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") @BlockEntity(name = "Banner")
public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class BannerBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getBannerColor(blockState) != -1;
}
@Override @Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
int bannerColor = BlockStateValues.getBannerColor(blockState); int bannerColor = BlockStateValues.getBannerColor(blockState);

View File

@ -31,11 +31,6 @@ import org.geysermc.connector.network.translators.world.block.BlockStateValues;
@BlockEntity(name = "Bed") @BlockEntity(name = "Bed")
public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState { public class BedBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getBedColor(blockState) != -1;
}
@Override @Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
byte bedcolor = BlockStateValues.getBedColor(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. * 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. * Update the block on Bedrock Edition.
* @param session GeyserSession. * @param session GeyserSession.

View File

@ -47,10 +47,9 @@ import java.util.Map;
public abstract class BlockEntityTranslator { public abstract class BlockEntityTranslator {
public static final Map<String, BlockEntityTranslator> BLOCK_ENTITY_TRANSLATORS = new HashMap<>(); 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. * A list of all block entities that only exist on Bedrock
* This list will be smaller with cache chunks on as we don't need to double-cache data
*/ */
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 * 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())); 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(BedrockOnlyBlockEntity.class)) {
for (Class<?> clazz : ref.getSubTypesOf(RequiresBlockState.class)) { GeyserConnector.getInstance().getLogger().debug("Found Bedrock-only block entity: " + clazz.getCanonicalName());
GeyserConnector.getInstance().getLogger().debug("Found block entity that requires block state: " + clazz.getCanonicalName());
try { try {
RequiresBlockState requiresBlockState = (RequiresBlockState) clazz.newInstance(); BedrockOnlyBlockEntity bedrockOnlyBlockEntity = (BedrockOnlyBlockEntity) clazz.newInstance();
if (cacheChunks && !(requiresBlockState instanceof BedrockOnlyBlockEntity)) { BEDROCK_ONLY_BLOCK_ENTITIES.add(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);
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
GeyserConnector.getInstance().getLogger().error(LanguageUtils.getLocaleStringLog("geyser.network.translator.block_state.failed", clazz.getCanonicalName())); 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); 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 * Chests have more block entity properties in Bedrock, which is solved by implementing the BedrockOnlyBlockEntity
*/ */
@BlockEntity(name = "Chest") @BlockEntity(name = "Chest")
public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity, RequiresBlockState { public class DoubleChestBlockEntityTranslator extends BlockEntityTranslator implements BedrockOnlyBlockEntity {
@Override @Override
public boolean isBlock(int blockState) { public boolean isBlock(int blockState) {
return BlockStateValues.getDoubleChestValues().containsKey(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.network.translators.world.block.BlockStateValues;
import org.geysermc.connector.utils.BlockEntityUtils; 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 * @param blockState the Java block state of a potential flower pot block
* @return true if the block is a flower pot * @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 com.nukkitx.protocol.bedrock.packet.BlockEventPacket;
import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.translators.world.block.BlockStateValues; 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 * Does not implement BlockEntityTranslator because it's only a block entity in Bedrock
*/ */
public class NoteblockBlockEntityTranslator implements RequiresBlockState { public class NoteblockBlockEntityTranslator {
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getNoteblockPitch(blockState) != -1;
}
public static void translate(GeyserSession session, Position position) { public static void translate(GeyserSession session, Position position) {
int blockState = session.getConnector().getConfig().isCacheChunks() ? int blockState = session.getConnector().getWorldManager().getBlockAt(session, position);
session.getConnector().getWorldManager().getBlockAt(session, position) :
ChunkUtils.CACHED_BLOCK_ENTITIES.removeInt(position);
BlockEventPacket blockEventPacket = new BlockEventPacket(); BlockEventPacket blockEventPacket = new BlockEventPacket();
blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ())); blockEventPacket.setBlockPosition(Vector3i.from(position.getX(), position.getY(), position.getZ()));
blockEventPacket.setEventType(0); 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 * Implemented in block entities if their Java block state is required for additional values in Bedrock
*/ */
public interface RequiresBlockState { 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; import javax.annotation.Nullable;
@BlockEntity(name = "ShulkerBox") @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} * Also used in {@link org.geysermc.connector.network.translators.inventory.translators.ShulkerInventoryTranslator}
* where {@code tag} is passed as null. * 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 class SkullBlockEntityTranslator extends BlockEntityTranslator implements RequiresBlockState {
public static boolean ALLOW_CUSTOM_SKULLS; public static boolean ALLOW_CUSTOM_SKULLS;
@Override
public boolean isBlock(int blockState) {
return BlockStateValues.getSkullVariant(blockState) != -1;
}
@Override @Override
public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) { public void translateTag(NbtMapBuilder builder, CompoundTag tag, int blockState) {
byte skullVariant = BlockStateValues.getSkullVariant(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 com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList; 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.Data;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import org.geysermc.connector.GeyserConnector; 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.BlockTranslator;
import org.geysermc.connector.network.translators.world.block.entity.BedrockOnlyBlockEntity; 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.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.block.entity.SkullBlockEntityTranslator;
import org.geysermc.connector.network.translators.world.chunk.BlockStorage; import org.geysermc.connector.network.translators.world.chunk.BlockStorage;
import org.geysermc.connector.network.translators.world.chunk.ChunkSection; import org.geysermc.connector.network.translators.world.chunk.ChunkSection;
@ -70,11 +67,6 @@ import static org.geysermc.connector.network.translators.world.block.BlockTransl
@UtilityClass @UtilityClass
public class ChunkUtils { 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) { private static int indexYZXtoXZY(int yzx) {
return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8); return (yzx >> 8) | (yzx & 0x0F0) | ((yzx & 0x00F) << 8);
@ -364,20 +356,12 @@ public class ChunkUtils {
return newLecternHasBook; return newLecternHasBook;
}); });
// Since Java stores bed colors/skull information as part of the namespaced ID and Bedrock stores it as a tag // Iterates through all Bedrock-only block entity translators and determines if a manual block entity packet
// This is the only place I could find that interacts with the Java block state and block updates // needs to be sent
// Iterates through all block entity translators and determines if the block state needs to be saved for (BedrockOnlyBlockEntity bedrockOnlyBlockEntity : BlockEntityTranslator.BEDROCK_ONLY_BLOCK_ENTITIES) {
for (RequiresBlockState requiresBlockState : BlockEntityTranslator.REQUIRES_BLOCK_STATE_LIST) { if (bedrockOnlyBlockEntity.isBlock(blockState)) {
if (requiresBlockState.isBlock(blockState)) {
// Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks // Flower pots are block entities only in Bedrock and are not updated anywhere else like note blocks
if (requiresBlockState instanceof BedrockOnlyBlockEntity) { bedrockOnlyBlockEntity.updateBlock(session, blockState, position);
((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);
}
break; //No block will be a part of two classes break; //No block will be a part of two classes
} }
} }
@ -412,7 +396,6 @@ public class ChunkUtils {
@Data @Data
public static final class ChunkData { public static final class ChunkData {
private final ChunkSection[] sections; private final ChunkSection[] sections;
private final NbtMap[] blockEntities; 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. # The default locale if we dont have the one the client requested. Uncomment to not use the default system language.
# default-locale: en_us # 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. # 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) # A value of 0 is disabled. (Default: 0)
cache-images: 0 cache-images: 0