package com.skcraft.playblock.projector; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; import java.io.IOException; import java.util.List; import li.cil.oc.api.machine.Arguments; import li.cil.oc.api.machine.Callback; import li.cil.oc.api.machine.Context; import li.cil.oc.api.network.SimpleComponent; import net.minecraft.entity.player.EntityPlayer; 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.util.AxisAlignedBB; import org.apache.logging.log4j.Level; import com.sk89q.forge.BehaviorList; import com.sk89q.forge.BehaviorListener; import com.sk89q.forge.BehaviorPayload; import com.sk89q.forge.PayloadReceiver; import com.sk89q.forge.TileEntityPayload; import com.skcraft.playblock.PacketHandler; import com.skcraft.playblock.PlayBlock; import com.skcraft.playblock.SharedRuntime; import com.skcraft.playblock.network.PlayBlockPayload; import com.skcraft.playblock.player.MediaPlayer; import com.skcraft.playblock.player.MediaPlayerClient; import com.skcraft.playblock.player.MediaPlayerHost; import com.skcraft.playblock.queue.ExposedQueue; import com.skcraft.playblock.queue.QueueBehavior; import com.skcraft.playblock.util.AccessList; import com.skcraft.playblock.util.DoubleThresholdRange; import com.skcraft.playblock.util.DoubleThresholdRange.RangeTest; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.Optional; import cpw.mods.fml.common.network.ByteBufUtils; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.network.internal.FMLProxyPacket; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; /** * The tile entity for the projector block. */ @Optional.Interface(iface = "li.cil.oc.api.network.SimpleComponent", modid = "OpenComputers") public class TileEntityProjector extends TileEntity implements BehaviorListener, PayloadReceiver, ExposedQueue, SimpleComponent { public static final String INTERNAL_NAME = "PlayBlockProjector"; private final BehaviorList behaviors = new BehaviorList(); private final MediaPlayer mediaPlayer; private final DoubleThresholdRange range; private final ProjectorOptions options; private final QueueBehavior queueBehavior; private final RangeTest rangeTest; private boolean withinRange = false; /** * Construct a new instance of the projector tile entity. */ public TileEntityProjector() { behaviors.addBehaviorListener(this); behaviors.add(range = new DoubleThresholdRange()); Side side = FMLCommonHandler.instance().getEffectiveSide(); if (side == Side.CLIENT) { behaviors.add(mediaPlayer = new MediaPlayerClient()); behaviors.add(queueBehavior = new QueueBehavior(null)); rangeTest = range.createRangeTest(); } else { behaviors.add(mediaPlayer = new MediaPlayerHost()); behaviors.add(queueBehavior = new QueueBehavior((MediaPlayerHost) mediaPlayer)); rangeTest = null; } behaviors.add(options = new ProjectorOptions(mediaPlayer, range)); if (side != Side.CLIENT) { options.useAccessList(true); } } /** * Get the behaviors of this projector. * * @return the list of behaviors */ public BehaviorList getBehaviors() { return behaviors; } /** * Get the media player. * * @return the media player */ public MediaPlayer getMediaPlayer() { return mediaPlayer; } /** * Get the range manager. * * @return the range manager, null if on the server */ public DoubleThresholdRange getRange() { return range; } /** * Get the options controller. * * @return options controller */ public ProjectorOptions getOptions() { return options; } /** * Get the access list. * * @return the access list, null if on the client */ public AccessList getAccessList() { return getOptions().getAccessList(); } /** * Get the queue behavior. * * @return the queue behavior */ @Override public QueueBehavior getQueueBehavior() { return queueBehavior; } /** * Get the local player is in range. * * @return true if in range */ public boolean inRange() { if (rangeTest == null) { throw new RuntimeException("Can't do range test on server"); } return rangeTest.getCachedInRange(); } @Override public void readPayload(EntityPlayer player, ByteBufInputStream in) throws IOException { BehaviorPayload payload = new BehaviorPayload(); payload.read(in); behaviors.readPayload(player, payload, in); } @Override public Packet getDescriptionPacket() { // Client -> Server NBTTagCompound tag = new NBTTagCompound(); behaviors.writeNetworkedNBT(tag); return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 0, tag); } @Override public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity packet) { // Only called on the client NBTTagCompound tag = packet.func_148857_g(); behaviors.readNetworkedNBT(tag); } @Override public void networkedNbt(NBTTagCompound tag) { if (!this.worldObj.isRemote) { try { super.writeToNBT(tag); // Coordinates ByteBufOutputStream out = new ByteBufOutputStream(Unpooled.buffer()); out.writeByte(PlayBlockPayload.Type.TILE_ENTITY_NBT.ordinal()); ByteBufUtils.writeTag(out.buffer(), tag); FMLProxyPacket packet = new FMLProxyPacket(out.buffer(), PlayBlock.CHANNEL_ID); SharedRuntime.networkWrapper.sendToAllAround(packet, new NetworkRegistry.TargetPoint(worldObj.provider.dimensionId, xCoord, yCoord, zCoord, 250)); out.close(); } catch (IOException e) { PlayBlock.log(Level.WARN, "Failed to send tile info to players!"); } } } @Override public void payloadSend(BehaviorPayload behaviorPayload, List<EntityPlayer> players) { PlayBlockPayload payload = new PlayBlockPayload(new TileEntityPayload(this, behaviorPayload)); if (worldObj.isRemote) { PacketHandler.sendToServer(payload); } else { PacketHandler.sendToClient(payload, players); } } @Override public void writeToNBT(NBTTagCompound tag) { // Saving to disk super.writeToNBT(tag); behaviors.writeSaveNBT(tag); } @Override public void readFromNBT(NBTTagCompound tag) { // Saving to disk super.readFromNBT(tag); behaviors.readSaveNBT(tag); } @Override public void onChunkUnload() { super.onChunkUnload(); if (this.worldObj.isRemote) { ((MediaPlayerClient) mediaPlayer).release(); } } @Override public void invalidate() { super.invalidate(); if (this.worldObj.isRemote) { ((MediaPlayerClient) mediaPlayer).release(); } } @Override public boolean canUpdate() { return true; } @Override public void updateEntity() { if (this.worldObj.isRemote) { if (rangeTest.inRange(xCoord, yCoord, zCoord)) { ((MediaPlayerClient) mediaPlayer).enable(); } else { ((MediaPlayerClient) mediaPlayer).disable(); } } } @Override @SideOnly(Side.CLIENT) public AxisAlignedBB getRenderBoundingBox() { // XXX: May want to use a less expansive render AABB return INFINITE_EXTENT_AABB; } // OpenComputers compat @Override public String getComponentName() { return "pbProjector"; } @Callback @Optional.Method(modid = "OpenComputers") public Object[] getURL(Context context, Arguments args) { String uri = this.mediaPlayer.getUri(); return new Object[] { uri }; } @Callback @Optional.Method(modid = "OpenComputers") public Object[] setURL(Context context, Arguments args) { String uri = args.checkString(0); if (uri != null) { this.mediaPlayer.setUri(uri); NBTTagCompound tag = new NBTTagCompound(); this.mediaPlayer.writeNetworkedNBT(tag); this.mediaPlayer.fireNetworkedNbt(tag); } return null; } @Callback @Optional.Method(modid = "OpenComputers") public Object[] setResolution(Context context, Arguments args) { float width = (float) args.checkDouble(0); float height = (float) args.checkDouble(1); if (width > 0.0F && height > 0.0F) { this.mediaPlayer.setWidth(width); this.mediaPlayer.setHeight(height); NBTTagCompound tag = new NBTTagCompound(); this.mediaPlayer.writeNetworkedNBT(tag); this.mediaPlayer.fireNetworkedNbt(tag); } return null; } @Callback @Optional.Method(modid = "OpenComputers") public Object[] setRanges(Context context, Arguments args) { float triggerRange = (float) args.checkDouble(0); float fadeRange = (float) args.checkDouble(1); range.setTriggerRange(triggerRange); range.setFadeRange(fadeRange); NBTTagCompound tag = new NBTTagCompound(); this.range.writeNetworkedNBT(tag); this.range.fireNetworkedNbt(tag); return null; } }