How do you toggle a lever?

Discussion in 'Plugin Development' started by sayberblade, May 29, 2012.

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

    sayberblade

    I'm making a plugin and I need to be able to have a redstone output from a block, I decided to do this with a lever, how do I toggle the lever? I have the location of it but I just don't know what to type in the code to make it flip while in game. Please help me!
     
  2. Offline

    r0306

    sayberblade
    Suppose your lever was contained in the variable 'block' :
    Code:
    Block block = your lever;
    Lever l = (Lever) block;
    l.setPowered(true); //toggles on
    l.setPowered(false); //toggles off
     
  3. Offline

    DocRedstone

    Try reading over this documentation: Lever Class

    You probably can't do it with 1 line of code but it looks like the functions: setFacingDirection() and setFacingDirection()
     
  4. Offline

    Mr Washington

    I think you may be able to... Its a function that is listed: setData. Just set the data bit 0x8 so it is thrown and it will throw the lever.
     
  5. Offline

    Njol

    The best way is to use the Level class, as it makes the code easier to understand.
    @r0306's code is wrong, you can't cast the block to Lever, but have to use the block's data instead:
    Code:
    Lever lever = new Lever(block.getData());
    lever.setPowered(!lever.isPowered());// this toggles the lever; use true or false to set it instead as in r0306's code
    block.setData(lever.getData());
     
  6. Offline

    sayberblade

    thanks a lot guys! especially Njol, now here's another question: when I use this code, I get a really weird lever glitch, it still works but I would rather have it look normal. How would I fix this? (BTW here's my plugin: RandStone ) switch glitch.png
     
  7. If there isn't a bukkit API method for it, just change the data yourself... looking at http://www.minecraftwiki.net/wiki/Data_value#Levers I see that bit 0x8 is the bit for set/unset lever, just using some bitwise operators we can add or remove only that bit without affecting other bits from the data value.
    Something like:
    Code:
    int data = block.getData();
    
    data = data & ~0x8; // remove the bit = set lever off
    data = data | 0x8; // add the bit = set lever on
    data = data ^ 0x8; // toggle value, if it was on set off and vice-versa
    
    // pick ONE of the above... and set it back to the block:
    block.setData((byte)data);
    EDIT: fixed & improved code

    EDIT #2:
    I looked into Lever class, I belive Njol 's method works just fine but the Lever's 2nd argument is data:
    Code:
    new Lever(Material.LEVER, (byte)data);
     
  8. Offline

    Njol

    Correct, but wtf Bukkit? :confused:
     
  9. Note that none of these methods work the same way as when you would click a lever manually.
    Main differences: No BlockRedstoneEvent is thrown, and redstone stuff next to the block where the lever is attached doesn't update.

    So, if you for example have, on a straight line:
    [LEVER] [STONE] [REDSTONE_WIRE] ...
    If you flip the lever, the redstone wire would normally update. It doesn't however when you use the bukkit API.

    Setting the data is only one line of code in a pretty big method when you right-click the lever. This is what happens when you interact with a lever (as a player).

    I ended up simply calling that method when the lever should be flipped.
    Code:
                Block b = ...; // wherever you get your block from
                boolean shouldBeOn = ...; // wether the lever should be on afterwards
               
                boolean wasOn = (b.getData() & 0x8) > 0;
               
                // make sure we actually change state
                if(wasOn != shouldBeOn) {
                   
                    net.minecraft.server.Block nmsBlock = net.minecraft.server.Block.byId[Material.LEVER.getId()];
                    net.minecraft.server.World nmsWorld = ((CraftWorld) b.getWorld()).getHandle();
                    EntityHuman nmsPlayer = context.triggerer.getHandle();
                   
                    // Note: The player argument isn't actually used by the method in BlockLever, but I pass it anyway, use null if you don't have a player.
                    // This method takes care of all the necessary block updates and redstone events.
                    nmsBlock.interact(nmsWorld, b.getX(), b.getY(), b.getZ(), nmsPlayer);
                   
                }
    Note: "context.triggerer" just happens to be a Player instance I had in my case, if you don't have that, simply pass null instead of it.

    What the interact method does:
    - calls a BlockRedstoneEvent for plugins to hook
    - sets the lever's data to the opposite on state (or whatever the result of the event was)
    - updates the block the lever is attached to appropiately

    What the interact method does NOT:
    - performing range checks with the passed EntityHuman
    - calling a PlayerInteractEvent
    - using the last parameter (EntityHuman) in any way, so you can actually pass null without problems

    PS: You have to build against CraftBukkit obviously.
     
  10. Offline

    Njol

    Isn't there an easier way to do this? Maybe by using block.setTypeIdAndData(id, data) to trigger a physics check and thus hopefully a redstone change event as well?
     
  11. The problem is that a lever not only applies for the adjacent blocks, but also the adjacent blocks of the block where it is attached to.

    If you look at the interact method I used, that's one part of what it does:
    Code:
                world.setData(i, j, k, i1 + j1);
                // ...
                world.applyPhysics(i, j, k, this.id);
                if (i1 == 1) {
                    world.applyPhysics(i - 1, j, k, this.id);
                } else if (i1 == 2) {
                    world.applyPhysics(i + 1, j, k, this.id);
                } else if (i1 == 3) {
                    world.applyPhysics(i, j, k - 1, this.id);
                } else if (i1 == 4) {
                    world.applyPhysics(i, j, k + 1, this.id);
                } else {
                    world.applyPhysics(i, j - 1, k, this.id);
                }
    The first two lines are what happen when you would manually set the data/id with a block update.
    The next conditional blocks are the magical part: Depending on the orientation of the lever (i1), the block where the lever is attached is also told to do physics updates.

    BTW, a physics update always updates adjacent blocks. And the BlockRedstoneEvent is directly created and called in that interact-method. Setting the data of a block won't call that method.

    What you could do is just call whatever that method calls manually. So you call the BlockRedstoneEvent, you check where the lever is attached to and you trigger physics updates for that block. But in the end, where's the difference to just calling that method when it is already there?

    Okay, you probably don't need to hook CraftBukkit, but for me, that's not really a benefit since I need to do that anyway :p
    And in the end, this native method probably breaks less often than if you do it manually. No obfuscated method names are involved, they will probably never change. But if Mojang or Bukkit ever decide to slightly alter that interact method (maybe give BlockRedstoneEvent another signature, or make the lever update more blocks), your manual way will be outdated, and mine will still work ;)

    Dude, why the hell do my posts become so friggin long today?!
     
Thread Status:
Not open for further replies.

Share This Page