Jump to content

[1.15.2] Questions regarding the overhead of tickable tile entities


DavidM

Recommended Posts

Hello.

I am creating a pipe block that can transfer items from one chest to another.

This would require a tickable tile entity.

However, since most of the pipes are not directly connected to a chest, but rather act as a connection between two pipes that have direct connection to chests, the majority of the pipes are not responsible for extracting items from inventories every tick.

I am wondering whether it is a good idea to remove pipes that are not directly connected to a chest from the world's tickable tile entity list to avoid having unnecessary virtual invocations on ITickableTileEntity#tick, and re-add a pipe into the tickable list if a chest is placed near the pipe later on.

Would this actually make the game perform better?

Edited by DavidM

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

14 minutes ago, DavidM said:

Hello.

I am creating a pipe block that can transfer items from one chest to another.

This would require a tickable tile entity.

However, since most of the pipes are not directly connected to a chest, but rather act as a connection between two pipes that have direct connection to chests, the majority of the pipes are not responsible for extracting items from inventories every tick.

I am wondering whether it is a good idea to remove pipes that are not directly connected to a chest from the world's tickable tile entity list to avoid having unnecessary virtual invocations on ITickableTileEntity#tick, and re-add a pipe into the tickable list if a chest is placed near the pipe later on.

Would this actually make the game perform better?

I think so, and I'd recommend master-slave system

  • Thanks 1
Link to comment
Share on other sites

25 minutes ago, poopoodice said:

I think so, and I'd recommend master-slave system

Please elaborate. I don't see how the master-slave pattern is applicable here.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

43 minutes ago, DavidM said:

Would this actually make the game perform better?

Yes it should. because it would be less function calls and less stack manipulation.

5 minutes ago, DavidM said:

Please elaborate. I don't see how the master-slave pattern is applicable here.

It's kinda applicable. All the Tickable TE's would be considered a master and all the non Tickable TE's would be considered slaves. Mind that you do not even need a TE for slaves if there is nothing to store there.

50 minutes ago, DavidM said:

This would require a tickable tile entity.

Sure it could require a tickable TileEntity. Or you could abstract another layer out. This is my preferred method for implementing pipes.

Having many Tickable TEs would require them all containing a connection network. Or running a pathfinding algorithm every time you want to send something from A to B.

 

However if you instead take away their autonomy just a little and instead store a "Network" of pipes in the world via a Capability and interact with it every tick with a World Tick event. You then save memory by only storing the connections once and you can cache the shortest/determined path between point A and B.

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Thanks for your replies.

 

14 minutes ago, Animefan8888 said:

It's kinda applicable. All the Tickable TE's would be considered a master and all the non Tickable TE's would be considered slaves. Mind that you do not even need a TE for slaves if there is nothing to store there.

In my case, I am checking if an applicable inventory is placed next to a pipe via Block#updatePostPlacement, and add the pipe to tickables if there is one. The same goes for removing inventories. The TEs are responsible for storing whether the connection on a direction is disabled the player.

 

18 minutes ago, Animefan8888 said:

However if you instead take away their autonomy just a little and instead store a "Network" of pipes in the world via a Capability and interact with it every tick with a World Tick event. You then save memory by only storing the connections once and you can cache the shortest/determined path between point A and B.

My current approach is to path find every time the network of pipes is updated (i.e. removal/addition of pipe or inventory, changing of the configuration of a pipe, etc), and assign each extracting pipe to a set of inserting pipes (with matching configuration/channels). Every tick the extracting pipe sends items to the stored inserting pipes. There is no actual connections of the pipes is memory.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

18 minutes ago, DavidM said:

My current approach is to path find every time the network of pipes is updated

So your current approach is to query the world for every block in the network every time the network is updated. This works but is sadly a slower way having to access the World for every block and connection direction.

20 minutes ago, DavidM said:

and assign each extracting pipe to a set of inserting pipes

This is still stored in memory. And you have the same set of inserting pipes for every extracting pipe on a network.

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

10 minutes ago, Animefan8888 said:

This is still stored in memory. And you have the same set of inserting pipes for every extracting pipe on a network.

Not exactly. Since each extracting/inserting pipe can have different channels (extracting pipe will only target inserting pipes with the same channel) and configurations (i.e. closest first, round robin, etc), the set of inserting pipes and the order of them might vary for each extracting pipe.

 

13 minutes ago, Animefan8888 said:

So your current approach is to query the world for every block in the network every time the network is updated. This works but is sadly a slower way having to access the World for every block and connection direction.

I might have understood this wrongly, but I am not aware of other alternatives for this. If I understood correctly, my current approach and the approach you illustrated above both require the updating of a network if there are additions/deletions to the network.

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

Just now, DavidM said:

my current approach and the approach you illustrated above both require the updating of a network if there are additions/deletions to the network.

Yes but my method already knows of all the pipe connections, where as yours does not. You have to do something like.
for Direction dir : Direction.values()

  TileEntity t = world.getTileEntity(pos.offset(dir));

  // Etc.

Where as mine already has a Set<BlockPos> where that is all of the connections. This set is updated when a block is placed or removed from the network. Then pathfinding runs on this set. Which also allows it to run on a separate thread as the World is not thread safe.

  • Thanks 1

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

7 minutes ago, Animefan8888 said:

Yes but my method already knows of all the pipe connections, where as yours does not. You have to do something like.
for Direction dir : Direction.values()

  TileEntity t = world.getTileEntity(pos.offset(dir));

  // Etc.

Where as mine already has a Set<BlockPos> where that is all of the connections. This set is updated when a block is placed or removed from the network. Then pathfinding runs on this set. Which also allows it to run on a separate thread as the World is not thread safe.

I see. I’ll switch to your method for performance.

However I am not sure how to obtain the network given the position of a pipe in it. How would that be accomplished (without iterating through all of the existing network)? I am thinking of given all pipes a reference to its network, but there might be a better way.

Edited by DavidM

Some tips:

Spoiler

Modder Support:

Spoiler

1. Do not follow tutorials on YouTube, especially TechnoVision (previously called Loremaster) and HarryTalks, due to their promotion of bad practice and usage of outdated code.

2. Always post your code.

3. Never copy and paste code. You won't learn anything from doing that.

4. 

Quote

Programming via Eclipse's hotfixes will get you nowhere

5. Learn to use your IDE, especially the debugger.

6.

Quote

The "picture that's worth 1000 words" only works if there's an obvious problem or a freehand red circle around it.

Support & Bug Reports:

Spoiler

1. Read the EAQ before asking for help. Remember to provide the appropriate log(s).

2. Versions below 1.11 are no longer supported due to their age. Update to a modern version of Minecraft to receive support.

 

 

Link to comment
Share on other sites

4 minutes ago, DavidM said:

I am thinking of given all pipes a reference to its network, but there might be a better way.

You could do that or if you use HashSet you get constant time checking if it is in the Set. So the finding the network that contains this position is O(n) where n is the number of networks.

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Hi

The best piece of advice I can give is:

code it the easy way, then stress test its performance.    I'm betting that even ticking hundreds or thousands of pipes is not going to drag down the game performance by a noticeable margin.

Although you could build a parallel data structure to record the pipe network, I wouldn't recommend doing that unless really necessary because it's harder to code and maintain, and keeping the data structure current (detecting all changes) could be quite difficult to get right due to chunks loading in and out.

Your idea of a fast exit tick if not next to a chest is a good one I think, because it would be fairly simple to implement.  There are several methods for blocks that are fired whenever the neighbour is updated, so a pipe can easily keep track of whether it's next to a chest.  You wouldn't even need a TileEntity, a block with three blockstates (start, middle, end) is probably enough if you use scheduled ticking.

When your "I'm next to a chest" tick occurs and discovers that the chest has an item in it, it can then hunt its way through the pipe network to find the chest at the other end; given this is very infrequent I can't imagine it would be a big burden.

 

-TGG

  • Thanks 1
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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • They were already updated, and just to double check I even did a cleanup and fresh update from that same page. I'm quite sure drivers are not the problem here. 
    • i tried downloading the drivers but it says no AMD graphics hardware has been detected    
    • Update your AMD/ATI drivers - get the drivers from their website - do not update via system  
    • As the title says i keep on crashing on forge 1.20.1 even without any mods downloaded, i have the latest drivers (nvidia) and vanilla minecraft works perfectly fine for me logs: https://pastebin.com/5UR01yG9
    • Hello everyone, I'm making this post to seek help for my modded block, It's a special block called FrozenBlock supposed to take the place of an old block, then after a set amount of ticks, it's supposed to revert its Block State, Entity, data... to the old block like this :  The problem I have is that the system breaks when handling multi blocks (I tried some fix but none of them worked) :  The bug I have identified is that the function "setOldBlockFields" in the item's "setFrozenBlock" function gets called once for the 1st block of multiblock getting frozen (as it should), but gets called a second time BEFORE creating the first FrozenBlock with the data of the 1st block, hence giving the same data to the two FrozenBlock :   Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=head] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@73681674 BlockEntityData : id:"minecraft:bed",x:3,y:-60,z:-6} Old Block Fields set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=3, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} Frozen Block Entity set BlockState : Block{minecraft:black_bed}[facing=east,occupied=false,part=foot] BlockPos{x=2, y=-60, z=-6} BlockEntity : net.minecraft.world.level.block.entity.BedBlockEntity@6d1aa3da BlockEntityData : {id:"minecraft:bed",x:2,y:-60,z:-6} here is the code inside my custom "freeze" item :    @Override     public @NotNull InteractionResult useOn(@NotNull UseOnContext pContext) {         if (!pContext.getLevel().isClientSide() && pContext.getHand() == InteractionHand.MAIN_HAND) {             BlockPos blockPos = pContext.getClickedPos();             BlockPos secondBlockPos = getMultiblockPos(blockPos, pContext.getLevel().getBlockState(blockPos));             if (secondBlockPos != null) {                 createFrozenBlock(pContext, secondBlockPos);             }             createFrozenBlock(pContext, blockPos);             return InteractionResult.SUCCESS;         }         return super.useOn(pContext);     }     public static void createFrozenBlock(UseOnContext pContext, BlockPos blockPos) {         BlockState oldState = pContext.getLevel().getBlockState(blockPos);         BlockEntity oldBlockEntity = oldState.hasBlockEntity() ? pContext.getLevel().getBlockEntity(blockPos) : null;         CompoundTag oldBlockEntityData = oldState.hasBlockEntity() ? oldBlockEntity.serializeNBT() : null;         if (oldBlockEntity != null) {             pContext.getLevel().removeBlockEntity(blockPos);         }         BlockState FrozenBlock = setFrozenBlock(oldState, oldBlockEntity, oldBlockEntityData);         pContext.getLevel().setBlockAndUpdate(blockPos, FrozenBlock);     }     public static BlockState setFrozenBlock(BlockState blockState, @Nullable BlockEntity blockEntity, @Nullable CompoundTag blockEntityData) {         BlockState FrozenBlock = BlockRegister.FROZEN_BLOCK.get().defaultBlockState();         ((FrozenBlock) FrozenBlock.getBlock()).setOldBlockFields(blockState, blockEntity, blockEntityData);         return FrozenBlock;     }  
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.