mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-01-19 15:43:30 +01:00
Support the 1.19.0 Java block prediction/ack system
This commit is contained in:
parent
ab4800c4f2
commit
2ce7ff850a
11 changed files with 145 additions and 33 deletions
|
@ -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.getNextSequence());
|
Direction.DOWN, session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(releaseItemPacket);
|
session.sendDownstreamPacket(releaseItemPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -401,6 +401,8 @@ public class GeyserSession implements GeyserConnection, CommandSender {
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private boolean emulatePost1_16Logic = true;
|
private boolean emulatePost1_16Logic = true;
|
||||||
|
@Setter
|
||||||
|
private boolean emulatePost1_18Logic = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current attack speed of the player. Used for sending proper cooldown timings.
|
* The current attack speed of the player. Used for sending proper cooldown timings.
|
||||||
|
@ -1278,9 +1280,9 @@ public class GeyserSession implements GeyserConnection, CommandSender {
|
||||||
|
|
||||||
ServerboundUseItemPacket useItemPacket;
|
ServerboundUseItemPacket useItemPacket;
|
||||||
if (playerInventory.getItemInHand().getJavaId() == shield.getJavaId()) {
|
if (playerInventory.getItemInHand().getJavaId() == shield.getJavaId()) {
|
||||||
useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, getNextSequence());
|
useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, worldCache.nextPredictionSequence());
|
||||||
} else if (playerInventory.getOffhand().getJavaId() == shield.getJavaId()) {
|
} else if (playerInventory.getOffhand().getJavaId() == shield.getJavaId()) {
|
||||||
useItemPacket = new ServerboundUseItemPacket(Hand.OFF_HAND, getNextSequence());
|
useItemPacket = new ServerboundUseItemPacket(Hand.OFF_HAND, worldCache.nextPredictionSequence());
|
||||||
} else {
|
} else {
|
||||||
// No blocking
|
// No blocking
|
||||||
return false;
|
return false;
|
||||||
|
@ -1309,7 +1311,7 @@ public class GeyserSession implements GeyserConnection, CommandSender {
|
||||||
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, getNextSequence());
|
Vector3i.ZERO, Direction.DOWN, worldCache.nextPredictionSequence());
|
||||||
sendDownstreamPacket(releaseItemPacket);
|
sendDownstreamPacket(releaseItemPacket);
|
||||||
playerEntity.setFlag(EntityFlag.BLOCKING, false);
|
playerEntity.setFlag(EntityFlag.BLOCKING, false);
|
||||||
return true;
|
return true;
|
||||||
|
@ -1687,10 +1689,6 @@ public class GeyserSession implements GeyserConnection, CommandSender {
|
||||||
sendDownstreamPacket(clientSettingsPacket);
|
sendDownstreamPacket(clientSettingsPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNextSequence() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for updating statistic values since we only get changes from the server
|
* Used for updating statistic values since we only get changes from the server
|
||||||
*
|
*
|
||||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.session.cache;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateTagsPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateTagsPacket;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntLists;
|
import it.unimi.dsi.fastutil.ints.IntLists;
|
||||||
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.registry.type.BlockMapping;
|
import org.geysermc.geyser.registry.type.BlockMapping;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
@ -82,6 +83,15 @@ public class TagCache {
|
||||||
this.requiresIronTool = IntList.of(blockTags.get("minecraft:needs_iron_tool"));
|
this.requiresIronTool = IntList.of(blockTags.get("minecraft:needs_iron_tool"));
|
||||||
this.requiresDiamondTool = IntList.of(blockTags.get("minecraft:needs_diamond_tool"));
|
this.requiresDiamondTool = IntList.of(blockTags.get("minecraft:needs_diamond_tool"));
|
||||||
|
|
||||||
|
// Hack btw
|
||||||
|
GeyserLogger logger = session.getGeyser().getLogger();
|
||||||
|
int[] convertableToMud = blockTags.get("minecraft:convertable_to_mud");
|
||||||
|
boolean emulatePost1_18Logic = convertableToMud != null && convertableToMud.length != 0;
|
||||||
|
session.setEmulatePost1_18Logic(emulatePost1_18Logic);
|
||||||
|
if (logger.isDebug()) {
|
||||||
|
logger.debug("Emulating post 1.18 block predication logic for " + session.name() + "? " + emulatePost1_18Logic);
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, int[]> itemTags = packet.getTags().get("minecraft:item");
|
Map<String, int[]> itemTags = packet.getTags().get("minecraft:item");
|
||||||
this.axolotlTemptItems = IntList.of(itemTags.get("minecraft:axolotl_tempt_items"));
|
this.axolotlTemptItems = IntList.of(itemTags.get("minecraft:axolotl_tempt_items"));
|
||||||
this.fishes = IntList.of(itemTags.get("minecraft:fishes"));
|
this.fishes = IntList.of(itemTags.get("minecraft:fishes"));
|
||||||
|
@ -93,8 +103,8 @@ public class TagCache {
|
||||||
// Hack btw
|
// Hack btw
|
||||||
boolean emulatePost1_14Logic = itemTags.get("minecraft:signs").length > 1;
|
boolean emulatePost1_14Logic = itemTags.get("minecraft:signs").length > 1;
|
||||||
session.setEmulatePost1_14Logic(emulatePost1_14Logic);
|
session.setEmulatePost1_14Logic(emulatePost1_14Logic);
|
||||||
if (session.getGeyser().getLogger().isDebug()) {
|
if (logger.isDebug()) {
|
||||||
session.getGeyser().getLogger().debug("Emulating post 1.14 villager logic for " + session.name() + "? " + emulatePost1_14Logic);
|
logger.debug("Emulating post 1.14 villager logic for " + session.name() + "? " + emulatePost1_14Logic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,18 @@
|
||||||
package org.geysermc.geyser.session.cache;
|
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.protocol.bedrock.packet.SetTitlePacket;
|
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
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 java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public final class WorldCache {
|
public final class WorldCache {
|
||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
|
@ -51,6 +57,9 @@ public final class WorldCache {
|
||||||
private int trueTitleStayTime;
|
private int trueTitleStayTime;
|
||||||
private int trueTitleFadeOutTime;
|
private int trueTitleFadeOutTime;
|
||||||
|
|
||||||
|
private int currentSequence;
|
||||||
|
private final Map<Vector3i, ServerVerifiedState> unverifiedPredictions = new Object2ObjectOpenHashMap<>(1);
|
||||||
|
|
||||||
public WorldCache(GeyserSession session) {
|
public WorldCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.scoreboard = new Scoreboard(session);
|
this.scoreboard = new Scoreboard(session);
|
||||||
|
@ -121,4 +130,75 @@ public final class WorldCache {
|
||||||
forceSyncCorrectTitleTimes();
|
forceSyncCorrectTitleTimes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
|
||||||
|
public int nextPredictionSequence() {
|
||||||
|
return ++currentSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores a record of a block at a certain position to rollback in the event it is incorrect.
|
||||||
|
*/
|
||||||
|
public void addServerCorrectBlockState(Vector3i position, int blockState) {
|
||||||
|
if (session.isEmulatePost1_18Logic()) {
|
||||||
|
// Cheap hack
|
||||||
|
// 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
|
||||||
|
// and the packet updating from the client)
|
||||||
|
this.unverifiedPredictions.compute(position, ($, serverVerifiedState) -> serverVerifiedState == null
|
||||||
|
? new ServerVerifiedState(currentSequence, blockState) : serverVerifiedState.setData(currentSequence, blockState));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateServerCorrectBlockState(Vector3i position) {
|
||||||
|
if (this.unverifiedPredictions.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.unverifiedPredictions.remove(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endPredictionsUpTo(int sequence) {
|
||||||
|
if (this.unverifiedPredictions.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator<Map.Entry<Vector3i, ServerVerifiedState>> it = this.unverifiedPredictions.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Map.Entry<Vector3i, ServerVerifiedState> entry = it.next();
|
||||||
|
ServerVerifiedState serverVerifiedState = entry.getValue();
|
||||||
|
if (serverVerifiedState.sequence <= sequence) {
|
||||||
|
// 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
|
||||||
|
ChunkUtils.updateBlockClientSide(session, serverVerifiedState.blockState, entry.getKey());
|
||||||
|
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 +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -38,7 +38,10 @@ import com.nukkitx.math.vector.Vector3f;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.*;
|
import com.nukkitx.protocol.bedrock.data.inventory.*;
|
||||||
import com.nukkitx.protocol.bedrock.packet.*;
|
import com.nukkitx.protocol.bedrock.packet.ContainerOpenPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.InventoryTransactionPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
@ -126,7 +129,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.getNextSequence()
|
session.getWorldCache().nextPredictionSequence()
|
||||||
);
|
);
|
||||||
session.sendDownstreamPacket(dropPacket);
|
session.sendDownstreamPacket(dropPacket);
|
||||||
|
|
||||||
|
@ -264,7 +267,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
Hand.MAIN_HAND,
|
Hand.MAIN_HAND,
|
||||||
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
|
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
|
||||||
false,
|
false,
|
||||||
session.getNextSequence());
|
session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(blockPacket);
|
session.sendDownstreamPacket(blockPacket);
|
||||||
|
|
||||||
if (packet.getItemInHand() != null) {
|
if (packet.getItemInHand() != null) {
|
||||||
|
@ -341,7 +344,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getNextSequence());
|
ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(useItemPacket);
|
session.sendDownstreamPacket(useItemPacket);
|
||||||
|
|
||||||
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
|
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
|
||||||
|
@ -406,12 +409,22 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int sequence = session.getWorldCache().nextPredictionSequence();
|
||||||
|
if (blockState != -1) {
|
||||||
|
session.getWorldCache().addServerCorrectBlockState(packet.getBlockPosition(), blockState);
|
||||||
|
} else {
|
||||||
|
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();
|
||||||
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
blockBreakPacket.setType(LevelEventType.PARTICLE_DESTROY_BLOCK);
|
||||||
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
|
blockBreakPacket.setPosition(packet.getBlockPosition().toFloat());
|
||||||
blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
|
blockBreakPacket.setData(session.getBlockMappings().getBedrockBlockId(blockState));
|
||||||
session.sendUpstreamPacket(blockBreakPacket);
|
session.sendUpstreamPacket(blockBreakPacket);
|
||||||
session.setBreakingBlock(BlockStateValues.JAVA_AIR_ID);
|
session.setBreakingBlock(-1);
|
||||||
|
|
||||||
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
|
||||||
if (itemFrameEntity != null) {
|
if (itemFrameEntity != null) {
|
||||||
|
@ -422,7 +435,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
|
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
|
||||||
ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, packet.getBlockPosition(), Direction.VALUES[packet.getBlockFace()], session.getNextSequence());
|
ServerboundPlayerActionPacket breakPacket = new ServerboundPlayerActionPacket(action, packet.getBlockPosition(), Direction.VALUES[packet.getBlockFace()], sequence);
|
||||||
session.sendDownstreamPacket(breakPacket);
|
session.sendDownstreamPacket(breakPacket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,7 +444,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.getNextSequence());
|
Direction.DOWN, session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(releaseItemPacket);
|
session.sendDownstreamPacket(releaseItemPacket);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -578,7 +591,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||||
Vector3f target = packet.getBlockPosition().toFloat().add(packet.getClickPosition());
|
Vector3f target = packet.getBlockPosition().toFloat().add(packet.getClickPosition());
|
||||||
lookAt(session, target);
|
lookAt(session, target);
|
||||||
|
|
||||||
ServerboundUseItemPacket itemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getNextSequence());
|
ServerboundUseItemPacket itemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(itemPacket);
|
session.sendDownstreamPacket(itemPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator<LecternUpda
|
||||||
Hand.MAIN_HAND,
|
Hand.MAIN_HAND,
|
||||||
0, 0, 0, // Java doesn't care about these when dealing with a lectern
|
0, 0, 0, // Java doesn't care about these when dealing with a lectern
|
||||||
false,
|
false,
|
||||||
session.getNextSequence());
|
session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(blockPacket);
|
session.sendDownstreamPacket(blockPacket);
|
||||||
} else {
|
} else {
|
||||||
// Bedrock wants to either move a page or exit
|
// Bedrock wants to either move a page or exit
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class BedrockMobEquipmentTranslator extends PacketTranslator<MobEquipment
|
||||||
// Activate shield since we are already sneaking
|
// Activate shield since we are already sneaking
|
||||||
// (No need to send a release item packet - Java doesn't do this when swapping items)
|
// (No need to send a release item packet - Java doesn't do this when swapping items)
|
||||||
// Required to do it a tick later or else it doesn't register
|
// Required to do it a tick later or else it doesn't register
|
||||||
session.scheduleInEventLoop(() -> session.sendDownstreamPacket(new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getNextSequence())),
|
session.scheduleInEventLoop(() -> session.sendDownstreamPacket(new ServerboundUseItemPacket(Hand.MAIN_HAND, session.getWorldCache().nextPredictionSequence())),
|
||||||
50, TimeUnit.MILLISECONDS);
|
50, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ 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;
|
||||||
|
@ -129,7 +128,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.getNextSequence());
|
vector, Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(dropItemPacket);
|
session.sendDownstreamPacket(dropItemPacket);
|
||||||
break;
|
break;
|
||||||
case STOP_SLEEP:
|
case STOP_SLEEP:
|
||||||
|
@ -155,7 +154,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
String identifier = BlockRegistries.JAVA_IDENTIFIERS.get().get(blockUp);
|
String identifier = BlockRegistries.JAVA_IDENTIFIERS.get().get(blockUp);
|
||||||
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
|
if (identifier.startsWith("minecraft:fire") || identifier.startsWith("minecraft:soul_fire")) {
|
||||||
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos,
|
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING, fireBlockPos,
|
||||||
Direction.VALUES[packet.getFace()], session.getNextSequence());
|
Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(startBreakingPacket);
|
session.sendDownstreamPacket(startBreakingPacket);
|
||||||
if (session.getGameMode() == GameMode.CREATIVE) {
|
if (session.getGameMode() == GameMode.CREATIVE) {
|
||||||
break;
|
break;
|
||||||
|
@ -163,17 +162,22 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING,
|
ServerboundPlayerActionPacket startBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.START_DIGGING,
|
||||||
vector, Direction.VALUES[packet.getFace()], session.getNextSequence());
|
vector, Direction.VALUES[packet.getFace()], session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(startBreakingPacket);
|
session.sendDownstreamPacket(startBreakingPacket);
|
||||||
break;
|
break;
|
||||||
case CONTINUE_BREAK:
|
case CONTINUE_BREAK:
|
||||||
if (session.getGameMode() == GameMode.CREATIVE) {
|
if (session.getGameMode() == GameMode.CREATIVE) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
int breakingBlock = session.getBreakingBlock();
|
||||||
|
if (breakingBlock == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
Vector3f vectorFloat = vector.toFloat();
|
Vector3f vectorFloat = vector.toFloat();
|
||||||
LevelEventPacket continueBreakPacket = new LevelEventPacket();
|
LevelEventPacket continueBreakPacket = new LevelEventPacket();
|
||||||
continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
|
continueBreakPacket.setType(LevelEventType.PARTICLE_CRACK_BLOCK);
|
||||||
continueBreakPacket.setData((session.getBlockMappings().getBedrockBlockId(session.getBreakingBlock())) | (packet.getFace() << 24));
|
continueBreakPacket.setData((session.getBlockMappings().getBedrockBlockId(breakingBlock)) | (packet.getFace() << 24));
|
||||||
continueBreakPacket.setPosition(vectorFloat);
|
continueBreakPacket.setPosition(vectorFloat);
|
||||||
session.sendUpstreamPacket(continueBreakPacket);
|
session.sendUpstreamPacket(continueBreakPacket);
|
||||||
|
|
||||||
|
@ -181,7 +185,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
LevelEventPacket updateBreak = new LevelEventPacket();
|
LevelEventPacket updateBreak = new LevelEventPacket();
|
||||||
updateBreak.setType(LevelEventType.BLOCK_UPDATE_BREAK);
|
updateBreak.setType(LevelEventType.BLOCK_UPDATE_BREAK);
|
||||||
updateBreak.setPosition(vectorFloat);
|
updateBreak.setPosition(vectorFloat);
|
||||||
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.get(session.getBreakingBlock())) * 20;
|
double breakTime = BlockUtils.getSessionBreakTime(session, BlockRegistries.JAVA_BLOCKS.get(breakingBlock)) * 20;
|
||||||
updateBreak.setData((int) (65535 / breakTime));
|
updateBreak.setData((int) (65535 / breakTime));
|
||||||
session.sendUpstreamPacket(updateBreak);
|
session.sendUpstreamPacket(updateBreak);
|
||||||
break;
|
break;
|
||||||
|
@ -198,13 +202,13 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, session.getNextSequence());
|
ServerboundPlayerActionPacket abortBreakingPacket = new ServerboundPlayerActionPacket(PlayerAction.CANCEL_DIGGING, vector, Direction.DOWN, session.getWorldCache().nextPredictionSequence());
|
||||||
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);
|
||||||
stopBreak.setPosition(vector.toFloat());
|
stopBreak.setPosition(vector.toFloat());
|
||||||
stopBreak.setData(0);
|
stopBreak.setData(0);
|
||||||
session.setBreakingBlock(BlockStateValues.JAVA_AIR_ID);
|
session.setBreakingBlock(-1);
|
||||||
session.sendUpstreamPacket(stopBreak);
|
session.sendUpstreamPacket(stopBreak);
|
||||||
break;
|
break;
|
||||||
case STOP_BREAK:
|
case STOP_BREAK:
|
||||||
|
|
|
@ -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.getNextSequence());
|
Direction.DOWN, session.getWorldCache().nextPredictionSequence());
|
||||||
session.sendDownstreamPacket(swapHandsPacket);
|
session.sendDownstreamPacket(swapHandsPacket);
|
||||||
|
|
||||||
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
||||||
|
|
|
@ -35,6 +35,6 @@ public class JavaBlockChangedAckTranslator extends PacketTranslator<ClientboundB
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ClientboundBlockChangedAckPacket packet) {
|
public void translate(GeyserSession session, ClientboundBlockChangedAckPacket packet) {
|
||||||
// TODO
|
session.getWorldCache().endPredictionsUpTo(packet.getSequence());
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -123,13 +123,21 @@ public class ChunkUtils {
|
||||||
* @param position the position of the block
|
* @param position the position of the block
|
||||||
*/
|
*/
|
||||||
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
|
public static void updateBlock(GeyserSession session, int blockState, Vector3i position) {
|
||||||
|
updateBlockClientSide(session, blockState, position);
|
||||||
|
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
||||||
|
session.getWorldCache().updateServerCorrectBlockState(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a block, but client-side only.
|
||||||
|
*/
|
||||||
|
public static void updateBlockClientSide(GeyserSession session, int blockState, Vector3i position) {
|
||||||
// Checks for item frames so they aren't tripped up and removed
|
// Checks for item frames so they aren't tripped up and removed
|
||||||
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
ItemFrameEntity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, position);
|
||||||
if (itemFrameEntity != null) {
|
if (itemFrameEntity != null) {
|
||||||
if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it
|
if (blockState == JAVA_AIR_ID) { // Item frame is still present and no block overrides that; refresh it
|
||||||
itemFrameEntity.updateBlock(true);
|
itemFrameEntity.updateBlock(true);
|
||||||
// Still update the chunk cache with the new block
|
// Still update the chunk cache with the new block if updateBlock is called
|
||||||
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
// Otherwise, let's still store our reference to the item frame, but let the new block take precedence for now
|
||||||
|
@ -175,7 +183,6 @@ public class ChunkUtils {
|
||||||
break; //No block will be a part of two classes
|
break; //No block will be a part of two classes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
session.getChunkCache().updateBlock(position.getX(), position.getY(), position.getZ(), blockState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sendEmptyChunk(GeyserSession session, int chunkX, int chunkZ, boolean forceUpdate) {
|
public static void sendEmptyChunk(GeyserSession session, int chunkX, int chunkZ, boolean forceUpdate) {
|
||||||
|
|
Loading…
Reference in a new issue