Jump to content

How to add a delay when an event is fired


Nurox

Recommended Posts

I've got a little experience with Java, but I'm new to modding Minecraft.

 

I want to write a simple mod that plants a sapling if the sapling item entity is dropped on grass, dirt, etc.

 

Here's my code as is. There's a lot of functionality that hasn't been implemented, like despawning the entity once a sapling has been placed, etc :

 

@Mod.EventBusSubscriber(modid=TutorialMod.MOD_ID, bus=Bus.FORGE)

public class SaplingHitsGrass {
	
	private static final CharSequence SAPLING=new StringBuffer("sapling");

	
	@SubscribeEvent
	public static void saplingHitsGrass(EntityJoinWorldEvent event) {
		
		
		if(event.getEntity() instanceof ItemEntity) {
			
			
			 
			ItemEntity item=(ItemEntity) event.getEntity();
			ItemStack stack=item.getItem();
			
			if(stack!=null&&stack.toString().contains(SAPLING)) {
					
				
						
					TutorialMod.LOGGER.info(stack.toString());
						
					TutorialMod.LOGGER.info("Passed first Conditional");

							
						while(item.isAlive()) {
							
							
							if(item.ticksExisted!=0&&item.ticksExisted%600==0&&item.onGround) {
							
							
									
								BlockState ground=item.getEntityWorld().getBlockState(item.getPosition().add(0, -1, 0));
								if (ground.getBlock().getMaterial(ground).equals(Material.EARTH)) {
										
									item.getEntityWorld().setBlockState(item.getPosition(), Blocks.OAK_SAPLING.getDefaultState());
										
								}
									
							}
								
								
						}
						
							
							
			

						
					
					
					
			}
			
		
			
			
	}
}
	
}

 

I know that the code executes all the way to "Passed first Conditional", and I've tried like 5 different ways to delay, loop, loop with a counter, etc after that. I haven't tried the code past that, don't know if its correct or not.

 

1) Should I be using a different event? I don't want to lag up the game by checking every entity every tick, so I decided to use EntityJoinWorldEvent.

2) Is there a better way to verify that the entity is a sapling? I'm currently calling the ItemStack's toString() method and checking if that string contains the CharSequence "sapling". This works, but seems janky at best.

3) I want the item to spawn, wait like 600 ticks, and if it still hasn't been despawned it should try to plant the sapling. This is the biggest problem I'm having so far. I've figured out that its a bad idea to use the wait(long delay) function. The best way that I can figure to solve this is to have a counter int and just increment it every tick. That said, I don't know how to do that.

 

Link to comment
Share on other sites

12 minutes ago, Nurox said:

Is there a better way to verify that the entity is a sapling?

Checking for the word "sapling" means your mod won't work in any language except English, so that's a really bad idea.

Is there a sapling tag? (hint: there is: #minecraft:saplings)

 

12 minutes ago, Nurox said:

Should I be using a different event?

 

I want the item to spawn, wait like 600 ticks,...

The approach that I would probably use is a capability in one of 2 ways:

Either on the item (add using the event and check for saplings there), or on the EntityItem (check the entity is entityitem with contained sapling).

Then subscribe to the entity tick event and check the capability (or check it's an entityItem, then check the item has the capability). Use the capability to store your 600 tick cooldown, then plant the sapling.

Edited by Alpvax
Checked for accuracy
Link to comment
Share on other sites

2 hours ago, Alpvax said:

Checking for the word "sapling" means your mod won't work in any language except English, so that's a really bad idea.

Is there a sapling tag? (hint: there is: #minecraft:saplings)

I know it's a terrible idea  ":P"

I also know about tags, but I don't know how to check them. Could you give an example as to how I'd do that?

 

 

 

2 hours ago, Alpvax said:

Then subscribe to the entity tick event and check the capability (or check it's an entityItem, then check the item has the capability). Use the capability to store your 600 tick cooldown, then plant the sapling.

How do I subscribe to that event? That was my original plan, but I couldn't figure out how to do it inside a method. Every time I've seen events called it's been in the arguments for a method,  not within a method.

 

 

Most of that was generic "Here's the idea and figure out the code yourself." Not to be rude, but I had the same general ideas. My issue is figuring out Minecraft's unique methods, classes, and so on. Specific code snippets, variables in classes, etc. would be much more helpful.

Edited by Nurox
Clarification of how I need help.
Link to comment
Share on other sites

4 hours ago, Nurox said:

Most of that was generic "Here's the idea and figure out the code yourself." Not to be rude, but I had the same general ideas. My issue is figuring out Minecraft's unique methods, classes, and so on. Specific code snippets, variables in classes, etc. would be much more helpful.

Here's how I would do it. I don't see any reason for a capability here. It's not like the information needs to be persistent/a lot of data. Create a class that has one event in it. The WorldTickEvent, you already know how to subscribe to events. Then in that class make a two static fields one which is an int that will just count ticks, and another that will map an int to an ItemEntity. Then in your EntityJoinWorldEvent add the entity to the list if it is a Sapling.

Use the key of the map(the int) to determine when your even will run. Then in order to place the Block that code would look something like this.

World world = event.getWorld();
		ItemEntity entity;
		BlockItem item = (BlockItem) entity.getItem().getItem();
		Block block = item.getBlock();
		BlockPos pos = entity.getPosition();
		world.setBlockState(pos, block.getDefaultState());

However that will just put the sapling where ever it is located at in the world. I would use World::getBlockState to check the block underneath it(is it on dirt, grass, etc) is there already a block here? Maybe some other things.

  • 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

3 hours ago, Animefan8888 said:

Here's how I would do it. I don't see any reason for a capability here. It's not like the information needs to be persistent/a lot of data. Create a class that has one event in it. The WorldTickEvent, you already know how to subscribe to events. Then in that class make a two static fields one which is an int that will just count ticks, and another that will map an int to an ItemEntity. Then in your EntityJoinWorldEvent add the entity to the list if it is a Sapling.

Use the key of the map(the int) to determine when your even will run. Then in order to place the Block that code would look something like this.


World world = event.getWorld();
		ItemEntity entity;
		BlockItem item = (BlockItem) entity.getItem().getItem();
		Block block = item.getBlock();
		BlockPos pos = entity.getPosition();
		world.setBlockState(pos, block.getDefaultState());

However that will just put the sapling where ever it is located at in the world. I would use World::getBlockState to check the block underneath it(is it on dirt, grass, etc) is there already a block here? Maybe some other things.

Thanks a ton, that was a massive help. I've got the behavior I'm looking for (though I did change some of your stuff around).  Also, thanks for introducing me to the Map class!

 

Do you know of a less janky way to tell if the ItemEntity is a sapling? I'm still using the .toString().contains(SAPLING) method. I've tried a few other ways, but can't figure it out.

 

I still have other behaviors to implement, like different sapling entities planting their respective trees, respawning the stack with one less sapling, etc. I'm also sure that there's potential bugs to iron out with non soild blocks.

 

Source code for those interested:

package com.asdf.tutorialmod.events;

import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;

import com.asdf.tutorialmod.TutorialMod;

import net.minecraft.block.Blocks;
import net.minecraft.block.GrassBlock;
import net.minecraft.entity.item.ItemEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.event.TickEvent.WorldTickEvent;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;

@Mod.EventBusSubscriber(modid=TutorialMod.MOD_ID, bus=Bus.FORGE)

public class SaplingHitsGrass {
	
	private static final CharSequence SAPLING=new StringBuffer("sapling");
	private static final int TICKS_TILL_PLANT=600;
	private static Map<ItemEntity,Integer> map=new HashMap<ItemEntity,Integer>();
	
	
	
	
	
	
	@SubscribeEvent
	public static void saplingHitsGrass(WorldTickEvent tick) {
		
		World world=tick.world;
		
		if(map!=null) {
		
			if(!map.isEmpty()) {
				try {
				map.forEach((item,timeLeft)->{
				
					if(!item.isAlive()) {
						map.remove(item);
						TutorialMod.LOGGER.info("Sapling removed from queue prematurely");
					}
					else if(timeLeft>1) {
						
						map.put(item, timeLeft-1);
					
					
					}
				
					else {
						
						map.remove(item);
						TutorialMod.LOGGER.info("Sapling removed from queue");
						
						BlockPos pos= item.getPosition();
						if(world.getBlockState(pos.add(0, -1, 0)).getBlock() instanceof GrassBlock) {
							world.setBlockState(pos, Blocks.OAK_SAPLING.getDefaultState());
						}
					}
				
				
				});
				}
				catch(ConcurrentModificationException e) {
					
					e.getStackTrace();
					
					
				}
			
			
		}
		
		}
		
		
	}
	
	
	
	
	
	@SubscribeEvent
	public static void populateMap(EntityJoinWorldEvent event) {
		
		
		
		if(event.getEntity() instanceof ItemEntity) {
			
			ItemEntity item=(ItemEntity) event.getEntity();
			ItemStack stack=item.getItem();
			
			if(stack!=null&&stack.toString().contains(SAPLING)) {
				
				map.put(item,TICKS_TILL_PLANT);
				TutorialMod.LOGGER.info("Sapling added to queue");
				
			}
			
		}
		
	}
}

 

Edited by Nurox
Link to comment
Share on other sites

47 minutes ago, Nurox said:

Do you know of a less janky way to tell if the ItemEntity is a sapling? I'm still using the .toString().contains(SAPLING) method. I've tried a few other ways, but can't figure it out.

Yes there is a system in Minecraft/Minecraft Forge called tags. Items, Blocks, entities, and fluids can be tagged together. There is a sapling one. To retrieve the tag do BlockTags.TAG_NAME.

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

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.



×
×
  • Create New...

Important Information

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