Compare commits

...
Author SHA1 Message Date
Martin Robertz 6d09aeca78 Merge remote-tracking branch 'NealDeal34/master' into dev 2026-01-30 11:19:16 +01:00
NealDeal34 4e1835ed12 sa 2026-01-28 05:01:39 +08:00
NealDeal34 319ed409f1 update 2026-01-28 04:16:10 +08:00
NealDeal34 ad153f1c0a sa 2026-01-28 04:02:42 +08:00
Martin Robertz e340423b28 Merge branch 'master' into master 2026-01-26 21:30:13 +01:00
NealDeal34 7b0feb16f9 Refactor ItemMobSpawner for clarity 2026-01-27 04:09:48 +08:00
Martin Robertz 8a13af1969 Merge branch 'master' into master 2026-01-10 01:49:44 +01:00
Martin Robertz d6d50a968d sa 2026-01-02 08:59:20 +01:00
NealDeal34 43459d4b41 Update ItemMobSpawner.java 2025-12-30 15:57:58 +08:00
NealDeal34 9b84ea7804 Update ItemMobSpawner.java 2025-12-30 15:32:57 +08:00
NealDeal34 de42a43b2c update 2025-12-30 03:50:49 +08:00
NealDeal34 b89734f3e6 Added checks for abstract classes when retrieving entities 2025-12-30 03:19:36 +08:00
+200 -69
View File
@@ -1,8 +1,11 @@
package codechicken.nei;
import java.util.HashMap;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.Entity;
@@ -26,10 +29,19 @@ import cpw.mods.fml.relauncher.SideOnly;
public class ItemMobSpawner extends ItemBlock {
private static final Map<Integer, EntityLiving> entityHashMap = new HashMap<>();
private static final Map<Integer, String> IDtoNameMap = new HashMap<>();
public static int idPig;
private static boolean loaded;
private static final Map<Integer, EntityLiving> entityHashMap = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<Integer, String> IDtoNameMap = new ConcurrentHashMap<>();
public static int idPig = 90;
private static boolean loaded = false;
private static final ThreadLocal<PlacementContext> placementContext = ThreadLocal
.withInitial(PlacementContext::new);
private static class PlacementContext {
int x, y, z;
}
public ItemMobSpawner() {
super(Blocks.mob_spawner);
@@ -37,99 +49,218 @@ public class ItemMobSpawner extends ItemBlock {
MinecraftForgeClient.registerItemRenderer(this, new SpawnerRenderer());
}
/**
* These are ASM translated from BlockMobSpawner
*/
public static int placedX;
public static int placedY;
public static int placedZ;
@Override
public IIcon getIconFromDamage(int par1) {
public IIcon getIconFromDamage(int meta) {
return Blocks.mob_spawner.getBlockTextureFromSide(0);
}
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7,
float par8, float par9, float par10) {
if (super.onItemUse(itemstack, entityplayer, world, x, y, z, par7, par8, par9, par10) && world.isRemote) {
TileEntityMobSpawner tileentitymobspawner = (TileEntityMobSpawner) world
.getTileEntity(placedX, placedY, placedZ);
if (tileentitymobspawner != null) {
setDefaultTag(itemstack);
String mobtype = IDtoNameMap.get(itemstack.getItemDamage());
if (mobtype != null) {
NEICPH.sendMobSpawnerID(placedX, placedY, placedZ, mobtype);
tileentitymobspawner.func_145881_a().setEntityName(mobtype);
public boolean onItemUse(ItemStack itemstack, EntityPlayer entityplayer, World world, int x, int y, int z, int side,
float hitX, float hitY, float hitZ) {
boolean placed = super.onItemUse(itemstack, entityplayer, world, x, y, z, side, hitX, hitY, hitZ);
if (placed && world.isRemote) {
TileEntityMobSpawner spawner = (TileEntityMobSpawner) world.getTileEntity(x, y, z);
if (spawner != null) {
String mobType = getMobTypeFromItemStack(itemstack);
if (mobType != null) {
NEICPH.sendMobSpawnerID(x, y, z, mobType);
spawner.func_145881_a().setEntityName(mobType);
}
}
return true;
}
return false;
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;
}
@SideOnly(Side.CLIENT)
@Override
public void addInformation(ItemStack itemstack, EntityPlayer par2EntityPlayer, List<String> list, boolean par4) {
setDefaultTag(itemstack);
int meta = itemstack.getItemDamage();
if (meta == 0) {
meta = idPig;
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);
}
Entity e = getEntity(meta);
list.add(
(e instanceof IMob ? EnumChatFormatting.DARK_RED : EnumChatFormatting.DARK_AQUA)
+ IDtoNameMap.get(meta));
}
public static EntityLiving getEntity(int ID) {
EntityLiving e = entityHashMap.get(ID);
if (e == null) {
loadSpawners();
Class<?> clazz = EntityList.IDtoClassMapping.get(ID);
World world = NEIClientUtils.mc().theWorld;
try {
e = (EntityLiving) clazz.getConstructor(new Class[] { World.class }).newInstance(world);
} catch (Throwable t) {
if (clazz == null)
NEIClientConfig.logger.error("Null class for entity (" + ID + ", " + IDtoNameMap.get(ID));
else NEIClientConfig.logger.error("Error creating instance of entity: " + clazz.getName(), t);
e = getEntity(idPig);
}
entityHashMap.put(ID, e);
public static EntityLiving getEntity(int id) {
EntityLiving cached = entityHashMap.get(id);
if (cached != null) {
return cached;
}
return e;
loadSpawners();
Class<?> entityClass = (Class<?>) 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 false;
}
int modifiers = entityClass.getModifiers();
if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers)) {
NEIClientConfig.logger.warn("Skipping abstract entity class: " + entityClass.getName());
return true;
}
return false;
}
private static EntityLiving createEntityInstance(Class<?> entityClass, World world, int id) {
if (entityClass == null || world == null) {
return getFallbackEntity(id);
}
try {
return (EntityLiving) entityClass.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) {
if (entityClass == null) {
NEIClientConfig.logger
.error("Null class for entity (ID: {}, Name: {})", id, IDtoNameMap.getOrDefault(id, "unknown"));
} else {
NEIClientConfig.logger.error("Error creating instance of entity: " + entityClass.getName(), 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);
} catch (Exception e) {
NEIClientConfig.logger.error("Failed to create fallback pig entity", e);
}
}
}
if (pig == null && originalId != idPig) {
NEIClientConfig.logger
.warn("Using null fallback for entity ID: {}, original fallback ID: {}", originalId, idPig);
}
return pig;
}
public static void clearEntityReferences() {
entityHashMap.clear();
}
private void setDefaultTag(ItemStack itemstack) {
if (!IDtoNameMap.containsKey(itemstack.getItemDamage())) itemstack.setItemDamage(idPig);
}
public static void loadSpawners() {
if (loaded) return;
loaded = true;
for (Map.Entry<Class<? extends Entity>, String> entry : EntityList.classToStringMapping.entrySet()) {
final Class<? extends Entity> clazz = entry.getKey();
if (EntityLiving.class.isAssignableFrom(clazz)) {
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
if (id == null) continue;
String name = entry.getValue();
if (name == null) continue;
if (name.equals("EnderDragon")) continue;
if (name.equals("Pig")) idPig = id;
if (clazz != EntityPig.class || name.equals("Pig")) {
IDtoNameMap.put(id, name);
if (loaded) {
return;
}
synchronized (ItemMobSpawner.class) {
if (loaded) {
return;
}
try {
for (Object entry : EntityList.classToStringMapping.entrySet()) {
Map.Entry<Class<? extends Entity>, String> mapEntry = (Map.Entry<Class<? extends Entity>, String>) entry;
Class<? extends Entity> clazz = mapEntry.getKey();
String name = mapEntry.getValue();
if (shouldRegisterEntity(clazz, name)) {
Integer id = (Integer) EntityList.classToIDMapping.get(clazz);
if (id != null) {
IDtoNameMap.put(id, name);
if ("Pig".equals(name)) {
idPig = id;
}
}
}
}
loaded = true;
} catch (Exception e) {
NEIClientConfig.logger.error("Error loading spawners", e);
}
}
}
private static boolean shouldRegisterEntity(Class<? extends Entity> clazz, String name) {
if (clazz == null || name == null) {
return false;
}
if (!EntityLiving.class.isAssignableFrom(clazz)) {
return false;
}
if ("EnderDragon".equals(name)) {
return false;
}
return true;
}
@Override
public void getSubItems(Item item, CreativeTabs tab, List<ItemStack> list) {
if (!NEIClientConfig.hasSMPCounterPart()) list.add(new ItemStack(item));
else for (int i : IDtoNameMap.keySet()) list.add(new ItemStack(item, 1, i));
if (!NEIClientConfig.hasSMPCounterPart()) {
list.add(new ItemStack(item));
} else {
for (int id : IDtoNameMap.keySet()) {
list.add(new ItemStack(item, 1, id));
}
}
}
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;
}
}