Skip to content

Commit 11857b9

Browse files
Merge branch 'master' into fix/write-race-condition
2 parents 43f8bac + 758700c commit 11857b9

15 files changed

+174
-108
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ The ultimate goal of this project is to allow Minecraft: Bedrock Edition users t
1515
Special thanks to the DragonProxy project for being a trailblazer in protocol translation and for all the team members who have joined us here!
1616

1717
## Supported Versions
18-
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.60 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
18+
Geyser is currently supporting Minecraft Bedrock 1.21.40 - 1.21.62 and Minecraft Java 1.21.4. For more information, please see [here](https://geysermc.org/wiki/geyser/supported-versions/).
1919

2020
## Setting Up
2121
Take a look [here](https://geysermc.org/wiki/geyser/setup/) for how to set up Geyser.

core/src/main/java/org/geysermc/geyser/dump/DumpInfo.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,8 @@ public static class NetworkInfo {
178178

179179
NetworkInfo() {
180180
if (AsteriskSerializer.showSensitive) {
181-
try {
181+
try (Socket socket = new Socket()) {
182182
// This is the most reliable for getting the main local IP
183-
Socket socket = new Socket();
184183
socket.connect(new InetSocketAddress("geysermc.org", 80));
185184
this.internalIP = socket.getLocalAddress().getHostAddress();
186185
} catch (IOException e1) {

core/src/main/java/org/geysermc/geyser/session/GeyserSession.java

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
341341

342342
@Setter
343343
private Vector2i lastChunkPosition = null;
344-
@Setter
345344
private int clientRenderDistance = -1;
346345
private int serverRenderDistance = -1;
347346

@@ -536,10 +535,17 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
536535

537536
/**
538537
* Counts how many ticks have occurred since an arm animation started.
539-
* -1 means there is no active arm swing; -2 means an arm swing will start in a tick.
538+
* -1 means there is no active arm swing
540539
*/
541540
private int armAnimationTicks = -1;
542541

542+
/**
543+
* The tick in which the player last hit air.
544+
* Used to ensure we dont send two sing packets for one hit.
545+
*/
546+
@Setter
547+
private int lastAirHitTick;
548+
543549
/**
544550
* Controls whether the daylight cycle gamerule has been sent to the client, so the sun/moon remain motionless.
545551
*/
@@ -1113,13 +1119,10 @@ public ScheduledFuture<?> scheduleInEventLoop(Runnable runnable, long duration,
11131119

11141120
public void updateTickingState(float tickRate, boolean frozen) {
11151121
tickThread.cancel(false);
1116-
11171122
this.tickingFrozen = frozen;
11181123

11191124
tickRate = MathUtils.clamp(tickRate, 1.0f, 10000.0f);
1120-
11211125
millisecondsPerTick = 1000.0f / tickRate;
1122-
11231126
nanosecondsPerTick = MathUtils.ceil(1000000000.0f / tickRate);
11241127
tickThread = tickEventLoop.scheduleAtFixedRate(this::tick, nanosecondsPerTick, nanosecondsPerTick, TimeUnit.NANOSECONDS);
11251128
}
@@ -1132,7 +1135,6 @@ private void executeRunnable(Runnable runnable) {
11321135
} catch (Throwable e) {
11331136
geyser.getLogger().error("Error thrown in " + this.bedrockUsername() + "'s event loop!", e);
11341137
}
1135-
11361138
}
11371139

11381140
/**
@@ -1365,13 +1367,10 @@ public void activateArmAnimationTicking() {
13651367
}
13661368

13671369
/**
1368-
* For <a href="https://github.com/GeyserMC/Geyser/issues/2113">issue 2113</a> and combating arm ticking activating being delayed in
1369-
* BedrockAnimateTranslator.
1370+
* You can't break blocks, attack entities, or use items while driving in a boat
13701371
*/
1371-
public void armSwingPending() {
1372-
if (armAnimationTicks == -1) {
1373-
armAnimationTicks = -2;
1374-
}
1372+
public boolean isHandsBusy() {
1373+
return steeringRight || steeringLeft;
13751374
}
13761375

13771376
/**
@@ -1453,11 +1452,31 @@ public void sendCommand(String command) {
14531452
sendDownstreamGamePacket(new ServerboundChatCommandSignedPacket(command, Instant.now().toEpochMilli(), 0L, Collections.emptyList(), 0, new BitSet()));
14541453
}
14551454

1455+
public void setClientRenderDistance(int clientRenderDistance) {
1456+
boolean oldSquareToCircle = this.clientRenderDistance < this.serverRenderDistance;
1457+
this.clientRenderDistance = clientRenderDistance;
1458+
boolean newSquareToCircle = this.clientRenderDistance < this.serverRenderDistance;
1459+
1460+
if (this.serverRenderDistance != -1 && oldSquareToCircle != newSquareToCircle) {
1461+
recalculateBedrockRenderDistance();
1462+
}
1463+
}
1464+
14561465
public void setServerRenderDistance(int renderDistance) {
14571466
// Ensure render distance is not above 96 as sending a larger value at any point crashes mobile clients and 96 is the max of any bedrock platform
14581467
renderDistance = Math.min(renderDistance, 96);
14591468
this.serverRenderDistance = renderDistance;
14601469

1470+
recalculateBedrockRenderDistance();
1471+
}
1472+
1473+
/**
1474+
* Ensures that the ChunkRadiusUpdatedPacket uses the correct render distance for whatever the client distance is set as.
1475+
* If the server render distance is larger than the client's, then account for this and add some extra padding.
1476+
* We don't want to apply this for every render distance, if at all possible, because
1477+
*/
1478+
private void recalculateBedrockRenderDistance() {
1479+
int renderDistance = ChunkUtils.squareToCircle(this.serverRenderDistance);
14611480
ChunkRadiusUpdatedPacket chunkRadiusUpdatedPacket = new ChunkRadiusUpdatedPacket();
14621481
chunkRadiusUpdatedPacket.setRadius(renderDistance);
14631482
upstream.sendPacket(chunkRadiusUpdatedPacket);

core/src/main/java/org/geysermc/geyser/session/cache/InputCache.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,11 @@
3232
import org.cloudburstmc.protocol.bedrock.data.InputMode;
3333
import org.cloudburstmc.protocol.bedrock.data.PlayerAuthInputData;
3434
import org.cloudburstmc.protocol.bedrock.packet.PlayerAuthInputPacket;
35+
import org.geysermc.geyser.entity.type.player.PlayerEntity;
3536
import org.geysermc.geyser.session.GeyserSession;
37+
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerState;
3638
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundPlayerInputPacket;
39+
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundPlayerCommandPacket;
3740

3841
import java.util.Set;
3942

@@ -53,7 +56,7 @@ public InputCache(GeyserSession session) {
5356
this.session = session;
5457
}
5558

56-
public void processInputs(PlayerAuthInputPacket packet) {
59+
public void processInputs(PlayerEntity entity, PlayerAuthInputPacket packet) {
5760
// Input is sent to the server before packet positions, as of 1.21.2
5861
Set<PlayerAuthInputData> bedrockInput = packet.getInputData();
5962
var oldInputPacket = this.inputPacket;
@@ -74,16 +77,29 @@ public void processInputs(PlayerAuthInputPacket packet) {
7477
right = analogMovement.getX() < 0;
7578
}
7679

80+
boolean sneaking = bedrockInput.contains(PlayerAuthInputData.SNEAKING);
81+
7782
// TODO when is UP_LEFT, etc. used?
7883
this.inputPacket = this.inputPacket
7984
.withForward(up)
8085
.withBackward(down)
8186
.withLeft(left)
8287
.withRight(right)
8388
.withJump(bedrockInput.contains(PlayerAuthInputData.JUMPING)) // Looks like this only triggers when the JUMP key input is being pressed. There's also JUMP_DOWN?
84-
.withShift(bedrockInput.contains(PlayerAuthInputData.SNEAKING))
89+
.withShift(sneaking)
8590
.withSprint(bedrockInput.contains(PlayerAuthInputData.SPRINTING)); // SPRINTING will trigger even if the player isn't moving
8691

92+
// Send sneaking state before inputs, matches Java client
93+
if (oldInputPacket.isShift() != sneaking) {
94+
if (sneaking) {
95+
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.START_SNEAKING));
96+
session.startSneaking();
97+
} else {
98+
session.sendDownstreamGamePacket(new ServerboundPlayerCommandPacket(entity.javaId(), PlayerState.STOP_SNEAKING));
99+
session.stopSneaking();
100+
}
101+
}
102+
87103
if (oldInputPacket != this.inputPacket) { // Simple equality check is fine since we're checking for an instance change.
88104
session.sendDownstreamGamePacket(this.inputPacket);
89105
}

core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistries.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import net.kyori.adventure.key.Key;
2929
import org.checkerframework.checker.nullness.qual.Nullable;
30+
import org.geysermc.geyser.inventory.item.BannerPattern;
3031
import org.geysermc.geyser.item.enchantment.Enchantment;
3132
import org.geysermc.geyser.item.type.Item;
3233
import org.geysermc.geyser.level.block.type.Block;
@@ -48,6 +49,7 @@ public class JavaRegistries {
4849
public static final JavaRegistryKey<Block> BLOCK = create("block", BlockRegistries.JAVA_BLOCKS, Block::javaId);
4950
public static final JavaRegistryKey<Item> ITEM = create("item", Registries.JAVA_ITEMS, Item::javaId);
5051
public static final JavaRegistryKey<Enchantment> ENCHANTMENT = create("enchantment", RegistryCache::enchantments);
52+
public static final JavaRegistryKey<BannerPattern> BANNER_PATTERNS = create("banner_pattern", RegistryCache::bannerPatterns);
5153

5254
private static <T> JavaRegistryKey<T> create(String key, JavaRegistryKey.NetworkSerializer<T> networkSerializer, JavaRegistryKey.NetworkDeserializer<T> networkDeserializer) {
5355
JavaRegistryKey<T> registry = new JavaRegistryKey<>(MinecraftKey.key(key), networkSerializer, networkDeserializer);

core/src/main/java/org/geysermc/geyser/translator/inventory/LoomInventoryTranslator.java

Lines changed: 14 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@
2525

2626
package org.geysermc.geyser.translator.inventory;
2727

28-
import it.unimi.dsi.fastutil.objects.Object2IntMap;
29-
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
28+
import net.kyori.adventure.key.Key;
3029
import org.checkerframework.checker.nullness.qual.Nullable;
3130
import org.cloudburstmc.nbt.NbtMap;
3231
import org.cloudburstmc.nbt.NbtType;
@@ -39,15 +38,19 @@
3938
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestAction;
4039
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.request.action.ItemStackRequestActionType;
4140
import org.cloudburstmc.protocol.bedrock.data.inventory.itemstack.response.ItemStackResponse;
41+
import org.geysermc.geyser.GeyserImpl;
4242
import org.geysermc.geyser.inventory.BedrockContainerSlot;
4343
import org.geysermc.geyser.inventory.GeyserItemStack;
4444
import org.geysermc.geyser.inventory.Inventory;
4545
import org.geysermc.geyser.inventory.SlotType;
46+
import org.geysermc.geyser.inventory.item.BannerPattern;
4647
import org.geysermc.geyser.inventory.updater.UIInventoryUpdater;
4748
import org.geysermc.geyser.item.type.BannerItem;
4849
import org.geysermc.geyser.item.type.DyeItem;
4950
import org.geysermc.geyser.level.block.Blocks;
5051
import org.geysermc.geyser.session.GeyserSession;
52+
import org.geysermc.geyser.session.cache.registry.JavaRegistries;
53+
import org.geysermc.geyser.session.cache.tags.Tag;
5154
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
5255
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
5356
import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
@@ -56,47 +59,8 @@
5659
import java.util.List;
5760

5861
public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
59-
/**
60-
* A map of Bedrock patterns to Java index. Used to request for a specific banner pattern.
61-
*/
62-
private static final Object2IntMap<String> PATTERN_TO_INDEX = new Object2IntOpenHashMap<>();
63-
64-
static {
65-
// Added from left-to-right then up-to-down in the order Java presents it
66-
int index = 0;
67-
PATTERN_TO_INDEX.put("bl", index++);
68-
PATTERN_TO_INDEX.put("br", index++);
69-
PATTERN_TO_INDEX.put("tl", index++);
70-
PATTERN_TO_INDEX.put("tr", index++);
71-
PATTERN_TO_INDEX.put("bs", index++);
72-
PATTERN_TO_INDEX.put("ts", index++);
73-
PATTERN_TO_INDEX.put("ls", index++);
74-
PATTERN_TO_INDEX.put("rs", index++);
75-
PATTERN_TO_INDEX.put("cs", index++);
76-
PATTERN_TO_INDEX.put("ms", index++);
77-
PATTERN_TO_INDEX.put("drs", index++);
78-
PATTERN_TO_INDEX.put("dls", index++);
79-
PATTERN_TO_INDEX.put("ss", index++);
80-
PATTERN_TO_INDEX.put("cr", index++);
81-
PATTERN_TO_INDEX.put("sc", index++);
82-
PATTERN_TO_INDEX.put("bt", index++);
83-
PATTERN_TO_INDEX.put("tt", index++);
84-
PATTERN_TO_INDEX.put("bts", index++);
85-
PATTERN_TO_INDEX.put("tts", index++);
86-
PATTERN_TO_INDEX.put("ld", index++);
87-
PATTERN_TO_INDEX.put("rd", index++);
88-
PATTERN_TO_INDEX.put("lud", index++);
89-
PATTERN_TO_INDEX.put("rud", index++);
90-
PATTERN_TO_INDEX.put("mc", index++);
91-
PATTERN_TO_INDEX.put("mr", index++);
92-
PATTERN_TO_INDEX.put("vh", index++);
93-
PATTERN_TO_INDEX.put("hh", index++);
94-
PATTERN_TO_INDEX.put("vhr", index++);
95-
PATTERN_TO_INDEX.put("hhb", index++);
96-
PATTERN_TO_INDEX.put("bo", index++);
97-
PATTERN_TO_INDEX.put("gra", index++);
98-
PATTERN_TO_INDEX.put("gru", index);
99-
}
62+
63+
private static final Tag<BannerPattern> NO_ITEMS_REQUIRED = new Tag<>(JavaRegistries.BANNER_PATTERNS, Key.key("no_item_required"));
10064

10165
public LoomInventoryTranslator() {
10266
super(4, Blocks.LOOM, ContainerType.LOOM, UIInventoryUpdater.INSTANCE);
@@ -136,8 +100,13 @@ public ItemStackResponse translateSpecialRequest(GeyserSession session, Inventor
136100

137101
String bedrockPattern = ((CraftLoomAction) headerData).getPatternId();
138102

139-
// Get the Java index of this pattern
140-
int index = PATTERN_TO_INDEX.getOrDefault(bedrockPattern, -1);
103+
BannerPattern requestedPattern = BannerPattern.getByBedrockIdentifier(bedrockPattern);
104+
if (requestedPattern == null) {
105+
GeyserImpl.getInstance().getLogger().warning("Unknown Bedrock pattern id: " + bedrockPattern);
106+
return rejectRequest(request);
107+
}
108+
109+
int index = session.getTagCache().get(NO_ITEMS_REQUIRED).indexOf(requestedPattern);
141110
if (index == -1) {
142111
return rejectRequest(request);
143112
}

core/src/main/java/org/geysermc/geyser/translator/level/block/entity/BrushableBlockEntityTranslator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void translateTag(GeyserSession session, NbtMapBuilder bedrockNbt, @Nulla
6464
}
6565
NbtMapBuilder itemBuilder = NbtMap.builder()
6666
.putString("Name", mapping.getBedrockIdentifier())
67-
.putByte("Count", (byte) itemTag.getByte("Count"));
67+
.putByte("Count", itemTag.getByte("Count"));
6868

6969
bedrockNbt.putCompound("item", itemBuilder.build());
7070
// controls which side the item protrudes from

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockAnimateTranslator.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,22 @@ public void translate(GeyserSession session, AnimatePacket packet) {
4545
}
4646

4747
if (packet.getAction() == AnimatePacket.Action.SWING_ARM) {
48-
session.armSwingPending();
49-
// Delay so entity damage can be processed first
48+
49+
// If this is the case, we just hit the air. Poor air.
50+
// Touch devices send PlayerAuthInputPackets with MISSED_SWING, and then the animate packet.
51+
// This tends to happen 1-2 ticks after the auth input packet.
52+
if (session.getTicks() - session.getLastAirHitTick() < 3) {
53+
return;
54+
}
55+
56+
// Windows unfortunately sends the animate packet first, then the auth input packet with the MISSED_SWING.
57+
// Often, these are sent in the same tick. In that case, the wait here ensures the auth input packet is processed first.
58+
// Other times, there is a 1-tick-delay, which would result in the swing packet sent here. The BedrockAuthInputTranslator's
59+
// MISSED_SWING case also accounts for that by checking if a swing was sent a tick ago here.
60+
61+
// Also, delay the swing so entity damage can be processed first
5062
session.scheduleInEventLoop(() -> {
51-
if (session.getArmAnimationTicks() != 0) {
63+
if (session.getArmAnimationTicks() != 0 && (session.getTicks() - session.getLastAirHitTick() > 2)) {
5264
// So, generally, a Java player can only do one *thing* at a time.
5365
// If a player right-clicks, for example, then there's probably only one action associated with
5466
// that right-click that will send a swing.
@@ -61,12 +73,12 @@ public void translate(GeyserSession session, AnimatePacket packet) {
6173
// This behavior was last touched on with ViaVersion 4.5.1 (with its packet limiter), Java 1.16.5,
6274
// and Bedrock 1.19.51.
6375
// Note for the future: we should probably largely ignore this packet and instead replicate
64-
// all actions on our end, and send swings where needed.
76+
// all actions on our end, and send swings where needed. Can be done once we replicate Block and Item interactions fully.
6577
session.sendDownstreamGamePacket(new ServerboundSwingPacket(Hand.MAIN_HAND));
6678
session.activateArmAnimationTicking();
6779
}
6880
},
69-
25,
81+
(long) (session.getMillisecondsPerTick() * 0.5),
7082
TimeUnit.MILLISECONDS
7183
);
7284
}

core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ public void translate(GeyserSession session, InventoryTransactionPacket packet)
454454
switch (packet.getActionType()) {
455455
case 0 -> processEntityInteraction(session, packet, entity); // Interact
456456
case 1 -> { // Attack
457+
if (session.isHandsBusy()) {
458+
// See Minecraft#startAttack and LocalPlayer#isHandsBusy
459+
return;
460+
}
461+
457462
int entityId;
458463
if (entity.getDefinition() == EntityDefinitions.ENDER_DRAGON) {
459464
// Redirects the attack to its body entity, this only happens when

0 commit comments

Comments
 (0)