Update to latest Protocol changes & cleanup item registry populator

This commit is contained in:
RednedEpic 2022-10-30 11:34:08 -05:00
parent 86ebfbbc6e
commit 3f42d68f4e
16 changed files with 307 additions and 208 deletions

View file

@ -39,7 +39,7 @@ public class FallingBlockEntity extends Entity {
public FallingBlockEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw, int javaId) {
super(session, entityId, geyserId, uuid, EntityDefinitions.FALLING_BLOCK, position, motion, yaw, pitch, headYaw);
this.dirtyMetadata.put(EntityDataTypes.VARIANT, session.getBlockMappings().getBedrockBlockId(javaId));
this.dirtyMetadata.put(EntityDataTypes.BLOCK, session.getBlockMappings().getBedrockBlock(javaId));
}
@Override

View file

@ -35,6 +35,7 @@ import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
@ -59,7 +60,7 @@ public class ItemFrameEntity extends Entity {
/**
* Specific block 'state' we are emulating in Bedrock.
*/
private final int bedrockRuntimeId;
private final BlockDefinition blockDefinition;
/**
* Rotation of item in frame.
*/
@ -90,7 +91,7 @@ public class ItemFrameEntity extends Entity {
.putByte("item_frame_photo_bit", (byte) 0);
blockBuilder.put("states", statesBuilder.build());
bedrockRuntimeId = session.getBlockMappings().getItemFrame(blockBuilder.build());
blockDefinition = session.getBlockMappings().getItemFrame(blockBuilder.build());
bedrockPosition = Vector3i.from(position.getFloorX(), position.getFloorY(), position.getFloorZ());
session.getItemFrameCache().put(bedrockPosition, this);
@ -152,7 +153,7 @@ public class ItemFrameEntity extends Entity {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockAir().getRuntimeId()); //TODO maybe set this to the world block or another item frame?
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockAir()); //TODO maybe set this to the world block or another item frame?
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
@ -190,7 +191,7 @@ public class ItemFrameEntity extends Entity {
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(bedrockPosition);
updateBlockPacket.setRuntimeId(bedrockRuntimeId);
updateBlockPacket.setDefinition(blockDefinition);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.PRIORITY);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);

View file

@ -93,7 +93,7 @@ public class BlockInventoryHolder extends InventoryHolder {
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState));
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(defaultJavaBlockState));
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
inventory.setHolderPosition(position);
@ -157,7 +157,7 @@ public class BlockInventoryHolder extends InventoryHolder {
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock));
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2019-2022 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.registry.populator;
import com.fasterxml.jackson.databind.JsonNode;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtUtils;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.ItemMappings;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
public class CreativeItemRegistryPopulator {
private static final List<BiPredicate<String, Integer>> JAVA_ONLY_ITEM_FILTER = List.of(
// Just shows an empty texture; either way it doesn't exist in the creative menu on Java
(identifier, data) -> identifier.equals("minecraft:debug_stick"),
// Bedrock-only as its own item
(identifier, data) -> identifier.equals("minecraft:empty_map") && data == 2,
// Bedrock-only banner patterns
(identifier, data) -> identifier.equals("minecraft:bordure_indented_banner_pattern") || identifier.equals("minecraft:field_masoned_banner_pattern")
);
public static void populate(Map.Entry<String, ItemRegistryPopulator.PaletteVersion> version, Map<String, ItemDefinition> definitions, Consumer<ItemData.Builder> itemConsumer) {
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
// Load creative items
JsonNode creativeItemEntries;
try (InputStream stream = bootstrap.getResource(String.format("bedrock/creative_items.%s.json", version.getKey()))) {
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items");
} catch (Exception e) {
throw new AssertionError("Unable to load creative items", e);
}
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(version.getValue().protocolVersion());
for (JsonNode itemNode : creativeItemEntries) {
ItemData.Builder itemBuilder = createItemData(itemNode, blockMappings, definitions);
if (itemBuilder == null) {
continue;
}
itemConsumer.accept(itemBuilder);
}
}
private static ItemData.Builder createItemData(JsonNode itemNode, BlockMappings blockMappings, Map<String, ItemDefinition> definitions) {
int count = 1;
int damage = 0;
int blockRuntimeId = 0;
NbtMap tag = null;
JsonNode damageNode = itemNode.get("damage");
if (damageNode != null) {
damage = damageNode.asInt();
}
JsonNode countNode = itemNode.get("count");
if (countNode != null) {
count = countNode.asInt();
}
JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId");
if (blockRuntimeIdNode != null) {
blockRuntimeId = blockRuntimeIdNode.asInt();
}
JsonNode nbtNode = itemNode.get("nbt_b64");
if (nbtNode != null) {
byte[] bytes = Base64.getDecoder().decode(nbtNode.asText());
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try {
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
} catch (IOException e) {
e.printStackTrace();
}
}
String identifier = itemNode.get("id").textValue();
for (BiPredicate<String, Integer> predicate : JAVA_ONLY_ITEM_FILTER) {
if (predicate.test(identifier, damage)) {
return null;
}
}
ItemDefinition definition = definitions.get(identifier);
if (definition == null) {
GeyserImpl.getInstance().getLogger().debug("Unknown item definition with identifier " + identifier + " when loading creative items!");
return null;
}
return ItemData.builder()
.definition(definition)
.damage(damage)
.count(count)
.tag(tag)
.blockDefinition(blockMappings.getBedrockBlock(blockRuntimeId));
}
}

View file

@ -25,20 +25,24 @@
package org.geysermc.geyser.registry.populator;
import com.google.common.collect.Multimap;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ComponentItemData;
import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import org.geysermc.geyser.api.util.TriState;
import org.geysermc.geyser.event.type.GeyserDefineCustomItemsEventImpl;
import org.geysermc.geyser.item.GeyserCustomMappingData;
import org.geysermc.geyser.item.components.ToolBreakSpeedsUtils;
import org.geysermc.geyser.item.components.WearableSlot;
import org.geysermc.geyser.item.mappings.MappingsConfigReader;
import org.geysermc.geyser.registry.type.GeyserMappingItem;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.registry.type.NonVanillaItemRegistration;
@ -50,6 +54,51 @@ import java.util.Map;
import java.util.OptionalInt;
public class CustomItemRegistryPopulator {
public static void populate(Map<String, GeyserMappingItem> items, Multimap<String, CustomItemData> customItems, List<NonVanillaCustomItemData> nonVanillaCustomItems) {
MappingsConfigReader mappingsConfigReader = new MappingsConfigReader();
// Load custom items from mappings files
mappingsConfigReader.loadMappingsFromJson((key, item) -> {
if (CustomItemRegistryPopulator.initialCheck(key, item, items)) {
customItems.get(key).add(item);
}
});
GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEventImpl(customItems, nonVanillaCustomItems) {
@Override
public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) {
if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) {
customItems.get(identifier).add(customItemData);
return true;
}
return false;
}
@Override
public boolean register(@NonNull NonVanillaCustomItemData customItemData) {
if (customItemData.identifier().startsWith("minecraft:")) {
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
" is attempting to masquerade as a vanilla Minecraft item!");
return false;
}
if (customItemData.javaId() < items.size()) {
// Attempting to overwrite an item that already exists in the protocol
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
" is attempting to overwrite a vanilla Minecraft item!");
return false;
}
nonVanillaCustomItems.add(customItemData);
return true;
}
});
int customItemCount = customItems.size() + nonVanillaCustomItems.size();
if (customItemCount > 0) {
GeyserImpl.getInstance().getLogger().info("Registered " + customItemCount + " custom items");
}
}
public static GeyserCustomMappingData registerCustomItem(String customItemName, GeyserMappingItem javaItem, CustomItemData customItemData, int bedrockId) {
ItemDefinition itemDefinition = new ItemDefinition(customItemName, bedrockId, true);
@ -86,7 +135,7 @@ public class CustomItemRegistryPopulator {
.javaId(customItemData.javaId())
.bedrockDefinition(new ItemDefinition(customIdentifier, customItemId, true))
.bedrockData(0)
.bedrockBlockId(0)
.bedrockBlockDefinition(null)
.stackSize(customItemData.stackSize())
.toolType(customItemData.toolType())
.toolTier(customItemData.toolTier())

View file

@ -26,13 +26,11 @@
package org.geysermc.geyser.registry.populator;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import com.nukkitx.nbt.NbtUtils;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
@ -45,7 +43,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.codec.v527.Bedrock_v527;
import org.cloudburstmc.protocol.bedrock.codec.v534.Bedrock_v534;
import org.cloudburstmc.protocol.bedrock.codec.v544.Bedrock_v544;
@ -60,10 +57,8 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.item.custom.CustomItemData;
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
import org.geysermc.geyser.event.type.GeyserDefineCustomItemsEventImpl;
import org.geysermc.geyser.inventory.item.StoredItemMappings;
import org.geysermc.geyser.item.GeyserCustomMappingData;
import org.geysermc.geyser.item.mappings.MappingsConfigReader;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.BlockMappings;
@ -75,24 +70,22 @@ import org.geysermc.geyser.registry.type.PaletteItem;
import org.geysermc.geyser.util.ItemUtils;
import org.geysermc.geyser.util.collection.FixedInt2IntMap;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Populates the item registries.
*/
public class ItemRegistryPopulator {
private record PaletteVersion(int protocolVersion, Map<String, String> additionalTranslatedItems) {
record PaletteVersion(int protocolVersion, Map<String, String> additionalTranslatedItems) {
}
public static void populate() {
@ -120,48 +113,10 @@ public class ItemRegistryPopulator {
// (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom
// of the list first, then ascends.
Multimap<String, CustomItemData> customItems = MultimapBuilder.hashKeys().arrayListValues().build();
List<NonVanillaCustomItemData> nonVanillaCustomItems;
List<NonVanillaCustomItemData> nonVanillaCustomItems = customItemsAllowed ? new ObjectArrayList<>() : Collections.emptyList();
MappingsConfigReader mappingsConfigReader = new MappingsConfigReader();
if (customItemsAllowed) {
// Load custom items from mappings files
mappingsConfigReader.loadMappingsFromJson((key, item) -> {
if (CustomItemRegistryPopulator.initialCheck(key, item, items)) {
customItems.get(key).add(item);
}
});
nonVanillaCustomItems = new ObjectArrayList<>();
GeyserImpl.getInstance().eventBus().fire(new GeyserDefineCustomItemsEventImpl(customItems, nonVanillaCustomItems) {
@Override
public boolean register(@NonNull String identifier, @NonNull CustomItemData customItemData) {
if (CustomItemRegistryPopulator.initialCheck(identifier, customItemData, items)) {
customItems.get(identifier).add(customItemData);
return true;
}
return false;
}
@Override
public boolean register(@NonNull NonVanillaCustomItemData customItemData) {
if (customItemData.identifier().startsWith("minecraft:")) {
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
" is attempting to masquerade as a vanilla Minecraft item!");
return false;
}
if (customItemData.javaId() < items.size()) {
// Attempting to overwrite an item that already exists in the protocol
GeyserImpl.getInstance().getLogger().error("The custom item " + customItemData.identifier() +
" is attempting to overwrite a vanilla Minecraft item!");
return false;
}
nonVanillaCustomItems.add(customItemData);
return true;
}
});
} else {
nonVanillaCustomItems = Collections.emptyList();
CustomItemRegistryPopulator.populate(items, customItems, nonVanillaCustomItems);
}
int customItemCount = customItems.size() + nonVanillaCustomItems.size();
@ -209,15 +164,6 @@ public class ItemRegistryPopulator {
Object2IntMap<String> bedrockBlockIdOverrides = new Object2IntOpenHashMap<>();
Object2IntMap<String> blacklistedIdentifiers = new Object2IntOpenHashMap<>();
// Load creative items
// We load this before item mappings to get overridden block runtime ID mappings
JsonNode creativeItemEntries;
try (InputStream stream = bootstrap.getResource(String.format("bedrock/creative_items.%s.json", palette.getKey()))) {
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items");
} catch (Exception e) {
throw new AssertionError("Unable to load creative items", e);
}
List<ItemDefinition> boats = new ObjectArrayList<>();
List<ItemDefinition> buckets = new ObjectArrayList<>();
List<ItemDefinition> spawnEggs = new ObjectArrayList<>();
@ -225,74 +171,31 @@ public class ItemRegistryPopulator {
List<ItemMapping> mappings = new ObjectArrayList<>();
// Temporary mapping to create stored items
Map<String, ItemMapping> identifierToMapping = new Object2ObjectOpenHashMap<>();
Map<String, ItemMapping> javaIdentifierToMapping = new Object2ObjectOpenHashMap<>();
int netId = 1;
List<ItemData> creativeItems = new ArrayList<>();
for (JsonNode itemNode : creativeItemEntries) {
int count = 1;
int damage = 0;
int blockRuntimeId = 0;
NbtMap tag = null;
JsonNode damageNode = itemNode.get("damage");
if (damageNode != null) {
damage = damageNode.asInt();
}
JsonNode countNode = itemNode.get("count");
if (countNode != null) {
count = countNode.asInt();
}
JsonNode blockRuntimeIdNode = itemNode.get("blockRuntimeId");
if (blockRuntimeIdNode != null) {
blockRuntimeId = blockRuntimeIdNode.asInt();
}
JsonNode nbtNode = itemNode.get("nbt_b64");
if (nbtNode != null) {
byte[] bytes = Base64.getDecoder().decode(nbtNode.asText());
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try {
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
} catch (IOException e) {
e.printStackTrace();
}
}
String identifier = itemNode.get("id").textValue();
if (identifier.equals("minecraft:debug_stick")) {
// Just shows an empty texture; either way it doesn't exist in the creative menu on Java
continue;
} else if (identifier.equals("minecraft:empty_map") && damage == 2) {
// Bedrock-only as its own item
continue;
} else if (identifier.equals("minecraft:bordure_indented_banner_pattern") || identifier.equals("minecraft:field_masoned_banner_pattern")) {
// Bedrock-only banner patterns
continue;
}
AtomicInteger creativeNetId = new AtomicInteger();
CreativeItemRegistryPopulator.populate(palette, definitions, itemBuilder -> {
ItemData item = itemBuilder.netId(creativeNetId.getAndIncrement()).build();
creativeItems.add(item);
ItemDefinition definition = definitions.get(identifier);
creativeItems.add(ItemData.builder()
.definition(definition)
.damage(damage)
.count(count)
.blockRuntimeId(blockRuntimeId)
.tag(tag)
.netId(netId++)
.build());
if (item.getBlockDefinition() != null) {
String identifier = item.getDefinition().getIdentifier();
if (blockRuntimeId != 0) {
// Add override for item mapping, unless it already exists... then we know multiple states can exist
if (!blacklistedIdentifiers.containsKey(identifier)) {
if (bedrockBlockIdOverrides.containsKey(identifier)) {
bedrockBlockIdOverrides.removeInt(identifier);
// Save this as a blacklist, but also as knowledge of what the block state name should be
blacklistedIdentifiers.put(identifier, blockRuntimeId);
blacklistedIdentifiers.put(identifier, item.getBlockDefinition().getRuntimeId());
} else {
// Unless there's multiple possibilities for this one state, let this be
bedrockBlockIdOverrides.put(identifier, blockRuntimeId);
bedrockBlockIdOverrides.put(identifier, item.getBlockDefinition().getRuntimeId());
}
}
}
}
});
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.getValue().protocolVersion());
@ -435,7 +338,7 @@ public class ItemRegistryPopulator {
break;
}
NbtMap states = blockMappings.getBedrockBlockPalette().get(itemData.getBlockRuntimeId()).getCompound("states");
NbtMap states = blockMappings.getBedrockBlockPalette().get(itemData.getBlockDefinition().getRuntimeId()).getCompound("states");
boolean valid = true;
for (Map.Entry<String, Object> nbtEntry : requiredBlockStates.entrySet()) {
if (!states.get(nbtEntry.getKey()).equals(nbtEntry.getValue())) {
@ -445,7 +348,7 @@ public class ItemRegistryPopulator {
}
}
if (valid) {
creativeItems.set(j, itemData.toBuilder().blockRuntimeId(bedrockBlockId).build());
creativeItems.set(j, itemData.toBuilder().blockDefinition(blockMappings.getBedrockBlock(bedrockBlockId)).build());
break;
}
}
@ -460,7 +363,7 @@ public class ItemRegistryPopulator {
.bedrockIdentifier(bedrockIdentifier.intern())
.bedrockDefinition(definition)
.bedrockData(mappingItem.getBedrockData())
.bedrockBlockId(bedrockBlockId)
.bedrockBlockDefinition(blockMappings.getBedrockBlock(bedrockBlockId))
.stackSize(stackSize)
.maxDamage(mappingItem.getMaxDamage())
.hasSuspiciousStewEffect(mappingItem.isHasSuspiciousStewEffect());
@ -534,7 +437,7 @@ public class ItemRegistryPopulator {
.definition(definition)
.damage(mapping.getBedrockData())
.count(1)
.blockRuntimeId(mapping.getBedrockBlockId())
.blockDefinition(mapping.getBedrockBlockDefinition())
.build());
} else if (javaIdentifier.startsWith("minecraft:music_disc_")) {
// The Java record level event uses the item ID as the "key" to play the record
@ -545,7 +448,7 @@ public class ItemRegistryPopulator {
}
mappings.add(mapping);
identifierToMapping.put(javaIdentifier, mapping);
javaIdentifierToMapping.put(javaIdentifier, mapping);
itemNames.add(javaIdentifier);
@ -570,16 +473,14 @@ public class ItemRegistryPopulator {
.javaId(-1)
.bedrockDefinition(lodestoneCompass)
.bedrockData(0)
.bedrockBlockId(-1)
.bedrockBlockDefinition(null)
.stackSize(1)
.customItemOptions(Collections.emptyList())
.build();
if (customItemsAllowed) {
// Add the furnace minecart as a custom item
int furnaceMinecartId = nextFreeBedrockId++;
ItemDefinition definition = new ItemDefinition("geysermc:furnace_minecart", (short) furnaceMinecartId, true);
// Add furnace minecart
ItemDefinition definition = new ItemDefinition("geysermc:furnace_minecart", nextFreeBedrockId, true);
definitions.put("geysermc:furnace_minecart", definition);
registry.add(definition);
@ -589,49 +490,18 @@ public class ItemRegistryPopulator {
.javaId(javaFurnaceMinecartId)
.bedrockDefinition(definition)
.bedrockData(0)
.bedrockBlockId(-1)
.bedrockBlockDefinition(null)
.stackSize(1)
.customItemOptions(Collections.emptyList()) // TODO check for custom items with furnace minecart
.build());
creativeItems.add(ItemData.builder()
.netId(netId++)
.netId(creativeNetId.getAndIncrement())
.definition(definition)
.count(1).build());
NbtMapBuilder builder = NbtMap.builder();
builder.putString("name", "geysermc:furnace_minecart")
.putInt("id", furnaceMinecartId);
NbtMapBuilder itemProperties = NbtMap.builder();
NbtMapBuilder componentBuilder = NbtMap.builder();
// Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already.
itemProperties.putCompound("minecraft:icon", NbtMap.builder()
.putString("texture", "minecart_furnace")
.putString("frame", "0.000000")
.putInt("frame_version", 1)
.putString("legacy_id", "").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)
.count(1)
.build());
// 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());
componentItemData.add(new ComponentItemData("geysermc:furnace_minecart", builder.build()));
registerFurnaceMinecart(nextFreeBedrockId++, componentItemData);
// Register any completely custom items given to us
IntSet registeredJavaIds = new IntOpenHashSet(); // Used to check for duplicate item java ids
@ -657,8 +527,9 @@ public class ItemRegistryPopulator {
if (customItem.creativeGroup() != null || customItem.creativeCategory().isPresent()) {
creativeItems.add(ItemData.builder()
.definition(registration.mapping().getBedrockDefinition())
.netId(netId++)
.count(1).build());
.netId(creativeNetId.getAndIncrement())
.count(1)
.build());
}
}
}
@ -669,7 +540,7 @@ public class ItemRegistryPopulator {
.definitionRegistry(registry.build())
.itemDefinitions(List.copyOf(definitions.values()))
.itemNames(itemNames.toArray(new String[0]))
.storedItems(new StoredItemMappings(identifierToMapping))
.storedItems(new StoredItemMappings(javaIdentifierToMapping))
.javaOnlyItems(javaOnlyItems)
.buckets(buckets)
.boats(boats)
@ -687,4 +558,40 @@ public class ItemRegistryPopulator {
ItemUtils.setDyeColors(dyeColors);
}
private static void registerFurnaceMinecart(int nextFreeBedrockId, List<ComponentItemData> componentItemData) {
NbtMapBuilder builder = NbtMap.builder();
builder.putString("name", "geysermc:furnace_minecart")
.putInt("id", nextFreeBedrockId);
NbtMapBuilder itemProperties = NbtMap.builder();
NbtMapBuilder componentBuilder = NbtMap.builder();
// Conveniently, as of 1.16.200, the furnace minecart has a texture AND translation string already.
itemProperties.putCompound("minecraft:icon", NbtMap.builder()
.putString("texture", "minecart_furnace")
.putString("frame", "0.000000")
.putInt("frame_version", 1)
.putString("legacy_id", "").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());
// 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());
componentItemData.add(new ComponentItemData("geysermc:furnace_minecart", builder.build()));
}
}

View file

@ -232,7 +232,7 @@ public class RecipeRegistryPopulator {
.definition(mapping.getBedrockDefinition())
.damage(damage)
.count(count)
.blockRuntimeId(mapping.isBlock() ? mapping.getBedrockBlockId() : 0)
.blockDefinition(mapping.getBedrockBlockDefinition())
.tag(tag)
.build();
}

View file

@ -73,18 +73,11 @@ public class BlockMappings {
return this.javaToBedrockBlocks[state];
}
public int getItemFrame(NbtMap tag) {
BlockDefinition definition = this.itemFrames.get(tag);
return definition == null ? -1 : definition.getRuntimeId();
public BlockDefinition getItemFrame(NbtMap tag) {
return this.itemFrames.get(tag);
}
public boolean isItemFrame(int bedrockBlockRuntimeId) {
for (Map.Entry<NbtMap, BlockDefinition> entry : this.itemFrames.entrySet()) {
if (entry.getValue().getRuntimeId() == bedrockBlockRuntimeId) {
return true;
}
}
return false;
public boolean isItemFrame(BlockDefinition definition) {
return this.itemFrames.containsKey(definition.getState());
}
}

View file

@ -26,11 +26,11 @@
package org.geysermc.geyser.registry.type;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
@ -48,7 +48,7 @@ public class ItemMapping {
0,
ItemDefinition.AIR,
0,
0, // Air is never sent in full over the network for this to serialize.
null, // Air is never sent in full over the network for this to serialize.
64,
null,
null,
@ -69,7 +69,7 @@ public class ItemMapping {
* The Bedrock block runtime ID to render this item with. The specific state *does* matter in how this item is rendered and used as a crafting ingredient.
* Required since 1.16.220.
*/
int bedrockBlockId;
BlockDefinition bedrockBlockDefinition;
int stackSize;
String toolType;
@ -92,7 +92,7 @@ public class ItemMapping {
* @return if this item is a block
*/
public boolean isBlock() {
return this.bedrockBlockId != -1;
return this.bedrockBlockDefinition != null;
}
/**

View file

@ -127,13 +127,13 @@ public class ItemMappings {
return lodestoneCompass;
}
boolean isBlock = data.getBlockRuntimeId() != 0;
boolean isBlock = data.getBlockDefinition() != null;
boolean hasDamage = data.getDamage() != 0;
for (ItemMapping mapping : this.items) {
if (mapping.getBedrockDefinition().equals(definition)) {
if (isBlock && !hasDamage) { // Pre-1.16.220 will not use block runtime IDs at all, so we shouldn't check either
if (data.getBlockRuntimeId() != mapping.getBedrockBlockId()) {
if (data.getBlockDefinition() != mapping.getBedrockBlockDefinition()) {
continue;
}
} else {

View file

@ -28,6 +28,7 @@ package org.geysermc.geyser.translator.inventory.chest;
import org.cloudburstmc.math.vector.Vector3i;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType;
import org.cloudburstmc.protocol.bedrock.packet.BlockEntityDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.ContainerClosePacket;
@ -82,12 +83,12 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
Vector3i position = session.getPlayerEntity().getPosition().toInt().add(Vector3i.UP);
Vector3i pairPosition = position.add(Vector3i.UNIT_X);
int bedrockBlockId = session.getBlockMappings().getBedrockBlockId(defaultJavaBlockState);
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(defaultJavaBlockState);
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(position);
blockPacket.setRuntimeId(bedrockBlockId);
blockPacket.setDefinition(definition);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
@ -107,7 +108,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(pairPosition);
blockPacket.setRuntimeId(bedrockBlockId);
blockPacket.setDefinition(definition);
blockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(blockPacket);
@ -154,7 +155,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock));
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
holderPos = holderPos.add(Vector3i.UNIT_X);
@ -162,7 +163,7 @@ public class DoubleChestInventoryTranslator extends ChestInventoryTranslator {
blockPacket = new UpdateBlockPacket();
blockPacket.setDataLayer(0);
blockPacket.setBlockPosition(holderPos);
blockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(realBlock));
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(realBlock));
session.sendUpstreamPacket(blockPacket);
}
}

View file

@ -26,18 +26,30 @@
package org.geysermc.geyser.translator.inventory.item;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.opennbt.tag.builtin.*;
import com.github.steveice10.opennbt.tag.builtin.ByteArrayTag;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.DoubleTag;
import com.github.steveice10.opennbt.tag.builtin.FloatTag;
import com.github.steveice10.opennbt.tag.builtin.IntArrayTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.LongArrayTag;
import com.github.steveice10.opennbt.tag.builtin.LongTag;
import com.github.steveice10.opennbt.tag.builtin.ShortTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.nukkitx.nbt.NbtList;
import com.nukkitx.nbt.NbtMap;
import com.nukkitx.nbt.NbtMapBuilder;
import com.nukkitx.nbt.NbtType;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.protocol.bedrock.data.defintions.ItemDefinition;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.registry.BlockRegistries;
@ -50,7 +62,11 @@ import org.geysermc.geyser.util.FileUtils;
import javax.annotation.Nonnull;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public abstract class ItemTranslator {
@ -169,7 +185,7 @@ public abstract class ItemTranslator {
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.getOrDefault(bedrockItem.getJavaId(), DEFAULT_TRANSLATOR);
ItemData.Builder builder = itemStackTranslator.translateToBedrock(itemStack, bedrockItem, session.getItemMappings());
if (bedrockItem.isBlock()) {
builder.blockRuntimeId(bedrockItem.getBedrockBlockId());
builder.blockDefinition(bedrockItem.getBedrockBlockDefinition());
}
if (nbt != null) {

View file

@ -80,7 +80,7 @@ public class FlowerPotBlockEntityTranslator implements BedrockOnlyBlockEntity {
BlockEntityUtils.updateBlockEntity(session, tag, position);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(blockState));
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(blockState));
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);

View file

@ -598,7 +598,7 @@ public class PistonBlockEntity {
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
updateBlockPacket.setBlockPosition(newPos);
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockMovingBlock().getRuntimeId());
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockMovingBlock());
updateBlockPacket.setDataLayer(0);
session.sendUpstreamPacket(updateBlockPacket);
// Update moving block with correct details

View file

@ -195,7 +195,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
}
// Bedrock sends block interact code for a Java entity so we send entity code back to Java
if (session.getBlockMappings().isItemFrame(packet.getBlockRuntimeId())) {
if (session.getBlockMappings().isItemFrame(packet.getBlockDefinition())) {
Entity itemFrameEntity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
if (itemFrameEntity != null) {
processEntityInteraction(session, packet, itemFrameEntity);
@ -313,7 +313,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
if (packet.getActions().isEmpty()) {
if (session.getOpPermissionLevel() >= 2 && session.getGameMode() == GameMode.CREATIVE) {
// Otherwise insufficient permissions
if (session.getBlockMappings().getJigsawStates().contains(packet.getBlockRuntimeId())) {
if (session.getBlockMappings().getJigsawStates().contains(packet.getBlockDefinition())) {
ContainerOpenPacket openPacket = new ContainerOpenPacket();
openPacket.setBlockPosition(packet.getBlockPosition());
openPacket.setId((byte) 1);
@ -539,14 +539,14 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(blockPos);
updateBlockPacket.setRuntimeId(session.getBlockMappings().getBedrockBlockId(javaBlockState));
updateBlockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(javaBlockState));
updateBlockPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(updateBlockPacket);
UpdateBlockPacket updateWaterPacket = new UpdateBlockPacket();
updateWaterPacket.setDataLayer(1);
updateWaterPacket.setBlockPosition(blockPos);
updateWaterPacket.setRuntimeId((BlockRegistries.WATERLOGGED.get().contains(javaBlockState) ? session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir()).getRuntimeId());
updateWaterPacket.setDefinition(BlockRegistries.WATERLOGGED.get().contains(javaBlockState) ? session.getBlockMappings().getBedrockWater() : session.getBlockMappings().getBedrockAir());
updateWaterPacket.getFlags().addAll(UpdateBlockPacket.FLAG_ALL_PRIORITY);
session.sendUpstreamPacket(updateWaterPacket);

View file

@ -27,6 +27,7 @@ package org.geysermc.geyser.util;
import org.cloudburstmc.math.vector.Vector2i;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.protocol.bedrock.data.defintions.BlockDefinition;
import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket;
import org.cloudburstmc.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket;
@ -133,12 +134,12 @@ public class ChunkUtils {
// Prevent moving_piston from being placed
// It's used for extending piston heads, but it isn't needed on Bedrock and causes pistons to flicker
if (!BlockStateValues.isMovingPiston(blockState)) {
int blockId = session.getBlockMappings().getBedrockBlockId(blockState);
BlockDefinition definition = session.getBlockMappings().getBedrockBlock(blockState);
UpdateBlockPacket updateBlockPacket = new UpdateBlockPacket();
updateBlockPacket.setDataLayer(0);
updateBlockPacket.setBlockPosition(position);
updateBlockPacket.setRuntimeId(blockId);
updateBlockPacket.setDefinition(definition);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NEIGHBORS);
updateBlockPacket.getFlags().add(UpdateBlockPacket.Flag.NETWORK);
session.sendUpstreamPacket(updateBlockPacket);
@ -147,9 +148,9 @@ public class ChunkUtils {
waterPacket.setDataLayer(1);
waterPacket.setBlockPosition(position);
if (BlockRegistries.WATERLOGGED.get().contains(blockState)) {
waterPacket.setRuntimeId(session.getBlockMappings().getBedrockWater().getRuntimeId());
waterPacket.setDefinition(session.getBlockMappings().getBedrockWater());
} else {
waterPacket.setRuntimeId(session.getBlockMappings().getBedrockAir().getRuntimeId());
waterPacket.setDefinition(session.getBlockMappings().getBedrockAir());
}
session.sendUpstreamPacket(waterPacket);
}
@ -197,7 +198,7 @@ public class ChunkUtils {
UpdateBlockPacket blockPacket = new UpdateBlockPacket();
blockPacket.setBlockPosition(pos);
blockPacket.setDataLayer(0);
blockPacket.setRuntimeId(1);
blockPacket.setDefinition(session.getBlockMappings().getBedrockBlock(1));
session.sendUpstreamPacket(blockPacket);
}
}