2014-04-29 10:54:20 +10:00
|
|
|
package codechicken.nei;
|
|
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
|
import java.util.Collections;
|
2023-09-25 00:35:24 +02:00
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
2026-02-09 04:49:58 +08:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2023-09-25 00:35:24 +02:00
|
|
|
|
2014-04-29 10:54:20 +10:00
|
|
|
import net.minecraft.creativetab.CreativeTabs;
|
|
|
|
|
import net.minecraft.entity.Entity;
|
|
|
|
|
import net.minecraft.entity.EntityList;
|
|
|
|
|
import net.minecraft.entity.EntityLiving;
|
|
|
|
|
import net.minecraft.entity.monster.IMob;
|
2014-08-10 12:02:35 +10:00
|
|
|
import net.minecraft.entity.passive.EntityPig;
|
2014-04-29 10:54:20 +10:00
|
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
|
|
|
import net.minecraft.init.Blocks;
|
|
|
|
|
import net.minecraft.item.Item;
|
|
|
|
|
import net.minecraft.item.ItemBlock;
|
|
|
|
|
import net.minecraft.item.ItemStack;
|
2026-02-09 04:49:58 +08:00
|
|
|
import net.minecraft.tileentity.TileEntity;
|
2014-04-29 10:54:20 +10:00
|
|
|
import net.minecraft.tileentity.TileEntityMobSpawner;
|
2025-06-15 12:09:55 +02:00
|
|
|
import net.minecraft.util.EnumChatFormatting;
|
2014-04-29 10:54:20 +10:00
|
|
|
import net.minecraft.util.IIcon;
|
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
|
import net.minecraftforge.client.MinecraftForgeClient;
|
2026-07-11 19:33:51 +02:00
|
|
|
import net.minecraftforge.common.util.ForgeDirection;
|
2014-04-29 10:54:20 +10:00
|
|
|
|
2025-06-15 12:09:55 +02:00
|
|
|
import cpw.mods.fml.relauncher.Side;
|
|
|
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
|
|
|
|
2022-06-24 12:32:13 +01:00
|
|
|
public class ItemMobSpawner extends ItemBlock {
|
2023-01-28 17:55:51 -08:00
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
private static final Map<Integer, EntityLiving> entityHashMap = new ConcurrentHashMap<>();
|
|
|
|
|
private static final Map<Integer, String> IDtoNameMap = new ConcurrentHashMap<>();
|
|
|
|
|
private static final Map<Class<? extends Entity>, String> ENTITY_CLASS_TO_NAME_CACHE = new ConcurrentHashMap<>();
|
|
|
|
|
|
|
|
|
|
public static int idPig = 90;
|
|
|
|
|
private static volatile boolean loaded = false;
|
|
|
|
|
|
|
|
|
|
// For asm
|
|
|
|
|
public static int placedX;
|
|
|
|
|
public static int placedY;
|
|
|
|
|
public static int placedZ;
|
|
|
|
|
|
|
|
|
|
public static void setLastPlacedPosition(int x, int y, int z) {
|
|
|
|
|
placedX = x;
|
|
|
|
|
placedY = y;
|
|
|
|
|
placedZ = z;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static int[] getLastPlacedPosition() {
|
|
|
|
|
return new int[] { placedX, placedY, placedZ };
|
|
|
|
|
}
|
2014-04-29 10:54:20 +10:00
|
|
|
|
2014-06-29 20:31:37 +10:00
|
|
|
public ItemMobSpawner() {
|
2014-04-29 10:54:20 +10:00
|
|
|
super(Blocks.mob_spawner);
|
|
|
|
|
hasSubtypes = true;
|
|
|
|
|
MinecraftForgeClient.registerItemRenderer(this, new SpawnerRenderer());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2026-02-09 04:49:58 +08:00
|
|
|
public IIcon getIconFromDamage(int meta) {
|
2014-04-29 10:54:20 +10:00
|
|
|
return Blocks.mob_spawner.getBlockTextureFromSide(0);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
@Override
|
|
|
|
|
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int side,
|
|
|
|
|
float hitX, float hitY, float hitZ) {
|
2026-07-11 19:33:51 +02:00
|
|
|
int targetX = x;
|
|
|
|
|
int targetY = y;
|
|
|
|
|
int targetZ = z;
|
|
|
|
|
|
|
|
|
|
if (!world.getBlock(x, y, z).isReplaceable(world, x, y, z)) {
|
|
|
|
|
ForgeDirection direction = ForgeDirection.getOrientation(side);
|
|
|
|
|
targetX += direction.offsetX;
|
|
|
|
|
targetY += direction.offsetY;
|
|
|
|
|
targetZ += direction.offsetZ;
|
|
|
|
|
}
|
2026-02-09 04:49:58 +08:00
|
|
|
|
2026-07-11 19:33:51 +02:00
|
|
|
placedX = targetX;
|
|
|
|
|
placedY = targetY;
|
|
|
|
|
placedZ = targetZ;
|
|
|
|
|
|
|
|
|
|
String mobType = getMobTypeFromItemStack(itemstack);
|
2026-02-09 04:49:58 +08:00
|
|
|
|
|
|
|
|
boolean placed = super.onItemUse(itemstack, entityplayer, world, x, y, z, side, hitX, hitY, hitZ);
|
|
|
|
|
|
2026-07-11 19:33:51 +02:00
|
|
|
if (placed) {
|
|
|
|
|
TileEntity tileEntity = world.getTileEntity(targetX, targetY, targetZ);
|
|
|
|
|
|
|
|
|
|
if (tileEntity instanceof TileEntityMobSpawner spawner && mobType != null) {
|
|
|
|
|
spawner.func_145881_a().setEntityName(mobType);
|
|
|
|
|
|
|
|
|
|
if (!world.isRemote) {
|
|
|
|
|
world.markBlockForUpdate(targetX, targetY, targetZ);
|
|
|
|
|
NEICPH.sendMobSpawnerID(targetX, targetY, targetZ, mobType);
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
2026-07-11 19:33:51 +02:00
|
|
|
|
2014-04-29 10:54:20 +10:00
|
|
|
return true;
|
|
|
|
|
}
|
2026-07-11 19:33:51 +02:00
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
return placed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String getMobTypeFromItemStack(ItemStack stack) {
|
|
|
|
|
int meta = getValidMetaData(stack);
|
|
|
|
|
return IDtoNameMap.get(meta);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int getValidMetaData(ItemStack stack) {
|
|
|
|
|
int meta = stack.getItemDamage();
|
|
|
|
|
if (meta == 0 || !IDtoNameMap.containsKey(meta)) {
|
|
|
|
|
return idPig;
|
|
|
|
|
}
|
|
|
|
|
return meta;
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
|
2025-06-15 12:09:55 +02:00
|
|
|
@SideOnly(Side.CLIENT)
|
2014-04-29 10:54:20 +10:00
|
|
|
@Override
|
2026-02-09 04:49:58 +08:00
|
|
|
public void addInformation(ItemStack itemstack, EntityPlayer player, List<String> tooltip, boolean advanced) {
|
|
|
|
|
int meta = getValidMetaData(itemstack);
|
|
|
|
|
EntityLiving entity = getEntity(meta);
|
|
|
|
|
String mobName = IDtoNameMap.get(meta);
|
|
|
|
|
|
|
|
|
|
if (entity != null && mobName != null) {
|
|
|
|
|
EnumChatFormatting color = (entity instanceof IMob) ? EnumChatFormatting.DARK_RED
|
|
|
|
|
: EnumChatFormatting.DARK_AQUA;
|
|
|
|
|
tooltip.add(color + mobName);
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
public static EntityLiving getEntity(int id) {
|
|
|
|
|
EntityLiving cached = entityHashMap.get(id);
|
|
|
|
|
if (cached != null) {
|
|
|
|
|
return cached;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadSpawners();
|
|
|
|
|
|
|
|
|
|
Class<?> entityClass = EntityList.IDtoClassMapping.get(id);
|
|
|
|
|
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
|
|
|
|
|
|
|
|
|
|
if (isInvalidEntityClass(entityClass)) {
|
|
|
|
|
return cacheFallback(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EntityLiving entity = createEntityInstance(entityClass, world, id);
|
|
|
|
|
if (entity != null) {
|
|
|
|
|
entityHashMap.put(id, entity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return entity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean isInvalidEntityClass(Class<?> entityClass) {
|
|
|
|
|
if (entityClass == null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int modifiers = entityClass.getModifiers();
|
|
|
|
|
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
|
|
|
|
|
NEIClientConfig.logger.warn("Skipping abstract/interface entity class: {}", entityClass.getName());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!EntityLiving.class.isAssignableFrom(entityClass)) {
|
|
|
|
|
NEIClientConfig.logger.warn("Skipping non-EntityLiving class: {}", entityClass.getName());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
private static EntityLiving createEntityInstance(Class<?> entityClass, World world, int id) {
|
|
|
|
|
if (entityClass == null || world == null) {
|
|
|
|
|
NEIClientConfig.logger.debug("World or entity class is null, using fallback for ID: {}", id);
|
|
|
|
|
return getFallbackEntity(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Class<? extends EntityLiving> livingClass = (Class<? extends EntityLiving>) entityClass;
|
|
|
|
|
return livingClass.getConstructor(World.class).newInstance(world);
|
|
|
|
|
} catch (Throwable t) {
|
|
|
|
|
logEntityCreationError(entityClass, id, t);
|
|
|
|
|
return getFallbackEntity(id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void logEntityCreationError(Class<?> entityClass, int id, Throwable t) {
|
|
|
|
|
String entityName = IDtoNameMap.getOrDefault(id, "unknown");
|
|
|
|
|
if (entityClass == null) {
|
|
|
|
|
NEIClientConfig.logger
|
|
|
|
|
.error("Failed to create entity instance: Class is null for ID {}, Name {}", id, entityName, t);
|
|
|
|
|
} else {
|
|
|
|
|
NEIClientConfig.logger.error(
|
|
|
|
|
"Failed to create instance of entity: Class {}, ID {}, Name {}",
|
|
|
|
|
entityClass.getName(),
|
|
|
|
|
id,
|
|
|
|
|
entityName,
|
|
|
|
|
t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static EntityLiving cacheFallback(int id) {
|
|
|
|
|
EntityLiving fallback = getFallbackEntity(id);
|
|
|
|
|
if (fallback != null) {
|
|
|
|
|
entityHashMap.put(id, fallback);
|
|
|
|
|
}
|
|
|
|
|
return fallback;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static EntityLiving getFallbackEntity(int originalId) {
|
|
|
|
|
EntityLiving pig = entityHashMap.get(idPig);
|
|
|
|
|
if (pig == null) {
|
|
|
|
|
World world = NEIClientUtils.mc() != null ? NEIClientUtils.mc().theWorld : null;
|
|
|
|
|
|
|
|
|
|
if (world != null) {
|
|
|
|
|
try {
|
|
|
|
|
pig = new EntityPig(world);
|
|
|
|
|
entityHashMap.put(idPig, pig);
|
|
|
|
|
NEIClientConfig.logger.debug("Created fallback pig entity for ID: {}", originalId);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
NEIClientConfig.logger
|
|
|
|
|
.error("Failed to create fallback pig entity for original ID: {}", originalId, e);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
NEIClientConfig.logger.warn("World is null, cannot create fallback entity for ID: {}", originalId);
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-09 04:49:58 +08:00
|
|
|
|
|
|
|
|
return pig;
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
|
2025-06-15 12:09:55 +02:00
|
|
|
public static void clearEntityReferences() {
|
|
|
|
|
entityHashMap.clear();
|
2026-02-09 04:49:58 +08:00
|
|
|
NEIClientConfig.logger.debug("Cleared all entity references from cache");
|
2022-06-06 11:05:02 -04:00
|
|
|
}
|
|
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
@SuppressWarnings("unchecked")
|
2025-06-15 12:09:55 +02:00
|
|
|
public static void loadSpawners() {
|
2026-02-09 04:49:58 +08:00
|
|
|
if (loaded) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
synchronized (ItemMobSpawner.class) {
|
|
|
|
|
try {
|
|
|
|
|
for (Map.Entry<Class<? extends Entity>, String> entry : ((Map<Class<? extends Entity>, String>) EntityList.classToStringMapping)
|
|
|
|
|
.entrySet()) {
|
|
|
|
|
|
|
|
|
|
Class<? extends Entity> clazz = entry.getKey();
|
|
|
|
|
String name = entry.getValue();
|
|
|
|
|
|
|
|
|
|
if (shouldRegisterEntity(clazz, name)) {
|
|
|
|
|
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
|
|
|
|
|
if (id != null) {
|
|
|
|
|
IDtoNameMap.put(id, name);
|
|
|
|
|
ENTITY_CLASS_TO_NAME_CACHE.put(clazz, name);
|
|
|
|
|
|
|
|
|
|
if ("Pig".equals(name)) {
|
|
|
|
|
idPig = id;
|
|
|
|
|
NEIClientConfig.logger.debug("Set pig entity ID to: {}", id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-06-15 12:09:55 +02:00
|
|
|
}
|
2026-02-09 04:49:58 +08:00
|
|
|
loaded = true;
|
|
|
|
|
NEIClientConfig.logger.info("Loaded {} spawner entity mappings", IDtoNameMap.size());
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
NEIClientConfig.logger.error("Error loading spawners from entity list", e);
|
2025-06-15 12:09:55 +02:00
|
|
|
}
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 04:49:58 +08:00
|
|
|
private static boolean shouldRegisterEntity(Class<? extends Entity> clazz, String name) {
|
|
|
|
|
if (clazz == null || name == null || name.trim().isEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!EntityLiving.class.isAssignableFrom(clazz)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ("EnderDragon".equals(name) || "Giant".equals(name)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-29 10:54:20 +10:00
|
|
|
@Override
|
2024-08-29 20:07:14 +02:00
|
|
|
public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) {
|
2026-02-09 04:49:58 +08:00
|
|
|
if (!NEIClientConfig.hasSMPCounterPart()) {
|
|
|
|
|
list.add(new ItemStack(item));
|
|
|
|
|
} else {
|
|
|
|
|
loadSpawners();
|
|
|
|
|
for (int id : IDtoNameMap.keySet()) {
|
|
|
|
|
ItemStack stack = new ItemStack(item, 1, id);
|
|
|
|
|
list.add(stack);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static String getEntityNameForMeta(int meta) {
|
|
|
|
|
loadSpawners();
|
|
|
|
|
return IDtoNameMap.getOrDefault(meta, IDtoNameMap.getOrDefault(idPig, "Pig"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isValidEntityMeta(int meta) {
|
|
|
|
|
loadSpawners();
|
|
|
|
|
return IDtoNameMap.containsKey(meta) || meta == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Map<Integer, String> getRegisteredSpawnerEntities() {
|
|
|
|
|
loadSpawners();
|
|
|
|
|
return Collections.unmodifiableMap(IDtoNameMap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static boolean isEntityValidForSpawner(Class<? extends Entity> entityClass) {
|
|
|
|
|
if (entityClass == null) return false;
|
|
|
|
|
|
|
|
|
|
String name = ENTITY_CLASS_TO_NAME_CACHE.get(entityClass);
|
|
|
|
|
if (name == null) {
|
|
|
|
|
name = (String) EntityList.classToStringMapping.get(entityClass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name != null && shouldRegisterEntity(entityClass, name);
|
2014-04-29 10:54:20 +10:00
|
|
|
}
|
|
|
|
|
}
|