Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ public enum FireworkExplosionShape {
STAR,
CREEPER,
BURST
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,26 @@
package org.geysermc.geyser.item.type;

import it.unimi.dsi.fastutil.ints.IntArrays;
import net.kyori.adventure.text.Component;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.item.TooltipOptions;
import org.geysermc.geyser.item.hashing.data.FireworkExplosionShape;
import org.geysermc.geyser.level.FireworkColor;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.Fireworks;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

public class FireworkRocketItem extends Item implements BedrockRequiresTagItem {
public FireworkRocketItem(String javaIdentifier, Builder builder) {
Expand All @@ -50,12 +54,11 @@ public FireworkRocketItem(String javaIdentifier, Builder builder) {

@Override
public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) {
super.translateComponentsToBedrock(session, components, tooltip, builder);

Comment thread
oryxel1 marked this conversation as resolved.
Fireworks fireworks = components.get(DataComponentTypes.FIREWORKS);
if (fireworks == null) {
return;
}
// We still need to translate the explosion so this is still correct, and can be reverse translate in translateNbtToJava.
NbtMapBuilder fireworksNbt = NbtMap.builder();
fireworksNbt.putByte("Flight", (byte) fireworks.getFlightDuration());

Expand All @@ -71,6 +74,49 @@ public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNul
fireworksNbt.put("Explosions", NbtList.EMPTY);
}
builder.putCompound("Fireworks", fireworksNbt.build());

// If the tooltip is hidden, don't add any lore.
if (!tooltip.showInTooltip(DataComponentTypes.FIREWORKS)) {
return;
}

final String locale = session.locale();

// Then we translate everything into lore since the explosion tag and everything is not visible anymore due to this being a data driven item.
List<String> lore = builder.getOrCreateLore();
lore.add(MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_rocket.flight"), locale) + " " + fireworks.getFlightDuration());

for (Fireworks.FireworkExplosion explosion : explosions) {
lore.add(" " + MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star.shape." + FireworkExplosionShape.values()[explosion.getShapeId()].name().toLowerCase(Locale.ROOT)), locale));

final StringBuilder colorBuilder = new StringBuilder(" ");
for (int color : explosion.getColors()) {
FireworkColor fireworkColor = FireworkColor.values()[FireworkColor.fromJavaRGB(color)];
colorBuilder.append(MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star." + fireworkColor.name().toLowerCase(Locale.ROOT)), locale));
colorBuilder.append(" ");
}
if (explosion.getColors().length != 0) {
lore.add(MessageTranslator.convertMessageForTooltip(Component.translatable(colorBuilder.toString()), locale));
}

final StringBuilder fadeColorBuilder = new StringBuilder();
for (int color : explosion.getFadeColors()) {
FireworkColor fireworkColor = FireworkColor.values()[FireworkColor.fromJavaRGB(color)];
fadeColorBuilder.append(MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star." + fireworkColor.name().toLowerCase(Locale.ROOT)), locale));
fadeColorBuilder.append(" ");
}
if (explosion.getFadeColors().length != 0) {
lore.add(" " + MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star.fade_to"), locale) + " " + fadeColorBuilder);
}

if (explosion.isHasTrail()) {
lore.add(" " + MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star.trail"), locale));
}

if (explosion.isHasTwinkle()) {
lore.add(" " + MessageTranslator.convertMessageForTooltip(Component.translatable("item.minecraft.firework_star.flicker"), locale));
}
}
}

@Override
Expand All @@ -79,6 +125,11 @@ public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap b

NbtMap fireworksTag = bedrockTag.getCompound("Fireworks");
if (!fireworksTag.isEmpty()) {
int flightDuration = 1;
if (fireworksTag.containsKey("Flight")) {
flightDuration = fireworksTag.getByte("Flight");
}

List<NbtMap> explosions = fireworksTag.getList("Explosions", NbtType.COMPOUND);
if (!explosions.isEmpty()) {
List<Fireworks.FireworkExplosion> javaExplosions = new ArrayList<>();
Expand All @@ -88,7 +139,9 @@ public void translateNbtToJava(@NonNull GeyserSession session, @NonNull NbtMap b
javaExplosions.add(javaExplosion);
}
}
components.put(DataComponentTypes.FIREWORKS, new Fireworks(1, javaExplosions));
components.put(DataComponentTypes.FIREWORKS, new Fireworks(flightDuration, javaExplosions));
} else {
components.put(DataComponentTypes.FIREWORKS, new Fireworks(flightDuration, List.of()));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,4 @@ public static int fromBedrockId(int id) {

return WHITE.color.value();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
import org.geysermc.geyser.inventory.item.StoredItemMappings;
import org.geysermc.geyser.item.GeyserCustomMappingData;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.TooltipOptions;
import org.geysermc.geyser.item.type.BlockItem;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.property.Properties;
Expand All @@ -80,6 +81,8 @@
import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.registry.type.NonVanillaItemRegistration;
import org.geysermc.geyser.registry.type.PaletteItem;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;

import java.io.InputStream;
import java.util.ArrayList;
Expand Down Expand Up @@ -216,6 +219,20 @@ public static void populate() {
}

ItemDefinition definition = new SimpleItemDefinition(entry.getName().intern(), id, ItemVersion.from(entry.getVersion()), entry.isComponentBased(), components);

// Some item on Bedrock Edition have a different stack size, so we're changing that through the component.
// This resolve https://github.com/GeyserMC/Geyser/issues/5612 and https://github.com/GeyserMC/Geyser/issues/4905
if (definition.getIdentifier().equals("minecraft:cake")) {
definition = new SimpleItemDefinition(entry.getName().intern(), id, ItemVersion.from(entry.getVersion()), true, fromItemDefinitionToDataDriven(definition, 1, null, null));
} else if (definition.getIdentifier().equals("minecraft:armor_stand")) {
// You have to change the item version to data driven for armor stand else this won't work.
definition = new SimpleItemDefinition(entry.getName().intern(), id, ItemVersion.DATA_DRIVEN, true, fromItemDefinitionToDataDriven(definition, 16, "armor_stand", "item.armor_stand.name"));
} else if (definition.getIdentifier().equals("minecraft:firework_rocket")) {
// For fireworks rocket, we purposely make this item data driven so now bedrock won't do client-sided boosting
// and now we can control fireworks boost ourselves! This resolve https://github.com/GeyserMC/Geyser/issues/5409
definition = new SimpleItemDefinition(entry.getName().intern(), id, ItemVersion.DATA_DRIVEN, true, fromItemDefinitionToDataDriven(definition, 64, "fireworks", "item.fireworks.name"));
}
Comment thread
oryxel1 marked this conversation as resolved.

definitions.put(entry.getName(), definition);
registry.put(definition.getRuntimeId(), definition);
}
Expand Down Expand Up @@ -725,4 +742,33 @@ private static NbtMap registerFurnaceMinecart(int nextFreeBedrockId) {
builder.putCompound("components", componentBuilder.build());
return builder.build();
}

private static NbtMap fromItemDefinitionToDataDriven(ItemDefinition definition, int maxStackSize, String texture, String displayName) {
NbtMapBuilder builder = NbtMap.builder();
builder.putString("name", definition.getIdentifier()).putInt("id", definition.getRuntimeId());

NbtMapBuilder itemProperties = NbtMap.builder();

NbtMapBuilder componentBuilder = NbtMap.builder();

if (texture != null) {
NbtMap iconMap = NbtMap.builder()
.putCompound("textures", NbtMap.builder()
.putString("default", texture)
.build())
.build();
itemProperties.putCompound("minecraft:icon", iconMap);
}

if (displayName != null) {
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", displayName).build());
}

itemProperties.putBoolean("allow_off_hand", true);
itemProperties.putInt("max_stack_size", maxStackSize);

componentBuilder.putCompound("item_properties", itemProperties.build());
builder.putCompound("components", componentBuilder.build());
return builder.build();
}
}
32 changes: 32 additions & 0 deletions core/src/main/java/org/geysermc/geyser/session/GeyserSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.definitions.DimensionDefinition;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.CreativeItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.data.inventory.crafting.recipe.CraftingRecipeData;
import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
Expand Down Expand Up @@ -148,6 +149,7 @@
import org.geysermc.geyser.inventory.recipe.GeyserSmithingRecipe;
import org.geysermc.geyser.inventory.recipe.GeyserStonecutterData;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.TooltipOptions;
import org.geysermc.geyser.item.type.BlockItem;
import org.geysermc.geyser.level.BedrockDimension;
import org.geysermc.geyser.level.JavaDimension;
Expand Down Expand Up @@ -185,6 +187,7 @@
import org.geysermc.geyser.session.dialog.DialogManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.ChunkUtils;
import org.geysermc.geyser.util.EntityUtils;
Expand All @@ -208,6 +211,7 @@
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.HandPreference;
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import org.geysermc.mcprotocollib.protocol.data.game.setting.ChatVisibility;
import org.geysermc.mcprotocollib.protocol.data.game.setting.ParticleStatus;
import org.geysermc.mcprotocollib.protocol.data.game.setting.SkinPart;
Expand All @@ -229,6 +233,7 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -843,6 +848,33 @@ public void connect() {
cameraPresetsPacket.getPresets().addAll(CameraDefinitions.CAMERA_PRESETS);
upstream.sendPacket(cameraPresetsPacket);

// Since the fireworks tag now won't show up due to this is being a data driven item, we have to translate it to lore ourselves
// so the item data can show up in the creative menu. Also doing it here so that we know what locale player choose and translate it properly.
for (int i = 0; i < this.itemMappings.getCreativeItems().size(); i++) {
CreativeItemData data = this.itemMappings.getCreativeItems().get(i);
if (!data.getItem().getDefinition().getIdentifier().equals("minecraft:firework_rocket")) {
continue;
}

NbtMap tag = null;
if (data.getItem().getTag() != null) {
final DataComponents components = new DataComponents(new HashMap<>());
Items.FIREWORK_ROCKET.translateNbtToJava(this, data.getItem().getTag(), components, this.getItemMappings().getMapping(Items.FIREWORK_ROCKET));
final BedrockItemBuilder builder = new BedrockItemBuilder();
Items.FIREWORK_ROCKET.translateComponentsToBedrock(this, components, TooltipOptions.ALL_SHOWN, builder);

tag = builder.build();
}

this.itemMappings.getCreativeItems().set(i, new CreativeItemData(ItemData.builder()
Comment thread
oryxel1 marked this conversation as resolved.
Outdated
.usingNetId(true)
.netId(data.getItem().getNetId())
.definition(data.getItem().getDefinition())
.tag(tag)
.count(data.getItem().getCount())
.build(), data.getNetId(), data.getGroupId()));
}

CreativeContentPacket creativePacket = new CreativeContentPacket();
creativePacket.getContents().addAll(this.itemMappings.getCreativeItems());
creativePacket.getGroups().addAll(this.itemMappings.getCreativeItemGroups());
Expand Down