diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/ArclightMain.java b/arclight-common/src/main/java/io/izzel/arclight/common/ArclightMain.java index bd999a43..3257730e 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/ArclightMain.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/ArclightMain.java @@ -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); diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/EntityTypeBridge.java b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/EntityTypeBridge.java new file mode 100644 index 00000000..28ed3a46 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/EntityTypeBridge.java @@ -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 bridge$entityFactory(); + + void bridge$setEntityFactory(Function function); +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/MaterialBridge.java b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/MaterialBridge.java index 5e1cae3a..3051f155 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/MaterialBridge.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/bridge/bukkit/MaterialBridge.java @@ -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 bridge$itemMetaFactory(); + + void bridge$setItemMetaFactory(Function func); + + Function bridge$blockStateFactory(); + + void bridge$setBlockStateFactory(Function func); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftBlockMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftBlockMixin.java new file mode 100644 index 00000000..bd7efc38 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftBlockMixin.java @@ -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 cir) { + MaterialBridge bridge = (MaterialBridge) (Object) getType(); + if (bridge.bridge$getType() != MaterialPropertySpec.MaterialType.VANILLA) { + cir.setReturnValue(bridge.bridge$blockStateFactory().apply((CraftBlock) (Object) this)); + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftEntityMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftEntityMixin.java new file mode 100644 index 00000000..8d8c6c42 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftEntityMixin.java @@ -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 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)); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftItemFactoryMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftItemFactoryMixin.java index e67934e4..db32b14a 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftItemFactoryMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftItemFactoryMixin.java @@ -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> TYPES = ImmutableMap - .>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 cir) { MaterialBridge bridge = (MaterialBridge) (Object) CraftLegacy.fromLegacy(material); if (bridge.bridge$getType() != MaterialPropertySpec.MaterialType.VANILLA) { - MaterialPropertySpec spec = bridge.bridge$getSpec(); - BiFunction 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); - } } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftWorldMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftWorldMixin.java new file mode 100644 index 00000000..0158bad9 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/CraftWorldMixin.java @@ -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 cir) { + Function factory = ((EntityTypeBridge) (Object) entityType).bridge$entityFactory(); + if (factory != null) { + cir.setReturnValue(((EntityBridge) factory.apply(loc)).bridge$getBukkitEntity()); + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/EntityTypeMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/EntityTypeMixin.java new file mode 100644 index 00000000..98c19a7b --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/EntityTypeMixin.java @@ -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 clazz; + + private net.minecraft.entity.EntityType handleType; + private EntityPropertySpec spec; + private Function 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) 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 bridge$entityFactory() { + return factory; + } + + @Override + public void bridge$setEntityFactory(Function function) { + this.factory = function; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java index fc09f8f2..8dfda531 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/MaterialMixin.java @@ -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> TYPES = ImmutableMap + .>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 arclight$metaFunc; + + @Override + public Function bridge$itemMetaFactory() { + return arclight$metaFunc; + } + + @Override + public void bridge$setItemMetaFactory(Function func) { + this.arclight$metaFunc = func; + } + + private Function arclight$stateFunc; + + @Override + public Function bridge$blockStateFactory() { + return arclight$stateFunc; + } + + @Override + public void bridge$setBlockStateFactory(Function 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 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 dynamicMetaCreator(String type) { + Function 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) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/entity/projectile/EggEntityMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/entity/projectile/EggEntityMixin.java index 858709ae..abf77168 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/entity/projectile/EggEntityMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/entity/projectile/EggEntityMixin.java @@ -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(); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java index 952ed44e..92b20128 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/BukkitRegistry.java @@ -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> MAT_CTOR = ImmutableList.of(int.class); + private static final List> ENTITY_CTOR = ImmutableList.of(String.class, Class.class, int.class); private static final Map BY_NAME = getStatic(Material.class, "BY_NAME"); private static final Map BLOCK_MATERIAL = getStatic(CraftMagicNumbers.class, "BLOCK_MATERIAL"); private static final Map ITEM_MATERIAL = getStatic(CraftMagicNumbers.class, "ITEM_MATERIAL"); private static final Map MATERIAL_ITEM = getStatic(CraftMagicNumbers.class, "MATERIAL_ITEM"); private static final Map MATERIAL_BLOCK = getStatic(CraftMagicNumbers.class, "MATERIAL_BLOCK"); + private static final Map 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 newTypes = new ArrayList<>(ForgeRegistries.ENTITIES.getEntries().size() - origin); + for (Map.Entry> 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 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 list = new ArrayList<>(); for (Map.Entry 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 getStatic(Class cl, String name) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModChestedHorse.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModChestedHorse.java new file mode 100644 index 00000000..12160c2c --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModChestedHorse.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModEntity.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModEntity.java new file mode 100644 index 00000000..52ce9bbf --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModEntity.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModHorse.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModHorse.java new file mode 100644 index 00000000..11d69d90 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModHorse.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModLivingEntity.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModLivingEntity.java new file mode 100644 index 00000000..73180962 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModLivingEntity.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecart.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecart.java new file mode 100644 index 00000000..00195845 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecart.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecartContainer.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecartContainer.java new file mode 100644 index 00000000..91c6dea1 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMinecartContainer.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMob.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMob.java new file mode 100644 index 00000000..3b4466ff --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModMob.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModProjectile.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModProjectile.java new file mode 100644 index 00000000..9d944cb7 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModProjectile.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModRaider.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModRaider.java new file mode 100644 index 00000000..2871ca8d --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModRaider.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModVillager.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModVillager.java new file mode 100644 index 00000000..2cc3e66d --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/server/entity/ArclightModVillager.java @@ -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 + '}'; + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ArclightBlockSnapshot.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ArclightBlockSnapshot.java index e323a993..97b09b71 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ArclightBlockSnapshot.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ArclightBlockSnapshot.java @@ -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.getClass()); - } else { - // Block without TileEntity: - return new CraftBlockState(this); - } - } - } - public static ArclightBlockSnapshot fromBlockSnapshot(BlockSnapshot blockSnapshot, boolean current) { return new ArclightBlockSnapshot(blockSnapshot, current); } diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ResourceLocationUtil.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ResourceLocationUtil.java new file mode 100644 index 00000000..eb70b571 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/ResourceLocationUtil.java @@ -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); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java new file mode 100644 index 00000000..c7ce438b --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ArclightEnumExtender.java @@ -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 names) { + String desc = Type.getObjectType(node.name).getDescriptor(); + FieldNode values = tryGetEnumArray(node); + values.access &= ~Opcodes.ACC_FINAL; + Set set = countEnum(node); + tryCreateCtor(node); + int count = set.size(); + for (MethodNode method : node.methods) { + if (method.name.equals("")) { + 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, "", "(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("") && method.desc.equals("(Ljava/lang/String;I)V")) { + found = true; + break; + } + } + if (!found) { + MethodNode methodNode = new MethodNode(Opcodes.ACC_PRIVATE | Opcodes.ACC_SYNTHETIC, "", "(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", "", "(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 countEnum(ClassNode node) { + Set 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 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); + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/util/EnumTypeFactory.java b/arclight-common/src/main/java/io/izzel/arclight/common/util/EnumTypeFactory.java new file mode 100644 index 00000000..2f8e114c --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/util/EnumTypeFactory.java @@ -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 TypeAdapter create(Gson gson, TypeToken type) { + Class rawType = type.getRawType(); + if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) { + return null; + } + if (!rawType.isEnum()) { + rawType = rawType.getSuperclass(); // handle anonymous subclasses + } + return (TypeAdapter) new EnumTypeAdapter(rawType); + } + + private static final class EnumTypeAdapter> extends TypeAdapter { + + private final Map nameToConstant = new HashMap(); + private final Map constantToName = new HashMap(); + + public EnumTypeAdapter(Class 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)); + } + } +} diff --git a/arclight-common/src/main/resources/mixins.arclight.bukkit.json b/arclight-common/src/main/resources/mixins.arclight.bukkit.json index 1acf0869..e1eed299 100644 --- a/arclight-common/src/main/resources/mixins.arclight.bukkit.json +++ b/arclight-common/src/main/resources/mixins.arclight.bukkit.json @@ -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", diff --git a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java index 4a3e7b6d..b23acf96 100644 --- a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java +++ b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/CompatSpec.java @@ -9,14 +9,25 @@ import java.util.Optional; @ConfigSerializable public class CompatSpec { - @Setting("property-override") - private Map overrides; + @Setting("material-property-overrides") + private Map materials; - public Map getOverrides() { - return overrides; + @Setting("entity-property-overrides") + private Map entities; + + public Map getMaterials() { + return materials; } - public Optional getOverride(String key) { - return Optional.ofNullable(overrides.get(key)); + public Optional getMaterial(String key) { + return Optional.ofNullable(materials.get(key)); + } + + public Map getEntities() { + return entities; + } + + public Optional getEntity(String key) { + return Optional.ofNullable(entities.get(key)); } } diff --git a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/EntityPropertySpec.java b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/EntityPropertySpec.java new file mode 100644 index 00000000..23dd4c0b --- /dev/null +++ b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/EntityPropertySpec.java @@ -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); + } + } +} diff --git a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/MaterialPropertySpec.java b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/MaterialPropertySpec.java index e6bc13f4..2877beb5 100644 --- a/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/MaterialPropertySpec.java +++ b/i18n-config/src/main/java/io/izzel/arclight/i18n/conf/MaterialPropertySpec.java @@ -62,6 +62,9 @@ public class MaterialPropertySpec implements Cloneable { @Setting("itemMetaType") public String itemMetaType; + @Setting("blockStateClass") + public String blockStateClass; + @Override public MaterialPropertySpec clone() { try { diff --git a/i18n-config/src/main/resources/META-INF/arclight.conf b/i18n-config/src/main/resources/META-INF/arclight.conf index de97ba14..a0988091 100644 --- a/i18n-config/src/main/resources/META-INF/arclight.conf +++ b/i18n-config/src/main/resources/META-INF/arclight.conf @@ -7,7 +7,8 @@ optimization { remove-stream = true } compatibility { - property-override { - + material-property-overrides { + } + entity-property-overrides { } } \ No newline at end of file diff --git a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf index cacdc9d9..22e61fab 100644 --- a/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf +++ b/i18n-config/src/main/resources/META-INF/i18n/zh_cn.conf @@ -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 {