3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-12-27 08:30:12 +01:00

Fix ghost blocks when insta-mining on 1.19+

Fixes #3113
Dieser Commit ist enthalten in:
Camotoy 2022-09-27 19:24:50 -04:00
Ursprung a84362af2c
Commit 1b6cfad5ad
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
11 geänderte Dateien mit 36 neuen und 62 gelöschten Zeilen

Datei anzeigen

@ -47,7 +47,7 @@ public class OffhandCommand extends GeyserCommand {
} }
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
Direction.DOWN, session.getWorldCache().nextPredictionSequence()); Direction.DOWN, 0);
session.sendDownstreamPacket(releaseItemPacket); session.sendDownstreamPacket(releaseItemPacket);
} }

Datei anzeigen

@ -234,7 +234,7 @@ public final class BlockRegistryPopulator {
BlockMapping.BlockMappingBuilder builder = BlockMapping.builder(); BlockMapping.BlockMappingBuilder builder = BlockMapping.builder();
JsonNode hardnessNode = entry.getValue().get("block_hardness"); JsonNode hardnessNode = entry.getValue().get("block_hardness");
if (hardnessNode != null) { if (hardnessNode != null) {
builder.hardness(hardnessNode.doubleValue()); builder.hardness(hardnessNode.floatValue());
} }
JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand"); JsonNode canBreakWithHandNode = entry.getValue().get("can_break_with_hand");

Datei anzeigen

@ -45,7 +45,7 @@ public class BlockMapping {
*/ */
int javaBlockId; int javaBlockId;
double hardness; float hardness;
boolean canBreakWithHand; boolean canBreakWithHand;
/** /**
* The index of this collision in collision.json * The index of this collision in collision.json

Datei anzeigen

@ -1311,7 +1311,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private boolean disableBlocking() { private boolean disableBlocking() {
if (playerEntity.getFlag(EntityFlag.BLOCKING)) { if (playerEntity.getFlag(EntityFlag.BLOCKING)) {
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM,
Vector3i.ZERO, Direction.DOWN, worldCache.nextPredictionSequence()); Vector3i.ZERO, Direction.DOWN, 0);
sendDownstreamPacket(releaseItemPacket); sendDownstreamPacket(releaseItemPacket);
playerEntity.setFlag(EntityFlag.BLOCKING, false); playerEntity.setFlag(EntityFlag.BLOCKING, false);
return true; return true;

Datei anzeigen

@ -28,17 +28,17 @@ package org.geysermc.geyser.session.cache;
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import com.nukkitx.math.vector.Vector3i; import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket; import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntMaps;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.Scoreboard;
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.ChunkUtils; import org.geysermc.geyser.util.ChunkUtils;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
public final class WorldCache { public final class WorldCache {
private final GeyserSession session; private final GeyserSession session;
@ -59,7 +59,7 @@ public final class WorldCache {
private int trueTitleFadeOutTime; private int trueTitleFadeOutTime;
private int currentSequence; private int currentSequence;
private final Map<Vector3i, ServerVerifiedState> unverifiedPredictions = new Object2ObjectOpenHashMap<>(1); private final Object2IntMap<Vector3i> unverifiedPredictions = new Object2IntOpenHashMap<>(1);
public WorldCache(GeyserSession session) { public WorldCache(GeyserSession session) {
this.session = session; this.session = session;
@ -135,30 +135,33 @@ public final class WorldCache {
/* Code to support the prediction structure introduced in Java Edition 1.19.0 /* Code to support the prediction structure introduced in Java Edition 1.19.0
Blocks can be rolled back if invalid, but this requires some client-side information storage. */ Blocks can be rolled back if invalid, but this requires some client-side information storage. */
/**
* This does not need to be called for all player action packets (as of 1.19.2) and can be set to 0 if blocks aren't
* changed in the action.
*/
public int nextPredictionSequence() { public int nextPredictionSequence() {
return ++currentSequence; return ++currentSequence;
} }
/** /**
* Stores a record of a block at a certain position to rollback in the event it is incorrect. * Stores a note that this position may need to be rolled back at a future point in time.
*/ */
public void addServerCorrectBlockState(Vector3i position, int blockState) { public void markPositionInSequence(Vector3i position) {
if (session.isEmulatePost1_18Logic()) { if (session.isEmulatePost1_18Logic()) {
// Cheap hack // Cheap hack
// On non-Bukkit platforms, ViaVersion will always confirm the sequence before the block is updated, // On non-Bukkit platforms, ViaVersion will always confirm the sequence before the block is updated,
// meaning we'd send two block updates after (ChunkUtils.updateBlockClientSide in endPredictionsUpTo // meaning we'd send two block updates after (ChunkUtils.updateBlockClientSide in endPredictionsUpTo
// and the packet updating from the client) // and the packet updating from the client)
this.unverifiedPredictions.compute(position, ($, serverVerifiedState) -> serverVerifiedState == null this.unverifiedPredictions.put(position, currentSequence);
? new ServerVerifiedState(currentSequence, blockState) : serverVerifiedState.setData(currentSequence, blockState));
} }
} }
public void updateServerCorrectBlockState(Vector3i position) { public void updateServerCorrectBlockState(Vector3i position, int blockState) {
if (this.unverifiedPredictions.isEmpty()) { if (!this.unverifiedPredictions.isEmpty()) {
return; this.unverifiedPredictions.removeInt(position);
} }
this.unverifiedPredictions.remove(position); ChunkUtils.updateBlock(session, blockState, position);
} }
public void endPredictionsUpTo(int sequence) { public void endPredictionsUpTo(int sequence) {
@ -166,40 +169,16 @@ public final class WorldCache {
return; return;
} }
Iterator<Map.Entry<Vector3i, ServerVerifiedState>> it = this.unverifiedPredictions.entrySet().iterator(); Iterator<Object2IntMap.Entry<Vector3i>> it = Object2IntMaps.fastIterator(this.unverifiedPredictions);
while (it.hasNext()) { while (it.hasNext()) {
Map.Entry<Vector3i, ServerVerifiedState> entry = it.next(); Object2IntMap.Entry<Vector3i> entry = it.next();
ServerVerifiedState serverVerifiedState = entry.getValue(); if (entry.getIntValue() <= sequence) {
if (serverVerifiedState.sequence <= sequence) {
// This block may be out of sync with the server // This block may be out of sync with the server
// In 1.19.0 Java, you can verify this by trying to mine in spawn protection // In 1.19.0 Java, you can verify this by trying to mine in spawn protection
ChunkUtils.updateBlockClientSide(session, serverVerifiedState.blockState, entry.getKey()); Vector3i position = entry.getKey();
ChunkUtils.updateBlockClientSide(session, session.getGeyser().getWorldManager().getBlockAt(session, position), position);
it.remove(); it.remove();
} }
} }
} }
private static class ServerVerifiedState {
private int sequence;
private int blockState;
ServerVerifiedState(int sequence, int blockState) {
this.sequence = sequence;
this.blockState = blockState;
}
ServerVerifiedState setData(int sequence, int blockState) {
this.sequence = sequence;
this.blockState = blockState;
return this;
}
@Override
public String toString() {
return "ServerVerifiedState{" +
"sequence=" + sequence +
", blockState=" + blockState +
'}';
}
}
} }

Datei anzeigen

@ -127,7 +127,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM, dropAll ? PlayerAction.DROP_ITEM_STACK : PlayerAction.DROP_ITEM,
Vector3i.ZERO, Vector3i.ZERO,
Direction.DOWN, Direction.DOWN,
session.getWorldCache().nextPredictionSequence() 0
); );
session.sendDownstreamPacket(dropPacket); session.sendDownstreamPacket(dropPacket);
@ -408,13 +408,10 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
} }
int sequence = session.getWorldCache().nextPredictionSequence(); int sequence = session.getWorldCache().nextPredictionSequence();
if (blockState != -1) { session.getWorldCache().markPositionInSequence(packet.getBlockPosition());
session.getWorldCache().addServerCorrectBlockState(packet.getBlockPosition(), blockState); // -1 means we don't know what block they're breaking
} else { if (blockState == -1) {
blockState = BlockStateValues.JAVA_AIR_ID; blockState = BlockStateValues.JAVA_AIR_ID;
// Client will desync here anyway
session.getWorldCache().addServerCorrectBlockState(packet.getBlockPosition(),
session.getGeyser().getWorldManager().getBlockAt(session, packet.getBlockPosition()));
} }
LevelEventPacket blockBreakPacket = new LevelEventPacket(); LevelEventPacket blockBreakPacket = new LevelEventPacket();
@ -442,7 +439,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
if (packet.getActionType() == 0) { if (packet.getActionType() == 0) {
// Followed to the Minecraft Protocol specification outlined at wiki.vg // Followed to the Minecraft Protocol specification outlined at wiki.vg
ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, Vector3i.ZERO, ServerboundPlayerActionPacket releaseItemPacket = new ServerboundPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, Vector3i.ZERO,
Direction.DOWN, session.getWorldCache().nextPredictionSequence()); Direction.DOWN, 0);
session.sendDownstreamPacket(releaseItemPacket); session.sendDownstreamPacket(releaseItemPacket);
} }
break; break;

Datei anzeigen

@ -41,6 +41,7 @@ import com.nukkitx.protocol.bedrock.packet.*;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.ItemFrameEntity; import org.geysermc.geyser.entity.type.ItemFrameEntity;
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
import org.geysermc.geyser.level.block.BlockStateValues;
import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
@ -128,7 +129,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
break; break;
case DROP_ITEM: case DROP_ITEM:
ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM, ServerboundPlayerActionPacket dropItemPacket = new ServerboundPlayerActionPacket(PlayerAction.DROP_ITEM,
vector, Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence()); vector, Direction.VALUES[packet.getFace()], 0);
session.sendDownstreamPacket(dropItemPacket); session.sendDownstreamPacket(dropItemPacket);
break; break;
case STOP_SLEEP: case STOP_SLEEP:
@ -171,7 +172,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
} }
int breakingBlock = session.getBreakingBlock(); int breakingBlock = session.getBreakingBlock();
if (breakingBlock == -1) { if (breakingBlock == -1) {
break; breakingBlock = BlockStateValues.JAVA_AIR_ID;
} }
Vector3f vectorFloat = vector.toFloat(); Vector3f vectorFloat = vector.toFloat();
@ -202,7 +203,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
} }
} }
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, session.getWorldCache().nextPredictionSequence()); ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, 0);
session.sendDownstreamPacket(abortBreakingPacket); session.sendDownstreamPacket(abortBreakingPacket);
LevelEventPacket stopBreak = new LevelEventPacket(); LevelEventPacket stopBreak = new LevelEventPacket();
stopBreak.setType(LevelEventType.BLOCK_STOP_BREAK); stopBreak.setType(LevelEventType.BLOCK_STOP_BREAK);

Datei anzeigen

@ -44,7 +44,7 @@ public class BedrockEmoteTranslator extends PacketTranslator<EmotePacket> {
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) {
// Activate the workaround - we should trigger the offhand now // Activate the workaround - we should trigger the offhand now
ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO, ServerboundPlayerActionPacket swapHandsPacket = new ServerboundPlayerActionPacket(PlayerAction.SWAP_HANDS, Vector3i.ZERO,
Direction.DOWN, session.getWorldCache().nextPredictionSequence()); Direction.DOWN, 0);
session.sendDownstreamPacket(swapHandsPacket); session.sendDownstreamPacket(swapHandsPacket);
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {

Datei anzeigen

@ -35,7 +35,6 @@ import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator; import org.geysermc.geyser.translator.sound.BlockSoundInteractionTranslator;
import org.geysermc.geyser.util.ChunkUtils;
@Translator(packet = ClientboundBlockUpdatePacket.class) @Translator(packet = ClientboundBlockUpdatePacket.class)
public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlockUpdatePacket> { public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlockUpdatePacket> {
@ -45,7 +44,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
Vector3i pos = packet.getEntry().getPosition(); Vector3i pos = packet.getEntry().getPosition();
boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock(); session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
ChunkUtils.updateBlock(session, packet.getEntry().getBlock(), pos); session.getWorldCache().updateServerCorrectBlockState(pos, packet.getEntry().getBlock());
if (updatePlacement) { if (updatePlacement) {
this.checkPlace(session, packet); this.checkPlace(session, packet);
} }

Datei anzeigen

@ -30,7 +30,6 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.Clientb
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
import org.geysermc.geyser.util.ChunkUtils;
@Translator(packet = ClientboundSectionBlocksUpdatePacket.class) @Translator(packet = ClientboundSectionBlocksUpdatePacket.class)
public class JavaSectionBlocksUpdateTranslator extends PacketTranslator<ClientboundSectionBlocksUpdatePacket> { public class JavaSectionBlocksUpdateTranslator extends PacketTranslator<ClientboundSectionBlocksUpdatePacket> {
@ -38,7 +37,7 @@ public class JavaSectionBlocksUpdateTranslator extends PacketTranslator<Clientbo
@Override @Override
public void translate(GeyserSession session, ClientboundSectionBlocksUpdatePacket packet) { public void translate(GeyserSession session, ClientboundSectionBlocksUpdatePacket packet) {
for (BlockChangeEntry entry : packet.getEntries()) { for (BlockChangeEntry entry : packet.getEntries()) {
ChunkUtils.updateBlock(session, entry.getBlock(), entry.getPosition()); session.getWorldCache().updateServerCorrectBlockState(entry.getPosition(), entry.getBlock());
} }
} }
} }

Datei anzeigen

@ -125,7 +125,6 @@ public class ChunkUtils {
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) { public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
updateBlockClientSide(session, blockState, position); updateBlockClientSide(session, blockState, position);
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState); session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
session.getWorldCache().updateServerCorrectBlockState(position);
} }
/** /**