Jump to content

[1.7.10] [SOLVED] Get all blocks in virtual pyramid.


Elix_x

Recommended Posts

Hi there and thanks for looking in this thread...

My problem is: i'm trying to get all entities and blocks in world within virtual rotated pyramid (similar to AABB, but pyramidal form and is rotated), but it goes not respoding and crashes without leaving any message in console.

Here's code that i'm using:

public class PositionnedRotatedRangedVec3 {

private World worldObj;

private double posX;
private double posY;
private double posZ;

private float rotYaw;
private float rotPitch;
private float rotR;

private double range;

public PositionnedRotatedRangedVec3(World worldObj, double posX, double posY, double posZ, float rotationYaw, float rotationPitch, float rotR, double range) {
	setData(worldObj, posX, posY, posZ, rotationYaw, rotationPitch, rotR, range);
}

public void setData(World worldObj, double posX, double posY, double posZ, float rotationYaw, float rotationPitch, float rotR, double range){
	this.worldObj = worldObj;
	this.posX = posX;
	this.posY = posY;
	this.posZ = posZ;
	this.rotYaw = rotationYaw;
	this.rotPitch = rotationPitch;
	this.rotR = rotR;
	this.range = range;

	/*Vec3 vec = Vec3Utils.getLookVec(rotationYaw + rotR, rotationPitch + rotR);
	adjustX = vec.xCoord;
	adjustY = vec.yCoord;
	adjustZ = vec.zCoord;*/
}

public Set<BlockPos> getAffectedBlocks(){
	Set<BlockPos> blocks = new HashSet<BlockPos>();

	Vec3[] vecs = new Vec3[]{Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ)};

	boolean arrived = false;
	while(!arrived){
		double minX = posX;
		double minY = posY;
		double minZ = posZ;
		double maxX = posX;
		double maxY = posY;
		double maxZ = posZ;

		for(int i = 0; i < 5; i++){
			Vec3 vec = vecs[i];

			if(i == 0){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw, rotPitch);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 1){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 2){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 3){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 4){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}

			minX = Math.min(minX, vec.xCoord);
			minY = Math.min(minY, vec.yCoord);
			minZ = Math.min(minZ, vec.zCoord);

			maxX = Math.max(maxX, vec.xCoord);
			maxY = Math.max(maxY, vec.yCoord);
			maxZ = Math.max(maxZ, vec.zCoord);

			vecs[i] = vec;
		}

//			AxisAlignedBB box = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
		for(int i = (int) minX; i <= maxX; i++){
			for(int j = (int) minY; j <= maxY; j++){
				for(int k = (int) minZ; k <= maxZ; k++){
					blocks.add(new BlockPos(i, j, k));
				}
			}
		}

		if(vecs[0].distanceTo(Vec3.createVectorHelper(posX, posY, posZ)) >= range){
			arrived = true;
			break;
		}
	}

	return blocks;
}

public Set<Entity> getAffectedEntities(){
	Set<Entity> entities = new HashSet<Entity>();

	Vec3[] vecs = new Vec3[]{Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ)};

	boolean arrived = false;
	while(!arrived){
		double minX = posX;
		double minY = posY;
		double minZ = posZ;
		double maxX = posX;
		double maxY = posY;
		double maxZ = posZ;

		for(int i = 0; i < 5; i++){
			Vec3 vec = vecs[i];

			if(i == 0){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw, rotPitch);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 1){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 2){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 3){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 4){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}

			minX = Math.min(minX, vec.xCoord);
			minY = Math.min(minY, vec.yCoord);
			minZ = Math.min(minZ, vec.zCoord);

			maxX = Math.max(maxX, vec.xCoord);
			maxY = Math.max(maxY, vec.yCoord);
			maxZ = Math.max(maxZ, vec.zCoord);

			vecs[i] = vec;
		}

		AxisAlignedBB box = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
		entities.addAll(worldObj.getEntitiesWithinAABB(Entity.class, box));
	}

	return entities;
}

}

 

Let me explain here: posX, Y, Z are start point of pyramid. Rot yaw and pitch are rotations of pyramid. Rot r is angle of rotations of borders relative to central axis. And finally range is height of pyramid...

BlockPos is class, that i created to store 3 ints of position.

 

It "crashes" when enters one of 2 methods, to scan for blocks/entities...

 

So my questions are:

Is there already existing methods to do what i want, and if not - what should i use or how should i improve mine...

If it's too hard with pyramids, will it be easier with rotated BB?

 

Thanks for help, and again: if you have any questions, need some more code... - Just ask!

 

Ps: i resolved problem by adding loop break (because i forgot it :D, that's why it went not responding and never stopped :D) and changing some more code to make it pyramid.

Here's what i obtained with height 100, border degree 30:

5C1YyW1.png

Link to comment
Share on other sites

I guess try running in debug mode, or add console statements to trace the execution to see if everything is executing as expected.

Okay, i modified code with console statements:

public class PositionnedRotatedRangedVec3 {

private static Logger logger = LogManager.getLogger("Pyramidal Vec3");

private World worldObj;

private double posX;
private double posY;
private double posZ;

private float rotYaw;
private float rotPitch;
private float rotR;

private double range;

public PositionnedRotatedRangedVec3(World worldObj, double posX, double posY, double posZ, float rotationYaw, float rotationPitch, float rotR, double range) {
	setData(worldObj, posX, posY, posZ, rotationYaw, rotationPitch, rotR, range);
}

public void setData(World worldObj, double posX, double posY, double posZ, float rotationYaw, float rotationPitch, float rotR, double range){
	this.worldObj = worldObj;
	this.posX = posX;
	this.posY = posY;
	this.posZ = posZ;
	this.rotYaw = rotationYaw;
	this.rotPitch = rotationPitch;
	this.rotR = rotR;
	this.range = range;

	/*Vec3 vec = Vec3Utils.getLookVec(rotationYaw + rotR, rotationPitch + rotR);
	adjustX = vec.xCoord;
	adjustY = vec.yCoord;
	adjustZ = vec.zCoord;*/
	logger.info("Created new pyramid: " + this);
}

public Set<BlockPos> getAffectedBlocks(){
	logger.info("Starting blocks section");
	Set<BlockPos> blocks = new HashSet<BlockPos>();

	Vec3[] vecs = new Vec3[]{Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ)};

	boolean arrived = false;

	logger.info("Initialized arrays and booleans, starting loop");
	while(!arrived){
		double minX = posX;
		double minY = posY;
		double minZ = posZ;
		double maxX = posX;
		double maxY = posY;
		double maxZ = posZ;

		logger.info("Updating start, values: " + minX + ", " + minY + ", " + minZ + ", " + maxX + ", " + maxY + ", " + maxZ);

		for(int i = 0; i < 5; i++){
			Vec3 vec = vecs[i];

			logger.info("Next vec: " + vec);

			if(i == 0){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw, rotPitch);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 1){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 2){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 3){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 4){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}

			minX = Math.min(minX, vec.xCoord);
			minY = Math.min(minY, vec.yCoord);
			minZ = Math.min(minZ, vec.zCoord);

			maxX = Math.max(maxX, vec.xCoord);
			maxY = Math.max(maxY, vec.yCoord);
			maxZ = Math.max(maxZ, vec.zCoord);

			logger.info("Next vec initialized: " + vec);

			vecs[i] = vec;
		}

		logger.info("Updating end, values: " + minX + ", " + minY + ", " + minZ + ", " + maxX + ", " + maxY + ", " + maxZ);

//			AxisAlignedBB box = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
		for(int i = (int) minX; i <= maxX; i++){
			for(int j = (int) minY; j <= maxY; j++){
				for(int k = (int) minZ; k <= maxZ; k++){
					logger.info("Adding next block pos: " + new BlockPos(i, j, k));
					blocks.add(new BlockPos(i, j, k));
				}
			}
		}

		logger.info("Checking for scanned distance");

		double dist = vecs[0].distanceTo(Vec3.createVectorHelper(posX, posY, posZ));

		logger.info("Distance to pass: " + range);
		logger.info("Distance passed: " + dist);

		if(dist >= range){
			logger.info("Finished scanning, breaking loop.");
			arrived = true;
			break;
		}
	}

	logger.info("Finished scanning, exited loop.");
	logger.info("Found blocks poss: " + blocks);

	return blocks;
}

public Set<Entity> getAffectedEntities(){
	Set<Entity> entities = new HashSet<Entity>();

	Vec3[] vecs = new Vec3[]{Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ), Vec3.createVectorHelper(posX, posY, posZ)};

	boolean arrived = false;
	while(!arrived){
		double minX = posX;
		double minY = posY;
		double minZ = posZ;
		double maxX = posX;
		double maxY = posY;
		double maxZ = posZ;

		for(int i = 0; i < 5; i++){
			Vec3 vec = vecs[i];

			if(i == 0){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw, rotPitch);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 1){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 2){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch + rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 3){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw + rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}
			if(i == 4){
				Vec3 vec2 = Vec3Utils.getLookVec(rotYaw - rotR, rotPitch - rotR);
				vec = vec.addVector(vec2.xCoord, vec2.yCoord, vec2.zCoord);
			}

			minX = Math.min(minX, vec.xCoord);
			minY = Math.min(minY, vec.yCoord);
			minZ = Math.min(minZ, vec.zCoord);

			maxX = Math.max(maxX, vec.xCoord);
			maxY = Math.max(maxY, vec.yCoord);
			maxZ = Math.max(maxZ, vec.zCoord);

			vecs[i] = vec;
		}

		AxisAlignedBB box = AxisAlignedBB.getBoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
		entities.addAll(worldObj.getEntitiesWithinAABB(Entity.class, box));
	}

	return entities;
}

@Override
public String toString() {
	return "PyramidalVec3{Pos:{" + posX + ", " + posY + ", " + posZ + "}, Rot:{" + rotYaw + ", " + rotPitch + "}, Borders degree: " + rotR + ", Height: " + range + "}";
}

}

 

I'll let it run for few minutes and see what is going on...

 

 

Hmm, that's interesting, it's working fine for small range values, and for once, but maybe each tick is too much...

Nope, each tick is fine for small range values, like 5 - 10.

 

Found good lag source: print in console, trying to remove...

Let's retry with range 25.

... (Time passes, tons of tests)

 

 

Okay it seems to be laggy on range 50+, so i hope nobody will use it.

 

And also it doesn't do it in pyramidal form... Something is wrong with block detection maybe...

Nope, i need to check all calculations, and see what's wrong with vec movement.

Strange, mc uses similar code to get entity looking vec, here i'm just adding 40 degrees to it... Do you have any ideas???

Link to comment
Share on other sites

Looking at your code... you are making the vector pyramid... okay.. You have a pyramid.

 

Then you turn it in a bounding box. Your pyramid is a square now.

 

Then you iterate through every block in that square and a new BlockPos to your map.

 

 

how big is your bounding box?

Its a simple matter of x * x to see how many blocks it is and how long it takes to iterate.

Like a bounding box of 25x25x25 = 15625 new Objects to create in memory, assign to a map, update map.

 

Doing that once per tick I can see how this could potentially getting laggy.

 

Lets just say for arguments sake a blockpos takes up 100 bits of space. you are creating within a tick(50ms) 1562500 bytes of data which is 190kb. which is not too bad.

Times 20 for a second its 3,72MB. which is also not too bad.

But then remember all the vectors you are creating and not cleaning up yourself. Each vector wil aslo be about 100 bits of data each, so now we have 3 vectors * 100 * 15625 = 572kb of data per tick, which is 11mb of data you are creating and discarding.

 

So our tally is already at 15mb per second roughly.

Now you are also adding it all to the hashset individually. This means the hashset has to allocate, check, add, do stuff. Which also costs memory.

 

Then you return the hashset.

 

Overall, I count this as a very expensive operation, that gets exponentially more expensive with each block larger radius(26 large Boudning box takes up 16mb per second, 30 block bounding box 25mb)

 

Now we have the garbage collector. It has to check and verify every object in the set if it's still bound, has a reference somewhere, if not, unset it. But until it does that your ram will fill up with your objects with a rate of 15-25mb the second. if not more.

 

In other words, you are giving your pc a lot of work to do, and I assume you are discarding the hashset each tick too since you are calculating each tick.

 

You are simply giving java and the garbage collector a lot of work to do in terms of memory management.

 

Wouldn' t it be more dreadfully efficient to just save the bounding box since you are returning everything within that cube wether its air or not...

 

How much wood could a woodchuck chuck if a wood chuck could chuck wood - Guybrush Treepwood

 

I wrote my own mod ish... still a few bugs to fix. http://thaumcraft.duckdns.org/downloads/MagicCookies-1.0.6.4.jar

Link to comment
Share on other sites

If you read carefully through code, i'm not converting pyramid in one bounding box, but in tons of small ones that are created each tick from min and max coords written before, just to detect blocks & entities...

Here's aproximative image:

wB7a0jf.png

 

You said, that i'm not cleaning up behind me. So how do i do it? I Can't just nullifiy objects - only references will be nullified...

Link to comment
Share on other sites

Ah, okay. It would still be more efficient to pass an array of bounding boxes though.

A bounding box has 6 ints to store, and you can use them as easy to loop through as you do with your blockpos set. And its more memory efficient.

Looping through and storing block posses only makes sense if you test for specific blocks. And not all blocks like you are doing now.

 

Setting to null actually does make sense. For all your vectors that you store in your array, and in the loop, set them to null when you are done.

Those are the only existing references to that object. By setting it to null you are actively saying that they are ready to be cleaned up. That way the garbage collector has a few references less to check.

 

You can also set them to final, then the gc also has an easier job to check stuff because it's not mutable. I prefer nulling stuff, then I know what state a thing is in what point in code. And nullpointer exceptions can help track variables through a maze of code if it does get passed by reference when it shouldn't.

 

You are initialising the vectors, so you can clean them up because after the method they are not needed, they are not passed on to other objects, so you can safely discard them, telling the gc it can clean them up.

 

 

Now back to your code. I would simply make an array of bounding boxes, because looking at your bounding box reference they are overlapping, so you would have a lot of overlapping spaces.

 

Then you can use the bounding boxes to calculate trails that you can iterate over.

I'm doing this from memory, so not all method names will be correct but you can get the gist.

 

//bounding boxes being the array you iterate over from your first vector set
// preinitialising ints so we only take up ten spots on the stack.
int c,x,y,z,startx,starty,startz,endx,endy,endz;
boolean escapeHatch = false;;
for(c=0;c<boundingboxes.length;c++) {
      escapeHatch = false;
      AxisAlignedBB current = boundingboxes[c];
    
      else {
           startx = current.minX < current.maxX ? current.minX : current.maxX;
           starty = current.minY < current.maxY ? current.minY : current.maxY;
           startz = current.minZ < current.maxZ ? current.minZ : current.maxZ;
           endx = current.minX > current.maxX ? current.minX : current.maxX;
           endy = current.minY > current.maxY ? current.minY : current.maxY;
           endz = current.minZ > current.maxZ ? current.minZ : current.maxZ;

           for(x=startx;x<endx;x++) {
               for(y=starty;y<endy;y++) {
                   for(z=startz;z<endz;z++) {
                         if(boundingboxes[c+1].intersectsWith(x,y,z)) {
                              // See if you will make a new object if it needs it or you simply check manually(more memory efficient)
                              //check if we intersect with the next one, which will always be larger, thus preferable I really think this one is messed up in my memory 
                             escapeHatch  = true; // let us know we need to escape!
                                 break;// continue to the preferable set. 
                         }

                        //do whatever you ultimately need to do with all the blocks. Setting a meteor on them seems fun?
                   }
              if(escapeHatch) {break;}
              }
              if(escapeHatch) {break;}
           }
          if(escapeHatch) {break;}
      }
      
}

How much wood could a woodchuck chuck if a wood chuck could chuck wood - Guybrush Treepwood

 

I wrote my own mod ish... still a few bugs to fix. http://thaumcraft.duckdns.org/downloads/MagicCookies-1.0.6.4.jar

Link to comment
Share on other sites

Actually:

1) it depends on conditions of player, for block to be removed.

2) I managed to find lag problem: entities detection (blocks one doesn't cause any lag at all, with nullifying)

3) It happens because, it's iterating through everybody. There's no other way? Or there's?

Link to comment
Share on other sites

Entities can be a pain yea. Is it only living entities?

 

Try worldObj.getEntitiesWithinAABB(Boundingbox)

 

That way only entities within the bounding box are selected.

How much wood could a woodchuck chuck if a wood chuck could chuck wood - Guybrush Treepwood

 

I wrote my own mod ish... still a few bugs to fix. http://thaumcraft.duckdns.org/downloads/MagicCookies-1.0.6.4.jar

Link to comment
Share on other sites

Entities can be a pain yea. Is it only living entities?

 

Try worldObj.getEntitiesWithinAABB(Boundingbox)

 

That way only entities within the bounding box are selected.

I know that. I was speaking about scanning them for each bounding box is too much... And i found a  way to avoid it: in the beginning i collect all entities in range in special list, and then, in each bounding box, just checking entities from list. Like that i'm doing worldObj.getEntitiesWithinAABB(Boundingbox) only once...

 

And it works, without lag on range 25!!!

 

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.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • My modded minecraft world don't go past 100% on the loading screen. My backup still work, but i've made a lot of progress from the point i was that save. i have already checked online and did some suggestions i found, but nothing worked. Here is my latest log + i don´t have crash reports: https://github.com/GmsantosPT/Minecraft-mod-problems/tree/main   Pls Help, Santos
    • Please share a link to your crash report, as explained in the FAQ
    • I have a block already registered in my mod, which I have used in some worlds. The problem arises when in code, I add a property called CURRENT_AGE, when running Minecraft it freezes. In the console it doesn't appear any excpetion except that it stays in this phase: [Render thread/DEBUG] [ne.mi.co.ca.CapabilityManager/CAPABILITIES]: Attempting to automatically register: Lnet/minecraftforge/items/IItemHandler; Does anyone have an idea what it could be? I show the block and its registration public class SoulLichenBlock extends MultifaceBlock implements SimpleWaterloggedBlock, EntityBlock { public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; public static final IntegerProperty SKILL_LEVEL = IntegerProperty.create("soullichen_level", 0, 30); public static final DirectionProperty FACE = DirectionProperty.create("soullichen_face"); public static final DirectionProperty DIRECTION = DirectionProperty.create("soullichen_direction"); public static final IntegerProperty CURRENT_AGE = BlockStateProperties.AGE_25; private final MultifaceSpreader spreader = new MultifaceSpreader(this); private final MultifaceSpreader.DefaultSpreaderConfig config = new MultifaceSpreader.DefaultSpreaderConfig(this); private LivingEntity owner; //private static final Integer MAX_AGE = 25; public SoulLichenBlock(Properties properties) { super(properties); this.registerDefaultState(this .defaultBlockState() .setValue(WATERLOGGED, Boolean.FALSE) .setValue(SKILL_LEVEL, 0) .setValue(FACE, Direction.DOWN) .setValue(DIRECTION, Direction.DOWN) .trySetValue(CURRENT_AGE, 0) ); } public static ToIntFunction<BlockState> emission(int p_181223_) { return (p_181221_) -> MultifaceBlock.hasAnyFace(p_181221_) ? p_181223_ : 0; } public static boolean hasFace(BlockState p_153901_, @NotNull Direction p_153902_) { BooleanProperty booleanproperty = getFaceProperty(p_153902_); return p_153901_.hasProperty(booleanproperty) && p_153901_.getValue(booleanproperty); } protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> stateDefinition) { stateDefinition.add(WATERLOGGED).add(SKILL_LEVEL).add(FACE).add(DIRECTION).add(CURRENT_AGE); super.createBlockStateDefinition(stateDefinition); } public @NotNull BlockState updateShape(BlockState p_153302_, @NotNull Direction p_153303_, @NotNull BlockState p_153304_, @NotNull LevelAccessor p_153305_, @NotNull BlockPos p_153306_, @NotNull BlockPos p_153307_) { if (p_153302_.getValue(WATERLOGGED)) { p_153305_.scheduleTick(p_153306_, Fluids.WATER, Fluids.WATER.getTickDelay(p_153305_)); } return super.updateShape(p_153302_, p_153303_, p_153304_, p_153305_, p_153306_, p_153307_); } @SuppressWarnings("deprecation") public @NotNull FluidState getFluidState(BlockState fluidState) { return fluidState.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(fluidState); } public boolean propagatesSkylightDown(BlockState p_181225_, @NotNull BlockGetter blockGetter, @NotNull BlockPos blockPos) { return p_181225_.getFluidState().isEmpty(); } public @NotNull MultifaceSpreader getSpreader() { return this.spreader; } public Optional<MultifaceSpreader.SpreadPos> spreadFromRandomFaceTowardRandomDirection( BlockState p_221620_, LevelAccessor p_221621_, BlockPos p_221622_, RandomSource p_221623_, int skillPoints, int age) { return Direction.allShuffled(p_221623_).stream().filter((p_221680_) -> { return this.config.canSpreadFrom(p_221620_, p_221680_); }).map((p_221629_) -> { return this.spreadFromFaceTowardRandomDirection(p_221620_, p_221621_, p_221622_, p_221629_, p_221623_, false, skillPoints, age); }).filter(Optional::isPresent).findFirst().orElse(Optional.empty()); } public Optional<MultifaceSpreader.SpreadPos> spreadFromFaceTowardRandomDirection( BlockState blockState, LevelAccessor levelAccessor, BlockPos blockPos, Direction face, RandomSource randomSource, boolean aBoolean, int skillPoints, int age) { return Direction.allShuffled(randomSource).stream().map((direction) -> spreadFromFaceTowardDirection(blockState, levelAccessor, blockPos, face, direction, aBoolean, skillPoints, age)) .filter(Optional::isPresent) .findFirst() .orElse(Optional.empty()); } public Optional<MultifaceSpreader.SpreadPos> spreadFromFaceTowardDirection( BlockState blockState, LevelAccessor levelAccessor, BlockPos blockPos, Direction face, Direction direction, boolean aBoolean, int skillPoints, int age) { //DevilRpg.LOGGER.debug("BEGIN ==================================== spreadFromFaceTowardDirection skillPoints {}", skillPoints); return skillPoints < 0 ? Optional.empty() : getSpreadFromFaceTowardDirection(blockState, levelAccessor, blockPos, face, direction, this::canSpreadInto) .flatMap((spreadPos) -> { //DevilRpg.LOGGER.debug("END ================================ spreadFromFaceTowardDirection spreadPos {}", spreadPos); return this.spreadToFace(levelAccessor, spreadPos, aBoolean, skillPoints, direction, age); }); } public boolean canSpreadInto(BlockGetter p_221685_, BlockPos p_221686_, MultifaceSpreader.SpreadPos p_221687_) { BlockState blockstate = p_221685_.getBlockState(p_221687_.pos()); return this.stateCanBeReplaced(p_221685_, p_221686_, p_221687_.pos(), p_221687_.face(), blockstate) && isValidStateForPlacement(p_221685_, blockstate, p_221687_.pos(), p_221687_.face()); } protected boolean stateCanBeReplaced(BlockGetter p_221688_, BlockPos p_221689_, BlockPos p_221690_, Direction p_221691_, BlockState p_221692_) { return p_221692_.isAir() || p_221692_.is(this) || p_221692_.is(Blocks.WATER) && p_221692_.getFluidState().isSource(); } public Optional<MultifaceSpreader.SpreadPos> getSpreadFromFaceTowardDirection(BlockState blockState, BlockGetter blockGetter, BlockPos blockPos, Direction face, Direction direction, MultifaceSpreader.SpreadPredicate spreadPredicate) { //DevilRpg.LOGGER.debug("--- getSpreadFromFaceTowardDirection direction.getAxis() == face.getAxis(): {}", direction.getAxis() == face.getAxis()); ArrayList<Direction> directions = new ArrayList<>(); directions.add(direction); if (direction.getAxis() == face.getAxis()) { if (direction.getAxis().isHorizontal()) { directions = Arrays.stream(Direction.values()).filter(dir -> dir.getAxis().isVertical()).collect(Collectors.toCollection(ArrayList::new)); } if (direction.getAxis().isVertical()) { directions = Arrays.stream(Direction.values()).filter(dir -> dir.getAxis().isHorizontal()).collect(Collectors.toCollection(ArrayList::new)); } } for (Direction directionElement : directions) { /*DevilRpg.LOGGER.debug("--->> getSpreadFromFaceTowardDirection config.isOtherBlockValidAsSource(blockState) {} || " + "hasFace(blockState, face) {} && " + "!hasFace(blockState, direction) {}", config.isOtherBlockValidAsSource(blockState), hasFace(blockState, face), !hasFace(blockState, directionElement));*/ if (config.isOtherBlockValidAsSource(blockState) || hasFace(blockState, face) && !hasFace(blockState, directionElement)) { for (MultifaceSpreader.SpreadType multifacespreader$spreadtype : config.getSpreadTypes()) { MultifaceSpreader.SpreadPos multifacespreader$spreadpos = multifacespreader$spreadtype.getSpreadPos(blockPos, directionElement, face); //DevilRpg.LOGGER.debug("--- test SpreadPos: {} direction {} face {} ", multifacespreader$spreadpos, directionElement, face); if (spreadPredicate.test(blockGetter, blockPos, multifacespreader$spreadpos)) { //DevilRpg.LOGGER.debug("--- spreadPredicate success:"); return Optional.of(multifacespreader$spreadpos); } } } } return Optional.empty(); } public boolean isValidStateForPlacement(@NotNull BlockGetter blockGetter, @NotNull BlockState blockState, @NotNull BlockPos blockPos, @NotNull Direction face) { //DevilRpg.LOGGER.debug("------ isValidStateForPlacement 1st condition: {} && ({} || {})", this.isFaceSupported(face), !blockState.is(this), !hasFace(blockState, face)); if (this.isFaceSupported(face) && (!blockState.is(this) || !hasFace(blockState, face))) { BlockPos blockpos = blockPos.relative(face); //DevilRpg.LOGGER.debug("------ isValidStateForPlacement 2nd condition: canAttachTo {} ", secondCondition); return canAttachTo(blockGetter, face, blockpos, blockGetter.getBlockState(blockpos)); } else { return false; } } @Nullable public BlockState getStateForPlacement(@NotNull BlockState blockState, @NotNull BlockGetter blockGetter, @NotNull BlockPos blockPos, @NotNull Direction face, int skillPoints, Direction direction, int age) { //DevilRpg.LOGGER.debug("--- getStateForPlacement"); boolean isNotValidStateForPlacement = !this.isValidStateForPlacement(blockGetter, blockState, blockPos, face); //DevilRpg.LOGGER.debug("------- isNotValidStateForPlacement: {}", isNotValidStateForPlacement); if (isNotValidStateForPlacement) { return null; } else { BlockState blockstate; if (blockState.is(this)) { blockstate = blockState; } else if (this.isWaterloggable() && blockState.getFluidState().isSourceOfType(Fluids.WATER)) { blockstate = this.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED, Boolean.TRUE); } else { blockstate = this.defaultBlockState(); } //DevilRpg.LOGGER.debug("------- getStateForPlacement -> blockStateResult "); return blockstate .setValue(getFaceProperty(face), Boolean.TRUE) .setValue(SKILL_LEVEL, skillPoints).setValue(FACE, face) .setValue(DIRECTION, direction) .setValue(CURRENT_AGE,age) ; } } public Optional<MultifaceSpreader.SpreadPos> spreadToFace(LevelAccessor levelAccessor, MultifaceSpreader.SpreadPos spreadPos, boolean p_221596_, int skillPoints, Direction direction, int age) { BlockState blockstate = levelAccessor.getBlockState(spreadPos.pos()); //DevilRpg.LOGGER.debug("---> spreadToFace blockstate{} direction: {}", blockstate, direction); return this.placeBlock(levelAccessor, spreadPos, blockstate, p_221596_, skillPoints, direction, age) ? Optional.of(spreadPos) : Optional.empty(); } public boolean placeBlock(LevelAccessor p_221702_, MultifaceSpreader.SpreadPos p_221703_, BlockState p_221704_, boolean p_221705_, int skillPoints, Direction direction, int age) { //DevilRpg.LOGGER.debug("---> placeBlock {} direction {} ", p_221703_, direction); BlockState blockstate = this.getStateForPlacement(p_221704_, p_221702_, p_221703_.pos(), p_221703_.face(), skillPoints, direction, age); if (blockstate != null) { if (p_221705_) { p_221702_.getChunk(p_221703_.pos()).markPosForPostprocessing(p_221703_.pos()); } //DevilRpg.LOGGER.debug("------> setBlock"); return p_221702_.setBlock(p_221703_.pos(), blockstate, 2); } else { return false; } } public long spreadFromFaceTowardAllDirections( BlockState blockState, LevelAccessor levelAccessor, BlockPos blockPos, Direction face, boolean aBoolean, int skillPoints, int age) { return Direction.stream().map((p_221656_) -> spreadFromFaceTowardDirection(blockState, levelAccessor, blockPos, face, p_221656_, aBoolean, skillPoints, age)) .filter(Optional::isPresent).count(); } private boolean isWaterloggable() { return this.stateDefinition.getProperties().contains(BlockStateProperties.WATERLOGGED); } @Override public void setPlacedBy(@NotNull Level level, @NotNull BlockPos blockPos, @NotNull BlockState blockState, @Nullable LivingEntity livingEntity, @NotNull ItemStack itemStack) { super.setPlacedBy(level, blockPos, blockState, livingEntity, itemStack); this.setOwner(livingEntity); } public LivingEntity getOwner() { return this.owner; } private void setOwner(LivingEntity livingEntity) { this.owner = livingEntity; } @Deprecated @Override public void entityInside(@NotNull BlockState blockState, @NotNull Level level, @NotNull BlockPos blockPos, @NotNull Entity entity) { if (entity instanceof LivingEntity /*&& entity.getType() != EntityType.BEE*/ && entity.getType() != ModEntities.LICHEN_SEEDBALL.get()) { entity.makeStuckInBlock(blockState, new Vec3(0.8D, 0.75D, 0.8D)); if (!level.isClientSide /*&& (entity.xOld != entity.getX() || entity.zOld != entity.getZ())*/) { // double d0 = Math.abs(entity.getX() - entity.xOld); // double d1 = Math.abs(entity.getZ() - entity.zOld); // if (d0 >= (double) 0.003F || d1 >= (double) 0.003F) { entity.hurt(level.damageSources().playerAttack((Player) owner), 1.0F); // Aplicar aceleración al movimiento double speedBoost = -0.4; // Ajusta este valor según lo rápido que quieras que sea el impulso double motionX = entity.getX() - entity.xOld; double motionZ = entity.getZ() - entity.zOld; double speed = Math.sqrt(motionX * motionX + motionZ * motionZ); //if (speed > 0.0) { entity.setDeltaMovement(entity.getDeltaMovement().multiply( (motionX / speed) * speedBoost, 0.0, (motionZ / speed) * speedBoost )); // } //} } } } @Nullable @Override public BlockEntity newBlockEntity(@NotNull BlockPos pos, @NotNull BlockState state) { return ModEntityBlocks.SOUL_LICHEN_ENTITY_BLOCK.get().create(pos, state); } @Nullable @Override public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, @NotNull BlockState blockState, @NotNull BlockEntityType<T> type) { return level.isClientSide ? null : (alevel, pos, aBlockstate, blockEntity) -> { if (blockEntity instanceof SoulLichenBlockEntity soulLichenBlockEntity && alevel.getGameTime() % 5 == 0) { soulLichenBlockEntity.tick(blockState, (ServerLevel) alevel, pos, alevel.getRandom()); //DevilRpg.LOGGER.info("-------->tick. this: {}", this.getClass().getSimpleName()); } }; } }   This is the registration:   public final class ModBlocks { public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, DevilRpg.MODID); ... public static final RegistryObject<SoulLichenBlock> SOUL_LICHEN_BLOCK = BLOCKS.register("soullichen", () -> new SoulLichenBlock( Block.Properties.copy(Blocks.GLOW_LICHEN).lightLevel(SoulLichenBlock.emission(7)).randomTicks() )); }  
    • If you are using AMD/ATI, check for driver updates on their website - do not update via system
    • Hi, Create a new class that extends "Block" class and you need json for it in resources/assets/modid/blockstates directory and resources/assets/modid/models. You can generate json for it using a tool like misodes model generator. Here, atleast, are blocks explained at forge docs.  Don't forge to look at vanilla code, like Magma Block is a good reference if you're trying a "green fire block".  Modid should be replaced with your actual forge mod namespace!
  • Topics

×
×
  • Create New...

Important Information

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