Skip to content

Commit

Permalink
Fix: non-vanilla custom blockstate registration (#5310)
Browse files Browse the repository at this point in the history
* Try fixing custom blockstate registration

* another attempt; round two

* register collisions

* don't register block states twice
  • Loading branch information
onebeastchris authored Feb 2, 2025
1 parent f328e5b commit ea13e58
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 80 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public interface JavaBlockState {
@Nullable String pistonBehavior();

/**
* Gets whether the block state has block entity
* Gets whether the block state has a block entity
*
* @return whether the block state has block entity
* @deprecated Does not have an effect. If you were using this to
Expand Down
14 changes: 11 additions & 3 deletions core/src/main/java/org/geysermc/geyser/level/block/type/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ public static final class Builder {
private Supplier<Item> pickItem;

// We'll use this field after building
private Property<?>[] propertyKeys;
private Property<?>[] propertyKeys = null;
private @Nullable Integer javaId = null;

/**
* For states that we're just tracking for mirroring Java states.
Expand Down Expand Up @@ -298,11 +299,18 @@ public Builder pickItem(Supplier<Item> pickItem) {
return this;
}

public Builder javaId(int javaId) {
this.javaId = javaId;
return this;
}

private List<BlockState> build(Block block) {
if (states.isEmpty()) {
BlockState state = new BlockState(block, BlockRegistries.BLOCK_STATES.get().size());
if (javaId == null) {
javaId = BlockRegistries.BLOCK_STATES.get().size();
}
BlockState state = new BlockState(block, javaId);
BlockRegistries.BLOCK_STATES.get().add(state);
propertyKeys = null;
return List.of(state);
} else if (states.size() == 1) {
// We can optimize because we don't need to worry about combinations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ public static void populate() {
CustomSkullRegistryPopulator.populate();
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.PRE_INIT);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.DEFINITION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_JAVA);
COLLISIONS.load();
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.NON_VANILLA_REGISTRATION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.VANILLA_REGISTRATION);
CustomBlockRegistryPopulator.populate(CustomBlockRegistryPopulator.Stage.CUSTOM_REGISTRATION);
BlockRegistryPopulator.populate(BlockRegistryPopulator.Stage.INIT_BEDROCK);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
import com.google.common.collect.Interners;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
Expand All @@ -52,27 +50,21 @@
import org.geysermc.geyser.api.block.custom.CustomBlockData;
import org.geysermc.geyser.api.block.custom.CustomBlockState;
import org.geysermc.geyser.api.block.custom.nonvanilla.JavaBlockState;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.property.Properties;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.block.type.FlowerPotBlock;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;

import java.io.DataInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -114,8 +106,8 @@ public static void populate(Stage stage) {
* Stores the raw blocks NBT until it is no longer needed.
*/
private static List<NbtMap> BLOCKS_NBT;
private static int MIN_CUSTOM_RUNTIME_ID = -1;
private static int JAVA_BLOCKS_SIZE = -1;
public static int MIN_CUSTOM_RUNTIME_ID = -1;
public static int JAVA_BLOCKS_SIZE = -1;

private static void nullifyBlocksNbt() {
BLOCKS_NBT = null;
Expand Down Expand Up @@ -411,19 +403,6 @@ private static void registerJavaBlocks() {
throw new AssertionError("Unable to load Java block mappings", e);
}

JAVA_BLOCKS_SIZE = BlockRegistries.BLOCK_STATES.get().size();

if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();

if (MIN_CUSTOM_RUNTIME_ID < blocksNbt.size()) {
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
}

JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1
}

int javaRuntimeId = -1;
for (BlockState javaBlockState : BlockRegistries.BLOCK_STATES.get()) {
javaRuntimeId++;
Expand All @@ -432,49 +411,8 @@ private static void registerJavaBlocks() {
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, javaRuntimeId);
}

if (!BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().isEmpty()) {
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();

for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier());
}

String javaId = javaBlockState.identifier();
int stateRuntimeId = javaBlockState.javaId();
String pistonBehavior = javaBlockState.pistonBehavior();

Block.Builder builder = Block.builder()
.destroyTime(javaBlockState.blockHardness())
.pushReaction(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior));
if (!javaBlockState.canBreakWithHand()) {
builder.requiresCorrectToolForDrops();
}
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
String pickItem = javaBlockState.pickItem();
Block block = new Block(cleanJavaIdentifier, builder) {
@Override
public ItemStack pickItem(BlockState state) {
if (this.item == null) {
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
if (this.item == null) {
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
+ " for getting the item for block " + javaBlockState.identifier());
this.item = Items.AIR;
}
}
return new ItemStack(this.item.javaId());
}
};
block.setJavaId(javaBlockState.stateGroupId());

BlockRegistries.JAVA_BLOCKS.registerWithAnyIndex(javaBlockState.stateGroupId(), block, Blocks.AIR);
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);
BlockRegistries.BLOCK_STATES.register(stateRuntimeId, new BlockState(block, stateRuntimeId));
}
}

BLOCKS_NBT = blocksNbt;
JAVA_BLOCKS_SIZE = blocksNbt.size();

JsonNode blockInteractionsJson;
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
Expand All @@ -485,8 +423,6 @@ public ItemStack pickItem(BlockState state) {

BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));

BlockRegistries.BLOCK_STATES.freeze();
}

private static BitSet toBlockStateSet(ArrayNode node) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtMap;
Expand All @@ -48,23 +50,38 @@
import org.geysermc.geyser.api.block.custom.property.PropertyType;
import org.geysermc.geyser.api.event.lifecycle.GeyserDefineCustomBlocksEvent;
import org.geysermc.geyser.api.util.CreativeCategory;
import org.geysermc.geyser.item.Items;
import org.geysermc.geyser.level.block.Blocks;
import org.geysermc.geyser.level.block.GeyserCustomBlockComponents;
import org.geysermc.geyser.level.block.GeyserCustomBlockData;
import org.geysermc.geyser.level.block.GeyserCustomBlockState;
import org.geysermc.geyser.level.block.GeyserGeometryComponent;
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
import org.geysermc.geyser.level.block.type.Block;
import org.geysermc.geyser.level.block.type.BlockState;
import org.geysermc.geyser.level.physics.BoundingBox;
import org.geysermc.geyser.level.physics.PistonBehavior;
import org.geysermc.geyser.registry.BlockRegistries;
import org.geysermc.geyser.registry.Registries;
import org.geysermc.geyser.registry.mappings.MappingsConfigReader;
import org.geysermc.geyser.registry.type.CustomSkull;
import org.geysermc.geyser.translator.collision.OtherCollision;
import org.geysermc.geyser.util.BlockUtils;
import org.geysermc.geyser.util.MathUtils;
import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack;

import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import static org.geysermc.geyser.registry.populator.BlockRegistryPopulator.JAVA_BLOCKS_SIZE;
import static org.geysermc.geyser.registry.populator.BlockRegistryPopulator.MIN_CUSTOM_RUNTIME_ID;

public class CustomBlockRegistryPopulator {

Expand Down Expand Up @@ -231,6 +248,73 @@ private static void populateVanilla() {
*/
private static void populateNonVanilla() {
BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.set(NON_VANILLA_BLOCK_STATE_OVERRIDES);

if (NON_VANILLA_BLOCK_STATE_OVERRIDES.isEmpty()) {
// Nothing left to register, freeze block state registry
BlockRegistries.BLOCK_STATES.freeze();
return;
}

MIN_CUSTOM_RUNTIME_ID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().min(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();
int maxCustomRuntimeID = BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet().stream().max(Comparator.comparing(JavaBlockState::javaId)).orElseThrow().javaId();

if (MIN_CUSTOM_RUNTIME_ID < BlockRegistries.BLOCK_STATES.get().size()) {
throw new RuntimeException("Non vanilla custom block state overrides runtime ID must start after the last vanilla block state (" + JAVA_BLOCKS_SIZE + ")");
}

JAVA_BLOCKS_SIZE = maxCustomRuntimeID + 1; // Runtime ids start at 0, so we need to add 1

// Now: Vanilla blocks are already loaded and registered; let's load non-vanilla properly too
IntSet usedNonVanillaRuntimeIDs = new IntOpenHashSet();

for (JavaBlockState javaBlockState : BlockRegistries.NON_VANILLA_BLOCK_STATE_OVERRIDES.get().keySet()) {
if (!usedNonVanillaRuntimeIDs.add(javaBlockState.javaId())) {
throw new RuntimeException("Duplicate runtime ID " + javaBlockState.javaId() + " for non vanilla Java block state " + javaBlockState.identifier());
}

String javaId = javaBlockState.identifier();
int stateRuntimeId = javaBlockState.javaId();
String pistonBehavior = javaBlockState.pistonBehavior();

Block.Builder builder = Block.builder()
.javaId(stateRuntimeId)
.destroyTime(javaBlockState.blockHardness())
.pushReaction(pistonBehavior == null ? PistonBehavior.NORMAL : PistonBehavior.getByName(pistonBehavior));
if (!javaBlockState.canBreakWithHand()) {
builder.requiresCorrectToolForDrops();
}
String cleanJavaIdentifier = BlockUtils.getCleanIdentifier(javaBlockState.identifier());
String pickItem = javaBlockState.pickItem();
Block block = new Block(cleanJavaIdentifier, builder) {
@Override
public ItemStack pickItem(BlockState state) {
if (this.item == null) {
this.item = Registries.JAVA_ITEM_IDENTIFIERS.get(pickItem);
if (this.item == null) {
GeyserImpl.getInstance().getLogger().warning("We could not find item " + pickItem
+ " for getting the item for block " + javaBlockState.identifier());
this.item = Items.AIR;
}
}
return new ItemStack(this.item.javaId());
}
};
block.setJavaId(javaBlockState.stateGroupId());

BlockRegistries.JAVA_BLOCKS.registerWithAnyIndex(javaBlockState.stateGroupId(), block, Blocks.AIR);
BlockRegistries.JAVA_IDENTIFIER_TO_ID.register(javaId, stateRuntimeId);

// TODO register different collision types?
BoundingBox[] geyserCollisions = Arrays.stream(javaBlockState.collision())
.map(box -> new BoundingBox(box.middleX(), box.middleY(), box.middleZ(),
box.sizeX(), box.sizeY(), box.sizeZ()))
.toArray(BoundingBox[]::new);
OtherCollision collision = new OtherCollision(geyserCollisions);
BlockRegistries.COLLISIONS.registerWithAnyIndex(javaBlockState.javaId(), collision, collision);
}

BlockRegistries.BLOCK_STATES.freeze();

if (!NON_VANILLA_BLOCK_STATE_OVERRIDES.isEmpty()) {
GeyserImpl.getInstance().getLogger().info("Registered " + NON_VANILLA_BLOCK_STATE_OVERRIDES.size() + " non-vanilla block overrides.");
}
Expand Down

0 comments on commit ea13e58

Please # to comment.