[Tutorial] Farmables with Falling Particles! (How to do Something Server-Side on Client-Side Particle Hit)

Started by Memebassador on

Topic category: User side tutorials

Last seen on 03:23, 12. Sep 2024
Joined Feb 2024
Points:

User statistics:

  • Modifications:
  • Forum topics:
  • Wiki pages:
  • MCreator plugins:
  • Comments:
[Tutorial] Farmables with Falling Particles! (How to do Something Server-Side on Client-Side Particle Hit)

Here's a question for you: When something hits the ground, how do you make that event cause something else? You might be thinking, "Easy! Use the projectile on-hit trigger, or check when the block below a living entity is not air!" Absolutely. But now imagine that this is for something that is supposed to be farmable. What happens when there are tons of entities in a small area? Think of a poorly designed chicken and egg farm: the game slows to a crawl. For every projectile entity that is in the air, the performance drops. It's even worse for a living entity, since you'd have to check the block beneath it (or the onGround tag) every game tick. So what other options are there?

Well, particles are not entities. Yes, a lot of particles can cause lag, but not how we will be implementing them. One big thing to consider with particles is that they are generated client-side; particles have an expiry trigger that can take advantage of the boolean onGround tag, but you can't do anything with that server-side directly because the particle's code is working on the client-side. More about that at the end.

In my mod, I am giving bats and silverfish a use: When a bat eats (kills) a silverfish, a custom logic NBT tag "CanPoop" is set to True. Prepare to see the word "poop" a lot through the rest of this tutorial. Now we can look at the code:

Code for pooping bats!

You will notice that we save the pre-poop Event/Target entity X, Y, and Z as custom number NBT tags instead of local variables. We do this because "Wait [ ] ticks..." does not support local variables and we cannot use the post-poop Event/Target entity coordinates inside the "Wait [ ] ticks..."  as the guano would then land relative to the current position of the bat, not the initial position of the poop! I know this seems unnecessary: why not just track the position of the particle directly and just see when it hits the ground? Well, because it is impossible.

The Minecraft Wiki has information on the speed particles fall at as a function of time and initial velocity (https://minecraft.wiki/w/Entity#Motion_of_entities). Actually it doesn't, but I can tell you right now that it is the same routine as that used for falling item entities. Even so, the provided equation(s) doesn't work. Minecraft kinematics are actually very complicated so the v(t) (velocity-time) equation alone doesn't tell you everything, and getting h(t) (height-time, or distance-time d(t)) from v(t) does you no good because of that. I tried. And what we need anyway is t(h) (time-height). So there is only one way to do it: see how much time it takes for an item to fall to bedrock at every height at or below the build limit. You could go higher, I just didn't; how would a bat get much higher than the world limit anyway?

So, now we have to define two arrays, one for t and one for h. To save you the trouble of doing what I did, here are those arrays:

Height: int[] d = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383};

Time: int[] t = {0, 2, 7, 11, 13, 15, 17, 19, 20, 22, 23, 25, 26, 27, 28, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 42, 43, 44, 45, 46, 47, 48, 48, 49, 50, 51, 52, 52, 53, 54, 55, 55, 56, 57, 58, 58, 59, 60, 60, 61, 62, 63, 63, 64, 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 73, 73, 74, 75, 75, 76, 77, 77, 78, 79, 79, 80, 80, 81, 82, 82, 83, 84, 84, 85, 85, 86, 87, 87, 88, 88, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 96, 96, 97, 97, 98, 98, 99, 100, 100, 101, 101, 102, 103, 103, 104, 104, 105, 105, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150, 150, 151, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, 162, 162, 163, 164, 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 181, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240, 240, 241};

These are what appear in the first two custom code segments. If you wanted to model a different entity than a falling item/particle for your gravitational acceleration, you would have to do the experiments I have done for that entity. If you are interested, leave a comment and I will update this post with my command block setup used to calculate the fall time for every height.

We are cooking now. In the third custom code segment, we iterate a variable (i) and find where distanceToGround = d(i). We then use that value i to get the fall time t(i) and set fallTime = t(i). Now, we finally have how far and how long to wait until we place the guano block below the poop particle's starting X, Y, Z.

One last thing: When making your custom particle, make sure that you include a procedure to use with "Additional particle expiry condition". Define the procedure as follows:

Additional particle expiry condition procedure.

You might be wondering why we didn't just put our block placing code in this procedure. That is because this event only triggers client-side. If you use the place/replace block procedure here, the block that gets placed only exists on the client-side! That means, it doesn't really exist; if you try to break the block in survival mode, it instantly replaces itself, and if you quit and rejoin the world, the block disappears.

And just like that, you're done! You now have a particle that places a block when it hits the ground. Please let me know how I can edit this post to be more helpful. Happy pooping!

Happy pooping!