mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-01-03 17:52:15 +01:00
Goat horns and item cooldowns for 1.21.2 (#5102)
Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
This commit is contained in:
parent
4588f341ec
commit
6cc2aa3697
19 changed files with 320 additions and 66 deletions
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.inventory.item;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.registry.JavaRegistry;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.MinecraftKey;
|
||||
import org.geysermc.geyser.util.SoundUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.level.sound.BuiltinSound;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public interface GeyserInstrument {
|
||||
|
||||
static GeyserInstrument read(RegistryEntryContext context) {
|
||||
NbtMap data = context.data();
|
||||
String soundEvent = SoundUtils.readSoundEvent(data, "instrument " + context.id());
|
||||
float range = data.getFloat("range");
|
||||
String description = MessageTranslator.deserializeDescriptionForTooltip(context.session(), data);
|
||||
BedrockInstrument bedrockInstrument = BedrockInstrument.getByJavaIdentifier(context.id());
|
||||
return new GeyserInstrument.Impl(soundEvent, range, description, bedrockInstrument);
|
||||
}
|
||||
|
||||
String soundEvent();
|
||||
|
||||
float range();
|
||||
|
||||
/**
|
||||
* In Bedrock format
|
||||
*/
|
||||
String description();
|
||||
|
||||
BedrockInstrument bedrockInstrument();
|
||||
|
||||
/**
|
||||
* @return the ID of the Bedrock counterpart for this instrument. If there is none ({@link #bedrockInstrument()} is null), then -1 is returned.
|
||||
*/
|
||||
default int bedrockId() {
|
||||
BedrockInstrument bedrockInstrument = bedrockInstrument();
|
||||
if (bedrockInstrument != null) {
|
||||
return bedrockInstrument.ordinal();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the ID of the Java counterpart for the given Bedrock ID. If an invalid Bedrock ID was given, or there is no counterpart, -1 is returned.
|
||||
*/
|
||||
static int bedrockIdToJava(GeyserSession session, int id) {
|
||||
JavaRegistry<GeyserInstrument> instruments = session.getRegistryCache().instruments();
|
||||
BedrockInstrument bedrockInstrument = BedrockInstrument.getByBedrockId(id);
|
||||
if (bedrockInstrument != null) {
|
||||
for (int i = 0; i < instruments.values().size(); i++) {
|
||||
GeyserInstrument instrument = instruments.byId(i);
|
||||
if (instrument.bedrockInstrument() == bedrockInstrument) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static GeyserInstrument fromHolder(GeyserSession session, Holder<Instrument> holder) {
|
||||
if (holder.isId()) {
|
||||
return session.getRegistryCache().instruments().byId(holder.id());
|
||||
}
|
||||
Instrument custom = holder.custom();
|
||||
return new Wrapper(custom, session.locale());
|
||||
}
|
||||
|
||||
record Wrapper(Instrument instrument, String locale) implements GeyserInstrument {
|
||||
@Override
|
||||
public String soundEvent() {
|
||||
return instrument.getSoundEvent().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range() {
|
||||
return instrument.getRange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return MessageTranslator.convertMessageForTooltip(instrument.getDescription(), locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BedrockInstrument bedrockInstrument() {
|
||||
if (instrument.getSoundEvent() instanceof BuiltinSound) {
|
||||
return BedrockInstrument.getByJavaIdentifier(MinecraftKey.key(instrument.getSoundEvent().getName()));
|
||||
}
|
||||
// Probably custom
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
record Impl(String soundEvent, float range, String description, @Nullable BedrockInstrument bedrockInstrument) implements GeyserInstrument {
|
||||
}
|
||||
|
||||
/**
|
||||
* Each vanilla instrument on Bedrock, ordered in their network IDs.
|
||||
*/
|
||||
enum BedrockInstrument {
|
||||
PONDER,
|
||||
SING,
|
||||
SEEK,
|
||||
FEEL,
|
||||
ADMIRE,
|
||||
CALL,
|
||||
YEARN,
|
||||
DREAM;
|
||||
|
||||
private static final BedrockInstrument[] VALUES = values();
|
||||
private final Key javaIdentifier;
|
||||
|
||||
BedrockInstrument() {
|
||||
this.javaIdentifier = MinecraftKey.key(this.name().toLowerCase(Locale.ENGLISH) + "_goat_horn");
|
||||
}
|
||||
|
||||
public static @Nullable BedrockInstrument getByJavaIdentifier(Key javaIdentifier) {
|
||||
for (BedrockInstrument instrument : VALUES) {
|
||||
if (instrument.javaIdentifier.equals(javaIdentifier)) {
|
||||
return instrument;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static @Nullable BedrockInstrument getByBedrockId(int bedrockId) {
|
||||
if (bedrockId >= 0 && bedrockId < VALUES.length) {
|
||||
return VALUES[bedrockId];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @param description only populated if {@link #bedrockEnchantment()} is not null.
|
||||
* @param description only populated if {@link #bedrockEnchantment()} is null.
|
||||
* @param anvilCost also as a rarity multiplier
|
||||
*/
|
||||
public record Enchantment(String identifier,
|
||||
|
@ -66,8 +66,6 @@ public record Enchantment(String identifier,
|
|||
|
||||
BedrockEnchantment bedrockEnchantment = BedrockEnchantment.getByJavaIdentifier(context.id().asString());
|
||||
|
||||
// TODO - description is a component. So if a hardcoded literal string is given, this will display normally on Java,
|
||||
// but Geyser will attempt to lookup the literal string as translation - and will fail, displaying an empty string as enchantment name.
|
||||
String description = bedrockEnchantment == null ? MessageTranslator.deserializeDescription(context.session(), data) : null;
|
||||
|
||||
return new Enchantment(context.id().asString(), effects, supportedItems, maxLevel,
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.geysermc.geyser.inventory.item.Potion;
|
|||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
|
||||
|
||||
|
@ -41,9 +42,9 @@ public class ArrowItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
Potion potion = Potion.getByTippedArrowDamage(itemData.getDamage());
|
||||
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
|
||||
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
|
||||
if (potion != null) {
|
||||
itemStack = Items.TIPPED_ARROW.newItemStack(itemStack.getAmount(), itemStack.getComponents());
|
||||
PotionContents contents = potion.toComponent();
|
||||
|
|
|
@ -43,11 +43,11 @@ public class CompassItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (isLodestoneCompass(components)) {
|
||||
return super.translateToBedrock(count, components, mappings.getLodestoneCompass(), mappings);
|
||||
return super.translateToBedrock(session, count, components, mappings.getLodestoneCompass(), mappings);
|
||||
}
|
||||
return super.translateToBedrock(count, components, mapping, mappings);
|
||||
return super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -78,12 +78,12 @@ public class CompassItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
if (mapping.getBedrockIdentifier().equals("minecraft:lodestone_compass")) {
|
||||
// Revert the entry back to the compass
|
||||
mapping = mappings.getStoredItems().compass();
|
||||
}
|
||||
|
||||
return super.translateToJava(itemData, mapping, mappings);
|
||||
return super.translateToJava(session, itemData, mapping, mappings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.item.type;
|
|||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
|
@ -37,8 +38,8 @@ public class FilledMapItem extends MapItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings);
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
if (components == null) {
|
||||
// This is a fallback for maps with no nbt (Change added back in June 2020; is it needed in 2023?)
|
||||
//return builder.tag(NbtMap.builder().putInt("map", 0).build()); TODO if this is *still* broken, let's move it to translateComponentsToBedrock
|
||||
|
|
|
@ -28,8 +28,11 @@ package org.geysermc.geyser.item.type;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.inventory.item.GeyserInstrument;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
@ -41,24 +44,45 @@ public class GoatHornItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
ItemData.Builder builder = super.translateToBedrock(count, components, mapping, mappings);
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
ItemData.Builder builder = super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
if (components == null) {
|
||||
return builder;
|
||||
}
|
||||
Holder<Instrument> instrument = components.get(DataComponentType.INSTRUMENT);
|
||||
if (instrument != null && instrument.isId()) {
|
||||
builder.damage(instrument.id());
|
||||
|
||||
Holder<Instrument> holder = components.get(DataComponentType.INSTRUMENT);
|
||||
if (holder != null) {
|
||||
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
|
||||
int bedrockId = instrument.bedrockId();
|
||||
if (bedrockId >= 0) {
|
||||
builder.damage(bedrockId);
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
|
||||
public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) {
|
||||
super.translateComponentsToBedrock(session, components, builder);
|
||||
|
||||
Holder<Instrument> holder = components.get(DataComponentType.INSTRUMENT);
|
||||
if (holder != null && components.get(DataComponentType.HIDE_TOOLTIP) == null
|
||||
&& components.get(DataComponentType.HIDE_ADDITIONAL_TOOLTIP) == null) {
|
||||
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
|
||||
if (instrument.bedrockInstrument() == null) {
|
||||
builder.getOrCreateLore().add(instrument.description());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
|
||||
|
||||
int damage = itemData.getDamage();
|
||||
itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(damage));
|
||||
// This could cause an issue since -1 is returned for non-vanilla goat horns
|
||||
itemStack.getOrCreateComponents().put(DataComponentType.INSTRUMENT, Holder.ofId(GeyserInstrument.bedrockIdToJava(session, damage)));
|
||||
|
||||
return itemStack;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public class Item {
|
|||
|
||||
/* Translation methods to Bedrock and back */
|
||||
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (this == Items.AIR || count <= 0) {
|
||||
// Return, essentially, air
|
||||
return ItemData.builder();
|
||||
|
@ -130,7 +130,7 @@ public class Item {
|
|||
return builder;
|
||||
}
|
||||
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
return GeyserItemStack.of(javaId, itemData.getCount());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
|
|||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
||||
|
@ -40,8 +41,8 @@ public class OminousBottleItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
var builder = super.translateToBedrock(count, components, mapping, mappings);
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, @Nullable DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
var builder = super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
if (components == null) {
|
||||
// Level 1 ominous bottle is null components - Java 1.21.
|
||||
return builder;
|
||||
|
@ -54,9 +55,9 @@ public class OminousBottleItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
// This item can be pulled from the creative inventory with amplifiers.
|
||||
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
|
||||
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
|
||||
int damage = itemData.getDamage();
|
||||
if (damage == 0) {
|
||||
return itemStack;
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
|||
import org.geysermc.geyser.inventory.item.Potion;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.item.CustomItemTranslator;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
|
@ -44,8 +45,8 @@ public class PotionItem extends Item {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (components == null) return super.translateToBedrock(count, components, mapping, mappings);
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (components == null) return super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
ItemDefinition customItemDefinition = CustomItemTranslator.getCustomItem(components, mapping);
|
||||
|
@ -64,13 +65,13 @@ public class PotionItem extends Item {
|
|||
.count(count);
|
||||
}
|
||||
}
|
||||
return super.translateToBedrock(count, components, mapping, mappings);
|
||||
return super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull GeyserItemStack translateToJava(@NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) {
|
||||
Potion potion = Potion.getByBedrockId(itemData.getDamage());
|
||||
GeyserItemStack itemStack = super.translateToJava(itemData, mapping, mappings);
|
||||
GeyserItemStack itemStack = super.translateToJava(session, itemData, mapping, mappings);
|
||||
if (potion != null) {
|
||||
itemStack.getOrCreateComponents().put(DataComponentType.POTION_CONTENTS, potion.toComponent());
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.geysermc.geyser.GeyserImpl;
|
|||
import org.geysermc.geyser.inventory.item.Potion;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents;
|
||||
|
@ -40,7 +41,7 @@ public class TippedArrowItem extends ArrowItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ItemData.Builder translateToBedrock(int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
public ItemData.Builder translateToBedrock(GeyserSession session, int count, DataComponents components, ItemMapping mapping, ItemMappings mappings) {
|
||||
if (components != null) {
|
||||
PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS);
|
||||
if (potionContents != null) {
|
||||
|
@ -54,6 +55,6 @@ public class TippedArrowItem extends ArrowItem {
|
|||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion (tipped arrow): " + potionContents.getPotionId());
|
||||
}
|
||||
}
|
||||
return super.translateToBedrock(count, components, mapping, mappings);
|
||||
return super.translateToBedrock(session, count, components, mapping, mappings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,21 +29,13 @@ import org.cloudburstmc.nbt.NbtMap;
|
|||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.session.cache.registry.RegistryEntryContext;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.SoundUtils;
|
||||
|
||||
public record JukeboxSong(String soundEvent, String description) {
|
||||
|
||||
public static JukeboxSong read(RegistryEntryContext context) {
|
||||
NbtMap data = context.data();
|
||||
Object soundEventObject = data.get("sound_event");
|
||||
String soundEvent;
|
||||
if (soundEventObject instanceof NbtMap map) {
|
||||
soundEvent = map.getString("sound_id");
|
||||
} else if (soundEventObject instanceof String string) {
|
||||
soundEvent = string;
|
||||
} else {
|
||||
soundEvent = "";
|
||||
GeyserImpl.getInstance().getLogger().debug("Sound event for " + context.id() + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject);
|
||||
}
|
||||
String soundEvent = SoundUtils.readSoundEvent(data, "jukebox song " + context.id());;
|
||||
String description = MessageTranslator.deserializeDescription(context.session(), data);
|
||||
return new JukeboxSong(soundEvent, description);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity;
|
|||
import org.geysermc.geyser.inventory.item.BannerPattern;
|
||||
import org.geysermc.geyser.inventory.recipe.TrimRecipe;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.inventory.item.GeyserInstrument;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.JukeboxSong;
|
||||
import org.geysermc.geyser.level.PaintingType;
|
||||
|
@ -90,6 +91,7 @@ public final class RegistryCache {
|
|||
register(JavaRegistries.BIOME, (cache, array) -> cache.biomeTranslations = array, BiomeTranslator::loadServerBiome);
|
||||
register(JavaRegistries.BANNER_PATTERN, cache -> cache.bannerPatterns, context -> BannerPattern.getByJavaIdentifier(context.id()));
|
||||
register(JavaRegistries.WOLF_VARIANT, cache -> cache.wolfVariants, context -> WolfEntity.BuiltInWolfVariant.getByJavaIdentifier(context.id().asString()));
|
||||
register(JavaRegistries.INSTRUMENT, cache -> cache.instruments, GeyserInstrument::read);
|
||||
|
||||
// Load from MCProtocolLib's classloader
|
||||
NbtMap tag = MinecraftProtocol.loadNetworkCodec();
|
||||
|
@ -129,6 +131,7 @@ public final class RegistryCache {
|
|||
|
||||
private final JavaRegistry<BannerPattern> bannerPatterns = new SimpleJavaRegistry<>();
|
||||
private final JavaRegistry<WolfEntity.BuiltInWolfVariant> wolfVariants = new SimpleJavaRegistry<>();
|
||||
private final JavaRegistry<GeyserInstrument> instruments = new SimpleJavaRegistry<>();
|
||||
|
||||
public RegistryCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
|
|
|
@ -31,15 +31,18 @@ import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||
import org.geysermc.geyser.scoreboard.Scoreboard;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.item.component.UseCooldown;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.setting.Difficulty;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
@ -72,7 +75,7 @@ public final class WorldCache {
|
|||
@Setter
|
||||
private boolean editingSignOnFront;
|
||||
|
||||
private final Object2IntMap<Item> activeCooldowns = new Object2IntOpenHashMap<>(2);
|
||||
private final Object2IntMap<String> activeCooldowns = new Object2IntOpenHashMap<>(2);
|
||||
|
||||
public WorldCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
|
@ -204,17 +207,24 @@ public final class WorldCache {
|
|||
return this.activeRecords.remove(pos);
|
||||
}
|
||||
|
||||
public void setCooldown(Item item, int ticks) {
|
||||
public void setCooldown(Key cooldownGroup, int ticks) {
|
||||
if (ticks == 0) {
|
||||
// As of Java 1.21
|
||||
this.activeCooldowns.removeInt(item);
|
||||
this.activeCooldowns.removeInt(cooldownGroup.asString());
|
||||
return;
|
||||
}
|
||||
this.activeCooldowns.put(item, session.getTicks() + ticks);
|
||||
this.activeCooldowns.put(cooldownGroup.asString(), session.getTicks() + ticks);
|
||||
}
|
||||
|
||||
public boolean hasCooldown(Item item) {
|
||||
return this.activeCooldowns.containsKey(item);
|
||||
public boolean hasCooldown(GeyserItemStack item) {
|
||||
UseCooldown cooldown = item.getComponent(DataComponentType.USE_COOLDOWN);
|
||||
String cooldownGroup;
|
||||
if (cooldown != null && cooldown.cooldownGroup() != null) {
|
||||
cooldownGroup = cooldown.cooldownGroup().asString();
|
||||
} else {
|
||||
cooldownGroup = item.asItem().javaIdentifier();
|
||||
}
|
||||
return this.activeCooldowns.containsKey(cooldownGroup);
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
|
@ -222,9 +232,9 @@ public final class WorldCache {
|
|||
// but we don't want the cooldown field to balloon in size from overuse.
|
||||
if (!this.activeCooldowns.isEmpty()) {
|
||||
int ticks = session.getTicks();
|
||||
Iterator<Object2IntMap.Entry<Item>> it = Object2IntMaps.fastIterator(this.activeCooldowns);
|
||||
Iterator<Object2IntMap.Entry<String>> it = Object2IntMaps.fastIterator(this.activeCooldowns);
|
||||
while (it.hasNext()) {
|
||||
Object2IntMap.Entry<Item> entry = it.next();
|
||||
Object2IntMap.Entry<String> entry = it.next();
|
||||
if (entry.getIntValue() <= ticks) {
|
||||
it.remove();
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.geysermc.geyser.entity.type.living.animal.tameable.WolfEntity;
|
|||
import org.geysermc.geyser.inventory.item.BannerPattern;
|
||||
import org.geysermc.geyser.item.enchantment.Enchantment;
|
||||
import org.geysermc.geyser.item.type.Item;
|
||||
import org.geysermc.geyser.inventory.item.GeyserInstrument;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.level.JukeboxSong;
|
||||
import org.geysermc.geyser.level.PaintingType;
|
||||
|
@ -62,6 +63,7 @@ public class JavaRegistries {
|
|||
public static final JavaRegistryKey<PaintingType> PAINTING_VARIANT = create("painting_variant", RegistryCache::paintings);
|
||||
public static final JavaRegistryKey<TrimMaterial> TRIM_MATERIAL = create("trim_material", RegistryCache::trimMaterials);
|
||||
public static final JavaRegistryKey<TrimPattern> TRIM_PATTERN = create("trim_pattern", RegistryCache::trimPatterns);
|
||||
public static final JavaRegistryKey<GeyserInstrument> INSTRUMENT = create("instrument", RegistryCache::instruments);
|
||||
/**
|
||||
* This registry should not be used in holder sets, tags, etc. It's simply used as a mapping from Java biomes to Bedrock ones.
|
||||
*/
|
||||
|
|
|
@ -108,7 +108,7 @@ public final class ItemTranslator {
|
|||
ItemMapping bedrockItem = session.getItemMappings().getMapping(data);
|
||||
Item javaItem = bedrockItem.getJavaItem();
|
||||
|
||||
GeyserItemStack itemStack = javaItem.translateToJava(data, bedrockItem, session.getItemMappings());
|
||||
GeyserItemStack itemStack = javaItem.translateToJava(session, data, bedrockItem, session.getItemMappings());
|
||||
|
||||
NbtMap nbt = data.getTag();
|
||||
if (nbt != null && !nbt.isEmpty()) {
|
||||
|
@ -198,7 +198,7 @@ public final class ItemTranslator {
|
|||
nbtMapBuilder.putIfAbsent("ench", NbtList.EMPTY);
|
||||
}
|
||||
|
||||
ItemData.Builder builder = javaItem.translateToBedrock(count, components, bedrockItem, session.getItemMappings());
|
||||
ItemData.Builder builder = javaItem.translateToBedrock(session, count, components, bedrockItem, session.getItemMappings());
|
||||
// Finalize the Bedrock NBT
|
||||
builder.tag(nbtBuilder.build());
|
||||
if (bedrockItem.isBlock()) {
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetIte
|
|||
import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||
import org.geysermc.geyser.entity.type.Entity;
|
||||
|
@ -51,6 +52,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack;
|
|||
import org.geysermc.geyser.inventory.Inventory;
|
||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||
import org.geysermc.geyser.inventory.click.Click;
|
||||
import org.geysermc.geyser.inventory.item.GeyserInstrument;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.item.type.BlockItem;
|
||||
import org.geysermc.geyser.item.type.BoatItem;
|
||||
|
@ -75,6 +77,7 @@ import org.geysermc.geyser.util.CooldownUtils;
|
|||
import org.geysermc.geyser.util.EntityUtils;
|
||||
import org.geysermc.geyser.util.InteractionResult;
|
||||
import org.geysermc.geyser.util.InventoryUtils;
|
||||
import org.geysermc.geyser.util.SoundUtils;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.Holder;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
|
@ -379,18 +382,28 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
|||
session.setCurrentBook(packet.getItemInHand());
|
||||
} else if (session.getPlayerInventory().getItemInHand().asItem() == Items.GOAT_HORN) {
|
||||
// Temporary workaround while we don't have full item/block use tracking.
|
||||
if (!session.getWorldCache().hasCooldown(Items.GOAT_HORN)) {
|
||||
Holder<Instrument> instrument = session.getPlayerInventory()
|
||||
if (!session.getWorldCache().hasCooldown(session.getPlayerInventory().getItemInHand())) {
|
||||
Holder<Instrument> holder = session.getPlayerInventory()
|
||||
.getItemInHand()
|
||||
.getComponent(DataComponentType.INSTRUMENT);
|
||||
if (instrument != null && instrument.isId()) {
|
||||
// BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20)
|
||||
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
|
||||
soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.id()));
|
||||
soundPacket.setPosition(session.getPlayerEntity().getPosition());
|
||||
soundPacket.setIdentifier("minecraft:player");
|
||||
soundPacket.setExtraData(-1);
|
||||
session.sendUpstreamPacket(soundPacket);
|
||||
if (holder != null) {
|
||||
GeyserInstrument instrument = GeyserInstrument.fromHolder(session, holder);
|
||||
if (instrument.bedrockInstrument() != null) {
|
||||
// BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20)
|
||||
LevelSoundEventPacket soundPacket = new LevelSoundEventPacket();
|
||||
soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.bedrockInstrument().ordinal()));
|
||||
soundPacket.setPosition(session.getPlayerEntity().getPosition());
|
||||
soundPacket.setIdentifier("minecraft:player");
|
||||
soundPacket.setExtraData(-1);
|
||||
session.sendUpstreamPacket(soundPacket);
|
||||
} else {
|
||||
PlaySoundPacket playSoundPacket = new PlaySoundPacket();
|
||||
playSoundPacket.setPosition(session.getPlayerEntity().position());
|
||||
playSoundPacket.setSound(SoundUtils.translatePlaySound(instrument.soundEvent()));
|
||||
playSoundPacket.setPitch(1.0F);
|
||||
playSoundPacket.setVolume(instrument.range() / 16.0F);
|
||||
session.sendUpstreamPacket(playSoundPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java.level;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundCooldownPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerStartItemCooldownPacket;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
|
@ -39,7 +40,11 @@ public class JavaCooldownTranslator extends PacketTranslator<ClientboundCooldown
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundCooldownPacket packet) {
|
||||
Item item = Registries.JAVA_ITEMS.get().get(0); // FIXME
|
||||
// If the cooldown group is a modded item, an item that Bedrock doesn't support custom cooldowns for, or a custom cooldown group,
|
||||
// then the cooldown won't be translated correctly. The cooldown won't show up on Bedrock, but they are still unable to use the item.
|
||||
Key cooldownGroup = packet.getCooldownGroup();
|
||||
Item item = Registries.JAVA_ITEM_IDENTIFIERS.get(cooldownGroup.asString());
|
||||
|
||||
// Not every item, as of 1.19, appears to be server-driven. Just these two.
|
||||
// Use a map here if it gets too big.
|
||||
String cooldownCategory;
|
||||
|
@ -58,6 +63,6 @@ public class JavaCooldownTranslator extends PacketTranslator<ClientboundCooldown
|
|||
session.sendUpstreamPacket(bedrockPacket);
|
||||
}
|
||||
|
||||
session.getWorldCache().setCooldown(item, packet.getCooldownTicks());
|
||||
session.getWorldCache().setCooldown(cooldownGroup, packet.getCooldownTicks());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,6 +137,18 @@ public class MessageTranslator {
|
|||
return convertMessage(message, locale, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Java message to the legacy format ready for bedrock, for use in item tooltips
|
||||
* (a gray color is applied).
|
||||
*
|
||||
* @param message Java message
|
||||
* @param locale Locale to use for translation strings
|
||||
* @return Parsed and formatted message for bedrock, in gray color
|
||||
*/
|
||||
public static String convertMessageForTooltip(Component message, String locale) {
|
||||
return RESET + ChatColor.GRAY + convertMessageRaw(message, locale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Java message to the legacy format ready for bedrock. Unlike {@link #convertMessage(Component, String)}
|
||||
* this version does not add a leading color reset. In Bedrock some places have build-in colors.
|
||||
|
@ -422,6 +434,15 @@ public class MessageTranslator {
|
|||
return convertMessage(session, parsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string.
|
||||
*/
|
||||
public static String deserializeDescriptionForTooltip(GeyserSession session, NbtMap tag) {
|
||||
Object description = tag.get("description");
|
||||
Component parsed = componentFromNbtTag(description);
|
||||
return convertMessageForTooltip(parsed, session.locale());
|
||||
}
|
||||
|
||||
public static Component componentFromNbtTag(Object nbtTag) {
|
||||
return componentFromNbtTag(nbtTag, Style.empty());
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.geysermc.geyser.util;
|
|||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket;
|
||||
|
@ -162,6 +163,20 @@ public final class SoundUtils {
|
|||
session.sendUpstreamPacket(soundPacket);
|
||||
}
|
||||
|
||||
public static String readSoundEvent(NbtMap data, String context) {
|
||||
Object soundEventObject = data.get("sound_event");
|
||||
String soundEvent;
|
||||
if (soundEventObject instanceof NbtMap map) {
|
||||
soundEvent = map.getString("sound_id");
|
||||
} else if (soundEventObject instanceof String string) {
|
||||
soundEvent = string;
|
||||
} else {
|
||||
soundEvent = "";
|
||||
GeyserImpl.getInstance().getLogger().debug("Sound event for " + context + " was of an unexpected type! Expected string or NBT map, got " + soundEventObject);
|
||||
}
|
||||
return soundEvent;
|
||||
}
|
||||
|
||||
private SoundUtils() {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue