Actually implement adventure mode predicates, kind of.

If it's just block IDs, it'll work.
This commit is contained in:
Camotoy 2024-05-19 23:15:52 -04:00
parent d85549c38d
commit 1b075badce
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
7 changed files with 71 additions and 58 deletions

View file

@ -66,11 +66,6 @@ public class BlockRegistries {
*/
public static final ListRegistry<BlockState> BLOCK_STATES = ListRegistry.create(RegistryLoaders.empty(ArrayList::new));
/**
* A mapped registry which stores Java to Bedrock block identifiers.
*/
public static final SimpleMappedRegistry<String, String> JAVA_TO_BEDROCK_IDENTIFIERS = SimpleMappedRegistry.create(RegistryLoaders.empty(Object2ObjectOpenHashMap::new));
/**
* A mapped registry containing which holds block IDs to its {@link BlockCollision}.
*/
@ -89,6 +84,9 @@ public class BlockRegistries {
/**
* A registry containing all the waterlogged blockstates.
* Properties.WATERLOGGED should not be relied on for two reasons:
* - Custom blocks
* - Seagrass, kelp, and bubble columns are assumed waterlogged and don't have a waterlogged property
*/
public static final SimpleRegistry<BitSet> WATERLOGGED = SimpleRegistry.create(RegistryLoaders.empty(BitSet::new));
@ -144,7 +142,6 @@ public class BlockRegistries {
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.POST_INIT);
System.out.println("Block registries loaded");
}
public static void init() {

View file

@ -204,6 +204,5 @@ public final class Registries {
biomesNbt.put(key, value.build());
}
BIOMES_NBT.set(biomesNbt.build());
System.out.println("Registries loaded");
}
}

View file

@ -236,6 +236,10 @@ public final class BlockRegistryPopulator {
GeyserBedrockBlock[] javaToBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
GeyserBedrockBlock[] javaToVanillaBedrockBlocks = new GeyserBedrockBlock[JAVA_BLOCKS_SIZE];
//List<String> javaToBedrockIdentifiers = new ArrayList<>(BlockRegistries.JAVA_BLOCKS.get().size());
var javaToBedrockIdentifiers = new Int2ObjectOpenHashMap<String>();
Block lastBlockSeen = null;
// Stream isn't ideal.
List<Block> javaPottable = BlockRegistries.JAVA_BLOCKS.get()
.parallelStream()
@ -291,27 +295,35 @@ public final class BlockRegistryPopulator {
case "minecraft:moving_piston[facing=north,type=normal]" -> movingBlockDefinition = bedrockDefinition;
}
if (blockState.block() == Blocks.JIGSAW) {
Block block = blockState.block();
if (block != lastBlockSeen) {
lastBlockSeen = block;
String bedrockName = bedrockDefinition.getState().getString("name");
if (!block.javaIdentifier().toString().equals(bedrockName)) {
javaToBedrockIdentifiers.put(block.javaId(), bedrockName.substring("minecraft:".length()).intern());
}
}
if (block == Blocks.JIGSAW) {
jigsawDefinitions.add(bedrockDefinition);
}
if (blockState.block() == Blocks.STRUCTURE_BLOCK) {
if (block == Blocks.STRUCTURE_BLOCK) {
String mode = blockState.getValue(Properties.STRUCTUREBLOCK_MODE);
structureBlockDefinitions.put(mode.toUpperCase(Locale.ROOT), bedrockDefinition);
}
boolean waterlogged = blockState.getValue(Properties.WATERLOGGED, false)
|| blockState.block() == Blocks.BUBBLE_COLUMN || blockState.block() == Blocks.KELP || blockState.block() == Blocks.SEAGRASS;
|| block == Blocks.BUBBLE_COLUMN || block == Blocks.KELP || block == Blocks.SEAGRASS;
if (waterlogged) {
int finalJavaRuntimeId = javaRuntimeId;
BlockRegistries.WATERLOGGED.register(set -> set.set(finalJavaRuntimeId));
BlockRegistries.WATERLOGGED.get().set(javaRuntimeId);
}
// Get the tag needed for non-empty flower pots
if (javaPottable.contains(blockState.block())) {
if (javaPottable.contains(block)) {
// Specifically NOT putIfAbsent - mangrove propagule breaks otherwise
flowerPotBlocks.put(blockState.block(), blockStates.get(bedrockDefinition.getRuntimeId()));
flowerPotBlocks.put(block, blockStates.get(bedrockDefinition.getRuntimeId()));
}
javaToVanillaBedrockBlocks[javaRuntimeId] = vanillaBedrockDefinition;
@ -367,9 +379,12 @@ public final class BlockRegistryPopulator {
javaToVanillaBedrockBlocks[stateRuntimeId] = bedrockDefinition; // TODO: Check this?
javaToBedrockBlocks[stateRuntimeId] = bedrockDefinition;
javaToBedrockIdentifiers.put(entry.getKey().stateGroupId(), entry.getValue().block().identifier());
}
}
javaToBedrockIdentifiers.trim();
// Loop around again to find all item frame runtime IDs
Object2ObjectMaps.fastForEach(blockStateOrderedMap, entry -> {
String name = entry.getKey().getString("name");
@ -381,6 +396,7 @@ public final class BlockRegistryPopulator {
BlockRegistries.BLOCKS.register(palette.valueInt(), builder.bedrockRuntimeMap(bedrockRuntimeMap)
.javaToBedrockBlocks(javaToBedrockBlocks)
.javaToVanillaBedrockBlocks(javaToVanillaBedrockBlocks)
.javaToBedrockIdentifiers(javaToBedrockIdentifiers)
.stateDefinitionMap(blockStateOrderedMap)
.itemFrames(itemFrames)
.flowerPotBlocks(flowerPotBlocks)
@ -484,15 +500,9 @@ public final class BlockRegistryPopulator {
};
block.setJavaId(javaBlockState.stateGroupId());
String bedrockIdentifier = customBlockState.block().identifier();
BlockRegistries.JAVA_BLOCKS.get().add(javaBlockState.stateGroupId(), block); //TODO don't allow duplicates, allow blanks
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
// Keeping this here since this is currently unchanged between versions
// It's possible to only have this store differences in names, but the key set of all Java names is used in sending command suggestions
BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.register(cleanJavaIdentifier.intern(), bedrockIdentifier.intern());
}
}

View file

@ -52,6 +52,12 @@ public class BlockMappings implements DefinitionRegistry<GeyserBedrockBlock> {
GeyserBedrockBlock[] javaToBedrockBlocks;
GeyserBedrockBlock[] javaToVanillaBedrockBlocks;
/**
* Java block ID -> Bedrock block ID (without minecraft:), IF they are different
* While Bedrock is progressing slowly through their flattening, some Bedrock identifiers may differ.
*/
Int2ObjectMap<String> javaToBedrockIdentifiers;
Map<NbtMap, GeyserBedrockBlock> stateDefinitionMap;
GeyserBedrockBlock[] bedrockRuntimeMap;
int[] remappedVanillaIds;

View file

@ -42,6 +42,7 @@ import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.inventory.GeyserItemStack;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.item.type.Item;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.CustomSkull;
@ -52,20 +53,12 @@ import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.InventoryUtils;
import org.geysermc.mcprotocollib.protocol.data.game.Identifier;
import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.ModifierOperation;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.AdventureModePredicate;
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.ItemAttributeModifiers;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.*;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
public final class ItemTranslator {
@ -185,11 +178,11 @@ public final class ItemTranslator {
translateCustomItem(components, builder, bedrockItem);
if (components != null) {
// Translate the canDestroy and canPlaceOn Java NBT
// Translate the canDestroy and canPlaceOn Java components
AdventureModePredicate canDestroy = components.get(DataComponentType.CAN_BREAK);
AdventureModePredicate canPlaceOn = components.get(DataComponentType.CAN_PLACE_ON);
String[] canBreak = getCanModify(canDestroy);
String[] canPlace = getCanModify(canPlaceOn);
String[] canBreak = getCanModify(session, canDestroy);
String[] canPlace = getCanModify(session, canPlaceOn);
if (canBreak != null) {
builder.canBreak(canBreak);
}
@ -325,27 +318,42 @@ public final class ItemTranslator {
* @param canModifyJava the list of items in Java
* @return the new list of items in Bedrock
*/
// TODO this is now more complicated in 1.20.5. Yippee!
private static String @Nullable [] getCanModify(@Nullable AdventureModePredicate canModifyJava) {
// TODO blocks by tag, maybe NBT, maybe properties
// Blocks by tag will be easy enough, most likely, we just need to... save all block tags.
// Probably do that with Guava interning around sessions
private static String @Nullable [] getCanModify(GeyserSession session, @Nullable AdventureModePredicate canModifyJava) {
if (canModifyJava == null) {
return null;
}
List<AdventureModePredicate.BlockPredicate> predicates = canModifyJava.getPredicates();
if (predicates.size() > 0) {
String[] canModifyBedrock = new String[predicates.size()];
for (int i = 0; i < canModifyBedrock.length; i++) {
// Get the Java identifier of the block that can be placed
String location = predicates.get(i).getLocation();
if (location == null) {
canModifyBedrock[i] = ""; // So it'll serialize
continue; // ???
if (!predicates.isEmpty()) {
List<String> canModifyBedrock = new ArrayList<>(); // This used to be an array, but we need to be flexible with what blocks can be supported
for (int i = 0; i < predicates.size(); i++) {
HolderSet holderSet = predicates.get(i).getBlocks();
if (holderSet == null) {
continue;
}
int[] holders = holderSet.getHolders();
if (holders == null) {
continue;
}
// Holders is an int state of Java block IDs (not block states)
for (int blockId : holders) {
// Get the Bedrock identifier of the item
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
Block block = BlockRegistries.JAVA_BLOCKS.get(blockId);
if (block == null) {
continue;
}
String identifier = session.getBlockMappings().getJavaToBedrockIdentifiers().get(block.javaId());
if (identifier == null) {
canModifyBedrock.add(block.javaIdentifier().value());
} else {
canModifyBedrock.add(identifier);
}
}
String block = Identifier.formalize(location);
// Get the Bedrock identifier of the item and replace it.
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
canModifyBedrock[i] = BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.getOrDefault(block, block).replace("minecraft:", "");
}
return canModifyBedrock;
return canModifyBedrock.toArray(new String[0]);
}
return null;
}

View file

@ -59,6 +59,7 @@ import java.util.*;
@Translator(packet = ClientboundCommandsPacket.class)
public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommandsPacket> {
private static final String[] ALL_BLOCK_NAMES = BlockRegistries.JAVA_BLOCKS.get().stream().map(block -> block.javaIdentifier().toString()).toArray(String[]::new);
private static final String[] ALL_EFFECT_IDENTIFIERS = EntityUtils.getAllEffectIdentifiers();
private static final String[] ATTRIBUTES = AttributeType.Builtin.BUILTIN.values().stream().map(AttributeType::getIdentifier).toList().toArray(new String[0]);
private static final String[] ENUM_BOOLEAN = {"true", "false"};
@ -246,7 +247,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
case RESOURCE_LOCATION, FUNCTION -> CommandParam.FILE_PATH;
case BOOL -> ENUM_BOOLEAN;
case OPERATION -> CommandParam.OPERATOR; // ">=", "==", etc
case BLOCK_STATE -> context.getBlockStates();
case BLOCK_STATE -> ALL_BLOCK_NAMES;
case ITEM_STACK -> context.getItemNames();
case COLOR -> VALID_COLORS;
case SCOREBOARD_SLOT -> VALID_SCOREBOARD_SLOTS;
@ -286,7 +287,6 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
private final GeyserSession session;
private Object biomesWithTags;
private Object biomesNoTags;
private String[] blockStates;
private String[] entityTypes;
private String[] itemNames;
private CommandEnumData teams;
@ -313,13 +313,6 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
return (biomesWithTags = identifiers != null ? identifiers : CommandParam.STRING);
}
private String[] getBlockStates() {
if (blockStates != null) {
return blockStates;
}
return (blockStates = BlockRegistries.JAVA_TO_BEDROCK_IDENTIFIERS.get().keySet().toArray(new String[0]));
}
private String[] getEntityTypes() {
if (entityTypes != null) {
return entityTypes;

View file

@ -15,7 +15,7 @@ protocol-connection = "3.0.0.Beta1-20240411.165033-128"
raknet = "1.0.0.CR3-20240416.144209-1"
blockstateupdater="1.20.80-20240411.142413-1"
mcauthlib = "e5b0bcc"
mcprotocollib = "1.20.6-2-20240515.051848-2" # Revert from jitpack after release
mcprotocollib = "1.20.6-2-20240520.030045-8"
adventure = "4.14.0"
adventure-platform = "4.3.0"
junit = "5.9.2"