#1275: Add internal ItemType and BlockType, delegate Material methods to them

By: Jishuna <joshl5324@gmail.com>
Also-by: Bjarne Koll <lynxplay101@gmail.com>
Also-by: DerFrZocker <derrieple@gmail.com>
Also-by: md_5 <git@md-5.net>
This commit is contained in:
CraftBukkit/Spigot
2024-05-05 10:08:54 +10:00
parent b4e6cc4dce
commit 8f55ed539f
14 changed files with 642 additions and 47 deletions

View File

@@ -1,10 +1,42 @@
package org.bukkit.craftbukkit.block;
import com.google.common.base.Preconditions;
import java.util.function.Consumer;
import net.minecraft.core.BlockPosition;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.EnumHand;
import net.minecraft.world.entity.player.EntityHuman;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.BlockAccessAir;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.BlockFalling;
import net.minecraft.world.level.block.BlockFire;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockBase;
import net.minecraft.world.level.block.state.IBlockData;
import net.minecraft.world.phys.MovingObjectPositionBlock;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.block.BlockType;
import org.bukkit.block.data.BlockData;
import org.bukkit.craftbukkit.CraftRegistry;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.data.CraftBlockData;
import org.bukkit.craftbukkit.inventory.CraftItemType;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.util.Handleable;
import org.bukkit.inventory.ItemType;
import org.jetbrains.annotations.NotNull;
public class CraftBlockType {
public class CraftBlockType<B extends BlockData> implements BlockType.Typed<B>, Handleable<Block> {
private final NamespacedKey key;
private final Block block;
private final Class<B> blockDataClass;
private final boolean interactable;
public static Material minecraftToBukkit(Block block) {
return CraftMagicNumbers.getMaterial(block);
@@ -13,4 +45,184 @@ public class CraftBlockType {
public static Block bukkitToMinecraft(Material material) {
return CraftMagicNumbers.getBlock(material);
}
public static BlockType minecraftToBukkitNew(Block minecraft) {
return CraftRegistry.minecraftToBukkit(minecraft, Registries.BLOCK, Registry.BLOCK);
}
public static Block bukkitToMinecraftNew(BlockType bukkit) {
return CraftRegistry.bukkitToMinecraft(bukkit);
}
private static boolean hasMethod(Class<?> clazz, String methodName, Class<?>... params) {
boolean hasMethod;
try {
hasMethod = clazz.getDeclaredMethod(methodName, params) != null;
} catch (NoSuchMethodException ex) {
hasMethod = false;
}
return hasMethod;
}
private static boolean isInteractable(Block block) {
Class<?> clazz = block.getClass();
boolean hasMethod = hasMethod(clazz, "useWithoutItem", IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, MovingObjectPositionBlock.class)
|| hasMethod(clazz, "useItemOn", net.minecraft.world.item.ItemStack.class, IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, EnumHand.class, MovingObjectPositionBlock.class);
if (!hasMethod && clazz.getSuperclass() != BlockBase.class) {
clazz = clazz.getSuperclass();
hasMethod = hasMethod(clazz, "useWithoutItem", IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, MovingObjectPositionBlock.class)
|| hasMethod(clazz, "useItemOn", net.minecraft.world.item.ItemStack.class, IBlockData.class, net.minecraft.world.level.World.class, BlockPosition.class, EntityHuman.class, EnumHand.class, MovingObjectPositionBlock.class);
}
return hasMethod;
}
public CraftBlockType(NamespacedKey key, Block block) {
this.key = key;
this.block = block;
this.blockDataClass = (Class<B>) CraftBlockData.fromData(block.defaultBlockState()).getClass().getInterfaces()[0];
this.interactable = isInteractable(block);
}
@Override
public Block getHandle() {
return block;
}
@NotNull
@Override
public Typed<BlockData> typed() {
return this.typed(BlockData.class);
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public <Other extends BlockData> Typed<Other> typed(@NotNull Class<Other> blockDataType) {
if (blockDataType.isAssignableFrom(this.blockDataClass)) return (Typed<Other>) this;
throw new IllegalArgumentException("Cannot type block type " + this.key.toString() + " to blockdata type " + blockDataType.getSimpleName());
}
@Override
public boolean hasItemType() {
if (this == AIR) {
return true;
}
return block.asItem() != Items.AIR;
}
@NotNull
@Override
public ItemType getItemType() {
if (this == AIR) {
return ItemType.AIR;
}
Item item = block.asItem();
Preconditions.checkArgument(item != Items.AIR, "The block type %s has no corresponding item type", getKey());
return CraftItemType.minecraftToBukkitNew(item);
}
@Override
public Class<B> getBlockDataClass() {
return blockDataClass;
}
@Override
public B createBlockData() {
return createBlockData((String) null);
}
@Override
public B createBlockData(Consumer<? super B> consumer) {
B data = createBlockData();
if (consumer != null) {
consumer.accept(data);
}
return data;
}
@Override
public B createBlockData(String data) {
return (B) CraftBlockData.newData(this, data);
}
@Override
public boolean isSolid() {
return block.defaultBlockState().blocksMotion();
}
@Override
public boolean isAir() {
return block.defaultBlockState().isAir();
}
@Override
public boolean isEnabledByFeature(@NotNull World world) {
Preconditions.checkNotNull(world, "World cannot be null");
return getHandle().isEnabled(((CraftWorld) world).getHandle().enabledFeatures());
}
@Override
public boolean isFlammable() {
return block.defaultBlockState().ignitedByLava();
}
@Override
public boolean isBurnable() {
return ((BlockFire) Blocks.FIRE).igniteOdds.getOrDefault(block, 0) > 0;
}
@Override
public boolean isOccluding() {
return block.defaultBlockState().isRedstoneConductor(BlockAccessAir.INSTANCE, BlockPosition.ZERO);
}
@Override
public boolean hasGravity() {
return block instanceof BlockFalling;
}
@Override
public boolean isInteractable() {
return interactable;
}
@Override
public float getHardness() {
return block.defaultBlockState().destroySpeed;
}
@Override
public float getBlastResistance() {
return block.getExplosionResistance();
}
@Override
public float getSlipperiness() {
return block.getFriction();
}
@NotNull
@Override
public String getTranslationKey() {
return block.getDescriptionId();
}
@Override
public NamespacedKey getKey() {
return key;
}
@Override
public Material asMaterial() {
return Registry.MATERIAL.get(this.key);
}
}

View File

@@ -31,6 +31,7 @@ import org.bukkit.SoundGroup;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.BlockSupport;
import org.bukkit.block.BlockType;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.structure.Mirror;
@@ -540,11 +541,9 @@ public class CraftBlockData implements BlockData {
Preconditions.checkState(MAP.put(nms, bukkit) == null, "Duplicate mapping %s->%s", nms, bukkit);
}
public static CraftBlockData newData(Material material, String data) {
Preconditions.checkArgument(material == null || material.isBlock(), "Cannot get data for not block %s", material);
public static CraftBlockData newData(BlockType blockType, String data) {
IBlockData blockData;
Block block = CraftBlockType.bukkitToMinecraft(material);
Block block = blockType == null ? null : ((CraftBlockType<?>) blockType).getHandle();
Map<IBlockState<?>, Comparable<?>> parsed = null;
// Data provided, use it