[Tutorial] [1.7.5] WASD Entity Riding

Discussion in 'Resources' started by DSH105, Jul 26, 2013.

Thread Status:
Not open for further replies.
  1. Note: This tutorial is for 1.7.5-1.7.9.

    Warning: If you are copying and pasting the finished code, you probably shouldn’t be using this in the first place.

    I have received a few requests on how to implement the WASD Entity Riding (as done in one of my plugins), so I am posting this here for everyone to use. However, before continuing with this tutorial, I advise that you read and completely understand this tutorial by Jacek to extend your own Entity. Keep in mind; your custom entity must extend EntityLiving (so extending net.minecraft.server.EntityZombie will work just fine).

    After creating your custom entity, we can start on making our entity 'rideable'. First off, Override the e(float.class, float.class) method from EntityLiving, as below. Here I will name my first variable sideMot and my second, forMot.
    Code:java
    1.  
    2. @Override
    3. public void e(float sideMot, float forMot) {
    4.  
    5. }
    6.  


    Now, because we only want Players to control the entity whilst riding, add in a check to see if the passenger is a human. We can also check to see if the Human riding is a certain player. In the code below, I check to see if the player riding is _DSH105_ (me :D).
    Code:java
    1.  
    2. @Override
    3. public void e(float sideMot, float forMot) {
    4. if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
    5. super.e(sideMot, forMot);
    6. this.W = 0.5F; // Make sure the entity can walk over half slabs, instead of jumping
    7. return;
    8. }
    9.  
    10. EntityHuman human = (EntityHuman) this.passenger;
    11. if (human.getBukkitEntity() != Bukkit.getPlayerExact("_DSH105_").getPlayer()) {
    12. // Same as before
    13. super.e(sideMot, forMot);
    14. this.W = 0.5F;
    15. return;
    16. }
    17.  
    18. }
    19.  


    Now that we have excluded the occurrence of any other passengers, we can move on to making the entity move and jump as the Player rider does. Most of the following code is taken from EntityHorse.

    First, change the custom entity's Pitch and Yaw to match that of the Rider.
    Code:java
    1.  
    2. public void e(float sideMot, float forMot) {
    3. if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
    4. super.e(sideMot, forMot);
    5. this.W = 0.5F; // Make sure the entity can walk over half slabs, instead of jumping
    6. return;
    7. }
    8.  
    9. EntityHuman human = (EntityHuman) this.passenger;
    10. if (human.getBukkitEntity() != Bukkit.getPlayerExact("_DSH105_").getPlayer()) {
    11. // Same as before
    12. super.e(sideMot, forMot);
    13. this.W = 0.5F;
    14. return;
    15. }
    16.  
    17. this.lastYaw = this.yaw = this.passenger.yaw;
    18. this.pitch = this.passenger.pitch * 0.5F;
    19.  
    20. // Set the entity's pitch, yaw, head rotation etc.
    21. this.b(this.yaw, this.pitch); //[url]https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/Entity.java#L163-L166[/url]
    22. this.aO = this.aM = this.yaw;
    23.  
    24. }
    25.  


    Next, get the passenger's motion and apply it to the custom entity. To make it realistic, we can also slow down backwards and sideways motion, and allow them to climb 1 high blocks automatically.
    Code:java
    1.  
    2. public void e(float sideMot, float forMot) {
    3. if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
    4. super.e(sideMot, forMot);
    5. this.W = 0.5F; // Make sure the entity can walk over half slabs, instead of jumping
    6. return;
    7. }
    8.  
    9. EntityHuman human = (EntityHuman) this.passenger;
    10. if (human.getBukkitEntity() != Bukkit.getPlayerExact("_DSH105_").getPlayer()) {
    11. // Same as before
    12. super.e(sideMot, forMot);
    13. this.W = 0.5F;
    14. return;
    15. }
    16.  
    17. this.lastYaw = this.yaw = this.passenger.yaw;
    18. this.pitch = this.passenger.pitch * 0.5F;
    19.  
    20. // Set the entity's pitch, yaw, head rotation etc.
    21. this.b(this.yaw, this.pitch); //[url]https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/Entity.java#L163-L166[/url]
    22. this.aO = this.aM = this.yaw;
    23.  
    24. this.W = 1.0F; // The custom entity will now automatically climb up 1 high blocks
    25.  
    26. sideMot = ((EntityLiving) this.passenger).bd * 0.5F;
    27. forMot = ((EntityLiving) this.passenger).be;
    28.  
    29. if (forMot <= 0.0F) {
    30. forMot *= 0.25F; // Make backwards slower
    31. }
    32. sideMot *= 0.75F; // Also make sideways slower
    33.  
    34. float speed = 0.35F; // 0.2 is the default entity speed. I made it slightly faster so that riding is better than walking
    35. this.i(speed); // Apply the speed
    36. super.e(sideMot, forMot); // Apply the motion to the entity
    37.  
    38. }
    39.  


    A note on applying speed: Accessing the GenericAttribute for speed can be done using the following code (where entity is your entity instance):

    Code:java
    1.  
    2. entity.getAttributeInstance(GenericAttributes.d).getValue(); // Returns a double
    3.  


    And there you have it, an entity that can be controlled by the Player riding it :).

    If you wish to continue this further and implement jumping (by hitting the Space Bar), follow the rest of this tutorial below.
    Show Spoiler

    After a quick search, we can see here that the jumping mechanism is controlled by the field bd, confirmed by the Method Profiler. We can access this using reflection.
    Code:java
    1.  
    2. Field jump = null;
    3. jump = EntityLiving.class.getDeclaredField("bc");
    4. jump.setAccessible(true);
    5.  

    To make the custom entity jump when the player jumps, we can use the following.
    Code:java
    1.  
    2. if (jump != null && this.onGround) { // Wouldn't want it jumping while on the ground would we?
    3. try {
    4. if (jump.getBoolean(this.passenger)) {
    5. double jumpHeight = 0.5D;
    6. this.motY = jumpHeight; // Used all the time in NMS for entity jumping
    7. }
    8. } catch (IllegalAccessException e) {
    9. e.printStackTrace();
    10. }
    11. }
    12.  


    So once inserted into the method explained in the main section of this post, it should finally look like this:
    Code:java
    1.  
    2. public void e(float sideMot, float forMot) {
    3. if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
    4. super.e(sideMot, forMot);
    5. this.W = 0.5F; // Make sure the entity can walk over half slabs, instead of jumping
    6. return;
    7. }
    8.  
    9. EntityHuman human = (EntityHuman) this.passenger;
    10. if (human.getBukkitEntity() != Bukkit.getPlayerExact("_DSH105_").getPlayer()) {
    11. // Same as before
    12. super.e(sideMot, forMot);
    13. this.W = 0.5F;
    14. return;
    15. }
    16.  
    17. this.lastYaw = this.yaw = this.passenger.yaw;
    18. this.pitch = this.passenger.pitch * 0.5F;
    19.  
    20. // Set the entity's pitch, yaw, head rotation etc.
    21. this.b(this.yaw, this.pitch); //[url]https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/Entity.java#L163-L166[/url]
    22. this.aO = this.aM = this.yaw;
    23.  
    24. this.W = 1.0F; // The custom entity will now automatically climb up 1 high blocks
    25.  
    26. sideMot = ((EntityLiving) this.passenger).bd * 0.5F;
    27. forMot = ((EntityLiving) this.passenger).be;
    28.  
    29. if (forMot <= 0.0F) {
    30. forMot *= 0.25F; // Make backwards slower
    31. }
    32. sideMot *= 0.75F; // Also make sideways slower
    33.  
    34. float speed = 0.35F; // 0.2 is the default entity speed. I made it slightly faster so that riding is better than walking
    35. this.i(speed); // Apply the speed
    36. super.e(sideMot, forMot); // Apply the motion to the entity
    37.  
    38.  
    39. Field jump = null;
    40. jump = EntityLiving.class.getDeclaredField("bc");
    41. jump.setAccessible(true);
    42.  
    43. if (jump != null && this.onGround) { // Wouldn't want it jumping while on the ground would we?
    44. try {
    45. if (jump.getBoolean(this.passenger)) {
    46. double jumpHeight = 0.5D;
    47. this.motY = jumpHeight; // Used all the time in NMS for entity jumping
    48. }
    49. } catch (IllegalAccessException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. }
    54.  



    I hope this helps someone, and please correct me on anything above if I am wrong :)
     
    ChipDev, MrKeals, MCCoding and 27 others like this.
  2. Going to be useful in a few plugins I am making. Thanks, great tutorial!
     
    _DSH105_ and hawkfalcon like this.
  3. I always wondered if this was possible. Thanks :)
     
  4. This is gonna be epic :eek:
     
  5. Offline

    Miro

    I was just looking through EntityHorse yesterday, but couldn't find how this worked, thanks alot for this tutorial!
     
  6. Offline

    Omerrg

    YOU ARE A GENIUS HAHAHAHA. ;)
     
  7. Offline

    Miro

    One Question though: How would i use this on for example a bat, since the EntityBat uses bh() instead of e() (because of the flying?)
     
  8. Miro I am not entirely sure about that, sorry. The way, i implemented it in my plugin, I actually left out the bh() method, resulting in a ground-dwelling Bat (which controllable through e(f, f1)). In theory, it should work, but I haven't tested it.
     
  9. Offline

    Miro

    I got it somewhat working, but the bat keeps its "awkward" flying (doesnt run in a straight line on floor), the bat has to disable bh() once a player mounts it or else it will become uncontrollable and has its own ai.

    this is my current code if anyone wants it:
    Code:java
    1. public class PetBat extends EntityBat{
    2.  
    3. public PetBat(World world) {
    4. super(world);
    5. this.a(0.5F, 0.9F);
    6. this.a(false);
    7. }
    8.  
    9. @Override
    10. public boolean e(){
    11. return true;
    12. }
    13.  
    14.  
    15. @Override
    16. public void e(float sideMot, float forMot) {
    17. if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
    18. super.e(sideMot, forMot);
    19. this.Y = 0.5F; // Make sure the entity can walk over half slabs, instead of jumping
    20. return;
    21. }
    22.  
    23. this.lastYaw = this.yaw = this.passenger.yaw;
    24. this.pitch = this.passenger.pitch * 0.5F;
    25.  
    26. // Set the entity's pitch, yaw, head rotation etc.
    27. this.b(this.yaw, this.pitch); //[url]https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/Entity.java#L155-L158[/url]
    28. this.aP = this.aN = this.yaw;
    29.  
    30. this.Y = 1.0F; // The custom entity will now automatically climb up 1 high blocks
    31.  
    32. sideMot = ((EntityLiving) this.passenger).be * 0.5F;
    33. forMot = ((EntityLiving) this.passenger).bf;
    34.  
    35. if (forMot <= 0.0F) {
    36. forMot *= 0.25F; // Make backwards slower
    37. }
    38. sideMot *= 0.75F; // Also make sideways slower
    39.  
    40. //speed doesnt work for bat
    41. float speed = 0.5F; // 0.2 is the default entity speed. I made it slightly faster so that riding is better than walking
    42. this.i(speed); // Apply the speed
    43. this.bf = 1F;
    44. super.e(sideMot, forMot); // Apply the motion to the entity
    45.  
    46. Field jump = null;
    47. try {
    48. jump = EntityLiving.class.getDeclaredField("bd");
    49. } catch (NoSuchFieldException e1) {
    50. // TODO Auto-generated catch block
    51. e1.printStackTrace();
    52. } catch (SecurityException e1) {
    53. // TODO Auto-generated catch block
    54. e1.printStackTrace();
    55. }
    56. jump.setAccessible(true);
    57.  
    58. if (jump != null) { // Wouldn't want it jumping while on the ground would we?
    59. try {
    60. if (jump.getBoolean(this.passenger)) {
    61.  
    62. double jumpHeight = 0.5D;
    63. this.motY = jumpHeight; // Used all the time in NMS for entity jumping
    64. }
    65. e.printStackTrace();
    66. }
    67. }
    68.  
    69. }
    70.  
    71. @Override
    72. protected void bh() {
    73. if(this.passenger == null){
    74. super.bh();
    75. }
    76. }
    77.  
    78. }


    The only problem is: i have no clue how to change the speed of the bat (or health)
     
  10. Miro
    Yeah, that's what I mentioned before, as EntityBat uses motX, motY etc to control the random movement. You can visit my implementation here.

    Also, what do you mean about changing the speed? this.i(float.class) changes the ride speed. If you wish to control the Bat's path movement, look into it's Navigation. nav.a(int.class, int.class, int.class, float.class) can achieve that. For health, is this what you mean?
     
  11. Offline

    Miro

    well, the this.i() doesnt actually change the speed of it flying,running(mounted or unmounted).. so yeh...
    And for the health, i found this:
    Code:java
    1. @Override
    2. public void ay() {
    3. super.ay();
    4. this.getAttributeInstance(GenericAttributes.a).setValue(12.0D); // 6 health
    5. }

    But the GeneraticAttributes.d doesnt change the speed either.
    Offtopic: damn, i keep pressing the reply button instead of edit.. lol..
     
  12. Finally my dream came true, now I can ride a chicken!
     
  13. Offline

    BR3TON

    Would this work for an Enderdragon?
     
  14. I doubt. Enderdragon is very complex entity.
     
    jb_aero and _DSH105_ like this.
  15. _DSH105_
    I'm trying to do this from my own human NPC class (I want to ride a player), extending EntityPlayer (which extends EntityHuman, which extends EntityLiving). In the NPC class, I'm overriding the e(float f, float f1)-function - my code that's overriding, however, is never executed... Any ideas on what I'm doing wrong?
     
  16. It should be called whenever you're riding the entity...I'll have a look into this when I can.
     
  17. _DSH105_
    Thank you. I'll also try to solve this - please let me know if you have solved it (I will do the same, of course :)).
     
  18. I have a working EnderDragon which is controllable whilst riding. You can see my class here. Basically, it just performs similar calculations in the c() method.
     
  19. Offline

    iZanax

    _DSH105_

    I got a few questions about this resource.
    • I want to have an acceleration factor.
    • I want to set a maximum speed.
    • I want to disable normal walk behavior.
    • I want to set a maximum health
    So for instance I create an enitity Pig.
    That have the following criteria:
    - maximum speed 0.5F
    - maximum health 100.0D
    - acceleration 0.1F (like 5 seconds to reach max speed)
    - disable normal walk around behavior of the pig

    Thanks in advance!
    Zanax
     
  20. 1. speed = speed + 1;
    2. if(speed > 5) speed = 5;
    3. Override the entity's pathfinding(read the tutorials in this section).
    4. Can't you already do this just with bukkit?
     
    _DSH105_ likes this.
  21. iZanax

    Garris0n pretty much answered all your questions. The tutorial I mentioned above, as well as this one, will help you with this, particularly your last request. I've provided you with a tutorial, it's now up you you to implement and extend :). All of your requests are easily possible with the information I provided.
     
  22. Updated to 1.7.2.

    Changes:
    • this.Y is now this.X
    • Added a note on GenericAttributes
     
  23. Question - Would this work on horses?
     
  24. Answer - Yes

    Most of the code is actually from horses.
     
    DSH105 and Tupay like this.
  25. Thanks! :)

    This'll help me a lot.
     
  26. Offline

    TimB0ne

    Hey guys! I try your bat code, but my Client crashes every time i try.

    Crash Report
    Code:
    ---- Minecraft Crash Report ----
    // You should try our sister game, Minceraft!
     
    Time: 04.01.14 22:35
    Description: Unexpected error
     
    java.lang.NullPointerException: Unexpected error
        at biv.a(SourceFile:496)
        at fq.a(SourceFile:97)
        at fq.a(SourceFile:15)
        at ef.a(SourceFile:164)
        at biy.e(SourceFile:212)
        at azd.o(SourceFile:1281)
        at azd.ad(SourceFile:753)
        at azd.e(SourceFile:704)
        at net.minecraft.client.main.Main.main(SourceFile:103)
     
     
    A detailed walkthrough of the error, its code path and all known details is as follows:
    ---------------------------------------------------------------------------------------
     
    -- Head --
    Stacktrace:
        at biv.a(SourceFile:496)
        at fq.a(SourceFile:97)
        at fq.a(SourceFile:15)
        at ef.a(SourceFile:164)
        at biy.e(SourceFile:212)
     
    -- Affected level --
    Details:
        Level name: MpServer
        All players: 1 total; [bje['TimB0ne'/44, l='MpServer', x=132,29, y=126,62, z=18,80]]
        Chunk stats: MultiplayerChunkCache: 169, 169
        Level seed: 0
        Level generator: ID 01 - flat, ver 0. Features enabled: false
        Level generator options:
        Level spawn location: World: (180,104,9), Chunk: (at 4,6,9 in 11,0; contains blocks 176,0,0 to 191,255,15), Region: (0,0; contains chunks 0,0 to 31,31, blocks 0,0,0 to 511,255,511)
        Level time: 191954384 game time, 1614500 day time
        Level dimension: 0
        Level storage version: 0x00000 - Unknown?
        Level weather: Rain time: 0 (now: false), thunder time: 0 (now: false)
        Level game mode: Game mode: adventure (ID 2). Hardcore: false. Cheats: false
        Forced entities: 4 total; [bje['TimB0ne'/44, l='MpServer', x=132,29, y=126,62, z=18,80], uq['Bat'/8, l='MpServer', x=141,96, y=103,84, z=-6,77], uq['Bat'/27, l='MpServer', x=160,29, y=99,68, z=21,73], vm['§6Welcome to MCInvasion'/45, l='MpServer', x=132,28, y=-50,00, z=18,81]]
        Retry entities: 0 total; []
        Server brand: CraftBukkit
        Server type: Non-integrated multiplayer server
    Stacktrace:
        at biz.a(SourceFile:284)
        at azd.b(SourceFile:1951)
        at azd.e(SourceFile:718)
        at net.minecraft.client.main.Main.main(SourceFile:103)
     
    -- System Details --
    Details:
        Minecraft Version: 1.7.2
        Operating System: Windows 8 (amd64) version 6.2
        Java Version: 1.7.0_25, Oracle Corporation
        Java VM Version: Java HotSpot(TM) 64-Bit Server VM (mixed mode), Oracle Corporation
        Memory: 96434728 bytes (91 MB) / 184877056 bytes (176 MB) up to 954466304 bytes (910 MB)
        JVM Flags: 2 total; -XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump -Xmx1G
        AABB Pool Size: 1411 (79016 bytes; 0 MB) allocated, 2 (112 bytes; 0 MB) used
        IntCache: cache: 0, tcache: 0, allocated: 0, tallocated: 0
        Launched Version: 1.7.2
        LWJGL: 2.9.0
        OpenGL: Intel(R) HD Graphics 3000 GL version 3.1.0 - Build 9.17.10.2932, Intel
        Is Modded: Probably not. Jar signature remains and client brand is untouched.
        Type: Client (map_client.txt)
        Resource Packs: []
        Current Language: English (US)
        Profiler Position: N/A (disabled)
        Vec3 Pool Size: 247 (13832 bytes; 0 MB) allocated, 20 (1120 bytes; 0 MB) used
        Anisotropic Filtering: Off (1)
    Custom Bat
    Code:
    package net.MCInvasion.libs;
     
    import java.lang.reflect.Field;
     
    import net.minecraft.server.v1_7_R1.EntityBat;
    import net.minecraft.server.v1_7_R1.EntityHuman;
    import net.minecraft.server.v1_7_R1.EntityLiving;
    import net.minecraft.server.v1_7_R1.World;
     
    public class ControlBat extends EntityBat {
     
        public ControlBat(World world) {
            super(world);
            this.a(0.5F, 0.9F);
            this.a(false);
        }
     
        @Override
        public boolean g_() {
            return true;
        }
     
        @Override
        public void e(float sideMot, float forMot) {
            if (this.passenger == null || !(this.passenger instanceof EntityHuman)) {
                super.e(sideMot, forMot);
                this.X = 0.5F; // Make sure the entity can walk over half slabs,
                                // instead of jumping
                return;
            }
     
            this.lastYaw = this.yaw = this.passenger.yaw;
            this.pitch = this.passenger.pitch * 0.5F;
     
            // Set the entity's pitch, yaw, head rotation etc.
            this.b(this.yaw, this.pitch); // [url]https://github.com/Bukkit/mc-dev/blob/master/net/minecraft/server/Entity.java#L155-L158[/url]
            this.aP = this.aN = this.yaw;
     
            this.X = 1.0F; // The custom entity will now automatically climb up 1
                            // high blocks
     
            sideMot = ((EntityLiving) this.passenger).be * 0.5F;
            forMot = ((EntityLiving) this.passenger).bf;
     
            if (forMot <= 0.0F) {
                forMot *= 0.25F; // Make backwards slower
            }
            sideMot *= 0.75F; // Also make sideways slower
     
            // speed doesnt work for bat
            //float speed = 0.5F; // 0.2 is the default entity speed. I made it
                                // slightly faster so that riding is better than
                                // walking
            //this.i(speed); // Apply the speed
            this.bf = 1F;
            super.e(sideMot, forMot); // Apply the motion to the entity
     
            Field jump = null;
            try {
                jump = EntityLiving.class.getDeclaredField("bd");
            } catch (NoSuchFieldException e1) {
                e1.printStackTrace();
            } catch (SecurityException e1) {
                e1.printStackTrace();
            }
            jump.setAccessible(true);
     
            if (jump != null) { // Wouldn't want it jumping while on the ground
                                // would we?
                try {
                    if (jump.getBoolean(this.passenger)) {
     
                        double jumpHeight = 0.5D;
                        this.motY = jumpHeight; // Used all the time in NMS for
                                                // entity jumping
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
     
        }
     
        @Override
        protected void bn() {
            if (this.passenger == null) {
                super.bn();
            }
        }
     
    }
    
    Spawn Code
    Code:
    package net.MCInvasion.hub.commands;
     
    import net.MCInvasion.Statics;
    import net.MCInvasion.libs.ControlBat;
    import net.minecraft.server.v1_7_R1.EntityBat;
     
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
    import org.bukkit.craftbukkit.v1_7_R1.CraftWorld;
    import org.bukkit.entity.Player;
    import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
     
    public class BatCommand implements CommandExecutor {
     
        @Override
        public boolean onCommand(CommandSender cs, Command cmd, String label, String[] args) {
            if (cs instanceof Player) {
                Player p = (Player)cs;
                EntityBat bat = new ControlBat(((CraftWorld)p.getWorld()).getHandle());
                bat.setLocation(p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ(), p.getLocation().getPitch(), p.getLocation().getYaw());
                ((CraftWorld)p.getWorld()).getHandle().addEntity(bat, SpawnReason.CUSTOM);
            } else {
                cs.sendMessage(Statics.NO_CONSOLE);
            }
            return true;
        }
       
    }
    
     
  27. TimB0ne likes this.
  28. Offline

    TimB0ne

    Thanks a lot ;)
     
  29. The class has been removed from GitHub?
     
  30. Here.
     
Thread Status:
Not open for further replies.

Share This Page