package mcjty.rftools.blocks.teleporter; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import mcjty.lib.api.MachineInformation; import mcjty.lib.entity.GenericEnergyReceiverTileEntity; import mcjty.lib.network.Argument; import mcjty.lib.varia.Coordinate; import mcjty.lib.varia.GlobalCoordinate; import mcjty.lib.varia.Logging; import mcjty.rftools.blocks.dimlets.DimletConfiguration; import mcjty.rftools.dimension.DimensionStorage; import mcjty.rftools.dimension.RfToolsDimensionManager; import net.minecraft.entity.Entity; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTTagString; import net.minecraft.server.MinecraftServer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.World; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.util.Constants; import net.minecraftforge.common.util.ForgeDirection; import java.util.*; public class MatterTransmitterTileEntity extends GenericEnergyReceiverTileEntity implements MachineInformation { public static final String CMD_SETNAME = "setName"; public static final String CMD_ADDPLAYER = "addPlayer"; public static final String CMD_DELPLAYER = "delPlayer"; public static final String CMD_SETPRIVATE = "setAccess"; public static final String CMD_SETBEAM = "setBeam"; public static final String CMD_GETPLAYERS = "getPlayers"; public static final String CLIENTCMD_GETPLAYERS = "getPlayers"; private static final String[] TAGS = new String[]{"dim", "coord", "name"}; private static final String[] TAG_DESCRIPTIONS = new String[]{"The dimension this transmitter is dialed too", "The coordinate this transmitter is dialed too", "The name of the destination"}; // Server side: current dialing destination. Old system. private TeleportDestination teleportDestination = null; // Server side: current dialing destination. New system. private Integer teleportId = null; // If this is true the dial is cleared as soon as a player teleports. private boolean once = false; private String name = null; private boolean privateAccess = false; private boolean beamHidden = false; private Set<String> allowedPlayers = new HashSet<String>(); private int status = TeleportationTools.STATUS_OK; // Server side: the player we're currently teleporting. private EntityPlayer teleportingPlayer = null; private int teleportTimer = 0; private int cooldownTimer = 0; private int totalTicks; private int goodTicks; private int badTicks; private int rfPerTick = 0; private int checkReceiverStatusCounter = 20; private AxisAlignedBB beamBox = null; public MatterTransmitterTileEntity() { super(TeleportConfiguration.TRANSMITTER_MAXENERGY, TeleportConfiguration.TRANSMITTER_RECEIVEPERTICK); } public String getName() { return name == null ? "" : name; } public void setName(String name) { this.name = name; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } public boolean isPrivateAccess() { return privateAccess; } public void setPrivateAccess(boolean privateAccess) { this.privateAccess = privateAccess; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } public boolean isBeamHidden() { return beamHidden; } public void setBeamHidden(boolean b) { this.beamHidden = b; worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } public boolean isOnce() { return once; } @Override public int getTagCount() { return 3; } @Override public String getTagName(int index) { return TAGS[index]; } @Override public String getTagDescription(int index) { return TAG_DESCRIPTIONS[index]; } @Override public String getData(int index, long millis) { TeleportDestination destination = getTeleportDestination(); if (destination == null) { return "<not dialed>"; } switch (index) { case 0: return Integer.toString(destination.getDimension()); case 1: return destination.getCoordinate().toString(); case 2: return destination.getName(); } return null; } public boolean checkAccess(String player) { if (!privateAccess) { return true; } return allowedPlayers.contains(player); } public int getStatus() { return status; } public List<PlayerName> getAllowedPlayers() { List<PlayerName> p = new ArrayList<PlayerName>(); for (String player : allowedPlayers) { p.add(new PlayerName(player)); } return p; } public void addPlayer(String player) { if (!allowedPlayers.contains(player)) { allowedPlayers.add(player); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } } public void delPlayer(String player) { if (allowedPlayers.contains(player)) { allowedPlayers.remove(player); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } } @Override public void readFromNBT(NBTTagCompound tagCompound) { super.readFromNBT(tagCompound); teleportTimer = tagCompound.getInteger("tpTimer"); cooldownTimer = tagCompound.getInteger("cooldownTimer"); totalTicks = tagCompound.getInteger("totalTicks"); goodTicks = tagCompound.getInteger("goodTicks"); badTicks = tagCompound.getInteger("badTicks"); String playerName = tagCompound.getString("tpPlayer"); if (playerName != null && !playerName.isEmpty()) { teleportingPlayer = worldObj.getPlayerEntityByName(playerName); } else { teleportingPlayer = null; } status = tagCompound.getInteger("status"); rfPerTick = tagCompound.getInteger("rfPerTick"); } @Override public void readRestorableFromNBT(NBTTagCompound tagCompound) { super.readRestorableFromNBT(tagCompound); name = tagCompound.getString("tpName"); Coordinate c = Coordinate.readFromNBT(tagCompound, "dest"); if (c == null) { teleportDestination = null; } else { int dim = tagCompound.getInteger("dim"); teleportDestination = new TeleportDestination(c, dim); } if (tagCompound.hasKey("destId")) { teleportId = tagCompound.getInteger("destId"); } else { teleportId = null; } privateAccess = tagCompound.getBoolean("private"); beamHidden = tagCompound.getBoolean("hideBeam"); once = tagCompound.getBoolean("once"); allowedPlayers.clear(); NBTTagList playerList = tagCompound.getTagList("players", Constants.NBT.TAG_STRING); if (playerList != null) { for (int i = 0 ; i < playerList.tagCount() ; i++) { String player = playerList.getStringTagAt(i); allowedPlayers.add(player); } } } @Override public void writeToNBT(NBTTagCompound tagCompound) { super.writeToNBT(tagCompound); tagCompound.setInteger("tpTimer", teleportTimer); tagCompound.setInteger("cooldownTimer", cooldownTimer); tagCompound.setInteger("totalTicks", totalTicks); tagCompound.setInteger("goodTicks", goodTicks); tagCompound.setInteger("badTicks", badTicks); if (teleportingPlayer != null) { tagCompound.setString("tpPlayer", teleportingPlayer.getDisplayName()); } tagCompound.setInteger("status", status); tagCompound.setInteger("rfPerTick", rfPerTick); } @Override public void writeRestorableToNBT(NBTTagCompound tagCompound) { super.writeRestorableToNBT(tagCompound); if (name != null && !name.isEmpty()) { tagCompound.setString("tpName", name); } if (teleportDestination != null) { Coordinate c = teleportDestination.getCoordinate(); if (c != null) { Coordinate.writeToNBT(tagCompound, "dest", c); tagCompound.setInteger("dim", teleportDestination.getDimension()); } } if (teleportId != null) { tagCompound.setInteger("destId", teleportId); } tagCompound.setBoolean("private", privateAccess); tagCompound.setBoolean("hideBeam", beamHidden); tagCompound.setBoolean("once", once); NBTTagList playerTagList = new NBTTagList(); for (String player : allowedPlayers) { playerTagList.appendTag(new NBTTagString(player)); } tagCompound.setTag("players", playerTagList); } public boolean isDialed() { return teleportId != null || teleportDestination != null; } public Integer getTeleportId() { if (isDialed() && teleportId == null) { getTeleportDestination(); } return teleportId; } public TeleportDestination getTeleportDestination() { if (teleportId != null) { TeleportDestinations teleportDestinations = TeleportDestinations.getDestinations(worldObj); GlobalCoordinate gc = teleportDestinations.getCoordinateForId(teleportId); if (gc == null) { return null; } else { return teleportDestinations.getDestination(gc.getCoordinate(), gc.getDimension()); } } return teleportDestination; } public void setTeleportDestination(TeleportDestination teleportDestination, boolean once) { this.teleportDestination = null; this.teleportId = null; this.once = once; if (teleportDestination != null) { TeleportDestinations destinations = TeleportDestinations.getDestinations(worldObj); Integer id = destinations.getIdForCoordinate(new GlobalCoordinate(teleportDestination.getCoordinate(), teleportDestination.getDimension())); if (id == null) { this.teleportDestination = teleportDestination; } else { this.teleportId = id; } } worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); markDirty(); } private void consumeIdlePower() { if (TeleportConfiguration.rfMatterIdleTick > 0 && teleportingPlayer == null) { if (getEnergyStored(ForgeDirection.DOWN) >= TeleportConfiguration.rfMatterIdleTick) { consumeEnergy(TeleportConfiguration.rfMatterIdleTick); } else { setTeleportDestination(null, false); } } } @Override protected void checkStateServer() { super.checkStateServer(); // Every few times we check if the receiver is ok (if we're dialed). if (isDialed()) { consumeIdlePower(); checkReceiverStatusCounter--; if (checkReceiverStatusCounter <= 0) { checkReceiverStatusCounter = 20; int newstatus; if (DialingDeviceTileEntity.isDestinationAnalyzerAvailable(worldObj, xCoord, yCoord, zCoord)) { newstatus = checkReceiverStatus(); } else { newstatus = TeleportationTools.STATUS_OK; } if (newstatus != status) { status = newstatus; markDirty(); worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); } } } if (isCoolingDown()) { // We're still in cooldown. Do nothing. return; } else if (teleportingPlayer == null) { // If we have a valid destination we check here if there is a player on this transmitter. if (isDestinationValid()) { searchForNearestPlayer(); } } else if (teleportDestination == null && teleportId == null) { // We were teleporting a player but for some reason the destination went away. Interrupt. Logging.warn(teleportingPlayer, "The destination vanished! Aborting."); clearTeleport(80); } else if (isPlayerOutsideBeam()) { // The player moved outside the beam. Interrupt the teleport. clearTeleport(80); } else { int rf = rfPerTick; if (getEnergyStored(ForgeDirection.DOWN) < rf) { // We don't have enough energy to handle this tick. handleEnergyShortage(); } else { // We have enough energy so this is a good tick. markDirty(); consumeEnergy(rf); goodTicks++; teleportTimer--; if (teleportTimer <= 0) { performTeleport(); } } } } // Server side only private int checkReceiverStatus() { TeleportDestination destination = getTeleportDestination(); if (destination == null) { return TeleportationTools.STATUS_WARN; } int dimension = destination.getDimension(); RfToolsDimensionManager dimensionManager = RfToolsDimensionManager.getDimensionManager(worldObj); if (dimensionManager.getDimensionInformation(dimension) != null) { // This is an RFTools dimension. Check power. DimensionStorage dimensionStorage = DimensionStorage.getDimensionStorage(worldObj); int energyLevel = dimensionStorage.getEnergyLevel(dimension); if (energyLevel < DimletConfiguration.DIMPOWER_WARN_TP) { return TeleportationTools.STATUS_WARN; } } World w = DimensionManager.getWorld(dimension); // By default we will not check if the dimension is not loaded. Can be changed in config. if (w == null) { if (TeleportConfiguration.matterTransmitterLoadWorld == -1) { return TeleportationTools.STATUS_UNKNOWN; } else { w = MinecraftServer.getServer().worldServerForDimension(dimension); checkReceiverStatusCounter = TeleportConfiguration.matterTransmitterLoadWorld; } } Coordinate c = destination.getCoordinate(); boolean exists = w.getChunkProvider().chunkExists(c.getX() >> 4, c.getZ() >> 4); if (!exists) { if (TeleportConfiguration.matterTransmitterLoadChunk == -1) { return TeleportationTools.STATUS_UNKNOWN; } else { checkReceiverStatusCounter = TeleportConfiguration.matterTransmitterLoadChunk; } } TileEntity tileEntity = w.getTileEntity(c.getX(), c.getY(), c.getZ()); if (!(tileEntity instanceof MatterReceiverTileEntity)) { return TeleportationTools.STATUS_WARN; } MatterReceiverTileEntity matterReceiverTileEntity = (MatterReceiverTileEntity) tileEntity; int status = matterReceiverTileEntity.checkStatus(); return (status == DialingDeviceTileEntity.DIAL_OK) ? TeleportationTools.STATUS_OK : TeleportationTools.STATUS_WARN; } private void clearTeleport(int cooldown) { markDirty(); TeleportationTools.applyBadEffectIfNeeded(teleportingPlayer, 0, badTicks, totalTicks, false); cooldownTimer = cooldown; teleportingPlayer = null; } private boolean isDestinationValid() { return teleportId != null || (teleportDestination != null && teleportDestination.isValid()); } private boolean isCoolingDown() { markDirty(); cooldownTimer--; if (cooldownTimer <= 0) { cooldownTimer = 0; } else { return true; } return false; } private void searchForNearestPlayer() { if (beamBox == null) { beamBox = AxisAlignedBB.getBoundingBox(xCoord, yCoord+1, zCoord, xCoord+1, yCoord+3, zCoord+1); } List<Entity> l = worldObj.getEntitiesWithinAABB(EntityPlayer.class, beamBox); Entity nearestPlayer = findNearestPlayer(l); if (nearestPlayer == null) { cooldownTimer = 5; return; } AxisAlignedBB playerBB = nearestPlayer.boundingBox; if (playerBB.intersectsWith(beamBox)) { startTeleportation(nearestPlayer); } else { cooldownTimer = 5; } } private Entity findNearestPlayer(List<Entity> l) { Entity nearestPlayer = null; double dmax = Double.MAX_VALUE; for (Entity entity : l) { EntityPlayer entityPlayer = (EntityPlayer) entity; if ((!isPrivateAccess()) || allowedPlayers.contains(entityPlayer.getDisplayName())) { double d1 = entity.getDistanceSq(xCoord + .5, yCoord + 1.5, zCoord + .5); if (d1 <= dmax) { nearestPlayer = entity; dmax = d1; } } } return nearestPlayer; } private void performTeleport() { // First check if the destination is still valid. if (!isDestinationStillValid()) { TeleportationTools.applyBadEffectIfNeeded(teleportingPlayer, 10, badTicks, totalTicks, false); Logging.warn(teleportingPlayer, "Missing destination!"); clearTeleport(200); return; } TeleportDestination dest = getTeleportDestination(); // The destination is valid. If this is a 'once' dial then we clear the destination here. if (once) { setTeleportDestination(null, false); } boolean boosted = DialingDeviceTileEntity.isMatterBoosterAvailable(worldObj, xCoord, yCoord, zCoord); if (boosted && getEnergyStored(ForgeDirection.DOWN) < TeleportConfiguration.rfBoostedTeleport) { // Not enough energy. We cannot do a boosted teleport. boosted = false; } boolean boostNeeded = TeleportationTools.performTeleport(teleportingPlayer, dest, badTicks, totalTicks, boosted); if (boostNeeded) { consumeEnergy(TeleportConfiguration.rfBoostedTeleport); } teleportingPlayer = null; } private boolean isDestinationStillValid() { TeleportDestination dest = getTeleportDestination(); return TeleportDestinations.getDestinations(worldObj).isDestinationValid(dest); } private void handleEnergyShortage() { markDirty(); // Not enough energy. This is a bad tick. badTicks++; if (TeleportationTools.mustInterrupt(badTicks, totalTicks)) { // Too many bad ticks. Total failure! Logging.warn(teleportingPlayer, "Power failure during transit!"); clearTeleport(200); } return; } private boolean isPlayerOutsideBeam() { AxisAlignedBB playerBB = teleportingPlayer.boundingBox; if (!playerBB.intersectsWith(beamBox)) { Logging.message(teleportingPlayer, "Teleportation was interrupted!"); return true; } return false; } public void startTeleportation(Entity entity) { if (cooldownTimer > 0) { // In cooldown. We can't do teleport right now. return; } if (teleportingPlayer != null) { // Already teleporting return; } if (!(entity instanceof EntityPlayer)) { return; } EntityPlayer player = (EntityPlayer) entity; TeleportDestination dest = teleportDestination; if (teleportId != null) { dest = getTeleportDestination(); } if (dest != null && dest.isValid()) { Coordinate cthis = new Coordinate(xCoord, yCoord, zCoord); int cost = TeleportationTools.calculateRFCost(worldObj, cthis, dest); cost = (int) (cost * (4.0f - getInfusedFactor()) / 4.0f); if (getEnergyStored(ForgeDirection.DOWN) < cost) { Logging.warn(player, "Not enough power to start the teleport!"); cooldownTimer = 80; return; } int srcId = worldObj.provider.dimensionId; int dstId = dest.getDimension(); if (!TeleportationTools.checkValidTeleport(player, srcId, dstId)) { cooldownTimer = 80; return; } Logging.message(player, "Start teleportation..."); teleportingPlayer = player; teleportTimer = TeleportationTools.calculateTime(worldObj, cthis, dest); teleportTimer = (int) (teleportTimer * (1.2f - getInfusedFactor()) / 1.2f); int rf = TeleportConfiguration.rfTeleportPerTick; rf = (int) (rf * (4.0f - getInfusedFactor()) / 4.0f); int totalRfUsed = cost + rf * (teleportTimer+1); rfPerTick = totalRfUsed / (teleportTimer+1); totalTicks = teleportTimer; goodTicks = 0; badTicks = 0; } else { Logging.warn(player, "Something is wrong with the destination!"); } } @Override public boolean execute(EntityPlayerMP playerMP, String command, Map<String, Argument> args) { boolean rc = super.execute(playerMP, command, args); if (rc) { return true; } if (CMD_SETNAME.equals(command)) { setName(args.get("name").getString()); return true; } else if (CMD_SETPRIVATE.equals(command)) { setPrivateAccess(args.get("private").getBoolean()); return true; } else if (CMD_SETBEAM.equals(command)) { setBeamHidden(args.get("hide").getBoolean()); return true; } else if (CMD_ADDPLAYER.equals(command)) { addPlayer(args.get("player").getString()); return true; } else if (CMD_DELPLAYER.equals(command)) { delPlayer(args.get("player").getString()); return true; } return false; } @Override public List executeWithResultList(String command, Map<String, Argument> args) { List rc = super.executeWithResultList(command, args); if (rc != null) { return rc; } if (CMD_GETPLAYERS.equals(command)) { return getAllowedPlayers(); } return null; } @Override public boolean execute(String command, List list) { boolean rc = super.execute(command, list); if (rc) { return true; } if (CLIENTCMD_GETPLAYERS.equals(command)) { GuiMatterTransmitter.storeAllowedPlayersForClient(list); return true; } return false; } @Override public boolean shouldRenderInPass(int pass) { return pass == 1; } @SideOnly(Side.CLIENT) @Override public AxisAlignedBB getRenderBoundingBox() { return AxisAlignedBB.getBoundingBox(xCoord, yCoord, zCoord, xCoord + 1, yCoord + 4, zCoord + 1); } }