Skip to content

Commit 0f08c11

Browse files
Merge branch 'master' into hardcore-fix
2 parents e161703 + f328e5b commit 0f08c11

File tree

60 files changed

+2648
-748
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2648
-748
lines changed

.github/ISSUE_TEMPLATE/bug_report.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
name: Bug report
22
description: Create a report to help us improve
3+
type: Bug
34
body:
45
- type: markdown
56
attributes:
67
value: |
78
Thanks for taking the time to fill out this bug report for Geyser! Fill out the following form to your best ability to help us fix the problem.
8-
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
9+
Only use this if you're absolutely sure that you found a bug and can reproduce it. For anything else, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
910
- type: textarea
1011
attributes:
1112
label: Describe the bug

.github/ISSUE_TEMPLATE/feature_request.yml

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
name: Feature request
22
description: Suggest an idea for this project
33
labels: "Feature Request"
4+
type: Feature
45
body:
56
- type: markdown
67
attributes:
78
value: |
89
Thanks for taking the time to fill out this feature request for Geyser! Please fill out the following form to your best ability to help us understand your feature request and significantly improve the chance of getting added.
9-
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://github.com/GeyserMC/Geyser/wiki/FAQ) or [the Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues).
10+
For anything else than a feature request, use: [our Discord server](https://discord.gg/geysermc), [the FAQ](https://geysermc.org/wiki/geyser/faq) or the [Common Issues](https://geysermc.org/wiki/geyser/common-issues).
1011
- type: textarea
1112
attributes:
1213
label: What feature do you want to see added?
@@ -18,4 +19,4 @@ body:
1819
label: Are there any alternatives?
1920
description: List any alternatives you might have tried
2021
validations:
21-
required: true
22+
required: true

api/src/main/java/org/geysermc/geyser/api/event/bedrock/SessionDisconnectEvent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public SessionDisconnectEvent(@NonNull GeyserConnection connection, @NonNull Str
5050
}
5151

5252
/**
53-
* Sets the disconnect reason, thereby overriding th original reason.
53+
* Sets the disconnect message shown to the Bedrock client.
5454
*
5555
* @param disconnectReason the reason for the disconnect
5656
*/

api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
import java.net.InetSocketAddress;
3434

3535
/**
36-
* Called whenever Geyser gets pinged
36+
* Called whenever Geyser gets pinged by a Bedrock client.
3737
* <p>
38-
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online,
39-
* Geyser will reply to the client with what was given.
38+
* This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online.
39+
* Geyser will reply to the client with the information provided in this event.
4040
*/
4141
public interface GeyserBedrockPingEvent extends Event {
4242

api/src/main/java/org/geysermc/geyser/api/event/java/ServerDefineCommandsEvent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* <br>
3838
* This event is mapped to the existence of Brigadier on the server.
3939
*/
40-
public class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
40+
public final class ServerDefineCommandsEvent extends ConnectionEvent implements Cancellable {
4141
private final Set<? extends CommandInfo> commands;
4242
private boolean cancelled;
4343

api/src/main/java/org/geysermc/geyser/api/event/java/ServerTransferEvent.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* Fired when the Java server sends a transfer request to a different Java server.
3838
* Geyser Extensions can listen to this event and set a target server ip/port for Bedrock players to be transferred to.
3939
*/
40-
public class ServerTransferEvent extends ConnectionEvent {
40+
public final class ServerTransferEvent extends ConnectionEvent {
4141

4242
private final String host;
4343
private final int port;

bootstrap/mod/fabric/src/main/resources/fabric.mod.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
],
2525
"depends": {
2626
"fabricloader": ">=0.16.7",
27-
"fabric": "*",
27+
"fabric-api": "*",
2828
"minecraft": ">=1.21.4"
2929
}
3030
}

bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotInjector.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
package org.geysermc.geyser.platform.spigot;
2727

28+
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
2829
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
2930
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
3031
import io.netty.bootstrap.ServerBootstrap;
@@ -176,9 +177,9 @@ private ChannelInitializer<Channel> getChildHandler(GeyserBootstrap bootstrap, C
176177
*/
177178
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
178179
MinecraftProtocol protocol = new MinecraftProtocol();
179-
LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(),
180-
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
181-
InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
180+
LocalSession session = new LocalSession(this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
181+
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.getGeyserConfig().getRemote().address());
182+
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.getGeyserConfig().getRemote().port());
182183
session.connect();
183184
}
184185

bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/command/SpigotCommandSource.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
3030
import org.bukkit.command.CommandSender;
3131
import org.bukkit.command.ConsoleCommandSender;
32+
import org.bukkit.command.RemoteConsoleCommandSender;
3233
import org.bukkit.entity.Player;
3334
import org.checkerframework.checker.nullness.qual.NonNull;
3435
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -76,7 +77,7 @@ public Object handle() {
7677

7778
@Override
7879
public boolean isConsole() {
79-
return handle instanceof ConsoleCommandSender;
80+
return handle instanceof ConsoleCommandSender || handle instanceof RemoteConsoleCommandSender;
8081
}
8182

8283
@Override

core/src/main/java/org/geysermc/geyser/GeyserImpl.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import org.geysermc.geyser.entity.EntityDefinitions;
7575
import org.geysermc.geyser.erosion.UnixSocketClientListener;
7676
import org.geysermc.geyser.event.GeyserEventBus;
77+
import org.geysermc.geyser.event.type.SessionDisconnectEventImpl;
7778
import org.geysermc.geyser.extension.GeyserExtensionManager;
7879
import org.geysermc.geyser.impl.MinecraftVersionImpl;
7980
import org.geysermc.geyser.level.BedrockDimension;
@@ -86,6 +87,7 @@
8687
import org.geysermc.geyser.scoreboard.ScoreboardUpdater;
8788
import org.geysermc.geyser.session.GeyserSession;
8889
import org.geysermc.geyser.session.PendingMicrosoftAuthentication;
90+
import org.geysermc.geyser.session.SessionDisconnectListener;
8991
import org.geysermc.geyser.session.SessionManager;
9092
import org.geysermc.geyser.session.cache.RegistryCache;
9193
import org.geysermc.geyser.skin.FloodgateSkinUploader;
@@ -101,7 +103,6 @@
101103
import org.geysermc.geyser.util.NewsHandler;
102104
import org.geysermc.geyser.util.VersionCheckUtils;
103105
import org.geysermc.geyser.util.WebUtils;
104-
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
105106

106107
import java.io.File;
107108
import java.io.FileWriter;
@@ -266,6 +267,8 @@ public void initialize() {
266267

267268
// Register our general permissions when possible
268269
eventBus.subscribe(this, GeyserRegisterPermissionsEvent.class, Permissions::register);
270+
// Replace disconnect messages whenever necessary
271+
eventBus.subscribe(this, SessionDisconnectEventImpl.class, SessionDisconnectListener::onSessionDisconnect);
269272

270273
startInstance();
271274

core/src/main/java/org/geysermc/geyser/entity/type/Entity.java

+1
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ protected void initializeMetadata() {
176176
setFlag(EntityFlag.HAS_COLLISION, true);
177177
setFlag(EntityFlag.CAN_SHOW_NAME, true);
178178
setFlag(EntityFlag.CAN_CLIMB, true);
179+
setFlag(EntityFlag.HIDDEN_WHEN_INVISIBLE, true);
179180
// Let the Java server (or us) supply all sounds for an entity
180181
setClientSideSilent();
181182
}

core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java

+13-4
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,23 @@ public void setItemInFrame(EntityMetadata<ItemStack, ?> entityMetadata) {
113113
if (entityMetadata.getValue() != null) {
114114
this.heldItem = entityMetadata.getValue();
115115
ItemData itemData = ItemTranslator.translateToBedrock(session, heldItem);
116-
117116
String customIdentifier = session.getItemMappings().getCustomIdMappings().get(itemData.getDefinition().getRuntimeId());
118117

119118
NbtMapBuilder builder = NbtMap.builder();
120-
121119
builder.putByte("Count", (byte) itemData.getCount());
122-
if (itemData.getTag() != null) {
123-
builder.put("tag", itemData.getTag());
120+
NbtMap itemDataTag = itemData.getTag();
121+
if (itemDataTag != null) {
122+
// Remove custom name that Geyser sets for items due to translating non-"custom_name" components
123+
String customName = ItemTranslator.getCustomName(session, heldItem.getDataComponents(),
124+
session.getItemMappings().getMapping(heldItem), 'f', true, false);
125+
if (customName == null) {
126+
// No custom name found, must modify tag if custom name exists
127+
NbtMapBuilder copy = itemDataTag.toBuilder();
128+
copy.remove("display"); // Also removes lore, but, should not matter
129+
itemDataTag = copy.build();
130+
}
131+
132+
builder.put("tag", itemDataTag);
124133
}
125134
builder.putShort("Damage", (short) itemData.getDamage());
126135
builder.putString("Name", customIdentifier != null ? customIdentifier : session.getItemMappings().getMapping(entityMetadata.getValue()).getBedrockIdentifier());

core/src/main/java/org/geysermc/geyser/entity/type/TextDisplayEntity.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ protected void initializeMetadata() {
6767
}
6868

6969
public void setText(EntityMetadata<Component, ?> entityMetadata) {
70-
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue()));
70+
this.dirtyMetadata.put(EntityDataTypes.NAME, MessageTranslator.convertMessage(entityMetadata.getValue(), session.locale()));
7171
calculateLineCount(entityMetadata.getValue());
7272
}
7373

core/src/main/java/org/geysermc/geyser/entity/type/living/monster/CreakingEntity.java

-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ public CreakingEntity(GeyserSession session, int entityId, long geyserId, UUID u
5353
@Override
5454
protected void initializeMetadata() {
5555
super.initializeMetadata();
56-
setFlag(EntityFlag.HIDDEN_WHEN_INVISIBLE, true);
5756
setFlag(EntityFlag.FIRE_IMMUNE, true);
5857
}
5958

core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaDisconnectTranslator.java core/src/main/java/org/geysermc/geyser/event/type/SessionDisconnectEventImpl.java

+17-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
2+
* Copyright (c) 2025 GeyserMC. http://geysermc.org
33
*
44
* Permission is hereby granted, free of charge, to any person obtaining a copy
55
* of this software and associated documentation files (the "Software"), to deal
@@ -23,19 +23,26 @@
2323
* @link https://github.com/GeyserMC/Geyser
2424
*/
2525

26-
package org.geysermc.geyser.translator.protocol.java;
26+
package org.geysermc.geyser.event.type;
2727

28-
import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.ClientboundDisconnectPacket;
28+
import lombok.Getter;
29+
import net.kyori.adventure.text.Component;
30+
import org.checkerframework.checker.nullness.qual.NonNull;
31+
import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent;
2932
import org.geysermc.geyser.session.GeyserSession;
30-
import org.geysermc.geyser.translator.protocol.PacketTranslator;
31-
import org.geysermc.geyser.translator.protocol.Translator;
3233
import org.geysermc.geyser.translator.text.MessageTranslator;
3334

34-
@Translator(packet = ClientboundDisconnectPacket.class)
35-
public class JavaDisconnectTranslator extends PacketTranslator<ClientboundDisconnectPacket> {
35+
/**
36+
* A wrapper around the {@link SessionDisconnectEvent} that allows
37+
* Geyser to access the underlying component when replacing disconnect messages.
38+
*/
39+
@Getter
40+
public class SessionDisconnectEventImpl extends SessionDisconnectEvent {
41+
42+
private final Component reasonComponent;
3643

37-
@Override
38-
public void translate(GeyserSession session, ClientboundDisconnectPacket packet) {
39-
session.disconnect(MessageTranslator.convertMessage(packet.getReason(), session.locale()));
44+
public SessionDisconnectEventImpl(@NonNull GeyserSession session, Component reason) {
45+
super(session, MessageTranslator.convertMessageRaw(reason, session.locale()));
46+
this.reasonComponent = reason;
4047
}
4148
}

core/src/main/java/org/geysermc/geyser/inventory/GeyserItemStack.java

+43-4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.geysermc.geyser.registry.Registries;
4040
import org.geysermc.geyser.registry.type.ItemMapping;
4141
import org.geysermc.geyser.session.GeyserSession;
42+
import org.geysermc.geyser.session.cache.BundleCache;
4243
import org.geysermc.geyser.translator.item.ItemTranslator;
4344
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
4445
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
@@ -59,19 +60,23 @@ public class GeyserItemStack {
5960
private DataComponents components;
6061
private int netId;
6162

63+
@EqualsAndHashCode.Exclude
64+
private BundleCache.BundleData bundleData;
65+
6266
@Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE)
6367
@EqualsAndHashCode.Exclude
6468
private Item item;
6569

6670
private GeyserItemStack(int javaId, int amount, DataComponents components) {
67-
this(javaId, amount, components, 1);
71+
this(javaId, amount, components, 1, null);
6872
}
6973

70-
private GeyserItemStack(int javaId, int amount, DataComponents components, int netId) {
74+
private GeyserItemStack(int javaId, int amount, DataComponents components, int netId, BundleCache.BundleData bundleData) {
7175
this.javaId = javaId;
7276
this.amount = amount;
7377
this.components = components;
7478
this.netId = netId;
79+
this.bundleData = bundleData;
7580
}
7681

7782
public static @NonNull GeyserItemStack of(int javaId, int amount) {
@@ -173,6 +178,24 @@ public int getNetId() {
173178
return isEmpty() ? 0 : netId;
174179
}
175180

181+
public int getBundleId() {
182+
if (isEmpty()) {
183+
return -1;
184+
}
185+
186+
return bundleData == null ? -1 : bundleData.bundleId();
187+
}
188+
189+
public void mergeBundleData(GeyserSession session, BundleCache.BundleData oldBundleData) {
190+
if (oldBundleData != null && this.bundleData != null) {
191+
// Old bundle; re-use old IDs
192+
this.bundleData.updateNetIds(session, oldBundleData);
193+
} else if (this.bundleData != null) {
194+
// New bundle; allocate new ID
195+
session.getBundleCache().markNewBundle(this.bundleData);
196+
}
197+
}
198+
176199
public void add(int add) {
177200
amount += add;
178201
}
@@ -186,6 +209,21 @@ public ItemStack getItemStack() {
186209
}
187210

188211
public @Nullable ItemStack getItemStack(int newAmount) {
212+
if (isEmpty()) {
213+
return null;
214+
}
215+
// Sync our updated bundle data to server, if applicable
216+
// Not fresh from server? Then we have changes to apply!~
217+
if (bundleData != null && !bundleData.freshFromServer()) {
218+
if (!bundleData.contents().isEmpty()) {
219+
getOrCreateComponents().put(DataComponentType.BUNDLE_CONTENTS, bundleData.toComponent());
220+
} else {
221+
if (components != null) {
222+
// Empty list = no component = should delete
223+
components.getDataComponents().remove(DataComponentType.BUNDLE_CONTENTS);
224+
}
225+
}
226+
}
189227
return isEmpty() ? null : new ItemStack(javaId, newAmount, components);
190228
}
191229

@@ -196,7 +234,8 @@ public ItemData getItemData(GeyserSession session) {
196234
ItemData.Builder itemData = ItemTranslator.translateToBedrock(session, javaId, amount, components);
197235
itemData.netId(getNetId());
198236
itemData.usingNetId(true);
199-
return itemData.build();
237+
238+
return session.getBundleCache().checkForBundle(this, itemData);
200239
}
201240

202241
public ItemMapping getMapping(GeyserSession session) {
@@ -229,6 +268,6 @@ public GeyserItemStack copy() {
229268
}
230269

231270
public GeyserItemStack copy(int newAmount) {
232-
return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, components == null ? null : components.clone(), netId);
271+
return isEmpty() ? EMPTY : new GeyserItemStack(javaId, newAmount, components == null ? null : components.clone(), netId, bundleData == null ? null : bundleData.copy());
233272
}
234273
}

core/src/main/java/org/geysermc/geyser/inventory/Inventory.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,21 @@ public void setItem(int slot, @NonNull GeyserItemStack newItem, GeyserSession se
142142
}
143143
}
144144

145-
protected void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
145+
public static void updateItemNetId(GeyserItemStack oldItem, GeyserItemStack newItem, GeyserSession session) {
146146
if (!newItem.isEmpty()) {
147147
ItemDefinition oldMapping = ItemTranslator.getBedrockItemDefinition(session, oldItem);
148148
ItemDefinition newMapping = ItemTranslator.getBedrockItemDefinition(session, newItem);
149149
if (oldMapping.equals(newMapping)) {
150150
newItem.setNetId(oldItem.getNetId());
151+
newItem.mergeBundleData(session, oldItem.getBundleData());
151152
} else {
152153
newItem.setNetId(session.getNextItemNetId());
154+
session.getBundleCache().markNewBundle(newItem.getBundleData());
155+
session.getBundleCache().onOldItemDelete(oldItem);
153156
}
157+
} else {
158+
// Empty item means no more bundle if one existed.
159+
session.getBundleCache().onOldItemDelete(oldItem);
154160
}
155161
}
156162

core/src/main/java/org/geysermc/geyser/inventory/StonecutterContainer.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525

2626
package org.geysermc.geyser.inventory;
2727

28-
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
2928
import lombok.Getter;
3029
import lombok.Setter;
3130
import org.checkerframework.checker.nullness.qual.NonNull;
3231
import org.geysermc.geyser.session.GeyserSession;
32+
import org.geysermc.mcprotocollib.protocol.data.game.inventory.ContainerType;
3333

3434
public class StonecutterContainer extends Container {
3535
/**

core/src/main/java/org/geysermc/geyser/inventory/click/Click.java

+3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@
3131
@AllArgsConstructor
3232
public enum Click {
3333
LEFT(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
34+
LEFT_BUNDLE(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
35+
LEFT_BUNDLE_FROM_CURSOR(ContainerActionType.CLICK_ITEM, ClickItemAction.LEFT_CLICK),
3436
RIGHT(ContainerActionType.CLICK_ITEM, ClickItemAction.RIGHT_CLICK),
37+
RIGHT_BUNDLE(ContainerActionType.CLICK_ITEM, ClickItemAction.RIGHT_CLICK),
3538
LEFT_SHIFT(ContainerActionType.SHIFT_CLICK_ITEM, ShiftClickItemAction.LEFT_CLICK),
3639
DROP_ONE(ContainerActionType.DROP_ITEM, DropItemAction.DROP_FROM_SELECTED),
3740
DROP_ALL(ContainerActionType.DROP_ITEM, DropItemAction.DROP_SELECTED_STACK),

0 commit comments

Comments
 (0)