Refactor for materials.

This commit is contained in:
IzzelAliz 2020-07-04 15:35:20 +08:00
parent 22a67f8ff8
commit 4822b37339
8 changed files with 361 additions and 99 deletions

View File

@ -0,0 +1,8 @@
package io.izzel.arclight.common.bridge.block;
import net.minecraft.block.Block;
public interface FireBlockBridge {
boolean bridge$canBurn(Block block);
}

View File

@ -1,14 +1,18 @@
package io.izzel.arclight.common.bridge.bukkit;
import org.bukkit.NamespacedKey;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import javax.annotation.Nullable;
public interface MaterialBridge {
void bridge$setKey(NamespacedKey namespacedKey);
void bridge$setupBlock(ResourceLocation key, Block block, MaterialPropertySpec spec);
void bridge$setInternal(net.minecraft.block.material.Material internal);
void bridge$setupItem(ResourceLocation key, Item item, MaterialPropertySpec spec);
void bridge$setItem();
void bridge$setBlock();
@Nullable
MaterialPropertySpec bridge$getSpec();
}

View File

@ -1,87 +1,256 @@
package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.bridge.block.FireBlockBridge;
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.FallingBlock;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ResourceLocation;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v.util.CraftNamespacedKey;
import org.bukkit.material.MaterialData;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Material.class)
import java.lang.reflect.Constructor;
@Mixin(value = Material.class, remap = false)
public abstract class MaterialMixin implements MaterialBridge {
// @formatter:off
@Shadow(remap = false) public abstract boolean isBlock();
@Override @Accessor(value = "key", remap = false)
public abstract void bridge$setKey(NamespacedKey namespacedKey);
@Shadow @Mutable @Final private NamespacedKey key;
@Shadow @Mutable @Final private Constructor<? extends MaterialData> ctor;
@Shadow @Mutable @Final public Class<?> data;
@Shadow public abstract boolean isBlock();
// @formatter:on
private net.minecraft.block.material.Material arclight$internal;
private Boolean arclight$isBlock;
private Boolean arclight$isItem;
private MaterialPropertySpec.MaterialType arclight$type = MaterialPropertySpec.MaterialType.VANILLA;
private MaterialPropertySpec arclight$spec;
@Override
public void bridge$setInternal(net.minecraft.block.material.Material internal) {
this.arclight$internal = internal;
}
@Inject(method = "isBlock", cancellable = true, remap = false, at = @At("HEAD"))
public void arclight$isBlock(CallbackInfoReturnable<Boolean> cir) {
if (arclight$isBlock != null) {
cir.setReturnValue(arclight$isBlock);
return;
}
if (arclight$internal != null) {
cir.setReturnValue(arclight$internal.isReplaceable());
@Inject(method = "isBlock", cancellable = true, at = @At("HEAD"))
private void arclight$isBlock(CallbackInfoReturnable<Boolean> cir) {
if (arclight$type != MaterialPropertySpec.MaterialType.VANILLA) {
cir.setReturnValue(arclight$type == MaterialPropertySpec.MaterialType.FORGE_BLOCK);
}
}
@Inject(method = "isItem", cancellable = true, remap = false, at = @At("HEAD"))
@Inject(method = "isItem", cancellable = true, at = @At("HEAD"))
private void arclight$isItem(CallbackInfoReturnable<Boolean> cir) {
if (arclight$isItem != null) {
cir.setReturnValue(arclight$isItem);
if (arclight$type != MaterialPropertySpec.MaterialType.VANILLA) {
cir.setReturnValue(arclight$type == MaterialPropertySpec.MaterialType.FORGE_ITEM);
}
}
@Inject(method = "isSolid", cancellable = true, remap = false, at = @At("HEAD"))
public void arclight$isSolid(CallbackInfoReturnable<Boolean> cir) {
if (arclight$internal != null) {
cir.setReturnValue(arclight$internal.isSolid());
@Inject(method = "isEdible", cancellable = true, at = @At("HEAD"))
private void arclight$isEdible(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.edible);
}
}
@Inject(method = "isTransparent", cancellable = true, remap = false, at = @At("HEAD"))
public void arclight$isTransparent(CallbackInfoReturnable<Boolean> cir) {
if (arclight$internal != null) {
cir.setReturnValue(this.isBlock() && !arclight$internal.isOpaque());
@Inject(method = "isRecord", cancellable = true, at = @At("HEAD"))
private void arclight$isRecord(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.record);
}
}
@Inject(method = "isFlammable", cancellable = true, remap = false, at = @At("HEAD"))
public void arclight$isLiquid(CallbackInfoReturnable<Boolean> cir) {
if (arclight$internal != null) {
cir.setReturnValue(arclight$internal.isFlammable());
@Inject(method = "isSolid", cancellable = true, at = @At("HEAD"))
private void arclight$isSolid(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.solid);
}
}
@Inject(method = "isOccluding", cancellable = true, remap = false, at = @At("HEAD"))
public void arclight$isOccluding(CallbackInfoReturnable<Boolean> cir) {
if (arclight$internal != null) {
cir.setReturnValue(arclight$internal.isOpaque());
@Inject(method = "isAir", cancellable = true, at = @At("HEAD"))
private void arclight$isAir(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.air);
}
}
@Inject(method = "isTransparent", cancellable = true, at = @At("HEAD"))
private void arclight$isTransparent(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.transparent);
}
}
@Inject(method = "isFlammable", cancellable = true, at = @At("HEAD"))
private void arclight$isFlammable(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.flammable);
}
}
@Inject(method = "isBurnable", cancellable = true, at = @At("HEAD"))
private void arclight$isBurnable(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.burnable);
}
}
@Inject(method = "isFuel", cancellable = true, at = @At("HEAD"))
private void arclight$isFuel(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.fuel);
}
}
@Inject(method = "isOccluding", cancellable = true, at = @At("HEAD"))
private void arclight$isOccluding(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.occluding);
}
}
@Inject(method = "hasGravity", cancellable = true, at = @At("HEAD"))
private void arclight$hasGravity(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.gravity);
}
}
@Inject(method = "isInteractable", cancellable = true, at = @At("HEAD"))
private void arclight$isInteractable(CallbackInfoReturnable<Boolean> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.interactable);
}
}
@Inject(method = "getHardness", cancellable = true, at = @At("HEAD"))
private void arclight$getHardness(CallbackInfoReturnable<Float> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.hardness);
}
}
@Inject(method = "getBlastResistance", cancellable = true, at = @At("HEAD"))
private void arclight$getBlastResistance(CallbackInfoReturnable<Float> cir) {
if (arclight$spec != null) {
cir.setReturnValue(arclight$spec.blastResistance);
}
}
@Inject(method = "getCraftingRemainingItem", cancellable = true, at = @At("HEAD"))
private void arclight$getCraftingRemainingItem(CallbackInfoReturnable<Material> cir) {
if (arclight$spec != null) {
cir.setReturnValue(Material.getMaterial(arclight$spec.craftingRemainingItem));
}
}
@Override
public void bridge$setItem() {
arclight$isItem = Boolean.TRUE;
public MaterialPropertySpec bridge$getSpec() {
return arclight$spec;
}
@Override
public void bridge$setBlock() {
arclight$isBlock = Boolean.TRUE;
public void bridge$setupBlock(ResourceLocation key, Block block, MaterialPropertySpec spec) {
this.arclight$spec = spec.clone();
arclight$type = MaterialPropertySpec.MaterialType.FORGE_BLOCK;
arclight$setupCommon(key, block, block.asItem());
}
@Override
public void bridge$setupItem(ResourceLocation key, Item item, MaterialPropertySpec spec) {
this.arclight$spec = spec.clone();
arclight$type = MaterialPropertySpec.MaterialType.FORGE_ITEM;
arclight$setupCommon(key, null, item);
}
@SuppressWarnings("unchecked")
private void arclight$setupCommon(ResourceLocation key, Block block, Item item) {
this.key = CraftNamespacedKey.fromMinecraft(key);
if (arclight$spec.materialDataClass != null) {
try {
Class<?> data = Class.forName(arclight$spec.materialDataClass);
if (MaterialData.class.isAssignableFrom(data)) {
this.data = data;
this.ctor = (Constructor<? extends MaterialData>) data.getConstructor(Material.class, byte.class);
}
} catch (Exception e) {
ArclightMod.LOGGER.warn("Bad material data class {} for {}", arclight$spec.materialDataClass, this);
ArclightMod.LOGGER.warn(e);
}
}
if (arclight$spec.maxStack == null) {
arclight$spec.maxStack = tryGetMaxStackSize(item);
}
if (arclight$spec.maxDurability == null) {
arclight$spec.maxDurability = tryGetDurability(item);
}
if (arclight$spec.edible == null) {
arclight$spec.edible = false;
}
if (arclight$spec.record == null) {
arclight$spec.record = false;
}
if (arclight$spec.solid == null) {
arclight$spec.solid = block != null && block.getDefaultState().isSolid();
}
if (arclight$spec.air == null) {
arclight$spec.air = block != null && block.getDefaultState().isAir();
}
if (arclight$spec.transparent == null) {
arclight$spec.transparent = block != null && block.getDefaultState().isTransparent();
}
if (arclight$spec.flammable == null) {
arclight$spec.flammable = block != null && ((FireBlockBridge) Blocks.FIRE).bridge$canBurn(block);
}
if (arclight$spec.burnable == null) {
arclight$spec.burnable = block != null && ((FireBlockBridge) Blocks.FIRE).bridge$canBurn(block);
}
if (arclight$spec.fuel == null) {
arclight$spec.fuel = item != null && new ItemStack(item).getBurnTime() > 0;
}
if (arclight$spec.occluding == null) {
arclight$spec.occluding = arclight$spec.solid;
}
if (arclight$spec.gravity == null) {
arclight$spec.gravity = block instanceof FallingBlock;
}
if (arclight$spec.interactable == null) {
arclight$spec.interactable = true;
}
if (arclight$spec.hardness == null) {
arclight$spec.hardness = block != null ? block.blockHardness : 0;
}
if (arclight$spec.blastResistance == null) {
arclight$spec.blastResistance = block != null ? block.getExplosionResistance() : 0;
}
}
private static int tryGetMaxStackSize(Item item) {
try {
return item.getItemStackLimit(new ItemStack(item));
} catch (Throwable t) {
try {
return item.getMaxStackSize();
} catch (Throwable t1) {
return 64;
}
}
}
private static int tryGetDurability(Item item) {
try {
return item.getMaxDamage(new ItemStack(item));
} catch (Throwable t) {
try {
return item.getMaxDamage();
} catch (Throwable t1) {
return 0;
}
}
}
}

View File

@ -1,5 +1,7 @@
package io.izzel.arclight.common.mixin.core.block;
import io.izzel.arclight.common.bridge.block.FireBlockBridge;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.FireBlock;
@ -15,6 +17,7 @@ import org.bukkit.craftbukkit.v.block.CraftBlockState;
import org.bukkit.craftbukkit.v.event.CraftEventFactory;
import org.bukkit.event.block.BlockBurnEvent;
import org.bukkit.event.block.BlockFadeEvent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
@ -25,10 +28,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Random;
@Mixin(FireBlock.class)
public abstract class FireBlockMixin {
public abstract class FireBlockMixin implements FireBlockBridge {
// @formatter:off
@Shadow public abstract BlockState getStateForPlacement(IBlockReader p_196448_1_, BlockPos p_196448_2_);
@Shadow @Final private Object2IntMap<net.minecraft.block.Block> flammabilities;
// @formatter:on
@Inject(method = "tryCatchFire", cancellable = true, at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/world/World;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"))
@ -62,4 +66,9 @@ public abstract class FireBlockMixin {
}
return false;
}
@Override
public boolean bridge$canBurn(net.minecraft.block.Block block) {
return this.flammabilities.containsKey(block);
}
}

View File

@ -7,6 +7,8 @@ import io.izzel.arclight.api.Unsafe;
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.common.mod.util.potion.ArclightPotionEffect;
import io.izzel.arclight.i18n.ArclightConfig;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.potion.Effect;
@ -17,12 +19,10 @@ import net.minecraftforge.registries.IForgeRegistry;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.v.util.CraftMagicNumbers;
import org.bukkit.craftbukkit.v.util.CraftNamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.potion.PotionEffectType;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@ -31,7 +31,7 @@ import java.util.Set;
@SuppressWarnings({"unchecked", "ConstantConditions"})
public class BukkitRegistry {
private static final List<Class<?>> MAT_CTOR = ImmutableList.of(int.class, int.class, int.class);
private static final List<Class<?>> MAT_CTOR = ImmutableList.of(int.class);
private static final Map<String, Material> BY_NAME = getStatic(Material.class, "BY_NAME");
private static final Map<Block, Material> BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL");
private static final Map<Item, Material> ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL");
@ -72,7 +72,6 @@ public class BukkitRegistry {
}
private static void loadMaterials() {
List<Material> newMats = new ArrayList<>(ForgeRegistries.BLOCKS.getKeys().size() + ForgeRegistries.ITEMS.getKeys().size());
int blocks = 0, items = 0;
int i = Material.values().length;
int origin = i;
@ -81,22 +80,14 @@ public class BukkitRegistry {
Block block = entry.getValue();
Material material = Material.matchMaterial(location.toString());
if (material == null) {
Item item = block.asItem();
int maxStack = tryGetMaxStackSize(item);
int durability = tryGetDurability(item);
String name = toName(location);
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i, maxStack, durability));
if (!newMats.contains(material)) {
newMats.add(material);
}
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i));
((MaterialBridge) (Object) material).bridge$setupBlock(location, block, spec(location));
BY_NAME.put(name, material);
((MaterialBridge) (Object) material).bridge$setInternal(block.getMaterial(block.getDefaultState()));
i++;
blocks++;
ArclightMod.LOGGER.debug("Registered {}: {} as block", location, material);
ArclightMod.LOGGER.debug("Registered {} as block {}", location, material);
}
((MaterialBridge) (Object) material).bridge$setKey(CraftNamespacedKey.fromMinecraft(location));
((MaterialBridge) (Object) material).bridge$setBlock();
BLOCK_MATERIAL.put(block, material);
MATERIAL_BLOCK.put(material, block);
}
@ -105,20 +96,14 @@ public class BukkitRegistry {
Item item = entry.getValue();
Material material = Material.matchMaterial(location.toString());
if (material == null) {
int maxStack = tryGetMaxStackSize(item);
int durability = tryGetDurability(item);
String name = toName(location);
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i, maxStack, durability));
if (!newMats.contains(material)) {
newMats.add(material);
}
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i));
((MaterialBridge) (Object) material).bridge$setupItem(location, item, spec(location));
BY_NAME.put(name, material);
i++;
items++;
ArclightMod.LOGGER.debug("Registered {}: {} as item", location, material);
ArclightMod.LOGGER.debug("Registered {} as item {}", location, material);
}
((MaterialBridge) (Object) material).bridge$setKey(CraftNamespacedKey.fromMinecraft(location));
((MaterialBridge) (Object) material).bridge$setItem();
ITEM_MATERIAL.put(item, material);
MATERIAL_ITEM.put(material, item);
}
@ -126,31 +111,15 @@ public class BukkitRegistry {
}
private static String toName(ResourceLocation location) {
return location.toString().replace(':', '_').toUpperCase(Locale.ENGLISH);
return location.toString()
.replace(':', '_')
.replaceAll("\\s+", "_")
.replaceAll("\\W", "")
.toUpperCase(Locale.ENGLISH);
}
private static int tryGetMaxStackSize(Item item) {
try {
return item.getItemStackLimit(item.getDefaultInstance());
} catch (Throwable t) {
try {
return item.getMaxStackSize();
} catch (Throwable t1) {
return 64;
}
}
}
private static int tryGetDurability(Item item) {
try {
return item.getMaxDamage(item.getDefaultInstance());
} catch (Throwable t) {
try {
return item.getMaxDamage();
} catch (Throwable t1) {
return 0;
}
}
private static MaterialPropertySpec spec(ResourceLocation location) {
return ArclightConfig.spec().getCompat().getOverride(location.toString()).orElse(MaterialPropertySpec.EMPTY);
}
private static <T> T getStatic(Class<?> cl, String name) {

View File

@ -0,0 +1,22 @@
package io.izzel.arclight.i18n.conf;
import ninja.leaping.configurate.objectmapping.Setting;
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
import java.util.Map;
import java.util.Optional;
@ConfigSerializable
public class CompatSpec {
@Setting("property-override")
private Map<String, MaterialPropertySpec> overrides;
public Map<String, MaterialPropertySpec> getOverrides() {
return overrides;
}
public Optional<MaterialPropertySpec> getOverride(String key) {
return Optional.ofNullable(overrides.get(key));
}
}

View File

@ -15,6 +15,9 @@ public class ConfigSpec {
@Setting("locale")
private LocaleSpec localeSpec;
@Setting("compatibility")
private CompatSpec compatSpec;
public int getVersion() {
return version;
}
@ -26,4 +29,8 @@ public class ConfigSpec {
public LocaleSpec getLocale() {
return localeSpec;
}
public CompatSpec getCompat() {
return compatSpec;
}
}

View File

@ -0,0 +1,74 @@
package io.izzel.arclight.i18n.conf;
import ninja.leaping.configurate.objectmapping.Setting;
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
@ConfigSerializable
public class MaterialPropertySpec implements Cloneable {
public static final MaterialPropertySpec EMPTY = new MaterialPropertySpec();
@Setting("materialDataClass")
public String materialDataClass;
@Setting("maxStack")
public Integer maxStack;
@Setting("maxDurability")
public Integer maxDurability;
@Setting("edible")
public Boolean edible;
@Setting("record")
public Boolean record;
@Setting("solid")
public Boolean solid;
@Setting("air")
public Boolean air;
@Setting("transparent")
public Boolean transparent;
@Setting("flammable")
public Boolean flammable;
@Setting("burnable")
public Boolean burnable;
@Setting("fuel")
public Boolean fuel;
@Setting("occluding")
public Boolean occluding;
@Setting("gravity")
public Boolean gravity;
@Setting("interactable")
public Boolean interactable;
@Setting("hardness")
public Float hardness;
@Setting("blastResistance")
public Float blastResistance;
@Setting("craftingRemainingItem")
public String craftingRemainingItem;
@Override
public MaterialPropertySpec clone() {
try {
return (MaterialPropertySpec) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
public enum MaterialType {
VANILLA, FORGE_BLOCK, FORGE_ITEM
}
}