Spawn a falling block

Discussion in 'Resources' started by PxlPanda, Aug 14, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    desht

    Been looking at falling blocks with interest, in particular thinking of how to modify them so that they don't fall by default (i.e. not affected by gravity). Looks like it's the h_() method in EntityFallingBlock.java that does that (https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/EntityFallingBlock.java#L56):
    PHP:
    this.motY -= 0.03999999910593033D;
    So... feasible to create an EntityFloatingBlock class which subclasses EntityFallingBlock and overrides the h_() method accordingly? The goal being to create block entities which stay where they are until their velocity is explicitly changed...
     
    Farrael likes this.
  2. Offline

    Icyene

    desht

    Mhm, I've done it with fireballs before to make large fireball trees. Also, its j_ in MC 1.4.X :)
     
    desht likes this.
  3. Offline

    desht

    Minor necro, so I apologise for that, but I found that the lag can be pretty much eliminated (even for huge explosions) by scheduling the spawnFallingBlock() calls so not too many are run per tick. Needs a bit of tuning to get the ideal quantity, but the idea is:
    • For each block in the event.blockList(), allocate it a slot number which is simply a random integer between 1 and N (N being the maximum number of ticks over which this should happen)
    • For each slot (1..N), schedule a syncDelayedTask() which calls spawnFallingBlock() for each block in that slot
    I've set off explosions with a power of 50+ (mass destruction) without noticeable lag, at least on a quiet test server (and it looks very cool). Trying to do that in a single tick (8000+ entities to be spawned) will bring your server to a halt :)
     
  4. Offline

    Shiny Quagsire

    I have actually already done that. :D

    I just extended EntityFallingBlock:
    Code:
    package org.zzl.minegaming.SmoothElevator;
     
    import java.lang.reflect.Field;
    import net.minecraft.server.AxisAlignedBB;
    import net.minecraft.server.Block;
    import net.minecraft.server.BlockPistonMoving;
    import net.minecraft.server.Entity;
    import net.minecraft.server.EntityFallingBlock;
    import net.minecraft.server.MathHelper;
    import net.minecraft.server.World;
    import net.minecraft.server.WorldServer;
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.craftbukkit.CraftWorld;
    import org.bukkit.craftbukkit.entity.CraftPlayer;
    import org.bukkit.entity.Player;
    import org.bukkit.event.player.PlayerTeleportEvent;
    import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
    import org.bukkit.util.Vector;
     
    public class FloatingBlock extends EntityFallingBlock
    {
        boolean ignoreGravity = true;
     
        private float brightness = 0.0F;
     
        public Material getMaterial()
        {
            return Material.getMaterial(this.id);
        }
     
        public int getBlockId() {
            return this.id;
        }
     
        public byte getBlockData()
        {
            return (byte)this.data;
        }
     
        public boolean getDropItem()
        {
            return false;
        }
     
        public void setPassenger(Entity e)
        {
            this.passenger = e;
        }
     
        public void setPassenger(Player p)
        {
            this.passenger = ((CraftPlayer)p).getHandle();
        }
     
        public FloatingBlock(World paramWorld)
        {
            super(paramWorld);
            this.dropItem = false;
        }
     
        public FloatingBlock(World paramWorld, double paramDouble1, double paramDouble2, double paramDouble3, int paramInt)
        {
            super(paramWorld, paramDouble1, paramDouble2, paramDouble3, paramInt);
            setBrightness(Block.lightEmission[this.id]);
            try
            {
                setFinalStatic(getClass().getField("boundingBox"), AxisAlignedBB.a(0.0D, 0.0D, 0.0D, 1.0D, 1.0D, 1.0D));
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
     
        static void setFinalStatic(Field field, Object newValue) throws Exception
        {
            field.setAccessible(true);
     
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.set(field, Integer.valueOf(field.getModifiers() & 0xFFFFFFEF));
     
            field.set(null, newValue);
        }
     
        public FloatingBlock(World paramWorld, double paramDouble1, double paramDouble2, double paramDouble3, int paramInt, int paramInt2)
        {
            super(paramWorld, paramDouble1, paramDouble2, paramDouble3, paramInt, paramInt2);
            setBrightness(Block.lightEmission[this.id]);
        }
     
        @Override
        public void j_()
        {
            this.lastX = this.locX;
            this.lastY = this.locY;
            this.lastZ = this.locZ;
            this.c += 1;
     
            if (!this.ignoreGravity)
            {
                this.motY -= 0.03999999910593033D;
            }
            move(this.motX, this.motY, this.motZ);
            this.motX *= 0.9800000190734863D;
            if (!this.ignoreGravity)
            {
                this.motY *= 0.9800000190734863D;
            }
            this.motZ *= 0.9800000190734863D;
     
            if (!this.world.isStatic) {
                int i = MathHelper.floor(this.locX);
                int j = MathHelper.floor(this.locY);
                int k = MathHelper.floor(this.locZ);
     
                if (this.c == 1) {
                    if ((this.c == 1) && (this.world.getTypeId(i, j, k) == this.id)) {
                        this.world.setTypeId(i, j, k, 0);
                    }
                    else if (!this.ignoreGravity) {
                        die();
                    }
                }
     
                if (this.onGround)
                {
                    this.motX *= 0.699999988079071D;
                    this.motZ *= 0.699999988079071D;
                    if (!this.ignoreGravity)
                    {
                        this.motY *= -0.5D;
                    }
     
                    if ((this.world.getTypeId(i, j, k) != Block.PISTON_MOVING.id) && (!this.ignoreGravity))
                    {
                        die();
                    }
                    else if (((this.c > 100) && (!this.world.isStatic) && ((j < 1) || (j > 256))) || ((this.c > 600) &&
                            (!this.ignoreGravity)))
                    {
                        if (this.dropItem) b(this.id, 1);
                        die();
                    }
                }
            }
        }
     
        public Vector getVelocity() {
            return new Vector(this.motX, this.motY, this.motZ);
        }
     
        public void setVelocity(Vector vel) {
            this.motX = vel.getX();
            this.motY = vel.getY();
            this.motZ = vel.getZ();
            this.velocityChanged = true;
        }
     
        public CraftWorld getWorld() {
            return ((WorldServer)this.world).getWorld();
        }
     
        public boolean teleport(Location location) {
            return teleport(location, PlayerTeleportEvent.TeleportCause.PLUGIN);
        }
     
        public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
            this.world = ((CraftWorld)location.getWorld()).getHandle();
            setLocation(location.getX(), location.getY(), location.getZ(),
                    location.getYaw(), location.getPitch());
     
            return true;
        }
     
        public void setYaw(float yaw) {
            this.yaw = yaw;
        }
     
        public float getYaw() {
            return this.yaw;
        }
     
        public void setPitch(float pitch) {
            this.pitch = pitch;
        }
     
        public float getPitch() {
            return this.pitch;
        }
     
        public Location getLocation()
        {
            return new Location(getWorld(), this.locX, this.locY, this.locZ);
        }
     
        public void setLocation(Location l)
        {
            this.locX = l.getX();
            this.locY = l.getY();
            this.locZ = l.getZ();
        }
     
        public void remove()
        {
            die();
        }
     
        public float c(float f)
        {
            return this.brightness;
        }
     
        public void setBrightness(float brightness) {
            this.brightness = brightness;
        }
     
        public float getBrightness() {
            return this.brightness;
        }
    }
    And overrided it like a normal entity as per this thread.
    Code:
            try{
                @SuppressWarnings("rawtypes")
                Class[] args = new Class[3];
                args[0] = Class.class;
                args[1] = String.class;
                args[2] = int.class;
     
                Method a = net.minecraft.server.EntityTypes.class.getDeclaredMethod("a", args);
                a.setAccessible(true);
     
                a.invoke(a, FloatingBlock.class, "FallingSand", 21);
            }
            catch (Exception e){
                e.printStackTrace();
                this.setEnabled(false);
            }
    However, you have to spawn it the "old fashioned way." I just copied the spawnFallingBlock from CraftServer.java, and modified it to spawn our new block:
    Code:
    public FloatingBlock spawnFallingBlock(Location location, org.bukkit.Material material, byte data) throws IllegalArgumentException {
            Validate.notNull(location, "Location cannot be null");
            Validate.notNull(material, "Material cannot be null");
            Validate.isTrue(material.isBlock(), "Material must be a block");
     
           
           
            double x = location.getBlockX() + 0.5;
            double y = location.getBlockY() + 1.5;
            double z = location.getBlockZ() + 0.5;
            WorldServer world = ((CraftWorld)location.getWorld()).getHandle();
     
            FloatingBlock entity = new FloatingBlock(world, x, y, z, material.getId(), data);
            entity.c = 1; // ticksLived
     
            world.addEntity(entity, SpawnReason.CUSTOM);
            return entity;
        }
     
    chasechocolate, WarmakerT and desht like this.
  5. Offline

    TechGuard

    Did you test this and it worked? Because I tried this before, and it did work server side, but on the client side it fell.
     
  6. Offline

    desht

    I'm seeing the same thing here, although I tried out SmoothElevators and it does work. I think it's working because it's continually giving the spawned block a smooth upward or downward velocity; there's a repeating task running every tick to give the block a velocity: https://github.com/shinyquagsire23/...zl/minegaming/SmoothElevator/Plugin.java#L434

    I've had no luck yet in getting a FloatingBlock to stay static in the air. If I using a repeating task to just give it a velocity of 0 every tick, the client still shows the entity falling to the ground. I've experimented with small upward velocities with partial success - I can get the entity to stay in the air, but it jitters badly and often drifts up or down.

    I'm guessing there is a client-side limitation here, which is disappointing, but I shall continue to experiment...
     
  7. Offline

    Uniclaw

    How can i let move fallingblocks to an specific location? And that he going trough walls?
     
  8. Offline

    desht

    Falling blocks are just entities, so you can use any generic entity methods on them (e.g. teleport(), setVelocity()...). The usual rules apply for moving through solid blocks (i.e. they don't) but you can always teleport them to the other side of a wall if you need to...
     
  9. Offline

    Uniclaw

    Anyone has a idea how to track a fallingblock :( ? So that if he lands, i can remove it :/

    A thing like FallingBlockLandEvent :D

    Because my tracker is very very laggy if there are many falling blocks :/

    Code:
    int s;
        public void trackFB(final FallingBlock fb, final Block b, final Material c){
      s = Bukkit.getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable(){
      public void run(){
      if(fb.getLocation().add(0,-1,0).getBlock().getTypeId() != 0){
      if(fb != null){
      fb.remove();
      }
      fb.getLocation().getBlock().setTypeId(0);
      b.setType(c);
      Bukkit.getScheduler().cancelTask(s);
      }
      }}, 2, 2);
        }
    And with this if the fallingblock fall in water it will not be removed :/fix'd

    And another thing: how can i make that a player can stay on a fallingblock? To make a thing like a elevator :D With setPassenger(Player) the Block is "buggy" - because the block move like if i make setTypeId(0), adding to loaction -1 on y, setType(block) etc..

    And how can i set the velocity so that the block move towards a location :OO?
     
  10. Offline

    desht

    Uniclaw

    You certainly cannot use an async scheduled task to do what you want. Do you want to simply ensure that blocks don't get created when the falling block lands? If so, see this pull request: https://github.com/Bukkit/CraftBukkit/pull/940. The default behaviour is for them to turn into their block form when they land - the entity gets automatically destroyed anyway (that happens in the j_() method of EntityFallingBlock).

    As for an event - there isn't one, but coincidentally I made the suggestion today that one be added in the above pull request. Don't count on that happening, though.

    You should be able to mount the player on a falling block entity with the NMS Player mount() - SmoothElevator does exactly that - https://github.com/shinyquagsire23/...zl/minegaming/SmoothElevator/Plugin.java#L370

    For setting the velocity, this should work (for any entity, in theory):
    PHP:
    // make Entity e move toward Location dest at the given speed
    public void moveToward(Entity eLocation destdouble speed) {
      
    e.setVelocity(dest.toVector().subtract(e.getLocation().toVector()).normalize().multiply(speed));
    }
     
  11. Offline

    Uniclaw

    desht Thanks :D !
    No, i don't would that the entity will not be transformed to an block on landing, but a other moethod that with the sheduler to check if its laded :/

    And i can't mount a player because i become the error "cant cast from FallingBlock to entity" :( !
     
  12. Offline

    desht

    PHP:
    ((CraftPlayer)player).getHandle().mount(((CraftEntity)fb).getHandle());
    should work. You need the NMS classes here, not the Bukkit classes.
     
  13. Offline

    Uniclaw

    desht Ah, thanks :D ! And what is the difference between mount() and setPassenger() ?

    And if i create a new FallingBlokc class, must i register them or can i just use it? For example, if i use the floatingblock class from SmoothElevator?

    And how i can cast from FloatingBlock to Entity?

    Code:
    moveToward(((CraftEntity)fb).getHandle(), FloatMethods.getLatestAirBlock(b.getLocation()), 1D);
    dont works :( (can't cast from FloatingBlock to entity and can't cast from floatingblock to craftentity too..)
     
  14. Offline

    desht

    My moveToward() method uses Bukkit entities.
     
  15. Offline

    Uniclaw

    desht I know ^^ But how can i cast from NMS Entity's to Bukkit Entity's?
     
  16. Offline

    desht

    Use the .getBukkitEntity() method that CraftBukkit kindly added.
     
  17. Offline

    Uniclaw

    desht Thanks :) !

    And another thing: I've now copied the code for a new FloatingBlock from the original Class, but my block not just fall, but fall diagonal - like this:

    x = a quarter if a block.
    c = a quarter of my falling entity

    xxxx xxxx
    xxxx xxxx
    xxcc ccxx
    xxcc ccxx

    xxcc ccxx
    xxcc ccxx
    xxxx xxxx
    xxxx xxxx
    (4blocks)
    Why? How fix it?
    Code:
    @Override
    public void j_() {
    if (this.id == 0) {
    this.die();
    } else {
    this.lastX = this.locX;
    this.lastY = this.locY;
    this.lastZ = this.locZ;
    ++this.c;
    this.motY -= 0.03999999910593033D;
    this.move(this.motX, this.motY, this.motZ);
    this.motX *= 0.9800000190734863D;
    this.motY *= 0.9800000190734863D;
    this.motZ *= 0.9800000190734863D;
    if (!this.world.isStatic) {
    int i = MathHelper.floor(this.locX);
    int j = MathHelper.floor(this.locY);
    int k = MathHelper.floor(this.locZ);
     
    if (this.c == 1) {
    if (this.c == 1 && this.world.getTypeId(i, j, k) == this.id) {
    this.world.setTypeId(i, j, k, 0);
    } else {
    this.die();
    return; // CraftBukkit
    }
    }
     
    if (this.onGround) {
    FloatMethods.landedFB(this, b, mat);
    this.motX *= 0.699999988079071D;
    this.motZ *= 0.699999988079071D;
    this.motY *= -0.5D;
    } else if (this.c > 100 && !this.world.isStatic && (j < 1 || j > 256) || this.c > 600) {
     
    this.die();
    }
    }
    }
    }
     
  18. Offline

    Pavlov

    Is it possible to mount a FallingBlock on a Player or maybe to another FallingBlock. So the fallingBlocks would follow the player. But probably the blocks try still to fall down on client side.
     
  19. Offline

    Shiny Quagsire

    Yes, it is, but it floats about 1/2 block above their head. It is also fun to make the player mount the block and have the block fly in the direction the player is looking, or even better, do it while invisible. :p
     
  20. Offline

    MiniDigger

    Has somebody an FloatingBlock class for 1.7?
     
  21. Offline

    Desle

    MiniDigger
    Dude.. really? This post is 2 years old.. and you dont need a class..
     
  22. Offline

    MiniDigger

    Desle I am trying to spawn an floating block but nothing seems to work. Actually don't looked at the date :D. Why do you thik I don't need a class?
     
  23. Offline

    MrInspector

    Well it is the resource section, i'm pretty sure everything can be bumped as they are used to help people, and if someone has a question, they'd just ask on the thread and hope the owner is alive to reply, or if someone else can help them.
     
    WarmakerT likes this.
Thread Status:
Not open for further replies.

Share This Page