Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2025-01-11 15:41:08 +01:00
Add an option to always quick-change armor
With thanks to f068217cb7/src/main/java/me/juancarloscp52/bedrockify/client/features/quickArmorSwap/ArmorReplacer.java
for making me realize this was possible.
Currently disabled by default in the event that a server implementation also has this feature. May be enabled by default in the future.
Dieser Commit ist enthalten in:
Ursprung
6667a53bca
Commit
e92633d657
@ -78,6 +78,8 @@ public interface GeyserConfiguration {
|
|||||||
|
|
||||||
boolean isDisableBedrockScaffolding();
|
boolean isDisableBedrockScaffolding();
|
||||||
|
|
||||||
|
boolean isAlwaysQuickChangeArmor();
|
||||||
|
|
||||||
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
|
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
|
||||||
|
|
||||||
String getDefaultLocale();
|
String getDefaultLocale();
|
||||||
|
@ -108,6 +108,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||||||
@JsonProperty("disable-bedrock-scaffolding")
|
@JsonProperty("disable-bedrock-scaffolding")
|
||||||
private boolean disableBedrockScaffolding = false;
|
private boolean disableBedrockScaffolding = false;
|
||||||
|
|
||||||
|
@JsonProperty("always-quick-change-armor")
|
||||||
|
private boolean alwaysQuickChangeArmor = false;
|
||||||
|
|
||||||
@JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class)
|
@JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class)
|
||||||
@JsonProperty("emote-offhand-workaround")
|
@JsonProperty("emote-offhand-workaround")
|
||||||
private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED;
|
private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED;
|
||||||
|
@ -313,18 +313,7 @@ public abstract class InventoryTranslator {
|
|||||||
|
|
||||||
if (!isSourceCursor && destination.getContainer() == ContainerSlotType.HOTBAR || destination.getContainer() == ContainerSlotType.HOTBAR_AND_INVENTORY) {
|
if (!isSourceCursor && destination.getContainer() == ContainerSlotType.HOTBAR || destination.getContainer() == ContainerSlotType.HOTBAR_AND_INVENTORY) {
|
||||||
// Tell the server we're pressing one of the hotbar keys to save clicks
|
// Tell the server we're pressing one of the hotbar keys to save clicks
|
||||||
Click click = switch (destination.getSlot()) {
|
Click click = InventoryUtils.getClickForHotbarSwap(destination.getSlot());
|
||||||
case 0 -> Click.SWAP_TO_HOTBAR_1;
|
|
||||||
case 1 -> Click.SWAP_TO_HOTBAR_2;
|
|
||||||
case 2 -> Click.SWAP_TO_HOTBAR_3;
|
|
||||||
case 3 -> Click.SWAP_TO_HOTBAR_4;
|
|
||||||
case 4 -> Click.SWAP_TO_HOTBAR_5;
|
|
||||||
case 5 -> Click.SWAP_TO_HOTBAR_6;
|
|
||||||
case 6 -> Click.SWAP_TO_HOTBAR_7;
|
|
||||||
case 7 -> Click.SWAP_TO_HOTBAR_8;
|
|
||||||
case 8 -> Click.SWAP_TO_HOTBAR_9;
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
if (click != null) {
|
if (click != null) {
|
||||||
plan.add(click, sourceSlot);
|
plan.add(click, sourceSlot);
|
||||||
break;
|
break;
|
||||||
|
@ -31,6 +31,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
|||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.InteractAction;
|
||||||
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.serverbound.inventory.ServerboundContainerClickPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundPlayerActionPacket;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundUseItemOnPacket;
|
||||||
@ -40,21 +41,26 @@ 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.*;
|
||||||
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
|
import org.geysermc.geyser.entity.type.CommandBlockMinecartEntity;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.inventory.click.Click;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
|
||||||
import org.geysermc.geyser.translator.sound.EntitySoundInteractionTranslator;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
|
import org.geysermc.geyser.translator.sound.EntitySoundInteractionTranslator;
|
||||||
import org.geysermc.geyser.util.BlockUtils;
|
import org.geysermc.geyser.util.BlockUtils;
|
||||||
|
import org.geysermc.geyser.util.InventoryUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -269,16 +275,6 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
session.setInteracting(true);
|
session.setInteracting(true);
|
||||||
}
|
}
|
||||||
case 1 -> {
|
case 1 -> {
|
||||||
if (packet.getActions().size() == 1 && packet.getLegacySlots().size() > 0) {
|
|
||||||
InventoryActionData actionData = packet.getActions().get(0);
|
|
||||||
LegacySetItemSlotData slotData = packet.getLegacySlots().get(0);
|
|
||||||
if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) {
|
|
||||||
// The player is trying to swap out an armor piece that already has an item in it
|
|
||||||
// Java Edition does not allow this; let's revert it
|
|
||||||
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handled when sneaking
|
// Handled when sneaking
|
||||||
if (session.getPlayerInventory().getItemInHand().getJavaId() == mappings.getStoredItems().shield().getJavaId()) {
|
if (session.getPlayerInventory().getItemInHand().getJavaId() == mappings.getStoredItems().shield().getJavaId()) {
|
||||||
break;
|
break;
|
||||||
@ -298,6 +294,39 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||||||
|
|
||||||
ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND);
|
ServerboundUseItemPacket useItemPacket = new ServerboundUseItemPacket(Hand.MAIN_HAND);
|
||||||
session.sendDownstreamPacket(useItemPacket);
|
session.sendDownstreamPacket(useItemPacket);
|
||||||
|
|
||||||
|
List<LegacySetItemSlotData> legacySlots = packet.getLegacySlots();
|
||||||
|
if (packet.getActions().size() == 1 && legacySlots.size() > 0) {
|
||||||
|
InventoryActionData actionData = packet.getActions().get(0);
|
||||||
|
LegacySetItemSlotData slotData = legacySlots.get(0);
|
||||||
|
if (slotData.getContainerId() == 6 && actionData.getToItem().getId() != 0) {
|
||||||
|
// The player is trying to swap out an armor piece that already has an item in it
|
||||||
|
if (session.getGeyser().getConfig().isAlwaysQuickChangeArmor()) {
|
||||||
|
// Java doesn't know when a player is in its own inventory and not, so we
|
||||||
|
// can abuse this feature to send a swap inventory packet
|
||||||
|
int bedrockHotbarSlot = packet.getHotbarSlot();
|
||||||
|
Click click = InventoryUtils.getClickForHotbarSwap(bedrockHotbarSlot);
|
||||||
|
if (click != null && slotData.getSlots().length != 0) {
|
||||||
|
Inventory playerInventory = session.getPlayerInventory();
|
||||||
|
// Bedrock sends us the index of the slot in the armor container; armor in Java
|
||||||
|
// Edition is offset by 5 in the player inventory
|
||||||
|
int armorSlot = slotData.getSlots()[0] + 5;
|
||||||
|
GeyserItemStack armorSlotItem = playerInventory.getItem(armorSlot);
|
||||||
|
GeyserItemStack hotbarItem = playerInventory.getItem(playerInventory.getOffsetForHotbar(bedrockHotbarSlot));
|
||||||
|
playerInventory.setItem(armorSlot, hotbarItem, session);
|
||||||
|
playerInventory.setItem(bedrockHotbarSlot, armorSlotItem, session);
|
||||||
|
|
||||||
|
ServerboundContainerClickPacket clickPacket = new ServerboundContainerClickPacket(
|
||||||
|
playerInventory.getId(), playerInventory.getStateId(), armorSlot,
|
||||||
|
click.actionType, click.action, null, Collections.emptyMap());
|
||||||
|
session.sendDownstreamPacket(clickPacket);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Disallowed; let's revert
|
||||||
|
session.getInventoryTranslator().updateInventory(session, session.getPlayerInventory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case 2 -> {
|
case 2 -> {
|
||||||
int blockState = session.getGameMode() == GameMode.CREATIVE ?
|
int blockState = session.getGameMode() == GameMode.CREATIVE ?
|
||||||
|
@ -41,6 +41,7 @@ import org.geysermc.geyser.inventory.Container;
|
|||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||||
|
import org.geysermc.geyser.inventory.click.Click;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
@ -50,6 +51,7 @@ import org.geysermc.geyser.translator.inventory.chest.DoubleChestInventoryTransl
|
|||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -330,4 +332,20 @@ public class InventoryUtils {
|
|||||||
session.sendUpstreamPacket(hotbarPacket);
|
session.sendUpstreamPacket(hotbarPacket);
|
||||||
// No need to send a Java packet as Bedrock sends a confirmation packet back that we translate
|
// No need to send a Java packet as Bedrock sends a confirmation packet back that we translate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static Click getClickForHotbarSwap(int slot) {
|
||||||
|
return switch (slot) {
|
||||||
|
case 0 -> Click.SWAP_TO_HOTBAR_1;
|
||||||
|
case 1 -> Click.SWAP_TO_HOTBAR_2;
|
||||||
|
case 2 -> Click.SWAP_TO_HOTBAR_3;
|
||||||
|
case 3 -> Click.SWAP_TO_HOTBAR_4;
|
||||||
|
case 4 -> Click.SWAP_TO_HOTBAR_5;
|
||||||
|
case 5 -> Click.SWAP_TO_HOTBAR_6;
|
||||||
|
case 6 -> Click.SWAP_TO_HOTBAR_7;
|
||||||
|
case 7 -> Click.SWAP_TO_HOTBAR_8;
|
||||||
|
case 8 -> Click.SWAP_TO_HOTBAR_9;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,6 +128,10 @@ show-coordinates: true
|
|||||||
# Whether Bedrock players are blocked from performing their scaffolding-style bridging.
|
# Whether Bedrock players are blocked from performing their scaffolding-style bridging.
|
||||||
disable-bedrock-scaffolding: false
|
disable-bedrock-scaffolding: false
|
||||||
|
|
||||||
|
# Whether Bedrock players can right-click outside of their inventory to replace armor in their inventory, even if the
|
||||||
|
# armor slot is already occupied (which Java Edition doesn't allow)
|
||||||
|
always-quick-change-armor: false
|
||||||
|
|
||||||
# If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind
|
# If set, when a Bedrock player performs any emote, it will swap the offhand and mainhand items, just like the Java Edition keybind
|
||||||
# There are three options this can be set to:
|
# There are three options this can be set to:
|
||||||
# disabled - the default/fallback, which doesn't apply this workaround
|
# disabled - the default/fallback, which doesn't apply this workaround
|
||||||
|
Laden…
x
In neuem Issue referenzieren
Einen Benutzer sperren