package com.rwtema.funkylocomotion.blocks; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.rwtema.funkylocomotion.FunkyLocomotion; import com.rwtema.funkylocomotion.helper.BlockHelper; import com.rwtema.funkylocomotion.helper.WeakSet; import com.rwtema.funkylocomotion.movers.MoveManager; import com.rwtema.funkylocomotion.particles.ObstructionHelper; import net.minecraft.block.BlockDirectional; import net.minecraft.block.state.IBlockState; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.network.NetworkManager; import net.minecraft.network.play.server.SPacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.DimensionManager; import gnu.trove.map.hash.TLongObjectHashMap; public class TileTeleport extends TilePusher { static TLongObjectHashMap<WeakSet<TileTeleport>> cache = new TLongObjectHashMap<>(); int teleportId; @Override public List<BlockPos> getBlocks(World world, BlockPos home, EnumFacing dir, boolean push) { BlockPos advance = home.offset(dir); if (BlockHelper.canStick(world, advance, dir.getOpposite(), profile)) return getBlocks(world, home, advance, null); return null; } @Override public List<BlockPos> checkPositions(World srcWorld, EnumFacing moveDir, ArrayList<BlockPos> posList, HashSet<BlockPos> posSet) { TileTeleport tile = getTileTeleport(); if (tile == null) return null; World dstWorld = tile.getWorld(); boolean fail = false; for (BlockPos pos : posList) { BlockPos adv = getDestinationPos(tile, pos); if ((dstWorld == srcWorld && posSet.contains(adv)) || !BlockHelper.canReplace(dstWorld, adv)) { if (!ObstructionHelper.sendObstructionPacket(srcWorld, pos, moveDir)) return null; fail = true; } } return fail ? null : posList; } private TileTeleport getTileTeleport() { if (teleportId == 0) return null; WeakSet<TileTeleport> tileTeleports = cache.get(teleportId); if (tileTeleports == null) return null; // should never happen for (Iterator<TileTeleport> iterator = tileTeleports.iterator(); iterator.hasNext(); ) { TileTeleport tile = iterator.next(); if (tile.isInvalid()) iterator.remove(); if (tile == this || !tile.hasWorld()) continue; World world = tile.getWorld(); if (world == null || world.isRemote) iterator.remove(); if (DimensionManager.getWorld(world.provider.getDimension()) != world) { iterator.remove(); } if (!world.isBlockLoaded(tile.getPos())) continue; return tile; } return null; } private BlockPos getDestinationPos(TileTeleport tile, BlockPos pos) { BlockPos srcPos = this.pos.offset(EnumFacing.values()[getBlockMetadata()]); BlockPos dstPos = tile.pos.offset(EnumFacing.values()[tile.getBlockMetadata()]); return new BlockPos( pos.getX() - srcPos.getX() + dstPos.getX(), pos.getY() - srcPos.getY() + dstPos.getY(), pos.getZ() - srcPos.getZ() + dstPos.getZ() ); } public void startMoving() { cooldown = -1; TileTeleport tileTeleport = getTileTeleport(); if (tileTeleport == null) return; int meta = getBlockMetadata(); EnumFacing dir = EnumFacing.values()[meta % 6]; boolean push = meta < 6; if (dir == null) return; List<BlockPos> posList = getBlocks(getWorld(), pos, dir, push); if (posList != null) { final int energy = posList.size() * powerPerTile; if (this.energy.extractEnergy(energy, true) != energy) { ObstructionHelper.sendObstructionPacket(getWorld(), this.pos, null); return; } if (tileTeleport.energy.extractEnergy(energy, true) != energy) return; ArrayList<TileBooster> boosters = new ArrayList<>(6); for (EnumFacing d : EnumFacing.values()) { if (d != dir) { BlockPos p = pos.offset(d); IBlockState state = getWorld().getBlockState(p); if (state.getBlock() == FunkyLocomotion.booster) { if (state.getValue(BlockDirectional.FACING) != d.getOpposite()) continue; TileEntity tile = BlockHelper.getTile(getWorld(), p); if (tile instanceof TileBooster) { TileBooster booster = (TileBooster) tile; if (booster.energy.extractEnergy(energy, true) != energy) continue; boosters.add(booster); } } } } if (!boosters.isEmpty()) { for (TileBooster booster : boosters) { booster.energy.extractEnergy(energy, false); } } this.energy.extractEnergy(energy, false); tileTeleport.energy.extractEnergy(energy, false); ArrayList<MoveManager.BlockLink> links = new ArrayList<>(posList.size()); for (BlockPos blockPos : posList) { links.add(new MoveManager.BlockLink(blockPos, getDestinationPos(tileTeleport, blockPos))); } MoveManager.startMoving(getWorld(), tileTeleport.getWorld(), links, null, moveTime[boosters.size()] * 2); } } @Override public void invalidate() { super.invalidate(); unCache(); } @Override public void onChunkUnload() { super.onChunkUnload(); unCache(); } private void unCache() { if (teleportId != 0 && (getWorld() == null || !getWorld().isRemote)) { WeakSet<TileTeleport> tileTeleports = cache.get(teleportId); if (tileTeleports != null) { tileTeleports.remove(this); } } } @Override public void validate() { super.validate(); if (teleportId != 0 && (getWorld() == null || !getWorld().isRemote)) { WeakSet<TileTeleport> tileTeleports = cache.get(teleportId); if (tileTeleports == null) { tileTeleports = new WeakSet<>(); cache.put(teleportId, tileTeleports); } tileTeleports.add(this); } } @Override public void readFromNBT(NBTTagCompound tag) { super.readFromNBT(tag); teleportId = tag.getInteger("ID"); } @Nonnull @Override public NBTTagCompound writeToNBT(NBTTagCompound tag) { super.writeToNBT(tag); tag.setInteger("ID", teleportId); return tag; } @Nonnull @Override public NBTTagCompound getUpdateTag() { NBTTagCompound tag = super.getUpdateTag(); tag.setInteger("ID", teleportId); return tag; } @Override public void handleUpdateTag(@Nonnull NBTTagCompound tag) { teleportId = tag.getInteger("ID"); } @Nullable @Override public SPacketUpdateTileEntity getUpdatePacket() { return new SPacketUpdateTileEntity(this.pos, 0, this.getUpdateTag()); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { handleUpdateTag(pkt.getNbtCompound()); } }