So, i've been doing a feature where you can throw 2 items on the ground, and then get an item back.
Currently, i'm using onWorldTick() to tick every time the world ticks, and inside the method, I create an instance of world.loadedEntityList
Then, I loop through 2 times, offsetting the second loop by one.
I create positions, instances of the EntityItem and instances of the Item inside EntityItem.
I then check for my desired items in loadedEntityList.
If I have those, i wait 100 ticks, then spawn my desired item, decrement the stackSize of the EntityItem, and remove the Entities.
//Checked if world is on server side with both
if(event.side.isServer()) {
World world = event.world;
if(!world.isRemote) {
List<Entity> entities = world.loadedEntityList;
for (int i = 0; i < entities.size(); i++)
{
for (int j = 1; j < entities.size(); j++)
{
if (entities.get(i) instanceof EntityItem && entities.get(j) instanceof EntityItem)
{
EntityItem target = (EntityItem) entities.get(i);
EntityItem targetJ = (EntityItem) entities.get(j);
int x = target.getPosition().getX();
int y = target.getPosition().getY();
int z = target.getPosition().getZ();
int xJ = targetJ.getPosition().getX();
int yJ = targetJ.getPosition().getY();
int zJ = targetJ.getPosition().getZ();
Item item = target.getEntityItem().getItem();
Item itemJ = targetJ.getEntityItem().getItem();
if ((item == Items.book && itemJ == Items.bone) || (item == Items.bone && itemJ == Items.book) && !target.isDead && !targetJ.isDead)
{
if (target.ticksExisted > 100 && targetJ.ticksExisted > 100)
{
//This is just position checking, I don't think there's much to change here?
if (Math.abs(x - xJ) <= 3 && Math.abs(y - yJ) <= 1 && Math
.abs(z - zJ) <= 3) {
target.getEntityItem().stackSize--;
targetJ.getEntityItem().stackSize--;
if (target.getEntityItem().stackSize == 0) world
.removeEntity(target);
if (targetJ.getEntityItem().stackSize == 0) world
.removeEntity(targetJ);
}
//closing brace stuff
My questions are:
- Is this the best way to do this? When i add a third loop, it lags out the game quite a lot, and even with only two loops, it decreases performance. I first thought of using some sort of event where it gets called every time an EntityItem hit a block, but ended up not finding anything and going with the loops.
- If there is no other way to do this, how can I check for a third item without rendering the game almost unplayable?
The first would be to either have a custom block that you throw them at (somewhat like botania's crafting systems, although it could keep the items as entities) and every time a new item hits the block, check all items in an area around the block.
The other option would be to have a custom Item, with a custom EntityItem implementation. Then you would throw that item down as a catalyst, and it would check for all items in the area and "craft". You could either consume the catalyst or leave it to be reused.
Alternatively, you could use these custom items as one of the recipe ingredients, but then your recipes system is limited.
A third option would be to listen to the PlayerTossEvent, but then you cannot autocraft easily.
Or you could listen to the entity tick event, if the entity is an item, check in an area around for other items.
I'd suggest iterating through the list once to find the first item entity, then using World#getEntitiesInAABBexcluding to get all other item entities with matching items nearby.
You can create a Set containing the Items you want to find, then remove each Item as you find it. You can also create a List to contain the matching EntityItems as you find them. When the Set is empty, you can perform the effect, iterate through the List, reduce each item entity's stack size by 1 and call Entity#setDead if the stack size is less than or equal to 0.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
Another way of finding the items would be to use an item you right click at where the item entities are. You can then use that to ray trace to the hit block, find the items around it and do something like what Choonster has suggested above to check for all items you want being there to then craft. Having some sort of trigger is the most efficient way of doing this, as checking ALL entities in the world is very processing intensive. So an item or a block (like suggested by Alpvax) used to trigger the crafting would be one of the best, and probably easiest methods.
The World#getEntitiesInAABBexcluding was the method I was suggesting you use, thanks to Choonster for pointing it out.
Is there any reason why a world tick event and looping through the entities is any better than just using the entity tick event in the first place?
There is no tick event for non-living entities.
Rollback Post to RevisionRollBack
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
Is there any reason why a world tick event and looping through the entities is any better than just using the entity tick event in the first place?
The advantage of the world tick event is you can make this work for ANY item entities, however it can be very processing intensive if you're iterating through 100s or 1000s of entities.
I'm presuming by "entity tick event" you mean the update methods within the entity class? Because these are much better since only a few item entities will have this checking (since it's just ones that are added by the mod) and it's less unnecessary processing.
I'd suggest iterating through the list once to find the first item entity, then using World#getEntitiesInAABBexcluding to get all other item entities with matching items nearby.
You can create a Set containing the Items you want to find, then remove each Item as you find it. You can also create a List to contain the matching EntityItems as you find them. When the Set is empty, you can perform the effect, iterate through the List, reduce each item entity's stack size by 1 and call Entity#setDead if the stack size is less than or equal to 0.
Is the way I create my AxisAlignedBB correct?
What do I use instead of TreeSet when I declare the Set?
My code is really messy. Am I doing something wrong? Looping through unnecessarily? I only need to check for two items, so is the best way really to use a set?
I didn't add the array list, because I only need to check for two items. (I know in my post I said three, but I only really need two)
You will probably want to have some sort of recipes system, so search for any item in your list of recipes (item1) then look around to see if any (item2)s are around. this will prevent duplicate crafting (item 1 is in the list, crafting occurs, both items set to be removed, then item2 appears in the list, crafting occurs a second time).
A mapping of item -> recipe would be my idea, where recipe has a second (and potentially third, fourth... etc. if you handle the item checking in the recipe (i.e. recipe.matches(<list of nearby items>))) item and a result.
Since I only had 2 items to check for, I removed the Set and the Array, and did one more check if forgot at the top for EntityItem
And it seems to be working, with fine performance (frame rate is pretty constant)
I did it with Choonster's method: by getting one item from the List of entities and then checking for the other one with
World.getEntitiesWithinAABBExcludingEntity
Here's the code: (I'm open to any other suggestions however)
@SubscribeEvent
public void onWorldTick(WorldTickEvent event) {
if (event.side.isServer()) {
World world = event.world;
// The item will spawn on the server side. If you don't check, it
// will run on client and create a 'phantom' item
if (!world.isRemote) {
Item wantedItem = Items.bone;
if (bookSpawnDelay > 0)
bookSpawnDelay--;
else {
@SuppressWarnings("unchecked")
List < Entity > entities = world.loadedEntityList;
for (int i = 0; i < entities.size(); i++) {
// Wanted to be book item
if (entities.get(i) instanceof EntityItem) {
EntityItem item = (EntityItem) entities.get(i);
if (item.getEntityItem().getItem() == Items.book) {
int xBound = item.getPosition().getX() + 3;
int yBound = item.getPosition().getY() + 3;
int zBound = item.getPosition().getZ() + 3;
List < Entity > list = world.getEntitiesWithinAABBExcludingEntity(item,
new AxisAlignedBB(item.getPosition(), new BlockPos(xBound, yBound, zBound)));
for (int j = 0; j < list.size(); j++) {
Entity entity = list.get(j);
if (entity instanceof EntityItem) {
EntityItem entityItem = (EntityItem) entity;
if (entityItem.getEntityItem().getItem() == wantedItem) {
bookSpawnDelay += 20;
int x = item.getPosition().getX();
int y = item.getPosition().getY();
int z = item.getPosition().getZ();
WorldServer worldServer = (WorldServer) world;
worldServer.spawnParticle(EnumParticleTypes.SMOKE_LARGE, false, x + 0.5 D,
y + 1.0 D, z + 0.5 D, 1, 0.0 D, 0.0 D, 0.0 D, 0.0 D, new int[0]);
world.spawnEntityInWorld(new EntityItem(world, x, y, z,
new ItemStack(ARKCraftItems.info_book)));
entityItem.getEntityItem().stackSize--;
item.getEntityItem().stackSize--;
if (entityItem.getEntityItem().stackSize <= 0)
entityItem.setDead();
if (item.getEntityItem().stackSize <= 0)
item.setDead();
}
}
}
}
}
}
}
}
}
}
What about using the Item#onEntityItemUpdate() method of your first item to check for the second one?
How could I do that using vanilla Items? I'm doing this inside my EventHandler, and I thought you only used Events inside there when you are using stuff from vanilla minecraft?
What do I use instead of TreeSet when I declare the Set?
My code is really messy. Am I doing something wrong? Looping through unnecessarily? I only need to check for two items, so is the best way really to use a set?
I didn't add the array list, because I only need to check for two items. (I know in my post I said three, but I only really need two)
Use the item's position minus 3 as the minimum coordinates of the AABB and the item's position plus 3 as the maximum coordinates. I suggest using the AxisAlignedBB(double, double, double, double, double, double) constructor.
HashSet is the Set implementation to use here (and in most cases). You want constant lookup times and you don't care about order.
If you add both Items you're searching for to the Set and use it to find the first item, you could reduce the iteration time for the outer loop because you'll be using the first entity of either item rather than looking for a specific one.
I suggest using World#getEntitiesInAABBexcluding to pre-filter the results to item entities whose Item is in the Set.
Your outer loop incorrectly casts every Entity in the loaded entity list to EntityItem without checking if it actually is an EntityItem.
There's no reason to use @SuppressWarnings("unchecked") here, you're not making any unchecked casts.
Entity positions are doubles, avoid truncating them to ints where possible.
Event handlers are singletons, your class must account for there being any number of matching item sets in the loaded chunks and can't store individual data like a single timer. You need a way to store this data for each set of matching items. This would be a lot easier if one of the items was your own, you could use a custom entity to control the transformation.
Rollback Post to RevisionRollBack
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
This doesn't work. I can't seem to see what I'm doing wrong.
I'm not sure exactly what's preventing it from working, but I do see some mistakes.
You're still using a global timer. Why should a book being spawned at 0,0,0 in the Overworld affect a book being spawned at 20,100,20 in the Nether?
You're removing elements from the itemsSet as you're iterating through it, which is undefined behaviour that will usually cause an error. The only way to remove an element from a collection while iterating is with Iterator#remove. You don't actually need to iterate through the Set at all, just use Set#contains or the return value of Set#remove.
You're also removing elements from World#loadedEntityList while iterating it.
The foundEntityItemsList should be defined within the outer for loop or cleared at the end of it to prevent partially matching entities (e.g. a book with no bone or feather near it) from being modified.
You're spawning your book twice.
I've implemented this in 1.10.2 using some Java 8 features like streams, lambdas and method references here. I didn't include a timer, but that could be done using capabilities.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
I'm not sure exactly what's preventing it from working, but I do see some mistakes.
You're still using a global timer. Why should a book being spawned at 0,0,0 in the Overworld affect a book being spawned at 20,100,20 in the Nether?
You're removing elements from the itemsSet as you're iterating through it, which is undefined behaviour that will usually cause an error. The only way to remove an element from a collection while iterating is with Iterator#remove. You don't actually need to iterate through the Set at all, just use Set#contains or the return value of Set#remove.
You're also removing elements from World#loadedEntityList while iterating it.
The foundEntityItemsList should be defined within the outer for loop or cleared at the end of it to prevent partially matching entities (e.g. a book with no bone or feather near it) from being modified.
You're spawning your book twice.
I've implemented this in 1.10.2 using some Java 8 features like streams, lambdas and method references here. I didn't include a timer, but that could be done using capabilities.
I'm still working on 1.8, so I can't use the lambadas, but I updated the code a bit by adding the hashset INPUT and creating a mutable copy of it.
However, I'm getting a ConcurrentModificationException on my outer for-loop line, and I don't know why it's causing it, I thought that error only shows if you try removing an object of a list while looping through it, and I don't loop through my Set.
So, i've been doing a feature where you can throw 2 items on the ground, and then get an item back.
Currently, i'm using onWorldTick() to tick every time the world ticks, and inside the method, I create an instance of world.loadedEntityList
Then, I loop through 2 times, offsetting the second loop by one.
I create positions, instances of the EntityItem and instances of the Item inside EntityItem.
I then check for my desired items in loadedEntityList.
If I have those, i wait 100 ticks, then spawn my desired item, decrement the stackSize of the EntityItem, and remove the Entities.
My questions are:
- Is this the best way to do this? When i add a third loop, it lags out the game quite a lot, and even with only two loops, it decreases performance. I first thought of using some sort of event where it gets called every time an EntityItem hit a block, but ended up not finding anything and going with the loops.
- If there is no other way to do this, how can I check for a third item without rendering the game almost unplayable?
Thanks
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
There are a few easy ways to do this.
The first would be to either have a custom block that you throw them at (somewhat like botania's crafting systems, although it could keep the items as entities) and every time a new item hits the block, check all items in an area around the block.
The other option would be to have a custom Item, with a custom EntityItem implementation. Then you would throw that item down as a catalyst, and it would check for all items in the area and "craft". You could either consume the catalyst or leave it to be reused.
Alternatively, you could use these custom items as one of the recipe ingredients, but then your recipes system is limited.
A third option would be to listen to the PlayerTossEvent, but then you cannot autocraft easily.
Or you could listen to the entity tick event, if the entity is an item, check in an area around for other items.
If I helped you, please click the green up arrow.
I'd suggest iterating through the list once to find the first item entity, then using World#getEntitiesInAABBexcluding to get all other item entities with matching items nearby.
You can create a Set containing the Items you want to find, then remove each Item as you find it. You can also create a List to contain the matching EntityItems as you find them. When the Set is empty, you can perform the effect, iterate through the List, reduce each item entity's stack size by 1 and call Entity#setDead if the stack size is less than or equal to 0.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
Another way of finding the items would be to use an item you right click at where the item entities are. You can then use that to ray trace to the hit block, find the items around it and do something like what Choonster has suggested above to check for all items you want being there to then craft. Having some sort of trigger is the most efficient way of doing this, as checking ALL entities in the world is very processing intensive. So an item or a block (like suggested by Alpvax) used to trigger the crafting would be one of the best, and probably easiest methods.
The World#getEntitiesInAABBexcluding was the method I was suggesting you use, thanks to Choonster for pointing it out.
Is there any reason why a world tick event and looping through the entities is any better than just using the entity tick event in the first place?
If I helped you, please click the green up arrow.
There is no tick event for non-living entities.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
The advantage of the world tick event is you can make this work for ANY item entities, however it can be very processing intensive if you're iterating through 100s or 1000s of entities.
I'm presuming by "entity tick event" you mean the update methods within the entity class? Because these are much better since only a few item entities will have this checking (since it's just ones that are added by the mod) and it's less unnecessary processing.
Is the way I create my AxisAlignedBB correct?
What do I use instead of TreeSet when I declare the Set?
My code is really messy. Am I doing something wrong? Looping through unnecessarily? I only need to check for two items, so is the best way really to use a set?
I didn't add the array list, because I only need to check for two items. (I know in my post I said three, but I only really need two)
I tried doing it like this:
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ) ?
All it is, is a reference to a cuboid area of 3D space in the world.
And yes, although you'll have multiple items to look for right? So you'll need to check for all the necessary items.
My bad, I thought there was an EntityTickEvent, looking at my code, I just use the world one.
If I helped you, please click the green up arrow.
You will probably want to have some sort of recipes system, so search for any item in your list of recipes (item1) then look around to see if any (item2)s are around. this will prevent duplicate crafting (item 1 is in the list, crafting occurs, both items set to be removed, then item2 appears in the list, crafting occurs a second time).
A mapping of item -> recipe would be my idea, where recipe has a second (and potentially third, fourth... etc. if you handle the item checking in the recipe (i.e. recipe.matches(<list of nearby items>))) item and a result.
If I helped you, please click the green up arrow.
Got it working!
Since I only had 2 items to check for, I removed the Set and the Array, and did one more check if forgot at the top for EntityItem
And it seems to be working, with fine performance (frame rate is pretty constant)
I did it with Choonster's method: by getting one item from the List of entities and then checking for the other one with
Here's the code: (I'm open to any other suggestions however)
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
What about using the Item#onEntityItemUpdate() method of your first item to check for the second one?
How could I do that using vanilla Items? I'm doing this inside my EventHandler, and I thought you only used Events inside there when you are using stuff from vanilla minecraft?
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
Use the item's position minus 3 as the minimum coordinates of the AABB and the item's position plus 3 as the maximum coordinates. I suggest using the AxisAlignedBB(double, double, double, double, double, double) constructor.
HashSet is the Set implementation to use here (and in most cases). You want constant lookup times and you don't care about order.
If you add both Items you're searching for to the Set and use it to find the first item, you could reduce the iteration time for the outer loop because you'll be using the first entity of either item rather than looking for a specific one.
I suggest using World#getEntitiesInAABBexcluding to pre-filter the results to item entities whose Item is in the Set.
You should use a for-each/enhanced for loop to iterate through collections like Lists.
Your outer loop incorrectly casts every Entity in the loaded entity list to EntityItem without checking if it actually is an EntityItem.
There's no reason to use @SuppressWarnings("unchecked") here, you're not making any unchecked casts.
Entity positions are doubles, avoid truncating them to ints where possible.
Event handlers are singletons, your class must account for there being any number of matching item sets in the loaded chunks and can't store individual data like a single timer. You need a way to store this data for each set of matching items. This would be a lot easier if one of the items was your own, you could use a custom entity to control the transformation.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
This doesn't work. I can't seem to see what I'm doing wrong.
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
Doesn't work how? Does it crash or does it just not work?
Please don't PM me asking for help, I will just redirect you to the appropriate forum, where there are others who are far more skilled than me.
This is not the signature you are looking for.
Banners and such things
Just doesn't work.
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--
I'm not sure exactly what's preventing it from working, but I do see some mistakes.
I've implemented this in 1.10.2 using some Java 8 features like streams, lambdas and method references here. I didn't include a timer, but that could be done using capabilities.
Chisel Facades: For all your decorative pipe-hiding needs.
Please don't PM me to ask for help or to join your mod development team. Asking your question in a public thread preserves it for people who are having the same problem in the future. I'm not interested in developing mods with people.
I'm still working on 1.8, so I can't use the lambadas, but I updated the code a bit by adding the hashset INPUT and creating a mutable copy of it.
However, I'm getting a ConcurrentModificationException on my outer for-loop line, and I don't know why it's causing it, I thought that error only shows if you try removing an object of a list while looping through it, and I don't loop through my Set.
Here is the error,
and Here is the current code.
I'm not crazy, i'm just not user friendly
--> Click here to get BETA ACCESS to this mod and upcoming server! <--