Add furnace minecart item translation (#2003)

Conveniently enough, the minecart furnace icon still exists in the vanilla Bedrock Edition game (thanks to Kastle for this discovery). With this and the translation string still being present, we can add the item into the game with only minor issues.
This commit is contained in:
Camotoy 2021-03-31 14:06:05 -04:00 committed by GitHub
parent 3a4b1e4dc7
commit 2f42a4c630
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 95 additions and 2 deletions

View File

@ -81,6 +81,8 @@ public interface GeyserConfiguration {
Path getFloodgateKeyPath();
boolean isAddNonBedrockItems();
boolean isAboveBedrockNetherBuilding();
boolean isCacheChunks();

View File

@ -115,6 +115,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
@JsonProperty("allow-custom-skulls")
private boolean allowCustomSkulls = true;
@JsonProperty("add-non-bedrock-items")
private boolean addNonBedrockItems = true;
@JsonProperty("above-bedrock-nether-building")
private boolean aboveBedrockNetherBuilding = false;

View File

@ -27,6 +27,7 @@ package org.geysermc.connector.network;
import com.nukkitx.protocol.bedrock.BedrockPacket;
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
import com.nukkitx.protocol.bedrock.data.ExperimentData;
import com.nukkitx.protocol.bedrock.data.ResourcePackType;
import com.nukkitx.protocol.bedrock.packet.*;
import com.nukkitx.protocol.bedrock.v428.Bedrock_v428;
@ -36,6 +37,7 @@ import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.AdvancementsCache;
import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
import org.geysermc.connector.network.translators.item.ItemRegistry;
import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_100;
import org.geysermc.connector.network.translators.world.block.BlockTranslator1_16_210;
import org.geysermc.connector.utils.*;
@ -133,6 +135,11 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.getUuid().toString(), header.getVersionString(), ""));
}
if (ItemRegistry.FURNACE_MINECART_DATA != null) {
// Allow custom items to work
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
}
session.sendUpstreamPacket(stackPacket);
break;

View File

@ -464,6 +464,12 @@ public class GeyserSession implements CommandSender {
// Set the hardcoded shield ID to the ID we just defined in StartGamePacket
upstream.getSession().getHardcodedBlockingId().set(ItemRegistry.SHIELD.getBedrockId());
if (ItemRegistry.FURNACE_MINECART_DATA != null) {
ItemComponentPacket componentPacket = new ItemComponentPacket();
componentPacket.getItems().add(ItemRegistry.FURNACE_MINECART_DATA);
upstream.sendPacket(componentPacket);
}
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();

View File

@ -28,14 +28,19 @@ package org.geysermc.connector.network.translators.item;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.google.common.collect.ImmutableSet;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.nbt.NbtUtils;
import com.nukkitx.protocol.bedrock.data.inventory.ComponentItemData;
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.geysermc.connector.GeyserConnector;
import org.geysermc.connector.utils.FileUtils;
import org.geysermc.connector.utils.LanguageUtils;
@ -55,8 +60,7 @@ public class ItemRegistry {
/**
* A list of all identifiers that only exist on Java. Used to prevent creative items from becoming these unintentionally.
*/
private static final List<String> JAVA_ONLY_ITEMS = Arrays.asList("minecraft:spectral_arrow", "minecraft:debug_stick",
"minecraft:knowledge_book", "minecraft:tipped_arrow", "minecraft:furnace_minecart");
private static final Set<String> JAVA_ONLY_ITEMS;
public static final ItemData[] CREATIVE_ITEMS;
@ -107,6 +111,11 @@ public class ItemRegistry {
public static int BARRIER_INDEX = 0;
/**
* Stores the properties and data of the "custom" furnace minecart item.
*/
public static final ComponentItemData FURNACE_MINECART_DATA;
public static void init() {
// no-op
}
@ -150,9 +159,16 @@ public class ItemRegistry {
}
int itemIndex = 0;
int javaFurnaceMinecartId = 0;
boolean usingFurnaceMinecart = GeyserConnector.getInstance().getConfig().isAddNonBedrockItems();
Iterator<Map.Entry<String, JsonNode>> iterator = items.fields();
while (iterator.hasNext()) {
Map.Entry<String, JsonNode> entry = iterator.next();
if (usingFurnaceMinecart && entry.getKey().equals("minecraft:furnace_minecart")) {
javaFurnaceMinecartId = itemIndex;
itemIndex++;
continue;
}
int bedrockId = entry.getValue().get("bedrock_id").intValue();
String bedrockIdentifier = bedrockIdToIdentifier.get(bedrockId);
if (bedrockIdentifier == null) {
@ -224,6 +240,9 @@ public class ItemRegistry {
itemIndex++;
}
itemNames.add("minecraft:furnace_minecart");
itemNames.add("minecraft:spectral_arrow");
if (lodestoneCompassId == 0) {
throw new RuntimeException("Lodestone compass not found in item palette!");
}
@ -248,9 +267,59 @@ public class ItemRegistry {
ItemData item = getBedrockItemFromJson(itemNode);
creativeItems.add(ItemData.fromNet(netId++, item.getId(), item.getDamage(), item.getCount(), item.getTag()));
}
if (usingFurnaceMinecart) {
// Add the furnace minecart as an item
int furnaceMinecartId = ITEMS.size() + 1;
ITEMS.add(new StartGamePacket.ItemEntry("geysermc:furnace_minecart", (short) furnaceMinecartId, true));
ITEM_ENTRIES.put(javaFurnaceMinecartId, new ItemEntry("minecraft:furnace_minecart", "geysermc:furnace_minecart", javaFurnaceMinecartId,
furnaceMinecartId, 0, false, 1));
creativeItems.add(ItemData.fromNet(netId, furnaceMinecartId, (short) 0, 1, null));
NbtMapBuilder builder = NbtMap.builder();
builder.putString("name", "geysermc:furnace_minecart")
.putInt("id", furnaceMinecartId);
NbtMapBuilder componentBuilder = NbtMap.builder();
// Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already.
componentBuilder.putCompound("minecraft:icon", NbtMap.builder().putString("texture", "minecart_furnace").build());
componentBuilder.putCompound("minecraft:display_name", NbtMap.builder().putString("value", "item.minecartFurnace.name").build());
// Indicate that the arm animation should play on rails
List<NbtMap> useOnTag = Collections.singletonList(NbtMap.builder().putString("tags", "q.any_tag('rail')").build());
componentBuilder.putCompound("minecraft:entity_placer", NbtMap.builder()
.putList("dispense_on", NbtType.COMPOUND, useOnTag)
.putString("entity", "minecraft:minecart")
.putList("use_on", NbtType.COMPOUND, useOnTag)
.build());
NbtMapBuilder itemProperties = NbtMap.builder();
// We always want to allow offhand usage when we can - matches Java Edition
itemProperties.putBoolean("allow_off_hand", true);
itemProperties.putBoolean("hand_equipped", false);
itemProperties.putInt("max_stack_size", 1);
itemProperties.putString("creative_group", "itemGroup.name.minecart");
itemProperties.putInt("creative_category", 4); // 4 - "Items"
componentBuilder.putCompound("item_properties", itemProperties.build());
builder.putCompound("components", componentBuilder.build());
FURNACE_MINECART_DATA = new ComponentItemData("geysermc:furnace_minecart", builder.build());
} else {
FURNACE_MINECART_DATA = null;
}
CREATIVE_ITEMS = creativeItems.toArray(new ItemData[0]);
ITEM_NAMES = itemNames.toArray(new String[0]);
Set<String> javaOnlyItems = new ObjectOpenHashSet<>();
Collections.addAll(javaOnlyItems, "minecraft:spectral_arrow", "minecraft:debug_stick",
"minecraft:knowledge_book", "minecraft:tipped_arrow");
if (!usingFurnaceMinecart) {
javaOnlyItems.add("minecraft:furnace_minecart");
}
JAVA_ONLY_ITEMS = ImmutableSet.copyOf(javaOnlyItems);
}
/**

View File

@ -139,6 +139,12 @@ cache-images: 0
# Allows custom skulls to be displayed. Keeping them enabled may cause a performance decrease on older/weaker devices.
allow-custom-skulls: true
# Whether to add (at this time, only) the furnace minecart as a separate item in the game, which normally does not exist in Bedrock Edition.
# This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching.
# If this is disabled, furnace minecart items will be mapped to hopper minecart items.
# This option requires a restart of Geyser in order to change its setting.
add-non-bedrock-items: true
# Bedrock prevents building and displaying blocks above Y127 in the Nether -
# enabling this config option works around that by changing the Nether dimension ID
# to the End ID. The main downside to this is that the sky will resemble that of