Bektor Posted April 13, 2017 Posted April 13, 2017 (edited) Hi. I'm currently working on some basic cables. I want those cables to work the way that the TileEntityPipeTransferEnergy transfers the energy from TileEntityPipeTransferEnergy A to B. The transfer pipes should be connect with some cables, but the energy should not go through these cables. So actually, the energy goes directly from point A to B while skipping all cables along the way. This should however only work when point A and B are connected with a cable. I think Extra Utilities 1 back in 1.7.10 worked also that way, thought not quite sure. Down below I've got my first attempt at doing this, which hasn't been tested yet. My question is know, where and how can I optimize this code to run with maximum possible performance to be able to have huge machine rooms without huge performance drops (like running MC with 120fps, going into the machine room, MC drops down to 20fps and stuff like that (not only the fps)). How the code currently works: check every 20 ticks the whole network to be sure the cables are still there, for that, check every direction and add checked positions to a list and positions to be checked to a second list, if all possible endings (it might be possible one cable connects 3 or more transfer pipes) are found, stop the scan get the capability and send to every transfer pipe the amount of energy this transfer pipe stores divided by the number of transfer pipes energy should be send to Things to note: TileEntityPipeTransferEnergy -> transfer pipes TileEntityEnergy -> base class which creates an Energy Storage for Forge Energy, called container using a wrapper class which differences between producer, consumer and holder just like Tesla (not fully implemented, thought); extends TileEntity implements ITickable TileEntityPipeEnergy -> the cable, currently no code in there; extends TileEntity (I think there isn't even a use for it being a tile entity, but I haven't changed it yet, in the past it had to be a tile entity, but no longer has to be) public class TileEntityPipeTransferEnergy extends TileEntityEnergy { private byte counter = 0; private ArrayList<BlockPos> connect = new ArrayList<>(); private ArrayList<BlockPos> copy = new ArrayList<>(); public TileEntityPipeTransferEnergy() { super(10, 10); this.container.setTransferMode(EnergyTransfer.PRODUCER); } @Override public void update() { boolean flag = false; ++this.counter; if(this.counter > 20 || this.counter < 0) this.counter = 0; if(this.counter == 20) { flag = true; this.findTransferPipes(); } for(int i = 0; i <= this.connect.size(); i++) { flag = true; if(this.copy.contains(this.connect.get(i))) continue; final TileEntity tile = this.getWorld().getTileEntity(this.connect.get(i)); for(EnumFacing order : EnumFacing.VALUES) { if(!tile.isInvalid() && tile.hasCapability(CapabilityEnergy.ENERGY, order)) { IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, order); if(storage != null) { this.copy.add(this.connect.get(i)); storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connect.size()), false), false); break; } } } } if(flag) this.markDirty(); } private void findTransferPipes() { if(this.connect.size() > 0) { this.connect.clear(); this.copy.clear(); } LinkedList<BlockPos> toSearch = new LinkedList<>(); LinkedList<BlockPos> scanned = new LinkedList<BlockPos>(); scanned.add(this.getPos()); if(toSearch.isEmpty() || toSearch.peek() == null) this.getBlocksToScan(toSearch, scanned, this.getPos()); BlockPos curScan = null; while(toSearch.peek() != null) { curScan = toSearch.poll(); scanned.add(curScan); if(this.getWorld().getTileEntity(curScan) instanceof TileEntityPipeEnergy) // check cable connection this.getBlocksToScan(toSearch, scanned, curScan); else if(this.getWorld().getTileEntity(curScan) instanceof TileEntityPipeTransferEnergy && !this.getPos().equals(curScan) && !scanned.contains(curScan)) this.connect.add(curScan); // found end of line } } private void getBlocksToScan(LinkedList<BlockPos> toSearch, LinkedList<BlockPos> scanned, BlockPos pos) { if(!scanned.contains(pos.north())) toSearch.add(pos.north()); if(!scanned.contains(pos.south())) toSearch.add(pos.south()); if(!scanned.contains(pos.west())) toSearch.add(pos.west()); if(!scanned.contains(pos.east())) toSearch.add(pos.east()); if(!scanned.contains(pos.up())) toSearch.add(pos.up()); if(!scanned.contains(pos.down())) toSearch.add(pos.down()); } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.container.deserializeNBT(compound.getCompoundTag("Energy")); } @Override public NBTTagCompound writeToNBT(NBTTagCompound compound) { compound.setTag("Energy", this.container.serializeNBT()); return super.writeToNBT(compound); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { super.onDataPacket(net, pkt); this.readFromNBT(pkt.getNbtCompound()); } } Also, besides performance, anything else you notice, just post it. Open for feedback of every sort to this code (and I'm sure in nearly every line things can be changed to get better performance etc.), I mean, without feedback you won't learn anything. Thx in advance. Bektor EDIT: Updated code: EDIT 2: Again updated code: EDIT 3: Updated Code + some problems: Edited April 27, 2017 by Bektor Quote Developer of Primeval Forest.
Bektor Posted April 13, 2017 Author Posted April 13, 2017 Hm, how can I pre-calculate the weight for each machine with the cable length? And what would be the best way to store this data (the first thing I would come up with is a HashTable, but not so sure if this is optimal there). And how can I notify a machine when the cable is broken? I mean, currently it doesn't even know to which machines it is connected to and to just move the scan code over into the cable wouldn't gain me anything performance wise. When building a network of cables it would make it even worse. Quote Developer of Primeval Forest.
Draco18s Posted April 13, 2017 Posted April 13, 2017 A* is awesome. Quote Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
Draco18s Posted April 13, 2017 Posted April 13, 2017 https://www.google.com/search?q=A*&ie=utf-8&oe=utf-8 Quote Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
Bektor Posted April 16, 2017 Author Posted April 16, 2017 On 4/13/2017 at 7:17 PM, Draco18s said: A* is awesome. Expand Well, problem is: I've got totally no idea how A* can help me with this problem efficiently. The only situation I can image where A* is of use and efficient is path finding for an AI. And when I would implement it, wouldn't it result into something similar I've already got? Quote Developer of Primeval Forest.
Draco18s Posted April 16, 2017 Posted April 16, 2017 Routing energy through a wirenetwork isn't pathfinding? Quote Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
Bektor Posted April 16, 2017 Author Posted April 16, 2017 On 4/16/2017 at 2:40 AM, Draco18s said: Routing energy through a wirenetwork isn't pathfinding? Expand Well, the transfer pipe knows the location of all other transfer pipes and just teleports the energy to them while making sure that everything is connected via cable. But besides that, I'm wondering where to cache all the data efficiently and how to let the transfer pipe know when a cable is disconnected or connected. Quote Developer of Primeval Forest.
Bektor Posted April 19, 2017 Author Posted April 19, 2017 Ok, I've just finished the new code, here it goes: The transfer pipe for sending and receiving energy: public class TileEntityPipeTransferEnergy extends TileEntityEnergy { //private Map<BlockPos, Integer> connected_weight = new HashMap<>(); // with pre-calculated weight, no clue how to do this... :( private Set<BlockPos> connected = new HashSet<>(); // without pre-calculated weight public boolean shouldRecalculate = false; public TileEntityPipeTransferEnergy() { super(10, 10); this.container.setTransferMode(EnergyTransfer.PRODUCER); } @Override public void update() { if(this.shouldRecalculate) { this.shouldRecalculate = false; this.findTransferPipes(); } Iterator<BlockPos> it = connected.iterator(); while(it.hasNext()) { final TileEntity tile = this.getWorld().getTileEntity(it.next()); for(EnumFacing order : EnumFacing.VALUES) { IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, order); if(storage != null) { storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); break; } } } this.markDirty(); } private void findTransferPipes() { if(this.connected.size() > 0) this.connected.clear(); LinkedList<BlockPos> toSearch = new LinkedList<>(); LinkedList<BlockPos> scanned = new LinkedList<BlockPos>(); scanned.add(this.getPos()); if(toSearch.isEmpty() || toSearch.peek() == null) this.getBlocksToScan(toSearch, scanned, this.getPos()); BlockPos curScan = null; while(toSearch.peek() != null) { curScan = toSearch.poll(); scanned.add(curScan); if(this.getWorld().getTileEntity(curScan) instanceof TileEntityPipeEnergy) // check cable connection this.getBlocksToScan(toSearch, scanned, curScan); else if(this.getWorld().getTileEntity(curScan) instanceof TileEntityPipeTransferEnergy && !this.getPos().equals(curScan) && !scanned.contains(curScan)) this.connected.add(curScan); // found end of line } } private void getBlocksToScan(LinkedList<BlockPos> toSearch, LinkedList<BlockPos> scanned, BlockPos pos) { if(!scanned.contains(pos.north())) toSearch.add(pos.north()); if(!scanned.contains(pos.south())) toSearch.add(pos.south()); if(!scanned.contains(pos.west())) toSearch.add(pos.west()); if(!scanned.contains(pos.east())) toSearch.add(pos.east()); if(!scanned.contains(pos.up())) toSearch.add(pos.up()); if(!scanned.contains(pos.down())) toSearch.add(pos.down()); } @Override public void readFromNBT(NBTTagCompound compound) { super.readFromNBT(compound); this.container.deserializeNBT(compound.getCompoundTag("Energy")); } @Override public NBTTagCompound writeToNBT(NBTTagCompound compound) { compound.setTag("Energy", this.container.serializeNBT()); return super.writeToNBT(compound); } @Override public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity pkt) { super.onDataPacket(net, pkt); this.readFromNBT(pkt.getNbtCompound()); } } The cable for just beeing there and give the player the illusion to transfer the energy public class BlockCable extends Block /*implements ITileEntityProvider*/ { private Set<BlockPos> connected = new HashSet<>(); // connection to transfer pipes public BlockCable() { super(Material.IRON); this.setHardness(1.5f); this.setResistance(0.f); this.setCreativeTab(ModCreativeTabs.mcpowerTab); } @Override public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state) { super.onBlockAdded(worldIn, pos, state); this.findTransferPipes(worldIn, pos); } private void findTransferPipes(World worldIn, BlockPos pos) { if(this.connected.size() > 0) this.connected.clear(); LinkedList<BlockPos> toSearch = new LinkedList<>(); LinkedList<BlockPos> scanned = new LinkedList<BlockPos>(); scanned.add(pos); if(toSearch.isEmpty() || toSearch.peek() == null) this.getBlocksToScan(toSearch, scanned, pos); BlockPos curScan = null; while(toSearch.peek() != null) { curScan = toSearch.poll(); scanned.add(curScan); if(worldIn.getTileEntity(curScan) instanceof TileEntityPipeEnergy) // check cable connection this.getBlocksToScan(toSearch, scanned, curScan); else if(worldIn.getTileEntity(curScan) instanceof TileEntityPipeTransferEnergy && !pos.equals(curScan) && !scanned.contains(curScan)) this.connected.add(curScan); // found end of line } } private void getBlocksToScan(LinkedList<BlockPos> toSearch, LinkedList<BlockPos> scanned, BlockPos pos) { if(!scanned.contains(pos.north())) toSearch.add(pos.north()); if(!scanned.contains(pos.south())) toSearch.add(pos.south()); if(!scanned.contains(pos.west())) toSearch.add(pos.west()); if(!scanned.contains(pos.east())) toSearch.add(pos.east()); if(!scanned.contains(pos.up())) toSearch.add(pos.up()); if(!scanned.contains(pos.down())) toSearch.add(pos.down()); } @Override public void breakBlock(World worldIn, BlockPos pos, IBlockState state) { super.breakBlock(worldIn, pos, state); this.recalculate(worldIn); this.findTransferPipes(worldIn, pos); } private void recalculate(World worldIn) { Iterator<BlockPos> it = this.connected.iterator(); while(it.hasNext()) { TileEntityPipeTransferEnergy tile = (TileEntityPipeTransferEnergy) worldIn.getTileEntity(it.next()); if(tile != null) tile.shouldRecalculate = true; } } @Override public boolean isSideSolid(IBlockState base_state, IBlockAccess world, BlockPos pos, EnumFacing side) { return super.isSideSolid(base_state, world, pos, side); } @Override public boolean canBeReplacedByLeaves(IBlockState state, IBlockAccess world, BlockPos pos) { return false; } /*@Override public TileEntity createNewTileEntity(World worldIn, int meta) { return new TileEntityPipeEnergy(); }*/ } So, I hope this attempt is better. @diesieben07 The code hopefully includes everything you said, even if I'm not quite sure about the performance gain from this code. Just to notice, I wasn't able to get the pre-calculated weight done as I'm not quite sure how to do it. I also have no clue why a cable needs a tile entity with the system you explained. Quote Developer of Primeval Forest.
Bektor Posted April 19, 2017 Author Posted April 19, 2017 (edited) On 4/19/2017 at 8:50 PM, diesieben07 said: Please don't use LinkedList. It's terrible in every possible way. If you want a Queue (or Stack) use ArrayDeque. The scanned list should be a Set. You should consider using the enhanced for loop instead of iterators. Makes for a lot cleaner code. You cannot store something like connected in the Block class. There is only one instance of this class for the whole block. This is why you might need a TE for the cables. Don't call markDirty every tick... You might want to call getCapability with side null, too. Expand Whats so terrible about LinkedList? And I just thought it would make sense to use something like a linked list because each object is linked to each other. So it might be that it's actually totally useless to use anything like that there. Why should the scanned list be a Set? It's just the same as the toSearch list and was added to make sure the block was not scanned yet, so that I don't scan stuff twice. Hm... don't ask me why I haven't done this and actually did it just 3 lines below. Well, I thought it would be a good idea as a block has methods like onBlockAdded and breakBlock while I don't know any equivalent method for tile entities. changed. Now it updates only when the list gets updated or when the tile entities energy gets updated [...] storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); flag = true; [...] Why do I want to call getCapability with side null? I'm currently just checking all sides for the capability as it might be that not all sides have one, like a solar panel which has only the energy capability for the EnumFacing.DOWN side. Besides of that, I've also done it because I just don't know how the transfer pipe A knows about the side the cable is connected to the transfer pipe B. If I would know this I can even remove this for loop. Edited April 19, 2017 by Bektor Quote Developer of Primeval Forest.
Bektor Posted April 19, 2017 Author Posted April 19, 2017 On 4/19/2017 at 9:22 PM, diesieben07 said: You need to store it with the position in the connected Set. Either you can use a custom class here (storing both the BlockPos and the side) or you can use a Map<BlockPos, EnumFacing>. Expand Well, a Map comes up with the problem that I can't just loop through it so easy as I am doing it now and how can I get the position from the HashMap? On 4/19/2017 at 9:22 PM, diesieben07 said: Because the capability might be exposed with side null and not any specific side. In that case your pipe would ignore that capability. Expand final TileEntity tile = this.getWorld().getTileEntity(pos); boolean flag1 = true; for(EnumFacing order : EnumFacing.VALUES) { IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, order); if(storage != null) { storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); flag = true; flag1 = false; break; } } if(flag1) { IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, null); if(storage != null) { storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); flag = true; break; } } On 4/19/2017 at 9:22 PM, diesieben07 said: Not sure what the method selection has to do with it. If you need the onBlockAdded and breakBlock "events" in the TE, pass them on from the Block class. Expand Well, these methods are required so that my cable can scan through the network for all transfer pipes it is connected to and when it get's removed, it can inform those transfer pipes with just accessing their boolean value (not sure if this is the best way). On 4/19/2017 at 9:22 PM, diesieben07 said: It's just that, a linked list. This data structure Expand On 4/19/2017 at 9:22 PM, diesieben07 said: Because you are often checking if it contains a certain element already. If it's a list Expand Oh, didn't know those things. Thanks. Quote Developer of Primeval Forest.
Bektor Posted April 19, 2017 Author Posted April 19, 2017 On 4/19/2017 at 9:45 PM, diesieben07 said: Map::keySet Expand Hm... The target type of this expression must be a functional interface. On 4/19/2017 at 9:45 PM, diesieben07 said: Map::forEach Expand How should this statement be implement into the loop? Thought this way it works: for(Map.Entry<BlockPos, EnumFacing> pos : connected.entrySet()) { Quote Developer of Primeval Forest.
Bektor Posted April 19, 2017 Author Posted April 19, 2017 (edited) On 4/19/2017 at 8:50 PM, diesieben07 said: Please don't use LinkedList. It's terrible in every possible way. If you want a Queue (or Stack) use ArrayDeque. Expand Well, ArrayDeque makes in this case even more sense than a normal ArrayList, as it provides some methods which directly allow to get an object and remove it from the list and it makes sure that the first added object get's checked first and not maybe never as the list grows and grows and grows. On 4/19/2017 at 9:22 PM, diesieben07 said: Are you serious? Expand Well, when each object is linked to each other the list can grow huge and so it makes sense to actually handle the first added thing first. On 4/19/2017 at 10:06 PM, diesieben07 said: It's a method. You call it, usually with a lambda, and it will call that lambda (or whatever other BiConsumer you give it) for every key-value pair in the Map. Expand Ah, ok. So the BiConsumer is the thing that allows me to do this stuff: (key, value) -> I better do not ask how this whole BiConsumer stuff actually works. ^^ So, now I'm stuck with the findTransferPipes method. I mean, I somehow have to get the direction in which the current search goes there to save the direction (or maybe the opposite direction?) from which the cable comes to the connected map. Edited April 19, 2017 by Bektor Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 7:49 AM, diesieben07 said: You need to change toSearch to contain both BlockPos and an EnumFacing. Then change your getBlocksToScan to something like this: Expand Thx. What does this Pair code do? (Pair.of and Queue<Pair<BlockPos, EnumFacing>>) And why do I have to change there the ArrayDeque to Queue<Pair<BlockPos, EnumFacing>>, I mean, what's the difference there, because there is also a HashMap which stores all that stuff in the main class itself. So why not change the toSearch to HashMap ? Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 4:45 PM, diesieben07 said: https://commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/apache/commons/lang3/tuple/Pair.html Expand Could you explain it to me as I don't quite get it after reading this JavaDoc? (with seems to contain not much information compared to the like 90% documentation and 10% code stuff from some Java classes) Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 5:34 PM, diesieben07 said: A Pair is something that holds two values, "left" and "right". That's it. Expand Nothing more to it? Somehow it sounds way too simple. Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 Ok. Here is my updated code which should include everything: Just a small question besides: Does this search code in getBlocksToScan (EnumSet.allOf(EnumFacing.class)) also takes care of the null side which you mentioned some time ago? Just asking because this side or face is added to the connected list from where it is going to be used for the getCapability check, so that my cable does not ignore the Capability when it's not exposed to a specific side. transfer pipes: public class TileEntityPipeTransferEnergy extends TileEntityEnergy { private boolean flag = false; private HashMap<BlockPos, EnumFacing> connected = new HashMap<>(); // without pre-calculated weight public boolean shouldRecalculate = false; [...] @Override public void update() { this.flag = false; if(this.shouldRecalculate) { this.shouldRecalculate = false; this.findTransferPipes(); this.flag = true; } this.connected.forEach((pos, side) -> { final TileEntity tile = this.getWorld().getTileEntity(pos); IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, side); if(storage != null) { storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); this.flag = true; // flag has to be a class member because of inner classes } }); if(this.flag) this.markDirty(); } private void findTransferPipes() { if(this.connected.size() > 0) this.connected.clear(); // we want to store the facing and the position Queue<Pair<BlockPos, EnumFacing>> toSearch = new ArrayDeque<>(); HashSet<BlockPos> scanned = new HashSet<BlockPos>(); scanned.add(this.getPos()); if(toSearch.isEmpty() || toSearch.peek() == null) this.getBlocksToScan(toSearch, scanned, this.getPos()); // temp object because we poll stuff out of the list Pair<BlockPos, EnumFacing> pair = null; BlockPos current = null; EnumFacing face = null; while(toSearch.peek() != null) { pair = toSearch.poll(); current = pair.getLeft(); face = pair.getRight(); scanned.add(current); if(this.getWorld().getTileEntity(current) instanceof TileEntityPipeEnergy) // check cable connection this.getBlocksToScan(toSearch, scanned, current); else if(this.getWorld().getTileEntity(current) instanceof TileEntityPipeTransferEnergy && !this.getPos().equals(current) && !scanned.contains(current)) this.connected.put(current, face); // found end of line } } private void getBlocksToScan(Queue<Pair<BlockPos, EnumFacing>> toSearch, HashSet<BlockPos> scanned, BlockPos pos) { for(EnumFacing face : EnumSet.allOf(EnumFacing.class)) { // same as pos.north(), just for all directions BlockPos offset = pos.offset(face); if(!scanned.contains(offset)) // create a new pair and add it to the list // a pair is just some class which holds two fields toSearch.add(Pair.of(offset, face.getOpposite())); } } [...] } Cable Block: public class BlockCable extends Block implements ITileEntityProvider { [...] @Override public void onBlockAdded(World worldIn, BlockPos pos, IBlockState state) { super.onBlockAdded(worldIn, pos, state); TileEntity tile = worldIn.getTileEntity(pos); if(tile != null && tile instanceof TileEntityPipeEnergy) { TileEntityPipeEnergy cable = (TileEntityPipeEnergy) tile; cable.searchNetwork(); } } @Override public void breakBlock(World worldIn, BlockPos pos, IBlockState state) { TileEntity tile = worldIn.getTileEntity(pos); if(tile != null && tile instanceof TileEntityPipeEnergy) { TileEntityPipeEnergy cable = (TileEntityPipeEnergy) tile; cable.searchNetwork(); } super.breakBlock(worldIn, pos, state); // execute code above before the tile entity gets removed } @Override public TileEntity createNewTileEntity(World worldIn, int meta) { return new TileEntityPipeEnergy(); } } Cable TileEntity: public class TileEntityPipeEnergy extends TileEntity { private HashSet<BlockPos> connected_machines = new HashSet<>(); public void searchNetwork() { if(this.connected_machines.size() > 0) this.connected_machines.clear(); ArrayDeque<BlockPos> toSearch = new ArrayDeque<>(); HashSet<BlockPos> scanned = new HashSet<>(); if(toSearch.isEmpty() || toSearch.peek() == null) this.getBlocksToScan(toSearch, scanned, this.getPos()); BlockPos current = null; while(toSearch.peek() != null) { current = toSearch.pop(); scanned.add(current); TileEntity tile = this.getWorld().getTileEntity(current); if(tile != null) { if(tile instanceof TileEntityPipeEnergy) this.getBlocksToScan(toSearch, scanned, current); else if(tile instanceof TileEntityPipeTransferEnergy) this.connected_machines.add(current); } } } private void getBlocksToScan(ArrayDeque<BlockPos> toSearch, HashSet<BlockPos> scanned, BlockPos pos) { if(!scanned.contains(pos.north())) toSearch.add(pos.north()); if(!scanned.contains(pos.south())) toSearch.add(pos.south()); if(!scanned.contains(pos.west())) toSearch.add(pos.west()); if(!scanned.contains(pos.east())) toSearch.add(pos.east()); if(!scanned.contains(pos.up())) toSearch.add(pos.up()); if(!scanned.contains(pos.down())) toSearch.add(pos.down()); } public void informNetwork() { this.connected_machines.forEach(pos -> { TileEntity tile = this.getWorld().getTileEntity(pos); // make sure the tile entity at 'pos' is really the one we think it is if(tile != null && tile instanceof TileEntityPipeTransferEnergy) { TileEntityPipeTransferEnergy transfer = (TileEntityPipeTransferEnergy) tile; transfer.shouldRecalculate = true; } }); } } I somehow think I learned with this one topic here more than in most other topics I created. Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 8:46 PM, diesieben07 said: Looks alright, and no, EnumSet does not contain null. Expand Hm, so my pipe will ignore all machines with don't care about the side? How can I add then the null check effeciently again? Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 8:53 PM, diesieben07 said: Make a collection that contains null and all sides. To avoid the cost of creating it every time, you can store it in a static final field: private static final EnumFacing[] SIDES = ObjectArrays.concat(null, EnumFacing.values()); Expand And then add this to the for-loop in getBlocksToScan instead of EnumSet.allOf(EnumFacing.class), I guess. Also, I'm getting the following error when using your line of code at that line: The method concat(EnumFacing, EnumFacing[]) is ambiguous for the type ObjectArrays. Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 9:00 PM, diesieben07 said: You need to cast null to EnumFacing to help out the compiler. And yes, you would use that field in the for loop. Expand Ah, ok. Thx. Seems a bit weird to cast null to EnumFacing as null is null. ^^ Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 9:24 PM, diesieben07 said: No, null is not null Null is not a special type. A variable of a certain object type (such as EnumFacing) actually is a reference to an object of that type, not the object itself. And null is a special "reference to nothing" placeholder. Expand And now null is a reference to a nothing EnumFacing. I hope it's not quite as complicated as it seems to be the case in C++ with references vs pointers. But quite interesting that no methods want a cast when there is null, but this method does. But why references, why not just some kind of pointer like nullptr in C++? (and even when Java want's to tell you that it does not has pointers, it has pointers ^^) Quote Developer of Primeval Forest.
Draco18s Posted April 20, 2017 Posted April 20, 2017 On 4/20/2017 at 10:05 PM, Bektor said: But quite interesting that no methods want a cast when there is null, but this method does. Expand There are circumstances where casting null is necessary. Such as when two methods have other wise ambiguous signatures, casting the null makes it unique. Quote Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable. If you think this is the case, JUST REPORT ME. Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice. Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked. DO NOT PM ME WITH PROBLEMS. No help will be given.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 10:24 PM, Draco18s said: There are circumstances where casting null is necessary. Such as when two methods have other wise ambiguous signatures, casting the null makes it unique. Expand Ah, ok. Didn't know that. Quote Developer of Primeval Forest.
Bektor Posted April 20, 2017 Author Posted April 20, 2017 On 4/20/2017 at 10:49 PM, diesieben07 said: References are an abstraction over pointers. Pointers are an actual number, like in C. A Reference is a construct which is implemented using pointers, but does not give you access to those pointers. Expand Ah, ok. Quote Developer of Primeval Forest.
Bektor Posted April 26, 2017 Author Posted April 26, 2017 Hm, got some problems after adding the energy handling logic (WIP) to all the blocks: @diesieben07 NullPointerException when having null as a side like you suggested. Energy seems not to get transfered to other the transfer pipe at the end of the cable Transfer Pipe: Reveal hidden contents private void getBlocksToScan(Queue<Pair<BlockPos, EnumFacing>> toSearch, HashSet<BlockPos> scanned, BlockPos pos) { // EnumSet.allOf(EnumFacing.class) does not include the null, so machines which don't require a specific // site will be ignored for(EnumFacing face : TileEntityPipeTransferEnergy.SIDES) { // same as pos.north(), just for all directions BlockPos offset = pos.offset(face); //NPE... with if(face == null) return; no error, but it won't check null (internal) energy stuff if(!scanned.contains(offset)) // create a new pair and add it to the list // a pair is just some class which holds two fields toSearch.add(Pair.of(offset, face.getOpposite())); } } Reveal hidden contents // in update this.connected.forEach((pos, side) -> { final TileEntity tile = this.getWorld().getTileEntity(pos); if(tile == null) return; IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, side); if(storage != null) { storage.receiveEnergy(this.container.extractEnergy((int)(10 / this.connected.size()), false), false); System.out.println("Transfer at " + pos.getX() + "," + pos.getY() + "," + pos.getZ() + " contains " + this.container.getEnergyStored() + "FE"); this.flag = true; // flag has to be a class member because of inner classes } }); Solar Panel update: Reveal hidden contents if(this.world.getTileEntity(this.pos.down()) instanceof TileEntityPipeTransferEnergy) { TileEntityPipeTransferEnergy tile = (TileEntityPipeTransferEnergy) world.getTileEntity(getPos().down()); tile.container.receiveEnergy(this.container.extractEnergy(10, false), false); } There is also no log output from transfer pipe with syso and when having it outside of the forEach loop, the log tells me the transfer pipe at the beginning has energy, but at the end of the pipe doesn't have energy. Quote Developer of Primeval Forest.
Bektor Posted April 26, 2017 Author Posted April 26, 2017 On 4/26/2017 at 7:15 PM, diesieben07 said: That was a derp on my part. You only need the null when querying getCapability. Expand With getCapability is the problem that the HashMap does not contains null as I removed it from getBlocksToScan and changed it back to EnumSet.allOf(EnumFacing.class). This can be seen there: IEnergyStorage storage = tile.getCapability(CapabilityEnergy.ENERGY, side); And the debug mode tells me that there seems to be a problem with the forEach loop. Everything above the forEach loop get's called, but not even the first line of the forEach loop get's executed. Quote Developer of Primeval Forest.
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.