Entity related mixin.

This commit is contained in:
IzzelAliz 2020-07-06 16:04:05 +08:00
parent a1bd86e8af
commit 2e6ad0cfb0
31 changed files with 1162 additions and 278 deletions

View File

@ -1,15 +1,19 @@
package io.izzel.arclight.common;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
import io.izzel.arclight.api.EnumHelper;
import io.izzel.arclight.api.Unsafe;
import io.izzel.arclight.common.mod.util.log.ArclightI18nLogger;
import io.izzel.arclight.common.mod.util.log.ArclightLazyLogManager;
import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper;
import io.izzel.arclight.common.util.EnumTypeFactory;
import io.izzel.arclight.i18n.ArclightConfig;
import io.izzel.arclight.i18n.ArclightLocale;
import net.minecraftforge.server.ServerMain;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Objects;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
@ -37,6 +41,7 @@ public abstract class ArclightMain {
ArclightI18nLogger.getLogger("Arclight").info("loading-mapping");
Objects.requireNonNull(ArclightRemapper.INSTANCE);
this.beforeStart();
this.dirtyHacks();
ServerMain.main(args);
} catch (Exception e) {
e.printStackTrace();
@ -44,6 +49,14 @@ public abstract class ArclightMain {
}
}
private void dirtyHacks() throws Exception {
TypeAdapters.ENUM_FACTORY.create(null, TypeToken.get(ArclightMain.class));
Field field = TypeAdapters.class.getDeclaredField("ENUM_FACTORY");
Object base = Unsafe.staticFieldBase(field);
long offset = Unsafe.staticFieldOffset(field);
Unsafe.putObjectVolatile(base, offset, new EnumTypeFactory());
}
private void printLogo() throws Exception {
try (InputStream stream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF")) {
Manifest manifest = new Manifest(stream);

View File

@ -0,0 +1,22 @@
package io.izzel.arclight.common.bridge.bukkit;
import io.izzel.arclight.i18n.conf.EntityPropertySpec;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.util.ResourceLocation;
import org.bukkit.Location;
import java.util.function.Function;
public interface EntityTypeBridge {
void bridge$setup(ResourceLocation location, EntityType<?> entityType, EntityPropertySpec spec);
EntityType<?> bridge$getHandle();
EntityPropertySpec bridge$getSpec();
Function<Location, ? extends Entity> bridge$entityFactory();
void bridge$setEntityFactory(Function<Location, ? extends Entity> function);
}

View File

@ -4,8 +4,13 @@ import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraft.util.ResourceLocation;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v.block.CraftBlock;
import org.bukkit.craftbukkit.v.inventory.CraftMetaItem;
import org.bukkit.inventory.meta.ItemMeta;
import javax.annotation.Nullable;
import java.util.function.Function;
public interface MaterialBridge {
@ -17,4 +22,12 @@ public interface MaterialBridge {
MaterialPropertySpec bridge$getSpec();
MaterialPropertySpec.MaterialType bridge$getType();
Function<CraftMetaItem, ItemMeta> bridge$itemMetaFactory();
void bridge$setItemMetaFactory(Function<CraftMetaItem, ItemMeta> func);
Function<CraftBlock, BlockState> bridge$blockStateFactory();
void bridge$setBlockStateFactory(Function<CraftBlock, BlockState> func);
}

View File

@ -0,0 +1,28 @@
package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v.block.CraftBlock;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(value = CraftBlock.class, remap = false)
public abstract class CraftBlockMixin {
// @formatter:off
@Shadow public abstract Material getType();
// @formatter:on
@Inject(method = "getState", cancellable = true, at = @At("HEAD"))
private void arclight$getState(CallbackInfoReturnable<BlockState> cir) {
MaterialBridge bridge = (MaterialBridge) (Object) getType();
if (bridge.bridge$getType() != MaterialPropertySpec.MaterialType.VANILLA) {
cir.setReturnValue(bridge.bridge$blockStateFactory().apply((CraftBlock) (Object) this));
}
}
}

View File

@ -0,0 +1,97 @@
package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.mod.server.entity.ArclightModChestedHorse;
import io.izzel.arclight.common.mod.server.entity.ArclightModEntity;
import io.izzel.arclight.common.mod.server.entity.ArclightModHorse;
import io.izzel.arclight.common.mod.server.entity.ArclightModLivingEntity;
import io.izzel.arclight.common.mod.server.entity.ArclightModMinecart;
import io.izzel.arclight.common.mod.server.entity.ArclightModMinecartContainer;
import io.izzel.arclight.common.mod.server.entity.ArclightModMob;
import io.izzel.arclight.common.mod.server.entity.ArclightModProjectile;
import io.izzel.arclight.common.mod.server.entity.ArclightModRaider;
import io.izzel.arclight.common.mod.server.entity.ArclightModVillager;
import net.minecraft.entity.AgeableEntity;
import net.minecraft.entity.Entity;
import net.minecraft.entity.FlyingEntity;
import net.minecraft.entity.IProjectile;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MobEntity;
import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
import net.minecraft.entity.item.minecart.ContainerMinecartEntity;
import net.minecraft.entity.merchant.villager.AbstractVillagerEntity;
import net.minecraft.entity.monster.AbstractRaiderEntity;
import net.minecraft.entity.passive.GolemEntity;
import net.minecraft.entity.passive.TameableEntity;
import net.minecraft.entity.passive.horse.AbstractChestedHorseEntity;
import net.minecraft.entity.passive.horse.AbstractHorseEntity;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftAgeable;
import org.bukkit.craftbukkit.v.entity.CraftEntity;
import org.bukkit.craftbukkit.v.entity.CraftFlying;
import org.bukkit.craftbukkit.v.entity.CraftGolem;
import org.bukkit.craftbukkit.v.entity.CraftTameableAnimal;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(value = CraftEntity.class, remap = false)
public class CraftEntityMixin {
@Inject(method = "getEntity", cancellable = true, at = @At(value = "NEW", target = "java/lang/AssertionError"))
private static void arclight$modEntity(CraftServer server, Entity entity, CallbackInfoReturnable<CraftEntity> cir) {
if (entity instanceof LivingEntity) {
if (entity instanceof MobEntity) {
if (entity instanceof AgeableEntity) {
if (entity instanceof AbstractHorseEntity) {
if (entity instanceof AbstractChestedHorseEntity) {
cir.setReturnValue(new ArclightModChestedHorse(server, (AbstractChestedHorseEntity) entity));
return;
}
cir.setReturnValue(new ArclightModHorse(server, (AbstractHorseEntity) entity));
return;
}
if (entity instanceof AbstractVillagerEntity) {
cir.setReturnValue(new ArclightModVillager(server, (AbstractVillagerEntity) entity));
return;
}
if (entity instanceof TameableEntity) {
cir.setReturnValue(new CraftTameableAnimal(server, (TameableEntity) entity));
return;
}
cir.setReturnValue(new CraftAgeable(server, (AgeableEntity) entity));
return;
}
if (entity instanceof FlyingEntity) {
cir.setReturnValue(new CraftFlying(server, (FlyingEntity) entity));
return;
}
if (entity instanceof AbstractRaiderEntity) {
cir.setReturnValue(new ArclightModRaider(server, (AbstractRaiderEntity) entity));
return;
}
if (entity instanceof GolemEntity) {
cir.setReturnValue(new CraftGolem(server, (GolemEntity) entity));
return;
}
cir.setReturnValue(new ArclightModMob(server, (MobEntity) entity));
return;
}
cir.setReturnValue(new ArclightModLivingEntity(server, (LivingEntity) entity));
return;
}
if (entity instanceof AbstractMinecartEntity) {
if (entity instanceof ContainerMinecartEntity) {
cir.setReturnValue(new ArclightModMinecartContainer(server, (ContainerMinecartEntity) entity));
return;
}
cir.setReturnValue(new ArclightModMinecart(server, (AbstractMinecartEntity) entity));
return;
}
if (entity instanceof IProjectile) {
cir.setReturnValue(new ArclightModProjectile(server, entity));
return;
}
cir.setReturnValue(new ArclightModEntity(server, entity));
}
}

View File

@ -1,29 +1,10 @@
package io.izzel.arclight.common.mixin.bukkit;
import com.google.common.collect.ImmutableMap;
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v.inventory.CraftItemFactory;
import org.bukkit.craftbukkit.v.inventory.CraftMetaArmorStand;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBanner;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBlockState;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBookSigned;
import org.bukkit.craftbukkit.v.inventory.CraftMetaCharge;
import org.bukkit.craftbukkit.v.inventory.CraftMetaCrossbow;
import org.bukkit.craftbukkit.v.inventory.CraftMetaEnchantedBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaFirework;
import org.bukkit.craftbukkit.v.inventory.CraftMetaItem;
import org.bukkit.craftbukkit.v.inventory.CraftMetaKnowledgeBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaLeatherArmor;
import org.bukkit.craftbukkit.v.inventory.CraftMetaMap;
import org.bukkit.craftbukkit.v.inventory.CraftMetaPotion;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSkull;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSpawnEgg;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSuspiciousStew;
import org.bukkit.craftbukkit.v.inventory.CraftMetaTropicalFishBucket;
import org.bukkit.craftbukkit.v.util.CraftLegacy;
import org.bukkit.inventory.meta.ItemMeta;
import org.spongepowered.asm.mixin.Mixin;
@ -31,83 +12,15 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.function.BiFunction;
@Mixin(value = CraftItemFactory.class, remap = false)
public class CraftItemFactoryMixin {
private static final Map<String, BiFunction<Material, CraftMetaItem, ItemMeta>> TYPES = ImmutableMap
.<String, BiFunction<Material, CraftMetaItem, ItemMeta>>builder()
.put("ARMOR_STAND", (a, b) -> new CraftMetaArmorStand(b))
.put("BANNER", (a, b) -> new CraftMetaBanner(b))
.put("TILE_ENTITY", (a, b) -> new CraftMetaBlockState(b, a))
.put("BOOK", (a, b) -> new CraftMetaBook(b))
.put("BOOK_SIGNED", (a, b) -> new CraftMetaBookSigned(b))
.put("SKULL", (a, b) -> new CraftMetaSkull(b))
.put("LEATHER_ARMOR", (a, b) -> new CraftMetaLeatherArmor(b))
.put("MAP", (a, b) -> new CraftMetaMap(b))
.put("POTION", (a, b) -> new CraftMetaPotion(b))
.put("SPAWN_EGG", (a, b) -> new CraftMetaSpawnEgg(b))
.put("ENCHANTED", (a, b) -> new CraftMetaEnchantedBook(b))
.put("FIREWORK", (a, b) -> new CraftMetaFirework(b))
.put("FIREWORK_EFFECT", (a, b) -> new CraftMetaCharge(b))
.put("KNOWLEDGE_BOOK", (a, b) -> new CraftMetaKnowledgeBook(b))
.put("TROPICAL_FISH_BUCKET", (a, b) -> new CraftMetaTropicalFishBucket(b))
.put("CROSSBOW", (a, b) -> new CraftMetaCrossbow(b))
.put("SUSPICIOUS_STEW", (a, b) -> new CraftMetaSuspiciousStew(b))
.put("UNSPECIFIC", (a, b) -> new CraftMetaItem(b))
.build();
@SuppressWarnings("AmbiguousMixinReference")
@Inject(method = "getItemMeta*", require = 0, expect = 0, cancellable = true, at = @At("HEAD"))
private void arclight$getItemMeta(Material material, CraftMetaItem meta, CallbackInfoReturnable<ItemMeta> cir) {
MaterialBridge bridge = (MaterialBridge) (Object) CraftLegacy.fromLegacy(material);
if (bridge.bridge$getType() != MaterialPropertySpec.MaterialType.VANILLA) {
MaterialPropertySpec spec = bridge.bridge$getSpec();
BiFunction<Material, CraftMetaItem, ItemMeta> func;
if (spec == null || spec.itemMetaType == null) {
func = (a, b) -> new CraftMetaItem(b);
} else {
func = TYPES.get(spec.itemMetaType);
if (func == null) {
func = (a, b) -> dynamicCreate(spec.itemMetaType, a, b);
}
}
cir.setReturnValue(func.apply(material, meta));
cir.setReturnValue(bridge.bridge$itemMetaFactory().apply(meta));
}
}
private ItemMeta dynamicCreate(String type, Material material, CraftMetaItem meta) {
try {
Class<?> cl = Class.forName(type);
if (!CraftMetaItem.class.isAssignableFrom(cl)) {
throw new IllegalArgumentException("" + cl + " is not assignable from " + CraftMetaItem.class);
}
for (Constructor<?> constructor : cl.getDeclaredConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == 1) {
if (parameterTypes[0] == Material.class) {
constructor.setAccessible(true);
return (ItemMeta) constructor.newInstance(material);
} else if (CraftMetaItem.class.isAssignableFrom(parameterTypes[0])) {
constructor.setAccessible(true);
return (ItemMeta) constructor.newInstance(meta);
}
} else if (parameterTypes.length == 2) {
if (parameterTypes[0] == Material.class && CraftMetaItem.class.isAssignableFrom(parameterTypes[1])) {
constructor.setAccessible(true);
return (ItemMeta) constructor.newInstance(material, meta);
} else if (parameterTypes[1] == Material.class && CraftMetaItem.class.isAssignableFrom(parameterTypes[0])) {
constructor.setAccessible(true);
return (ItemMeta) constructor.newInstance(meta, material);
}
}
}
} catch (Exception e) {
ArclightMod.LOGGER.warn("Bad itemMetaType {} for {}: {}", type, material, e);
}
return new CraftMetaItem(meta);
}
}

View File

@ -0,0 +1,26 @@
package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.bridge.bukkit.EntityTypeBridge;
import io.izzel.arclight.common.bridge.entity.EntityBridge;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v.CraftWorld;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.function.Function;
@Mixin(value = CraftWorld.class, remap = false)
public class CraftWorldMixin {
@Inject(method = "spawnEntity", cancellable = true, at = @At("HEAD"))
private void arclight$useFactory(Location loc, EntityType entityType, CallbackInfoReturnable<Entity> cir) {
Function<Location, ? extends net.minecraft.entity.Entity> factory = ((EntityTypeBridge) (Object) entityType).bridge$entityFactory();
if (factory != null) {
cir.setReturnValue(((EntityBridge) factory.apply(loc)).bridge$getBukkitEntity());
}
}
}

View File

@ -0,0 +1,88 @@
package io.izzel.arclight.common.mixin.bukkit;
import io.izzel.arclight.common.bridge.bukkit.EntityTypeBridge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.i18n.LocalizedException;
import io.izzel.arclight.i18n.conf.EntityPropertySpec;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.server.ServerWorld;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v.CraftWorld;
import org.bukkit.craftbukkit.v.util.CraftNamespacedKey;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
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 java.util.function.Function;
@Mixin(value = EntityType.class, remap = false)
public class EntityTypeMixin implements EntityTypeBridge {
@Shadow @Final @Mutable private NamespacedKey key;
@Shadow @Final @Mutable private Class<? extends Entity> clazz;
private net.minecraft.entity.EntityType<?> handleType;
private EntityPropertySpec spec;
private Function<Location, ? extends net.minecraft.entity.Entity> factory;
@Override
public void bridge$setup(ResourceLocation location, net.minecraft.entity.EntityType<?> entityType, EntityPropertySpec spec) {
this.key = CraftNamespacedKey.fromMinecraft(location);
this.handleType = entityType;
this.spec = spec.clone();
this.setup();
}
@SuppressWarnings("unchecked")
private void setup() {
if (this.spec.entityClass != null) {
try {
Class<?> cl = Class.forName(this.spec.entityClass);
if (!Entity.class.isAssignableFrom(cl)) {
throw LocalizedException.checked("registry.entity.not-subclass", cl, Entity.class);
}
this.clazz = (Class<? extends Entity>) cl;
} catch (Exception e) {
if (e instanceof LocalizedException) {
ArclightMod.LOGGER.warn(((LocalizedException) e).node(), ((LocalizedException) e).args());
} else {
ArclightMod.LOGGER.warn("registry.entity.error", this, this.spec.entityClass, e);
}
}
}
this.factory = loc -> {
if (loc != null && loc.getWorld() != null) {
ServerWorld world = ((CraftWorld) loc.getWorld()).getHandle();
net.minecraft.entity.Entity entity = handleType.create(world);
if (entity != null) {
entity.setPositionAndRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
}
return entity;
} else return null;
};
}
@Override
public net.minecraft.entity.EntityType<?> bridge$getHandle() {
return this.handleType;
}
@Override
public EntityPropertySpec bridge$getSpec() {
return spec;
}
@Override
public Function<Location, ? extends net.minecraft.entity.Entity> bridge$entityFactory() {
return factory;
}
@Override
public void bridge$setEntityFactory(Function<Location, ? extends net.minecraft.entity.Entity> function) {
this.factory = function;
}
}

View File

@ -1,18 +1,44 @@
package io.izzel.arclight.common.mixin.bukkit;
import com.google.common.collect.ImmutableMap;
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.LocalizedException;
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.tileentity.TileEntity;
import net.minecraft.util.ResourceLocation;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.v.block.CraftBlock;
import org.bukkit.craftbukkit.v.block.CraftBlockEntityState;
import org.bukkit.craftbukkit.v.block.CraftBlockState;
import org.bukkit.craftbukkit.v.inventory.CraftMetaArmorStand;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBanner;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBlockState;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaBookSigned;
import org.bukkit.craftbukkit.v.inventory.CraftMetaCharge;
import org.bukkit.craftbukkit.v.inventory.CraftMetaCrossbow;
import org.bukkit.craftbukkit.v.inventory.CraftMetaEnchantedBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaFirework;
import org.bukkit.craftbukkit.v.inventory.CraftMetaItem;
import org.bukkit.craftbukkit.v.inventory.CraftMetaKnowledgeBook;
import org.bukkit.craftbukkit.v.inventory.CraftMetaLeatherArmor;
import org.bukkit.craftbukkit.v.inventory.CraftMetaMap;
import org.bukkit.craftbukkit.v.inventory.CraftMetaPotion;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSkull;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSpawnEgg;
import org.bukkit.craftbukkit.v.inventory.CraftMetaSuspiciousStew;
import org.bukkit.craftbukkit.v.inventory.CraftMetaTropicalFishBucket;
import org.bukkit.craftbukkit.v.util.CraftNamespacedKey;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.MaterialData;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -23,6 +49,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
@Mixin(value = Material.class, remap = false)
public abstract class MaterialMixin implements MaterialBridge {
@ -34,6 +63,29 @@ public abstract class MaterialMixin implements MaterialBridge {
@Shadow public abstract boolean isBlock();
// @formatter:on
private static final Map<String, BiFunction<Material, CraftMetaItem, ItemMeta>> TYPES = ImmutableMap
.<String, BiFunction<Material, CraftMetaItem, ItemMeta>>builder()
.put("ARMOR_STAND", (a, b) -> b instanceof CraftMetaArmorStand ? b : new CraftMetaArmorStand(b))
.put("BANNER", (a, b) -> b instanceof CraftMetaBanner ? b : new CraftMetaBanner(b))
.put("TILE_ENTITY", (a, b) -> new CraftMetaBlockState(b, a))
.put("BOOK", (a, b) -> b != null && b.getClass().equals(CraftMetaBook.class) ? b : new CraftMetaBook(b))
.put("BOOK_SIGNED", (a, b) -> b instanceof CraftMetaBookSigned ? b : new CraftMetaBookSigned(b))
.put("SKULL", (a, b) -> b instanceof CraftMetaSkull ? b : new CraftMetaSkull(b))
.put("LEATHER_ARMOR", (a, b) -> b instanceof CraftMetaLeatherArmor ? b : new CraftMetaLeatherArmor(b))
.put("MAP", (a, b) -> b instanceof CraftMetaMap ? b : new CraftMetaMap(b))
.put("POTION", (a, b) -> b instanceof CraftMetaPotion ? b : new CraftMetaPotion(b))
.put("SPAWN_EGG", (a, b) -> b instanceof CraftMetaSpawnEgg ? b : new CraftMetaSpawnEgg(b))
.put("ENCHANTED", (a, b) -> b instanceof CraftMetaEnchantedBook ? b : new CraftMetaEnchantedBook(b))
.put("FIREWORK", (a, b) -> b instanceof CraftMetaFirework ? b : new CraftMetaFirework(b))
.put("FIREWORK_EFFECT", (a, b) -> b instanceof CraftMetaCharge ? b : new CraftMetaCharge(b))
.put("KNOWLEDGE_BOOK", (a, b) -> b instanceof CraftMetaKnowledgeBook ? b : new CraftMetaKnowledgeBook(b))
.put("TROPICAL_FISH_BUCKET", (a, b) -> b instanceof CraftMetaTropicalFishBucket ? b : new CraftMetaTropicalFishBucket(b))
.put("CROSSBOW", (a, b) -> b instanceof CraftMetaCrossbow ? b : new CraftMetaCrossbow(b))
.put("SUSPICIOUS_STEW", (a, b) -> b instanceof CraftMetaSuspiciousStew ? b : new CraftMetaSuspiciousStew(b))
.put("UNSPECIFIC", (a, b) -> new CraftMetaItem(b))
.put("NULL", (a, b) -> null)
.build();
private MaterialPropertySpec.MaterialType arclight$type = MaterialPropertySpec.MaterialType.VANILLA;
private MaterialPropertySpec arclight$spec;
@ -159,6 +211,30 @@ public abstract class MaterialMixin implements MaterialBridge {
return arclight$type;
}
private Function<CraftMetaItem, ItemMeta> arclight$metaFunc;
@Override
public Function<CraftMetaItem, ItemMeta> bridge$itemMetaFactory() {
return arclight$metaFunc;
}
@Override
public void bridge$setItemMetaFactory(Function<CraftMetaItem, ItemMeta> func) {
this.arclight$metaFunc = func;
}
private Function<CraftBlock, BlockState> arclight$stateFunc;
@Override
public Function<CraftBlock, BlockState> bridge$blockStateFactory() {
return arclight$stateFunc;
}
@Override
public void bridge$setBlockStateFactory(Function<CraftBlock, BlockState> func) {
this.arclight$stateFunc = func;
}
@Override
public void bridge$setupBlock(ResourceLocation key, Block block, MaterialPropertySpec spec) {
this.arclight$spec = spec.clone();
@ -233,6 +309,124 @@ public abstract class MaterialMixin implements MaterialBridge {
if (arclight$spec.blastResistance == null) {
arclight$spec.blastResistance = block != null ? block.getExplosionResistance() : 0;
}
if (arclight$spec.itemMetaType == null) {
arclight$spec.itemMetaType = "UNSPECIFIC";
}
BiFunction<Material, CraftMetaItem, ItemMeta> function = TYPES.get(arclight$spec.itemMetaType);
if (function != null) {
this.arclight$metaFunc = meta -> function.apply((Material) (Object) this, meta);
} else {
this.arclight$metaFunc = dynamicMetaCreator(arclight$spec.itemMetaType);
}
this.setupBlockStateFunc();
}
private void setupBlockStateFunc() {
if (arclight$spec.blockStateClass != null) {
try {
Class<?> cl = Class.forName(arclight$spec.blockStateClass);
if (!CraftBlockState.class.isAssignableFrom(cl)) {
throw LocalizedException.checked("registry.block-state.not-subclass", cl, CraftBlockState.class);
}
for (Constructor<?> constructor : cl.getDeclaredConstructors()) {
if (constructor.getParameterTypes().length == 1
&& org.bukkit.block.Block.class.isAssignableFrom(constructor.getParameterTypes()[0])) {
constructor.setAccessible(true);
this.arclight$stateFunc = b -> {
try {
return (BlockState) constructor.newInstance(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
} catch (Exception e) {
if (e instanceof LocalizedException) {
ArclightMod.LOGGER.warn(((LocalizedException) e).node(), ((LocalizedException) e).args());
} else {
ArclightMod.LOGGER.warn("registry.block-state.error", this, arclight$spec.blockStateClass, e);
}
}
if (this.arclight$stateFunc == null) {
ArclightMod.LOGGER.warn("registry.block-state.no-candidate", this, arclight$spec.blockStateClass);
}
}
if (this.arclight$stateFunc == null) {
this.arclight$stateFunc = b -> {
TileEntity tileEntity = b.getCraftWorld().getHandle().getTileEntity(b.getPosition());
return tileEntity == null ? new CraftBlockState(b) : new CraftBlockEntityState<>(b, tileEntity.getClass());
};
}
}
private Function<CraftMetaItem, ItemMeta> dynamicMetaCreator(String type) {
Function<CraftMetaItem, ItemMeta> candidate = null;
try {
Class<?> cl = Class.forName(type);
if (!CraftMetaItem.class.isAssignableFrom(cl)) {
throw LocalizedException.checked("registry.meta-type.not-subclass", cl, CraftMetaItem.class);
}
for (Constructor<?> constructor : cl.getDeclaredConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
if (parameterTypes.length == 1) {
if (parameterTypes[0] == Material.class) {
constructor.setAccessible(true);
candidate = meta -> {
try {
return (ItemMeta) constructor.newInstance(this);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
break;
} else if (CraftMetaItem.class.isAssignableFrom(parameterTypes[0])) {
constructor.setAccessible(true);
candidate = meta -> {
try {
return (ItemMeta) constructor.newInstance(meta);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
break;
}
} else if (parameterTypes.length == 2) {
if (parameterTypes[0] == Material.class && CraftMetaItem.class.isAssignableFrom(parameterTypes[1])) {
constructor.setAccessible(true);
candidate = meta -> {
try {
return (ItemMeta) constructor.newInstance(this, meta);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
break;
} else if (parameterTypes[1] == Material.class && CraftMetaItem.class.isAssignableFrom(parameterTypes[0])) {
constructor.setAccessible(true);
candidate = meta -> {
try {
return (ItemMeta) constructor.newInstance(meta, this);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
break;
}
}
}
} catch (Exception e) {
if (e instanceof LocalizedException) {
ArclightMod.LOGGER.warn(((LocalizedException) e).node(), ((LocalizedException) e).args());
} else {
ArclightMod.LOGGER.warn("registry.meta-type.error", this, type, e);
}
}
if (candidate == null) {
ArclightMod.LOGGER.warn("registry.meta-type.no-candidate", this, type);
candidate = CraftMetaItem::new;
}
return candidate;
}
private static int tryGetMaxStackSize(Item item) {

View File

@ -11,6 +11,7 @@ import net.minecraft.util.math.EntityRayTraceResult;
import net.minecraft.util.math.RayTraceResult;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v.entity.CraftEntity;
import org.bukkit.entity.Ageable;
import org.bukkit.entity.Egg;
import org.bukkit.event.entity.CreatureSpawnEvent;
@ -50,7 +51,7 @@ public abstract class EggEntityMixin extends ThrowableEntityMixin {
}
if (hatching) {
for (int i = 0; i < b0; ++i) {
final Entity entity = ((WorldBridge) this.world).bridge$getWorld().createEntity(new Location(((WorldBridge) this.world).bridge$getWorld(), this.posX, this.posY, this.posZ, this.rotationYaw, 0.0f), hatchingType.getEntityClass());
Entity entity = ((CraftEntity) ((WorldBridge) this.world).bridge$getWorld().spawnEntity(new Location(((WorldBridge) this.world).bridge$getWorld(), this.posX, this.posY, this.posZ, this.rotationYaw, 0.0f), hatchingType)).getHandle();
if (((EntityBridge) entity).bridge$getBukkitEntity() instanceof Ageable) {
((Ageable) ((EntityBridge) entity).bridge$getBukkitEntity()).setBaby();
}

View File

@ -4,10 +4,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import io.izzel.arclight.api.EnumHelper;
import io.izzel.arclight.api.Unsafe;
import io.izzel.arclight.common.bridge.bukkit.EntityTypeBridge;
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
import io.izzel.arclight.common.mod.ArclightMod;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import io.izzel.arclight.common.mod.util.potion.ArclightPotionEffect;
import io.izzel.arclight.i18n.ArclightConfig;
import io.izzel.arclight.i18n.conf.EntityPropertySpec;
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
@ -17,12 +20,16 @@ import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.ForgeRegistry;
import net.minecraftforge.registries.IForgeRegistry;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.craftbukkit.v.enchantments.CraftEnchantment;
import org.bukkit.craftbukkit.v.util.CraftMagicNumbers;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
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;
@ -32,16 +39,46 @@ import java.util.Set;
public class BukkitRegistry {
private static final List<Class<?>> MAT_CTOR = ImmutableList.of(int.class);
private static final List<Class<?>> ENTITY_CTOR = ImmutableList.of(String.class, Class.class, 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");
private static final Map<Material, Item> MATERIAL_ITEM = getStatic(CraftMagicNumbers.class, "MATERIAL_ITEM");
private static final Map<Material, Block> MATERIAL_BLOCK = getStatic(CraftMagicNumbers.class, "MATERIAL_BLOCK");
private static final Map<String, EntityType> ENTITY_NAME_MAP = getStatic(EntityType.class, "NAME_MAP");
public static void registerAll() {
loadMaterials();
loadPotions();
loadEnchantments();
loadEntities();
}
private static void loadEntities() {
int origin = EntityType.values().length;
int i = origin;
List<EntityType> newTypes = new ArrayList<>(ForgeRegistries.ENTITIES.getEntries().size() - origin);
for (Map.Entry<ResourceLocation, net.minecraft.entity.EntityType<?>> entry : ForgeRegistries.ENTITIES.getEntries()) {
ResourceLocation location = entry.getKey();
net.minecraft.entity.EntityType<?> type = entry.getValue();
EntityType entityType = null;
boolean found = false;
if (location.getNamespace().equals(NamespacedKey.MINECRAFT)) {
entityType = EntityType.fromName(location.getPath());
if (entityType != null) found = true;
else ArclightMod.LOGGER.warn("Not found {} in {}", location, EntityType.class);
}
if (!found) {
String name = ResourceLocationUtil.standardize(location);
entityType = EnumHelper.makeEnum(EntityType.class, name, i++, ENTITY_CTOR, ImmutableList.of(ResourceLocationUtil.standardize(location).toLowerCase(Locale.ROOT), Entity.class, -1));
((EntityTypeBridge) (Object) entityType).bridge$setup(location, type, entitySpec(location));
newTypes.add(entityType);
ArclightMod.LOGGER.debug("Registered {} as entity {}", location, entityType);
}
ENTITY_NAME_MAP.put(location.toString(), entityType);
}
EnumHelper.addEnums(EntityType.class, newTypes);
ArclightMod.LOGGER.info("registry.entity-type", newTypes.size());
}
private static void loadEnchantments() {
@ -62,7 +99,7 @@ public class BukkitRegistry {
putStatic(PotionEffectType.class, "byId", types);
putBool(PotionEffectType.class, "acceptingNew", true);
for (Map.Entry<ResourceLocation, Effect> entry : ForgeRegistries.POTIONS.getEntries()) {
String name = toName(entry.getKey());
String name = ResourceLocationUtil.standardize(entry.getKey());
ArclightPotionEffect effect = new ArclightPotionEffect(entry.getValue(), name);
PotionEffectType.registerPotionEffectType(effect);
ArclightMod.LOGGER.debug("Registered {}: {} as potion", entry.getKey(), effect);
@ -75,18 +112,20 @@ public class BukkitRegistry {
int blocks = 0, items = 0;
int i = Material.values().length;
int origin = i;
List<Material> list = new ArrayList<>();
for (Map.Entry<ResourceLocation, Block> entry : ForgeRegistries.BLOCKS.getEntries()) {
ResourceLocation location = entry.getKey();
Block block = entry.getValue();
Material material = Material.matchMaterial(location.toString());
if (material == null) {
String name = toName(location);
String name = ResourceLocationUtil.standardize(location);
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i));
((MaterialBridge) (Object) material).bridge$setupBlock(location, block, spec(location));
((MaterialBridge) (Object) material).bridge$setupBlock(location, block, matSpec(location));
BY_NAME.put(name, material);
i++;
blocks++;
ArclightMod.LOGGER.debug("Registered {} as block {}", location, material);
list.add(material);
}
BLOCK_MATERIAL.put(block, material);
MATERIAL_BLOCK.put(material, block);
@ -96,30 +135,28 @@ public class BukkitRegistry {
Item item = entry.getValue();
Material material = Material.matchMaterial(location.toString());
if (material == null) {
String name = toName(location);
String name = ResourceLocationUtil.standardize(location);
material = EnumHelper.makeEnum(Material.class, name, i, MAT_CTOR, ImmutableList.of(i));
((MaterialBridge) (Object) material).bridge$setupItem(location, item, spec(location));
((MaterialBridge) (Object) material).bridge$setupItem(location, item, matSpec(location));
BY_NAME.put(name, material);
i++;
items++;
ArclightMod.LOGGER.debug("Registered {} as item {}", location, material);
list.add(material);
}
ITEM_MATERIAL.put(item, material);
MATERIAL_ITEM.put(material, item);
}
EnumHelper.addEnums(Material.class, list);
ArclightMod.LOGGER.info("registry.material", i - origin, blocks, items);
}
private static String toName(ResourceLocation location) {
return location.toString()
.replace(':', '_')
.replaceAll("\\s+", "_")
.replaceAll("\\W", "")
.toUpperCase(Locale.ENGLISH);
private static MaterialPropertySpec matSpec(ResourceLocation location) {
return ArclightConfig.spec().getCompat().getMaterial(location.toString()).orElse(MaterialPropertySpec.EMPTY);
}
private static MaterialPropertySpec spec(ResourceLocation location) {
return ArclightConfig.spec().getCompat().getOverride(location.toString()).orElse(MaterialPropertySpec.EMPTY);
private static EntityPropertySpec entitySpec(ResourceLocation location) {
return ArclightConfig.spec().getCompat().getEntity(location.toString()).orElse(EntityPropertySpec.EMPTY);
}
private static <T> T getStatic(Class<?> cl, String name) {

View File

@ -0,0 +1,35 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.passive.horse.AbstractChestedHorseEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftChestedHorse;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Horse;
import org.jetbrains.annotations.NotNull;
public class ArclightModChestedHorse extends CraftChestedHorse {
private final EntityType entityType;
public ArclightModChestedHorse(CraftServer server, AbstractChestedHorseEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return this.entityType;
}
@Override
public Horse.@NotNull Variant getVariant() {
return Horse.Variant.HORSE;
}
@Override
public String toString() {
return "ArclightModChestedHorse{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.Entity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftEntity;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModEntity extends CraftEntity {
private final EntityType entityType;
public ArclightModEntity(CraftServer server, Entity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModEntity{" + entityType + '}';
}
}

View File

@ -0,0 +1,36 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.passive.horse.AbstractHorseEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftAbstractHorse;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Horse;
import org.jetbrains.annotations.NotNull;
public class ArclightModHorse extends CraftAbstractHorse {
private final EntityType entityType;
public ArclightModHorse(CraftServer server, AbstractHorseEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
@NotNull
public EntityType getType() {
return this.entityType;
}
@Override
public Horse.@NotNull Variant getVariant() {
return Horse.Variant.HORSE;
}
@Override
public String toString() {
return "ArclightModHorse{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.LivingEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftLivingEntity;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModLivingEntity extends CraftLivingEntity {
private final EntityType entityType;
public ArclightModLivingEntity(CraftServer server, LivingEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModLivingEntity{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.item.minecart.AbstractMinecartEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftMinecart;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModMinecart extends CraftMinecart {
private final EntityType entityType;
public ArclightModMinecart(CraftServer server, AbstractMinecartEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModMinecart{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.item.minecart.ContainerMinecartEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftMinecartContainer;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModMinecartContainer extends CraftMinecartContainer {
private final EntityType entityType;
public ArclightModMinecartContainer(CraftServer server, ContainerMinecartEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModMinecartContainer{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.MobEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftMob;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModMob extends CraftMob {
private final EntityType entityType;
public ArclightModMob(CraftServer server, MobEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModMob{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.Entity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftProjectile;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModProjectile extends CraftProjectile {
private final EntityType entityType;
public ArclightModProjectile(CraftServer server, Entity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModProjectile{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.monster.AbstractRaiderEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftRaider;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModRaider extends CraftRaider {
private final EntityType entityType;
public ArclightModRaider(CraftServer server, AbstractRaiderEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return entityType;
}
@Override
public String toString() {
return "ArclightModRaider{" + entityType + '}';
}
}

View File

@ -0,0 +1,29 @@
package io.izzel.arclight.common.mod.server.entity;
import io.izzel.arclight.common.mod.util.ResourceLocationUtil;
import net.minecraft.entity.merchant.villager.AbstractVillagerEntity;
import net.minecraftforge.registries.ForgeRegistries;
import org.bukkit.craftbukkit.v.CraftServer;
import org.bukkit.craftbukkit.v.entity.CraftAbstractVillager;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.NotNull;
public class ArclightModVillager extends CraftAbstractVillager {
private final EntityType entityType;
public ArclightModVillager(CraftServer server, AbstractVillagerEntity entity) {
super(server, entity);
this.entityType = EntityType.valueOf(ResourceLocationUtil.standardize(ForgeRegistries.ENTITIES.getKey(entity.getType())));
}
@Override
public @NotNull EntityType getType() {
return this.entityType;
}
@Override
public String toString() {
return "ArclightModVillager{" + entityType + '}';
}
}

View File

@ -1,19 +1,15 @@
package io.izzel.arclight.common.mod.util;
import net.minecraft.block.BlockState;
import net.minecraft.tileentity.TileEntity;
import net.minecraftforge.common.util.BlockSnapshot;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v.block.*;
import org.bukkit.craftbukkit.v.block.CraftBlock;
public class ArclightBlockSnapshot extends CraftBlock {
private final BlockSnapshot blockSnapshot;
private final BlockState blockState;
public ArclightBlockSnapshot(BlockSnapshot blockSnapshot, boolean current) {
super(blockSnapshot.getWorld(), blockSnapshot.getPos());
this.blockSnapshot = blockSnapshot;
this.blockState = current ? blockSnapshot.getCurrentBlock() : blockSnapshot.getReplacedBlock();
}
@ -22,169 +18,6 @@ public class ArclightBlockSnapshot extends CraftBlock {
return blockState;
}
@SuppressWarnings("unchecked")
@Override
public org.bukkit.block.BlockState getState() {
Material material = getType();
switch (material) {
case ACACIA_SIGN:
case ACACIA_WALL_SIGN:
case BIRCH_SIGN:
case BIRCH_WALL_SIGN:
case DARK_OAK_SIGN:
case DARK_OAK_WALL_SIGN:
case JUNGLE_SIGN:
case JUNGLE_WALL_SIGN:
case OAK_SIGN:
case OAK_WALL_SIGN:
case SPRUCE_SIGN:
case SPRUCE_WALL_SIGN:
return new CraftSign(this);
case CHEST:
case TRAPPED_CHEST:
return new CraftChest(this);
case FURNACE:
return new CraftFurnaceFurnace(this);
case DISPENSER:
return new CraftDispenser(this);
case DROPPER:
return new CraftDropper(this);
case END_GATEWAY:
return new CraftEndGateway(this);
case HOPPER:
return new CraftHopper(this);
case SPAWNER:
return new CraftCreatureSpawner(this);
case JUKEBOX:
return new CraftJukebox(this);
case BREWING_STAND:
return new CraftBrewingStand(this);
case CREEPER_HEAD:
case CREEPER_WALL_HEAD:
case DRAGON_HEAD:
case DRAGON_WALL_HEAD:
case PLAYER_HEAD:
case PLAYER_WALL_HEAD:
case SKELETON_SKULL:
case SKELETON_WALL_SKULL:
case WITHER_SKELETON_SKULL:
case WITHER_SKELETON_WALL_SKULL:
case ZOMBIE_HEAD:
case ZOMBIE_WALL_HEAD:
return new CraftSkull(this);
case COMMAND_BLOCK:
case CHAIN_COMMAND_BLOCK:
case REPEATING_COMMAND_BLOCK:
return new CraftCommandBlock(this);
case BEACON:
return new CraftBeacon(this);
case BLACK_BANNER:
case BLACK_WALL_BANNER:
case BLUE_BANNER:
case BLUE_WALL_BANNER:
case BROWN_BANNER:
case BROWN_WALL_BANNER:
case CYAN_BANNER:
case CYAN_WALL_BANNER:
case GRAY_BANNER:
case GRAY_WALL_BANNER:
case GREEN_BANNER:
case GREEN_WALL_BANNER:
case LIGHT_BLUE_BANNER:
case LIGHT_BLUE_WALL_BANNER:
case LIGHT_GRAY_BANNER:
case LIGHT_GRAY_WALL_BANNER:
case LIME_BANNER:
case LIME_WALL_BANNER:
case MAGENTA_BANNER:
case MAGENTA_WALL_BANNER:
case ORANGE_BANNER:
case ORANGE_WALL_BANNER:
case PINK_BANNER:
case PINK_WALL_BANNER:
case PURPLE_BANNER:
case PURPLE_WALL_BANNER:
case RED_BANNER:
case RED_WALL_BANNER:
case WHITE_BANNER:
case WHITE_WALL_BANNER:
case YELLOW_BANNER:
case YELLOW_WALL_BANNER:
return new CraftBanner(this);
case STRUCTURE_BLOCK:
return new CraftStructureBlock(this);
case SHULKER_BOX:
case WHITE_SHULKER_BOX:
case ORANGE_SHULKER_BOX:
case MAGENTA_SHULKER_BOX:
case LIGHT_BLUE_SHULKER_BOX:
case YELLOW_SHULKER_BOX:
case LIME_SHULKER_BOX:
case PINK_SHULKER_BOX:
case GRAY_SHULKER_BOX:
case LIGHT_GRAY_SHULKER_BOX:
case CYAN_SHULKER_BOX:
case PURPLE_SHULKER_BOX:
case BLUE_SHULKER_BOX:
case BROWN_SHULKER_BOX:
case GREEN_SHULKER_BOX:
case RED_SHULKER_BOX:
case BLACK_SHULKER_BOX:
return new CraftShulkerBox(this);
case ENCHANTING_TABLE:
return new CraftEnchantingTable(this);
case ENDER_CHEST:
return new CraftEnderChest(this);
case DAYLIGHT_DETECTOR:
return new CraftDaylightDetector(this);
case COMPARATOR:
return new CraftComparator(this);
case BLACK_BED:
case BLUE_BED:
case BROWN_BED:
case CYAN_BED:
case GRAY_BED:
case GREEN_BED:
case LIGHT_BLUE_BED:
case LIGHT_GRAY_BED:
case LIME_BED:
case MAGENTA_BED:
case ORANGE_BED:
case PINK_BED:
case PURPLE_BED:
case RED_BED:
case WHITE_BED:
case YELLOW_BED:
return new CraftBed(this);
case CONDUIT:
return new CraftConduit(this);
case BARREL:
return new CraftBarrel(this);
case BELL:
return new CraftBell(this);
case BLAST_FURNACE:
return new CraftBlastFurnace(this);
case CAMPFIRE:
return new CraftCampfire(this);
case JIGSAW:
return new CraftJigsaw(this);
case LECTERN:
return new CraftLectern(this);
case SMOKER:
return new CraftSmoker(this);
default:
TileEntity tileEntity = blockSnapshot.getTileEntity();
if (tileEntity != null) {
// block with unhandled TileEntity:
return new CraftBlockEntityState<>(this, (Class<TileEntity>) tileEntity.getClass());
} else {
// Block without TileEntity:
return new CraftBlockState(this);
}
}
}
public static ArclightBlockSnapshot fromBlockSnapshot(BlockSnapshot blockSnapshot, boolean current) {
return new ArclightBlockSnapshot(blockSnapshot, current);
}

View File

@ -0,0 +1,28 @@
package io.izzel.arclight.common.mod.util;
import com.google.common.base.Preconditions;
import net.minecraft.util.ResourceLocation;
import org.jetbrains.annotations.Contract;
import java.util.Locale;
public class ResourceLocationUtil {
@Contract("null -> fail")
public static String standardize(ResourceLocation location) {
Preconditions.checkNotNull(location, "location");
return location.toString()
.replace(':', '_')
.replaceAll("\\s+", "_")
.replaceAll("\\W", "")
.toUpperCase(Locale.ENGLISH);
}
public static String standardizeLower(ResourceLocation location) {
return location.toString()
.replace(':', '_')
.replaceAll("\\s+", "_")
.replaceAll("\\W", "")
.toLowerCase(Locale.ENGLISH);
}
}

View File

@ -0,0 +1,156 @@
package io.izzel.arclight.common.mod.util.remapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.NamespacedKey;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
// 你好
// 不要抄
@SuppressWarnings("unused")
public class ArclightEnumExtender {
private static final Logger LOGGER = LogManager.getLogger("EnumExtender");
public static void process(ClassNode node, List<String> names) {
String desc = Type.getObjectType(node.name).getDescriptor();
FieldNode values = tryGetEnumArray(node);
values.access &= ~Opcodes.ACC_FINAL;
Set<String> set = countEnum(node);
tryCreateCtor(node);
int count = set.size();
for (MethodNode method : node.methods) {
if (method.name.equals("<clinit>")) {
InsnList list = new InsnList();
InsnList postList = new InsnList();
for (String name : names) {
boolean found = false;
if (name.startsWith(NamespacedKey.MINECRAFT + ":")) {
if (!set.contains(standardize(name.substring(NamespacedKey.MINECRAFT.length() + 1)))) {
LOGGER.warn("Expect {} found in {}, but not", name, node.name);
} else found = true;
}
if (!found) {
name = standardize(name);
FieldNode fieldNode = new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM, name, desc, null, null);
node.fields.add(fieldNode);
list.add(new TypeInsnNode(Opcodes.NEW, node.name));
list.add(new InsnNode(Opcodes.DUP));
list.add(new LdcInsnNode(name));
list.add(loadInt(count));
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, node.name, "<init>", "(Ljava/lang/String;I)V", false));
list.add(new FieldInsnNode(Opcodes.PUTSTATIC, node.name, name, desc));
postList.add(new InsnNode(Opcodes.DUP));
postList.add(loadInt(count));
postList.add(new FieldInsnNode(Opcodes.GETSTATIC, node.name, name, desc));
postList.add(new InsnNode(Opcodes.AASTORE));
LOGGER.info("Added {} to {}", name, node.name);
}
count++;
}
list.add(new FieldInsnNode(Opcodes.GETSTATIC, node.name, values.name, values.desc));
list.add(loadInt(0));
list.add(loadInt(count));
list.add(new TypeInsnNode(Opcodes.ANEWARRAY, node.name));
list.add(new InsnNode(Opcodes.DUP));
list.add(new FieldInsnNode(Opcodes.PUTSTATIC, node.name, values.name, values.desc));
list.add(loadInt(0));
list.add(loadInt(set.size()));
list.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V"));
list.add(new FieldInsnNode(Opcodes.GETSTATIC, node.name, values.name, values.desc));
postList.add(new InsnNode(Opcodes.POP));
for (AbstractInsnNode insnNode : method.instructions) {
if (insnNode.getOpcode() == Opcodes.RETURN) {
method.instructions.insertBefore(insnNode, list);
method.instructions.insertBefore(insnNode, postList);
}
}
}
}
}
private static AbstractInsnNode loadInt(int i) {
if (i >= -1 && i < 6) {
return new InsnNode(Opcodes.ICONST_0 + i);
} else if (i >= -128 && i < 128) {
return new IntInsnNode(Opcodes.BIPUSH, i);
} else if (i >= -32768 && i < 32768) {
return new IntInsnNode(Opcodes.SIPUSH, i);
} else {
return new LdcInsnNode(i);
}
}
private static void tryCreateCtor(ClassNode node) {
boolean found = false;
for (MethodNode method : node.methods) {
if (method.name.equals("<init>") && method.desc.equals("(Ljava/lang/String;I)V")) {
found = true;
break;
}
}
if (!found) {
MethodNode methodNode = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, "<init>", "(Ljava/lang/String;I)V", null, null);
InsnList list = new InsnList();
list.add(new VarInsnNode(Opcodes.ALOAD, 0));
list.add(new VarInsnNode(Opcodes.ALOAD, 1));
list.add(new VarInsnNode(Opcodes.ILOAD, 2));
list.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, "java/lang/Enum", "<init>", "(Ljava/lang/String;I)V", false));
list.add(new InsnNode(Opcodes.RETURN));
methodNode.instructions = list;
node.methods.add(methodNode);
}
}
private static String standardize(String str) {
return str
.replace(':', '_')
.replaceAll("\\s+", "_")
.replaceAll("\\W", "")
.toUpperCase(Locale.ENGLISH);
}
private static Set<String> countEnum(ClassNode node) {
Set<String> ret = new HashSet<>();
for (FieldNode field : node.fields) {
if ((field.access & Opcodes.ACC_ENUM) != 0) {
ret.add(field.name);
}
}
return ret;
}
private static FieldNode tryGetEnumArray(ClassNode node) {
String desc = '[' + Type.getObjectType(node.name).getDescriptor();
List<FieldNode> candidates = new ArrayList<>();
for (FieldNode field : node.fields) {
if (Modifier.isStatic(field.access) && field.desc.equals(desc)) {
candidates.add(field);
}
}
if (candidates.size() != 1) {
throw new RuntimeException("No $VALUES candidate found in enum class " + node.name);
}
return candidates.get(0);
}
}

View File

@ -0,0 +1,72 @@
package io.izzel.arclight.common.util;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
// code from gson, Apache license
// mute the assetiong error throwing because we dynamically add elements to enums
public class EnumTypeFactory implements TypeAdapterFactory {
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
Class<? super T> rawType = type.getRawType();
if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
return null;
}
if (!rawType.isEnum()) {
rawType = rawType.getSuperclass(); // handle anonymous subclasses
}
return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
}
private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
private final Map<String, T> nameToConstant = new HashMap<String, T>();
private final Map<T, String> constantToName = new HashMap<T, String>();
public EnumTypeAdapter(Class<T> classOfT) {
for (T constant : classOfT.getEnumConstants()) {
String name = constant.name();
SerializedName annotation;
try {
annotation = classOfT.getField(name).getAnnotation(SerializedName.class);
} catch (NoSuchFieldException e) {
annotation = null;
}
if (annotation != null) {
name = annotation.value();
for (String alternate : annotation.alternate()) {
nameToConstant.put(alternate, constant);
}
}
nameToConstant.put(name, constant);
constantToName.put(constant, name);
}
}
@Override
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return nameToConstant.get(in.nextString());
}
@Override
public void write(JsonWriter out, T value) throws IOException {
out.value(value == null ? null : constantToName.get(value));
}
}
}

View File

@ -5,16 +5,21 @@
"target": "@env(DEFAULT)",
"refmap": "mixins.arclight.refmap.json",
"setSourceFile": true,
"plugin": "io.izzel.arclight.common.mod.ArclightMixinPlugin",
"mixins": [
"BukkitCommandWrapperMixin",
"ColouredConsoleSenderMixin",
"CraftBlockMixin",
"CraftBlockStateMixin",
"CraftChunkMixin",
"CraftConsoleCommandSenderMixin",
"CraftEntityMixin",
"CraftEventFactoryMixin",
"CraftItemFactoryMixin",
"CraftMagicNumbersMixin",
"CraftServerMixin",
"CraftWorldMixin",
"EntityTypeMixin",
"JavaPluginLoaderMixin",
"JavaPluginMixin",
"MaterialMixin",

View File

@ -9,14 +9,25 @@ import java.util.Optional;
@ConfigSerializable
public class CompatSpec {
@Setting("property-override")
private Map<String, MaterialPropertySpec> overrides;
@Setting("material-property-overrides")
private Map<String, MaterialPropertySpec> materials;
public Map<String, MaterialPropertySpec> getOverrides() {
return overrides;
@Setting("entity-property-overrides")
private Map<String, EntityPropertySpec> entities;
public Map<String, MaterialPropertySpec> getMaterials() {
return materials;
}
public Optional<MaterialPropertySpec> getOverride(String key) {
return Optional.ofNullable(overrides.get(key));
public Optional<MaterialPropertySpec> getMaterial(String key) {
return Optional.ofNullable(materials.get(key));
}
public Map<String, EntityPropertySpec> getEntities() {
return entities;
}
public Optional<EntityPropertySpec> getEntity(String key) {
return Optional.ofNullable(entities.get(key));
}
}

View File

@ -0,0 +1,25 @@
package io.izzel.arclight.i18n.conf;
import ninja.leaping.configurate.objectmapping.Setting;
import ninja.leaping.configurate.objectmapping.serialize.ConfigSerializable;
@ConfigSerializable
public class EntityPropertySpec implements Cloneable {
public static final EntityPropertySpec EMPTY = new EntityPropertySpec();
@Setting("entityClass")
public String entityClass;
@Setting("entityImplClass")
public String entityImplClass;
@Override
public EntityPropertySpec clone() {
try {
return (EntityPropertySpec) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -62,6 +62,9 @@ public class MaterialPropertySpec implements Cloneable {
@Setting("itemMetaType")
public String itemMetaType;
@Setting("blockStateClass")
public String blockStateClass;
@Override
public MaterialPropertySpec clone() {
try {

View File

@ -7,7 +7,8 @@ optimization {
remove-stream = true
}
compatibility {
property-override {
material-property-overrides {
}
entity-property-overrides {
}
}

View File

@ -47,6 +47,21 @@ registry {
enchantment = "注册了 {} 个新的附魔"
potion = "注册了 {} 个新的药水效果"
material = "注册了 {} 个材料,其中 {} 个方块 {} 个物品"
entity-type = "注册了 {} 个新的生物类型"
meta-type {
not-subclass = "{} 不是 {} 的子类"
error = "{} 提供的 itemMetaType {} 无效: {}"
no-candidate = "{} 未在提供的 itemMetaType {} 找到合适的构造方法"
}
block-state {
not-subclass = "{} 不是 {} 的子类"
error = "{} 提供的 itemMetaType {} 无效: {}"
no-candidate = "{} 未在提供的 blockStateClass {} 找到合适的构造方法"
}
entity {
not-subclass = "{} 不是 {} 的子类"
error = "{} 提供的 entityClass {} 无效: {}"
}
}
comments {