Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.11.2] [SOLVED] TileEntity performance


Bektor
 Share

Recommended Posts

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:

  1. TileEntityPipeTransferEnergy -> transfer pipes
  2. 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
  3. 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 by Bektor

Developer of Primeval Forest.

Link to comment
Share on other sites

You can cache a lot of this. A machine only needs to know a set of machines it is connected to (Set<BlockPos>) and possibly a pre-calculated weight for each one if you want to take cable length into account.

A cable needs to know all machines it is connected to directly or indirectly via other cables. Then when the cable is broken you notify the machines that are connected that they need to re-calculate their network (i.e. what they are connected to). This does require every cable to have a tile entity, but they can be non-ticking and memory is much cheaper and easy to increase than CPU power.

Link to comment
Share on other sites

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.

Developer of Primeval Forest.

Link to comment
Share on other sites

A* is awesome.

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.

Link to comment
Share on other sites

3 minutes ago, Draco18s said:

A* is awesome.

?

What does that mean?

YouTube:

https://www.youtube.com/channel/UCaK5oRw4yd7PurBWww9tVhA

Kuribo64 (where I am most active):

http://kuribo64.net/board/profile.php?id=1423

 

Thanks for your answer!

 

Current projects:

 

3D Geometry Dash: 13%

Terracraft (MC Mod): 8%

New Super Kaizo Bros DS: 100% (no download for now :'( )

New Super Kaizo Bros. Wii: 12%

Super Mario Galaxy 1.5: 96%

New Super Mario Bros Galaxy: 85%

Kerbal Space Program Advance: 47%

MC CREATE: 20%

Mystery Projects: 10%, 40%, 13%, and 94%

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

Just now, Draco18s said:

Whoops. :/

YouTube:

https://www.youtube.com/channel/UCaK5oRw4yd7PurBWww9tVhA

Kuribo64 (where I am most active):

http://kuribo64.net/board/profile.php?id=1423

 

Thanks for your answer!

 

Current projects:

 

3D Geometry Dash: 13%

Terracraft (MC Mod): 8%

New Super Kaizo Bros DS: 100% (no download for now :'( )

New Super Kaizo Bros. Wii: 12%

Super Mario Galaxy 1.5: 96%

New Super Mario Bros Galaxy: 85%

Kerbal Space Program Advance: 47%

MC CREATE: 20%

Mystery Projects: 10%, 40%, 13%, and 94%

Link to comment
Share on other sites

On 13.4.2017 at 9:17 PM, Draco18s said:

A* is awesome.

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?

Developer of Primeval Forest.

Link to comment
Share on other sites

Routing energy through a wirenetwork isn't pathfinding?

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.

Link to comment
Share on other sites

17 hours ago, Draco18s said:

Routing energy through a wirenetwork isn't pathfinding?

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.

 

Developer of Primeval Forest.

Link to comment
Share on other sites

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 :P

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.

Developer of Primeval Forest.

Link to comment
Share on other sites

  • 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.
Link to comment
Share on other sites

19 minutes ago, 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.
  • 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 by Bektor

Developer of Primeval Forest.

Link to comment
Share on other sites

Just now, Bektor said:

Whats so terrible about LinkedList?

  • It's just that, a linked list. This data structure is terrible for the pipeline found in pretty much every modern CPU. Without going into much detail, the CPU is much faster than the RAM can supply it with data, at least when the data is accessed randomly. That is why CPUs try to predict which data you are going to access and pre-fetch it from memory before you even need it. With a linked list going from one element to the next requires following a pointer, i.e. going to some random location in memory (random because the GC moves your objects around constantly). This random location is basically unpredictable for the CPU, meaning it cannot pre-fetch this value. An array on the other hand (the underlying data structure of an ArrayList or an ArrayDeque) is a continuous block of memory. Going from one element to the next means just going to the next memory cell. This is very easily predicted by the CPU.
  • A linked list does not have random access, meaning doing get(i) requires the list to go through all elements until it finds the right one.
  • Due to all the pointers and wrappers a linked list uses much more memory than an array-based structure
5 minutes ago, Bektor said:

And I just thought it would make sense to use something like a linked list because each object is linked to each other.

Are you serious?

 

5 minutes ago, Bektor said:

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.

Because you are often checking if it contains a certain element already. If it's a list that requires scanning all elements in the list to find if it's in there (O(n)). A (hash-based) Set on the other hand only needs to calculate the hash-code and then check if that hash-code is in the set, this is O(1). Basically: contains is faster for a Set, usually.

 

8 minutes ago, Bektor said:

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.

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.

 

10 minutes ago, Bektor said:

Why do I want to call getCapability with side null?

Because the capability might be exposed with side null and not any specific side. In that case your pipe would ignore that capability.

 

10 minutes ago, Bektor said:

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.

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>.

  • Like 1
Link to comment
Share on other sites

10 minutes ago, 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>.

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?

 

12 minutes ago, 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.

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;
                }
            }

 

13 minutes ago, 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.

 

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).

 

15 minutes ago, diesieben07 said:

It's just that, a linked list. This data structure

 

15 minutes ago, diesieben07 said:

Because you are often checking if it contains a certain element already. If it's a list

Oh, didn't know those things. Thanks. ;)

Developer of Primeval Forest.

Link to comment
Share on other sites

4 minutes ago, Bektor said:

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?

Map::keySet

Map::entrySet

Map::forEach

 

6 minutes ago, Bektor said:

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).

I know what you use them for, and that's perfectly fine. But it doesn't mean you can't use a TileEntity...

Link to comment
Share on other sites

3 minutes ago, diesieben07 said:

Map::keySet

Hm... The target type of this expression must be a functional interface.

 

6 minutes ago, diesieben07 said:

Map::forEach

How should this statement be implement into the loop?

 

Thought this way it works:

for(Map.Entry<BlockPos, EnumFacing> pos : connected.entrySet()) {

 

Developer of Primeval Forest.

Link to comment
Share on other sites

7 minutes ago, Bektor said:

Hm... The target type of this expression must be a functional interface.

Dude... I even linked you the Javadoc. It's obvious that that's not how you use this method.

 

8 minutes ago, Bektor said:

How should this statement be implement into the loop?

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.

 

8 minutes ago, Bektor said:

Thought this way it works:

[snip]

Yes, that works.

Link to comment
Share on other sites

1 hour ago, diesieben07 said:
  • Please don't use LinkedList. It's terrible in every possible way. If you want a Queue (or Stack) use ArrayDeque.

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.

 

1 hour ago, diesieben07 said:

Are you serious?

 

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.

 

38 minutes ago, 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.

 

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 by Bektor

Developer of Primeval Forest.

Link to comment
Share on other sites

9 hours ago, Bektor said:

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. ^^

BiConsumer is a normal interface. Only special thing about it is, that it only has a single abstract method (SAM), in this case accept. (Almost) every interface with a SAM can be implemented with a lambda. There is nothing unique about BiConsumer there.

 

9 hours ago, Bektor said:

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.

You need to change toSearch to contain both BlockPos and an EnumFacing. Then change your getBlocksToScan to something like this:

private void getBlocksToScan(Queue<Pair<BlockPos, EnumFacing>> toSearch, Set<BlockPos> scanned, BlockPos pos) {
  for (EnumFacing face : EnumSet.allOf(EnumFacing.class)) {
    BlockPos offset = pos.offset(face);
    if (!scanned.contains(offset)) {
        scanned.add(Pair.of(offset, face.getOpposite()));
    }
  }
}

 

Link to comment
Share on other sites

8 hours ago, diesieben07 said:

You need to change toSearch to contain both BlockPos and an EnumFacing. Then change your getBlocksToScan to something like this:



 

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 ?

Developer of Primeval Forest.

Link to comment
Share on other sites

9 minutes ago, Bektor said:

What does this Pair code do?

https://commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/apache/commons/lang3/tuple/Pair.html

 

9 minutes ago, Bektor said:

And why do I have to change there the ArrayDeque to Queue<Pair<BlockPos, EnumFacing>>

Code against interfaces (first explanation I could find, there are many more).

 

11 minutes ago, Bektor said:

because there is also a

HashMap which stores all that stuff in the main class itself. So why not change the toSearch to HashMap ?

HashMap (or rather Map) is a fundamentally different data structure to a Queue. A Map stores (usually unordered) key-value pairs. A Queue stores single values and allows you to quickly access the top value (or top and bottom in case of a Deque (Deque = double ended queue). It can be used for example as a stack. Read the documentation on these classes if you want to know more.

Link to comment
Share on other sites

11 minutes ago, diesieben07 said:

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)

Developer of Primeval Forest.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



  • Recently Browsing

    No registered users viewing this page.

  • Posts

    • I am attempting to create a Valhelsia 3 server and keep running into this error on startup 2022-01-28 16:29:43,899 main WARN Advanced terminal features are not available in this environment [16:29:43] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher running: args [--gameDir, ., --launchTarget, fmlserver, --fml.forgeVersion, 36.2.22, --fml.mcpVersion, 20210115.111550, --fml.mcVersion, 1.16.5, --fml.forgeGroup, net.minecraftforge, nogui] [16:29:43] [main/INFO] [cp.mo.mo.Launcher/MODLAUNCHER]: ModLauncher 8.0.9+86+master.3cf110c starting: java version 1.8.0_322 by Temurin [16:29:44] [main/INFO] [ne.mi.fm.lo.FixSSL/CORE]: Added Lets Encrypt root certificates as additional trust [16:29:44] [main/INFO] [mixin/]: SpongePowered MIXIN Subsystem Version=0.8.4 Source=file:/C:/multimc/Valhesia%20server/libraries/org/spongepowered/mixin/0.8.4/mixin-0.8.4.jar Service=ModLauncher Env=SERVER [16:29:45] [main/INFO] [STDERR/]: [org.antlr.v4.runtime.ConsoleErrorListener:syntaxError:38]: line 1:0 token recognition error at: '~' Init CreativeCore coremods ... [16:29:46] [main/ERROR] [mixin/]: Mixin config notenoughcrashes.mixins.json does not specify "minVersion" property [16:29:46] [main/ERROR] [mixin/]: Mixin config notenoughcrashes.forge.mixins.json does not specify "minVersion" property Exception in thread "main" [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]: java.lang.NoSuchMethodError: sun.security.util.ManifestEntryVerifier.<init>(Ljava/util/jar/Manifest;)V [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.SecureJarHandler.createCodeSource(SecureJarHandler.java:66) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.TransformingClassLoader$DelegatedClassLoader.findClass(TransformingClassLoader.java:275) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.TransformingClassLoader.loadClass(TransformingClassLoader.java:136) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.TransformingClassLoader.loadClass(TransformingClassLoader.java:98) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at java.lang.ClassLoader.loadClass(ClassLoader.java:351) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at java.lang.Class.forName0(Native Method) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at java.lang.Class.forName(Class.java:348) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.service.modlauncher.ModLauncherClassProvider.findClass(ModLauncherClassProvider.java:67) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.platform.MixinConnectorManager.loadConnectors(MixinConnectorManager.java:70) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.platform.MixinConnectorManager.inject(MixinConnectorManager.java:59) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.platform.MixinPlatformManager.inject(MixinPlatformManager.java:196) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.MixinBootstrap.inject(MixinBootstrap.java:202) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.MixinLaunchPluginLegacy.initializeLaunch(MixinLaunchPluginLegacy.java:201) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at org.spongepowered.asm.launch.MixinLaunchPluginLegacy.initializeLaunch(MixinLaunchPluginLegacy.java:195) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.LaunchPluginHandler.lambda$announceLaunch$9(LaunchPluginHandler.java:97) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at java.util.HashMap.forEach(HashMap.java:1290) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.LaunchPluginHandler.announceLaunch(LaunchPluginHandler.java:97) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:52) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.LaunchServiceHandler.launch(LaunchServiceHandler.java:72) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.Launcher.run(Launcher.java:82) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at cpw.mods.modlauncher.Launcher.main(Launcher.java:66) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at net.minecraftforge.server.ServerMain$Runner.runLauncher(ServerMain.java:63) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at net.minecraftforge.server.ServerMain$Runner.access$100(ServerMain.java:60) [16:29:46] [main/INFO] [STDERR/]: [java.lang.ThreadGroup:uncaughtException:1052]:       at net.minecraftforge.server.ServerMain.main(ServerMain.java:57) I have tried JDK 8 from oracle and adoptium with no luck.
    • 👀my issue sorry😅, then do it on client an send a custom package to the server https://forge.gemwire.uk/wiki/SimpleChannel
    • downgrade your java to java 8 320
  • Topics

  • Who's Online (See full list)

×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.