Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try fixing custom blockstate registration #5310

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

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