diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java index cbc0515f9fa..70212f59252 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/CreativeItemRegistryPopulator.java @@ -45,7 +45,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; import java.util.Base64; import java.util.List; import java.util.Locale; @@ -59,7 +58,8 @@ public class CreativeItemRegistryPopulator { (identifier, data) -> identifier.equals("minecraft:empty_map") && data == 2 ); - static List readCreativeItemGroups(ItemRegistryPopulator.PaletteVersion palette, List creativeItemData) { + static void readCreativeItemGroups(ItemRegistryPopulator.PaletteVersion palette, List creativeItemData, + List creativeItemGroups, Map groupIndexMap, Map lastCreativeItemGroup) { GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap(); JsonNode creativeItemEntries; @@ -69,7 +69,6 @@ static List readCreativeItemGroups(ItemRegistryPopulator.Pale throw new AssertionError("Unable to load creative item groups", e); } - List creativeItemGroups = new ArrayList<>(); for (JsonNode creativeItemEntry : creativeItemEntries) { CreativeItemCategory category = CreativeItemCategory.valueOf(creativeItemEntry.get("category").asText().toUpperCase(Locale.ROOT)); String name = creativeItemEntry.get("name").asText(); @@ -88,10 +87,29 @@ static List readCreativeItemGroups(ItemRegistryPopulator.Pale .orElseThrow(); } + if (!name.isEmpty()) { + groupIndexMap.put(name, creativeItemGroups.size()); + } + creativeItemGroups.add(new CreativeItemGroup(category, name, itemData)); } - return creativeItemGroups; + CreativeItemCategory category = null; + for (int i = 0; i < creativeItemGroups.size(); i++) { + CreativeItemGroup creativeItemGroup = creativeItemGroups.get(i); + if (category == null) { + category = creativeItemGroup.getCategory(); + } + + if (creativeItemGroup.getCategory() != category) { + lastCreativeItemGroup.put(category, i--); + category = creativeItemGroup.getCategory(); + } + + if (i == creativeItemGroups.size() - 1) { + lastCreativeItemGroup.put(creativeItemGroup.getCategory(), i); + } + } } static void populate(ItemRegistryPopulator.PaletteVersion palette, Map definitions, Map items, BiConsumer itemConsumer) { diff --git a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java index cfc18da3c9b..0ea8ea31492 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java +++ b/core/src/main/java/org/geysermc/geyser/registry/populator/ItemRegistryPopulator.java @@ -52,6 +52,7 @@ import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.SimpleItemDefinition; +import org.cloudburstmc.protocol.bedrock.data.inventory.CreativeItemCategory; import org.cloudburstmc.protocol.bedrock.data.inventory.CreativeItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.CreativeItemGroup; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; @@ -276,7 +277,11 @@ public static void populate() { } }); - List creativeItemGroups = CreativeItemRegistryPopulator.readCreativeItemGroups(palette, creativeItems); + List creativeItemGroups = new ObjectArrayList<>(); + Map creativeGroupIds = new Object2IntOpenHashMap<>(); + Map lastCreativeGroupIds = new Object2IntOpenHashMap<>(); + CreativeItemRegistryPopulator.readCreativeItemGroups(palette, creativeItems, creativeItemGroups, creativeGroupIds, lastCreativeGroupIds); + BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion()); Set javaOnlyItems = new ObjectOpenHashSet<>(); @@ -459,18 +464,26 @@ public static void populate() { customBlockItemDefinitions.put(customBlockData, definition); customIdMappings.put(customProtocolId, bedrockIdentifier); + CreativeItemCategory category = customBlockData.creativeCategory() == null ? CreativeItemCategory.UNDEFINED : + CreativeItemCategory.values()[customBlockData.creativeCategory().ordinal()]; + CreativeItemData newData = new CreativeItemData(itemData.getItem().toBuilder() .definition(definition) .blockDefinition(bedrockBlock) .netId(itemData.getNetId()) .count(1) - .build(), itemData.getNetId(), 0); + .build(), itemData.getNetId(), getCreativeIndex( + customBlockData.creativeGroup(), + category, + creativeGroupIds, + lastCreativeGroupIds, creativeItemGroups) + ); creativeItems.set(j, newData); } else { CreativeItemData creativeItemData = new CreativeItemData(itemData.getItem().toBuilder() .blockDefinition(bedrockBlock) - .build(), itemData.getNetId(), 0); + .build(), itemData.getNetId(), itemData.getGroupId()); creativeItems.set(j, creativeItemData); } @@ -525,7 +538,13 @@ public static void populate() { .definition(customMapping.itemDefinition()) .blockDefinition(null) .count(1) - .build(), creativeNetId.get(), customItem.bedrockOptions().creativeCategory().id()); + .build(), creativeNetId.get(), getCreativeIndex( + customItem.bedrockOptions().creativeGroup(), + CreativeItemCategory.values()[customItem.bedrockOptions().creativeCategory().id()], + creativeGroupIds, + lastCreativeGroupIds, + creativeItemGroups) + ); creativeItems.add(creativeItemData); } @@ -611,7 +630,7 @@ public static void populate() { .netId(creativeNetId.incrementAndGet()) .definition(definition) .count(1) - .build(), creativeNetId.get(), 99)); // todo do not hardcode! + .build(), creativeNetId.get(), getCreativeIndex("itemGroup.name.minecart", CreativeItemCategory.ITEMS, creativeGroupIds, lastCreativeGroupIds, creativeItemGroups))); // Register any completely custom items given to us IntSet registeredJavaIds = new IntOpenHashSet(); // Used to check for duplicate item java ids @@ -644,7 +663,12 @@ public static void populate() { .definition(registration.mapping().getBedrockDefinition()) .netId(creativeNetId.incrementAndGet()) .count(1) - .build(), creativeNetId.get(), customItem.bedrockOptions().creativeCategory().id()); + .build(), creativeNetId.get(), + getCreativeIndex(customItem.bedrockOptions().creativeGroup(), + CreativeItemCategory.values()[customItem.bedrockOptions().creativeCategory().id()], + creativeGroupIds,lastCreativeGroupIds, + creativeItemGroups) + ); creativeItems.add(creativeItemData); } @@ -679,12 +703,20 @@ public static void populate() { GeyserBedrockBlock bedrockBlock = blockMappings.getCustomBlockStateDefinitions().getOrDefault(customBlock.defaultBlockState(), null); if (bedrockBlock != null && customBlock.includedInCreativeInventory()) { + CreativeItemCategory category = customBlock.creativeCategory() == null ? CreativeItemCategory.UNDEFINED : + CreativeItemCategory.values()[customBlock.creativeCategory().ordinal()]; + CreativeItemData creativeItemData = new CreativeItemData(ItemData.builder() .definition(definition) .blockDefinition(bedrockBlock) .netId(creativeNetId.incrementAndGet()) .count(1) - .build(), creativeNetId.get(), customBlock.creativeCategory().id()); + .build(), creativeNetId.get(), getCreativeIndex( + customBlock.creativeGroup(), + category, + creativeGroupIds, + lastCreativeGroupIds, creativeItemGroups) + ); creativeItems.add(creativeItemData); } } @@ -748,6 +780,34 @@ private static NbtMap registerFurnaceMinecart(int nextFreeBedrockId) { return builder.build(); } + public static int getCreativeIndex(String creativeGroup, CreativeItemCategory creativeItemCategory, Map groupIndexes, Map fallBacks, List creativeItemGroups) { + if (fallBacks.isEmpty()) { + // Not post rewrite, this index won't be used either way + return 0; + } + + if (creativeGroup != null) { + if (groupIndexes.containsKey(creativeGroup)) { + return groupIndexes.get(creativeGroup); + } + } + + if (creativeItemCategory != null) { + if (fallBacks.containsKey(creativeItemCategory)) { + return fallBacks.get(creativeItemCategory); + } + + // Must "register" this category first + // Sending a creative item group with no items in it crashes the client. Fun. + creativeItemGroups.add(new CreativeItemGroup(creativeItemCategory, "", ItemData.AIR)); + fallBacks.put(creativeItemCategory, creativeItemGroups.size() - 1); + + return fallBacks.get(creativeItemCategory); + } + + return fallBacks.get(CreativeItemCategory.UNDEFINED); + } + /** * Compares custom item definitions based on their predicates: *