Applied-Energistics-2-tiler.../src/main/java/appeng/tile/AEBaseTile.java

645 lines
15 KiB
Java
Raw Normal View History

2014-11-14 12:02:52 +01:00
/*
* This file is part of Applied Energistics 2.
* Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
*
* Applied Energistics 2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Applied Energistics 2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Applied Energistics 2. If not, see <http://www.gnu.org/licenses/lgpl>.
*/
2014-09-24 02:26:27 +02:00
package appeng.tile;
2014-09-24 02:26:27 +02:00
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 java.util.Map;
2015-05-09 13:06:09 +02:00
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
2014-09-24 02:26:27 +02:00
2015-12-24 02:07:03 +01:00
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
2015-06-16 02:44:59 +02:00
import net.minecraft.block.state.IBlockState;
2014-09-24 02:26:27 +02:00
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.play.server.SPacketUpdateTileEntity;
2014-09-24 02:26:27 +02:00
import net.minecraft.tileentity.TileEntity;
2015-06-16 02:44:59 +02:00
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
2014-09-24 02:26:27 +02:00
import net.minecraft.world.World;
2015-12-24 02:07:03 +01:00
2014-09-24 02:26:27 +02:00
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.IStackSrc;
2014-09-24 02:26:27 +02:00
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
2014-09-24 02:26:27 +02:00
{
private static final ThreadLocal<WeakReference<AEBaseTile>> DROP_NO_ITEMS = new ThreadLocal<WeakReference<AEBaseTile>>();
private static final Map<Class<? extends AEBaseTile>, Map<TileEventType, List<AETileEventHandler>>> HANDLERS = new HashMap<Class<? extends AEBaseTile>, Map<TileEventType, List<AETileEventHandler>>>();
private static final Map<Class<? extends TileEntity>, IStackSrc> ITEM_STACKS = new HashMap<>();
private int renderFragment = 0;
@Nullable
private String customName;
2015-06-16 02:44:59 +02:00
private EnumFacing forward = null;
private EnumFacing up = null;
2014-09-24 02:26:27 +02:00
private IBlockState state;
2015-06-16 02:44:59 +02:00
@Override
public boolean shouldRefresh( final World world, final BlockPos pos, final IBlockState oldState, final IBlockState newSate )
2015-06-16 02:44:59 +02:00
{
return newSate.getBlock() != oldState.getBlock(); // state dosn't change tile entities in AE2.
}
2015-09-30 14:24:40 +02:00
public static void registerTileItem( final Class<? extends TileEntity> c, final IStackSrc wat )
2014-09-24 02:26:27 +02:00
{
ITEM_STACKS.put( c, wat );
2014-09-24 02:26:27 +02:00
}
public boolean dropItems()
{
2015-09-30 14:24:40 +02:00
final WeakReference<AEBaseTile> what = DROP_NO_ITEMS.get();
2014-09-24 02:26:27 +02:00
return what == null || what.get() != this;
}
public boolean notLoaded()
{
2016-12-08 12:42:00 +01:00
return !this.world.isBlockLoaded( this.pos );
2014-09-24 02:26:27 +02:00
}
@Nonnull
2014-09-24 02:26:27 +02:00
public TileEntity getTile()
{
return this;
}
@Nullable
2015-09-30 14:24:40 +02:00
protected ItemStack getItemFromTile( final Object obj )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IStackSrc src = ITEM_STACKS.get( obj.getClass() );
if( src == null )
2015-04-29 02:30:53 +02:00
{
return ItemStack.EMPTY;
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
return src.stack( 1 );
}
@Nonnull
public IBlockState getBlockState()
{
if( state == null )
{
2016-12-08 12:42:00 +01:00
state = world.getBlockState( getPos() );
}
return state;
}
/**
* for dormant chunk cache.
*/
public void onChunkLoad()
2014-09-24 02:26:27 +02:00
{
if( this.isInvalid() )
2015-04-29 02:30:53 +02:00
{
this.validate();
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
@Override
// NOTE: WAS FINAL, changed for Immibis
2015-09-30 14:24:40 +02:00
public final void readFromNBT( final NBTTagCompound data )
2014-09-24 02:26:27 +02:00
{
super.readFromNBT( data );
2014-09-24 02:26:27 +02:00
if( data.hasKey( "customName" ) )
2015-04-29 02:30:53 +02:00
{
this.customName = data.getString( "customName" );
2015-04-29 02:30:53 +02:00
}
else
2015-04-29 02:30:53 +02:00
{
this.customName = null;
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
try
{
if( this.canBeRotated() )
{
2015-06-16 02:44:59 +02:00
this.forward = EnumFacing.valueOf( data.getString( "forward" ) );
this.up = EnumFacing.valueOf( data.getString( "up" ) );
}
}
2015-09-30 14:24:40 +02:00
catch( final IllegalArgumentException ignored )
{
}
2014-09-24 02:26:27 +02:00
2015-09-30 14:24:40 +02:00
for( final AETileEventHandler h : this.getHandlerListFor( TileEventType.WORLD_NBT_READ ) )
{
h.readFromNBT( this, data );
}
2014-09-24 02:26:27 +02:00
}
@Override
// NOTE: WAS FINAL, changed for Immibis
public final NBTTagCompound writeToNBT( final NBTTagCompound data )
2014-09-24 02:26:27 +02:00
{
super.writeToNBT( data );
if( this.canBeRotated() )
2014-09-24 02:26:27 +02:00
{
data.setString( "forward", this.getForward().name() );
data.setString( "up", this.getUp().name() );
2014-09-24 02:26:27 +02:00
}
if( this.customName != null )
2015-04-29 02:30:53 +02:00
{
2014-12-29 15:13:47 +01:00
data.setString( "customName", this.customName );
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
2015-09-30 14:24:40 +02:00
for( final AETileEventHandler h : this.getHandlerListFor( TileEventType.WORLD_NBT_WRITE ) )
2015-04-29 02:30:53 +02:00
{
2014-09-24 02:26:27 +02:00
h.writeToNBT( this, data );
2015-04-29 02:30:53 +02:00
}
return data;
2014-09-24 02:26:27 +02:00
}
@Override
public SPacketUpdateTileEntity getUpdatePacket()
{
return new SPacketUpdateTileEntity( this.pos, 64, getUpdateTag() );
}
private boolean hasHandlerFor( final TileEventType type )
{
final List<AETileEventHandler> list = this.getHandlerListFor( type );
return !list.isEmpty();
}
@Override
public void onDataPacket( final NetworkManager net, final SPacketUpdateTileEntity pkt )
{
// / pkt.actionType
if( pkt.getTileEntityType() == 64 )
{
handleUpdateTag( pkt.getNbtCompound() );
}
}
@Override
public void onChunkUnload()
{
if( !this.isInvalid() )
{
this.invalidate();
}
}
/**
* This builds a tag with the actual data that should be sent to the client for update syncs.
* If the tile entity doesn't need update syncs, it returns null.
*/
private NBTTagCompound writeUpdateData()
{
2015-09-30 14:24:40 +02:00
final NBTTagCompound data = new NBTTagCompound();
2015-09-30 14:24:40 +02:00
final ByteBuf stream = Unpooled.buffer();
2014-09-24 02:26:27 +02:00
try
{
this.writeToStream( stream );
if( stream.readableBytes() == 0 )
2015-04-29 02:30:53 +02:00
{
return null;
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
2015-09-30 14:24:40 +02:00
catch( final Throwable t )
2014-09-24 02:26:27 +02:00
{
AELog.debug( t );
2014-09-24 02:26:27 +02:00
}
stream.capacity( stream.readableBytes() );
data.setByteArray( "X", stream.array() );
return data;
}
/**
* Handles tile entites that are being sent to the client as part of a full chunk.
*/
@Override
public NBTTagCompound getUpdateTag()
{
final NBTTagCompound data = writeUpdateData();
if( data == null )
{
return new NBTTagCompound();
2014-09-24 02:26:27 +02:00
}
data.setInteger( "x", pos.getX() );
data.setInteger( "y", pos.getY() );
data.setInteger( "z", pos.getZ() );
return data;
2014-09-24 02:26:27 +02:00
}
/**
* Handles tile entites that are being received by the client as part of a full chunk.
*/
@Override
public void handleUpdateTag( NBTTagCompound tag )
{
final ByteBuf stream = Unpooled.copiedBuffer( tag.getByteArray( "X" ) );
if( this.readFromStream( stream ) )
2015-04-29 02:30:53 +02:00
{
this.markForUpdate();
2015-04-29 02:30:53 +02:00
}
}
private final boolean readFromStream( final ByteBuf data )
2014-09-24 02:26:27 +02:00
{
boolean output = false;
try
{
if( this.canBeRotated() )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final EnumFacing old_Forward = this.forward;
final EnumFacing old_Up = this.up;
2014-09-24 02:26:27 +02:00
2015-09-30 14:24:40 +02:00
final byte orientation = data.readByte();
this.forward = EnumFacing.VALUES[orientation & 0x7];
this.up = EnumFacing.VALUES[orientation >> 3];
2014-09-24 02:26:27 +02:00
output = this.forward != old_Forward || this.up != old_Up;
2014-09-24 02:26:27 +02:00
}
2014-12-29 15:13:47 +01:00
this.renderFragment = 100;
2015-09-30 14:24:40 +02:00
for( final AETileEventHandler h : this.getHandlerListFor( TileEventType.NETWORK_READ ) )
2015-04-29 02:30:53 +02:00
{
if( h.readFromStream( this, data ) )
2015-04-29 02:30:53 +02:00
{
2014-09-24 02:26:27 +02:00
output = true;
2015-04-29 02:30:53 +02:00
}
}
2014-09-24 02:26:27 +02:00
if( ( this.renderFragment & 1 ) == 1 )
2015-04-29 02:30:53 +02:00
{
2014-09-24 02:26:27 +02:00
output = true;
2015-04-29 02:30:53 +02:00
}
2014-12-29 15:13:47 +01:00
this.renderFragment = 0;
2014-09-24 02:26:27 +02:00
}
2015-09-30 14:24:40 +02:00
catch( final Throwable t )
2014-09-24 02:26:27 +02:00
{
AELog.debug( t );
2014-09-24 02:26:27 +02:00
}
return output;
}
public void markForUpdate()
{
if( this.renderFragment > 0 )
2015-04-29 02:30:53 +02:00
{
this.renderFragment |= 1;
2015-04-29 02:30:53 +02:00
}
else
{
// TODO: Optimize Network Load
2016-12-08 12:42:00 +01:00
if( this.world != null )
{
2015-09-30 14:26:54 +02:00
AELog.blockUpdate( this.pos, this );
2016-12-08 12:42:00 +01:00
this.world.notifyBlockUpdate( this.pos, getBlockState(), getBlockState(), 3 );
}
}
}
private final void writeToStream( final ByteBuf data )
{
try
{
if( this.canBeRotated() )
{
2015-09-30 14:24:40 +02:00
final byte orientation = (byte) ( ( this.up.ordinal() << 3 ) | this.forward.ordinal() );
data.writeByte( orientation );
}
2015-09-30 14:24:40 +02:00
for( final AETileEventHandler h : this.getHandlerListFor( TileEventType.NETWORK_WRITE ) )
2015-04-29 02:30:53 +02:00
{
h.writeToStream( this, data );
2015-04-29 02:30:53 +02:00
}
}
2015-09-30 14:24:40 +02:00
catch( final Throwable t )
{
AELog.debug( t );
}
}
2014-09-24 02:26:27 +02:00
/**
* By default all blocks can have orientation, this handles saving, and loading, as well as synchronization.
2015-02-03 12:04:13 +01:00
*
2014-09-27 23:17:47 +02:00
* @return true if tile can be rotated
2014-09-24 02:26:27 +02:00
*/
@Override
public boolean canBeRotated()
{
return true;
}
@Nonnull
2015-09-30 14:24:40 +02:00
private List<AETileEventHandler> getHandlerListFor( final TileEventType type )
{
final Map<TileEventType, List<AETileEventHandler>> eventToHandlers = this.getEventToHandlers();
final List<AETileEventHandler> handlers = this.getHandlers( eventToHandlers, type );
return handlers;
}
@Nonnull
private Map<TileEventType, List<AETileEventHandler>> getEventToHandlers()
{
final Class<? extends AEBaseTile> clazz = this.getClass();
final Map<TileEventType, List<AETileEventHandler>> storedHandlers = HANDLERS.get( clazz );
if( storedHandlers == null )
{
final Map<TileEventType, List<AETileEventHandler>> newStoredHandlers = new EnumMap<TileEventType, List<AETileEventHandler>>( TileEventType.class );
HANDLERS.put( clazz, newStoredHandlers );
2015-09-30 14:24:40 +02:00
for( final Method method : clazz.getMethods() )
{
2015-09-30 14:24:40 +02:00
final TileEvent event = method.getAnnotation( TileEvent.class );
if( event != null )
{
this.addHandler( newStoredHandlers, event.value(), method );
}
}
return newStoredHandlers;
}
else
{
return storedHandlers;
}
}
@Nonnull
private List<AETileEventHandler> getHandlers( final Map<TileEventType, List<AETileEventHandler>> eventToHandlers, final TileEventType event )
{
final List<AETileEventHandler> oldHandlers = eventToHandlers.get( event );
if( oldHandlers == null )
2015-04-29 02:30:53 +02:00
{
final List<AETileEventHandler> newHandlers = new LinkedList<AETileEventHandler>();
eventToHandlers.put( event, newHandlers );
return newHandlers;
}
else
{
return oldHandlers;
}
}
2015-09-30 14:24:40 +02:00
private void addHandler( final Map<TileEventType, List<AETileEventHandler>> handlerSet, final TileEventType value, final Method m )
{
List<AETileEventHandler> list = handlerSet.get( value );
if( list == null )
2015-04-29 02:30:53 +02:00
{
list = new ArrayList<AETileEventHandler>();
handlerSet.put( value, list );
2015-04-29 02:30:53 +02:00
}
list.add( new AETileEventHandler( m ) );
}
2014-09-24 02:26:27 +02:00
@Override
2015-06-16 02:44:59 +02:00
public EnumFacing getForward()
2014-09-24 02:26:27 +02:00
{
2016-01-01 01:50:28 +01:00
if( this.forward == null )
2016-01-01 01:49:05 +01:00
{
2016-01-01 01:48:15 +01:00
return EnumFacing.NORTH;
2016-01-01 01:49:05 +01:00
}
2014-12-29 15:13:47 +01:00
return this.forward;
2014-09-24 02:26:27 +02:00
}
@Override
2015-06-16 02:44:59 +02:00
public EnumFacing getUp()
2014-09-24 02:26:27 +02:00
{
2016-01-01 01:50:28 +01:00
if( this.up == null )
2016-01-01 01:49:05 +01:00
{
2016-01-01 01:48:15 +01:00
return EnumFacing.UP;
2016-01-01 01:49:05 +01:00
}
2014-12-29 15:13:47 +01:00
return this.up;
2014-09-24 02:26:27 +02:00
}
@Override
2015-09-30 14:24:40 +02:00
public void setOrientation( final EnumFacing inForward, final EnumFacing inUp )
2014-09-24 02:26:27 +02:00
{
2014-12-29 15:13:47 +01:00
this.forward = inForward;
this.up = inUp;
this.markForUpdate();
2016-12-08 12:42:00 +01:00
Platform.notifyBlocksOfNeighbors( this.world, this.pos );
2014-09-24 02:26:27 +02:00
}
2015-09-30 14:24:40 +02:00
public void onPlacement( final ItemStack stack, final EntityPlayer player, final EnumFacing side )
2014-09-24 02:26:27 +02:00
{
if( stack.hasTagCompound() )
2014-09-24 02:26:27 +02:00
{
2014-12-29 15:13:47 +01:00
this.uploadSettings( SettingsFrom.DISMANTLE_ITEM, stack.getTagCompound() );
2014-09-24 02:26:27 +02:00
}
}
/**
* depending on the from, different settings will be accepted, don't call this with null
*
* @param from source of settings
* @param compound compound of source
*/
2015-09-30 14:24:40 +02:00
public void uploadSettings( final SettingsFrom from, final NBTTagCompound compound )
2014-09-24 02:26:27 +02:00
{
if( compound != null && this instanceof IConfigurableObject )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IConfigManager cm = ( (IConfigurableObject) this ).getConfigManager();
if( cm != null )
2015-04-29 02:30:53 +02:00
{
cm.readFromNBT( compound );
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
if( this instanceof IPriorityHost )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IPriorityHost pHost = (IPriorityHost) this;
pHost.setPriority( compound.getInteger( "priority" ) );
2014-09-24 02:26:27 +02:00
}
if( this instanceof ISegmentedInventory )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IInventory inv = ( (ISegmentedInventory) this ).getInventoryByName( "config" );
if( inv instanceof AppEngInternalAEInventory )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final AppEngInternalAEInventory target = (AppEngInternalAEInventory) inv;
final AppEngInternalAEInventory tmp = new AppEngInternalAEInventory( null, target.getSizeInventory() );
tmp.readFromNBT( compound, "config" );
for( int x = 0; x < tmp.getSizeInventory(); x++ )
2015-04-29 02:30:53 +02:00
{
target.setInventorySlotContents( x, tmp.getStackInSlot( x ) );
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
}
}
/**
* returns the contents of the tile entity, into the world, defaults to dropping everything in the inventory.
2015-02-03 12:04:13 +01:00
*
* @param w world
* @param x x pos of tile entity
* @param y y pos of tile entity
* @param z z pos of tile entity
2014-09-27 23:17:47 +02:00
* @param drops drops of tile entity
2014-09-24 02:26:27 +02:00
*/
@Override
2015-09-30 14:24:40 +02:00
public void getDrops( final World w, final BlockPos pos, final List<ItemStack> drops )
2014-09-24 02:26:27 +02:00
{
if( this instanceof IInventory )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IInventory inv = (IInventory) this;
2014-09-24 02:26:27 +02:00
for( int l = 0; l < inv.getSizeInventory(); l++ )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final ItemStack is = inv.getStackInSlot( l );
if( is != null )
2015-04-29 02:30:53 +02:00
{
2014-09-24 02:26:27 +02:00
drops.add( is );
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
}
}
2015-09-30 14:24:40 +02:00
public void getNoDrops( final World w, final BlockPos pos, final List<ItemStack> drops )
2014-09-24 02:26:27 +02:00
{
}
public void onReady()
{
}
/**
* null means nothing to store...
2015-02-03 12:04:13 +01:00
*
2014-09-27 23:17:47 +02:00
* @param from source of settings
*
2014-09-27 23:17:47 +02:00
* @return compound of source
2014-09-24 02:26:27 +02:00
*/
2015-09-30 14:24:40 +02:00
public NBTTagCompound downloadSettings( final SettingsFrom from )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final NBTTagCompound output = new NBTTagCompound();
2014-09-24 02:26:27 +02:00
if( this.hasCustomName() )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final NBTTagCompound dsp = new NBTTagCompound();
2014-12-29 15:13:47 +01:00
dsp.setString( "Name", this.getCustomName() );
2014-09-24 02:26:27 +02:00
output.setTag( "display", dsp );
}
if( this instanceof IConfigurableObject )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IConfigManager cm = ( (IConfigurableObject) this ).getConfigManager();
if( cm != null )
2015-04-29 02:30:53 +02:00
{
2014-09-24 02:26:27 +02:00
cm.writeToNBT( output );
2015-04-29 02:30:53 +02:00
}
2014-09-24 02:26:27 +02:00
}
if( this instanceof IPriorityHost )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IPriorityHost pHost = (IPriorityHost) this;
2014-09-24 02:26:27 +02:00
output.setInteger( "priority", pHost.getPriority() );
}
if( this instanceof ISegmentedInventory )
2014-09-24 02:26:27 +02:00
{
2015-09-30 14:24:40 +02:00
final IInventory inv = ( (ISegmentedInventory) this ).getInventoryByName( "config" );
if( inv instanceof AppEngInternalAEInventory )
2014-09-24 02:26:27 +02:00
{
( (AppEngInternalAEInventory) inv ).writeToNBT( output, "config" );
2014-09-24 02:26:27 +02:00
}
}
return output.hasNoTags() ? null : output;
}
@Override
public String getCustomName()
{
return this.hasCustomName() ? this.customName : this.getClass().getSimpleName();
}
@Override
public boolean hasCustomName()
{
return this.customName != null && this.customName.length() > 0;
}
2014-09-24 02:26:27 +02:00
public void securityBreak()
{
2016-12-08 12:42:00 +01:00
this.world.destroyBlock( this.pos, true );
2014-12-29 15:13:47 +01:00
this.disableDrops();
2014-09-24 02:26:27 +02:00
}
public void disableDrops()
{
DROP_NO_ITEMS.set( new WeakReference<AEBaseTile>( this ) );
}
2014-09-24 02:26:27 +02:00
public void saveChanges()
{
super.markDirty();
}
public boolean requiresTESR()
{
return false;
}
2015-09-30 14:24:40 +02:00
public void setName( final String name )
2014-09-24 02:26:27 +02:00
{
this.customName = name;
}
}