package appeng.tile; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.EnumMap; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import appeng.api.implementations.tiles.ISegmentedInventory; import appeng.api.util.ICommonTile; import appeng.api.util.IConfigManager; import appeng.api.util.IConfigurableObject; import appeng.api.util.IOrientable; import appeng.core.AELog; import appeng.core.features.ItemStackSrc; import appeng.helpers.ICustomNameObject; import appeng.helpers.IPriorityHost; import appeng.tile.events.AETileEventHandler; import appeng.tile.events.TileEventType; import appeng.tile.inventory.AppEngInternalAEInventory; import appeng.util.Platform; import appeng.util.SettingsFrom; public class AEBaseTile extends TileEntity implements IOrientable, ICommonTile, ICustomNameObject { static private final HashMap>> handlers = new HashMap>>(); static private final HashMap myItem = new HashMap(); private ForgeDirection forward = ForgeDirection.UNKNOWN; private ForgeDirection up = ForgeDirection.UNKNOWN; public static ThreadLocal> dropNoItems = new ThreadLocal(); public void disableDrops() { dropNoItems.set( new WeakReference( this ) ); } public boolean dropItems() { WeakReference what = dropNoItems.get(); return what == null || what.get() != this; } public int renderFragment = 0; public String customName; public boolean notLoaded() { return !worldObj.blockExists( xCoord, yCoord, zCoord ); } public TileEntity getTile() { return this; } static public void registerTileItem(Class c, ItemStackSrc wat) { myItem.put( c, wat ); } protected ItemStack getItemFromTile(Object obj) { ItemStackSrc src = myItem.get( obj.getClass() ); if ( src == null ) return null; return src.stack( 1 ); } protected boolean hasHandlerFor(TileEventType type) { List list = getHandlerListFor( type ); return list != null && !list.isEmpty(); } protected List getHandlerListFor(TileEventType type) { Class clz = getClass(); EnumMap> handlerSet = handlers.get( clz ); if ( handlerSet == null ) { handlers.put( clz, handlerSet = new EnumMap>( TileEventType.class ) ); for (Method m : clz.getMethods()) { TileEvent te = m.getAnnotation( TileEvent.class ); if ( te != null ) { addHandler( handlerSet, te.value(), m ); } } } List list = handlerSet.get( type ); if ( list == null ) handlerSet.put( type, list = new LinkedList() ); return list; } private void addHandler(EnumMap> handlerSet, TileEventType value, Method m) { List list = handlerSet.get( value ); if ( list == null ) handlerSet.put( value, list = new ArrayList() ); list.add( new AETileEventHandler( m, value ) ); } @Override final public boolean canUpdate() { return hasHandlerFor( TileEventType.TICK ); } final public void Tick() { } @Override final public void updateEntity() { for (AETileEventHandler h : getHandlerListFor( TileEventType.TICK )) h.Tick( this ); } @Override public void onChunkUnload() { if ( !isInvalid() ) invalidate(); } /** * for dormant chunk cache. */ public void onChunkLoad() { if ( isInvalid() ) validate(); } @Override // NOTE: WAS FINAL, changed for Immibis final public void writeToNBT(NBTTagCompound data) { super.writeToNBT( data ); if ( canBeRotated() ) { data.setString( "orientation_forward", forward.name() ); data.setString( "orientation_up", up.name() ); } if ( customName != null ) data.setString( "customName", customName ); for (AETileEventHandler h : getHandlerListFor( TileEventType.WORLD_NBT_WRITE )) h.writeToNBT( this, data ); } @Override // NOTE: WAS FINAL, changed for Immibis final public void readFromNBT(NBTTagCompound data) { super.readFromNBT( data ); if ( data.hasKey( "customName" ) ) customName = data.getString( "customName" ); else customName = null; try { if ( canBeRotated() ) { forward = ForgeDirection.valueOf( data.getString( "orientation_forward" ) ); up = ForgeDirection.valueOf( data.getString( "orientation_up" ) ); } } catch (IllegalArgumentException iae) { } for (AETileEventHandler h : getHandlerListFor( TileEventType.WORLD_NBT_READ )) { h.readFromNBT( this, data ); } } final public void writeToStream(ByteBuf data) { try { if ( canBeRotated() ) { byte orientation = (byte) ((up.ordinal() << 3) | forward.ordinal()); data.writeByte( orientation ); } for (AETileEventHandler h : getHandlerListFor( TileEventType.NETWORK_WRITE )) h.writeToStream( this, data ); } catch (Throwable t) { AELog.error( t ); } } final public boolean readFromStream(ByteBuf data) { boolean output = false; try { if ( canBeRotated() ) { ForgeDirection old_Forward = forward; ForgeDirection old_Up = up; byte orientation = data.readByte(); forward = ForgeDirection.getOrientation( orientation & 0x7 ); up = ForgeDirection.getOrientation( orientation >> 3 ); output = !forward.equals( old_Forward ) || !up.equals( old_Up ); } renderFragment = 100; for (AETileEventHandler h : getHandlerListFor( TileEventType.NETWORK_READ )) if ( h.readFromStream( this, data ) ) output = true; if ( (renderFragment & 1) == 1 ) output = true; renderFragment = 0; } catch (Throwable t) { AELog.error( t ); } return output; } /** * By default all blocks can have orientation, this handles saving, and loading, as well as synchronization. * * @return */ @Override public boolean canBeRotated() { return true; } @Override public ForgeDirection getForward() { return forward; } @Override public ForgeDirection getUp() { return up; } @Override public void setOrientation(ForgeDirection inForward, ForgeDirection inUp) { forward = inForward; up = inUp; markForUpdate(); Platform.notifyBlocksOfNeighbors( worldObj, xCoord, yCoord, zCoord ); } public void onPlacement(ItemStack stack, EntityPlayer player, int side) { if ( stack.hasTagCompound() ) { uploadSettings( SettingsFrom.DISMANTLE_ITEM, stack.getTagCompound() ); } } @Override public Packet getDescriptionPacket() { NBTTagCompound data = new NBTTagCompound(); ByteBuf stream = Unpooled.buffer(); try { writeToStream( stream ); if ( stream.readableBytes() == 0 ) return null; } catch (Throwable t) { AELog.error( t ); } stream.capacity( stream.readableBytes() ); data.setByteArray( "X", stream.array() ); return new S35PacketUpdateTileEntity( xCoord, yCoord, zCoord, 64, data ); } @Override public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { // / pkt.actionType if ( pkt.func_148853_f() == 64 ) { ByteBuf stream = Unpooled.copiedBuffer( pkt.func_148857_g().getByteArray( "X" ) ); if ( readFromStream( stream ) ) markForUpdate(); } } public void markForUpdate() { if ( renderFragment > 0 ) renderFragment = renderFragment | 1; else { // TODO: Optimize Network Load if ( worldObj != null ) { AELog.blockUpdate( xCoord, yCoord, zCoord, this ); worldObj.markBlockForUpdate( xCoord, yCoord, zCoord ); } } } /** * returns the contents of the tile entity, into the world, defaults to dropping everything in the inventory. * * @param w * @param x * @param y * @param z * @param drops */ @Override public void getDrops(World w, int x, int y, int z, ArrayList drops) { if ( this instanceof IInventory ) { IInventory inv = (IInventory) this; for (int l = 0; l < inv.getSizeInventory(); l++) { ItemStack is = inv.getStackInSlot( l ); if ( is != null ) drops.add( is ); } } } public void getNoDrops(World w, int x, int y, int z, ArrayList drops) { } public void onReady() { } /** * depending on the from, different settings will be accepted, don't call this with null * * @param from * @param compound */ public void uploadSettings(SettingsFrom from, NBTTagCompound compound) { if ( compound != null && this instanceof IConfigurableObject ) { IConfigManager cm = ((IConfigurableObject) this).getConfigManager(); if ( cm != null ) cm.readFromNBT( compound ); } if ( this instanceof IPriorityHost ) { IPriorityHost pHost = (IPriorityHost) this; pHost.setPriority( compound.getInteger( "priority" ) ); } if ( this instanceof ISegmentedInventory ) { IInventory inv = ((ISegmentedInventory) this).getInventoryByName( "config" ); if ( inv != null && inv instanceof AppEngInternalAEInventory ) { AppEngInternalAEInventory target = (AppEngInternalAEInventory) inv; AppEngInternalAEInventory tmp = new AppEngInternalAEInventory( null, target.getSizeInventory() ); tmp.readFromNBT( compound, "config" ); for (int x = 0; x < tmp.getSizeInventory(); x++) target.setInventorySlotContents( x, tmp.getStackInSlot( x ) ); } } } /** * null means nothing to store... * * @param from * @return */ public NBTTagCompound downloadSettings(SettingsFrom from) { NBTTagCompound output = new NBTTagCompound(); if ( hasCustomName() ) { NBTTagCompound dsp = new NBTTagCompound(); dsp.setString( "Name", getCustomName() ); output.setTag( "display", dsp ); } if ( this instanceof IConfigurableObject ) { IConfigManager cm = ((IConfigurableObject) this).getConfigManager(); if ( cm != null ) cm.writeToNBT( output ); } if ( this instanceof IPriorityHost ) { IPriorityHost pHost = (IPriorityHost) this; output.setInteger( "priority", pHost.getPriority() ); } if ( this instanceof ISegmentedInventory ) { IInventory inv = ((ISegmentedInventory) this).getInventoryByName( "config" ); if ( inv != null && inv instanceof AppEngInternalAEInventory ) { ((AppEngInternalAEInventory) inv).writeToNBT( output, "config" ); } } return output.hasNoTags() ? null : output; } public void securityBreak() { worldObj.func_147480_a( xCoord, yCoord, zCoord, true ); disableDrops(); } public void saveChanges() { super.markDirty(); } public boolean requiresTESR() { return false; } public void setName(String name) { this.customName = name; } @Override public String getCustomName() { return hasCustomName() ? customName : getClass().getSimpleName(); } @Override public boolean hasCustomName() { return customName != null && customName.length() > 0; } }