Change the direction of a sign

Discussion in 'Plugin Development' started by Altostratus, Apr 6, 2020.

Thread Status:
Not open for further replies.
  1. Hello,

    I've read much of what's in this forum concerning the issue, but everything seems to be outdated or deprecated information (at least this is what Eclipse tells me).

    I am trying to change the facing direction of a wall sign. I've understood that it should either be done with setData, setBlockData or something similar, but I can't get it to work.

    My code (you can safely ignore the Location variables, they work as they should):

    Code:
                    // Create a Location object for the sign
                    Location signLoc = new Location(player.getWorld(), barBlockLocation.getX(), barBlockLocation.getY() - scaledValue, barBlockLocation.getZ() + 1);
                   
                    // Place the sign and add the source name to its text
                    signLoc.getBlock().setType(Material.BIRCH_WALL_SIGN);
                    Sign s = (Sign) signLoc.getBlock().getState();
                    s.setLine(0, sourceName);
                    s.update();
    This places the sign facing in the opposite direction, facing the block it's supposed to be attached to, with a gap nearly as large as a full block.

    Thanks for your help.
     
  2. The data value defines its direction, see it as a yaw.
    #setData(4) will rotate it 4 1/16th rotations around a circle.

    I can imagine you could create a setSignDirection method with this information to make it a bit easier for yourself (and maybe others).
     
  3. Well, I could try if only I could understand what the API was expecting of me... If I add s.setData to the above, the method asks for a MaterialData object. And I have no idea how to pass yaw information to it.
     
  4. Offline

    wand555

    Wild guess, but Sign extends Rotatable so I think you can cast it and use #setRotation
    If that works then that's the non-deprecated way to do it
     
  5. Apparently this isn't a straightforward issue and I'm still stumped after many attempts.

    This thread suggest that the correct method is to do this (adapting the suggestion by @Pr0totype2):

    Code:
    BlockState blockstate = signLoc.getBlock().getState();
                    MaterialData blockdata = blockstate.getData();
                    blockdata.setData((byte)4);
                    blockstate.update();
    This, however, doesn't affect the sign block in the least. As a side note, it is also marked as deprecated.

    I have also tried this, which is also deprecated and also doesn't work:

    Code:
    signLoc.getBlock().getState().setRawData((byte)4);
    According to this 2019 thread, setting the byte in BlockData doesn't work anymore.

    I've also tried this, which got me pretty close:

    Code:
    BlockData data = signLoc.getBlock().getBlockData();
    getLogger().info(data.toString());
    
    // Outputs:
    // CraftBlockData{minecraft:birch_wall_sign[facing=north,waterlogged=false]}
    But I have no idea on how to set the "facing" property here.

    I know it might seem obvious to you but please bear in mind that I'm asking the question here because I'm only getting started with this API, and apparently I've bumped into one of its more obscure corners.

    Thanks for your help.
     
  6. Offline

    wand555

    Have you tried something like:
    Code:
    //you know your block is a sign
    Rotatable signRotatable = (Rotatable) block.getBlockData();
    signRotatable.setRotation(BlockFace.YOURCHOICE);
    block.setBlockData(signRotatable);
     
  7. Thanks. I've tried your suggestion, this doesn't cause a warning in Eclipse but results in a runtime exception: "class org.bukkit.craftbukkit.v1_15_R1.block.impl.CraftWallSign cannot be cast to class org.bukkit.block.data.Rotatable". And unfortunately I can't use #setRotation directly on a BlockData object.
     
  8. Offline

    timtower Administrator Administrator Moderator

  9. @timtower I could, but it's quite long and most of it is irrelevant. It's a large onCommand() method which places different blocks based on data in a CSV file (a problem you helped me solve). Most of it is working as intended. The section concerning the sign comes at the very end. It looks like this (it uses two variables from earlier in the class but this should be self-explanatory as it's very simple):

    Code:
    // Create a Location object for the sign
                    // based on barBlockLocation
                    Location signLoc = new Location(player.getWorld(), barBlockLocation.getX(), barBlockLocation.getY() - scaledValue, barBlockLocation.getZ() - 1);
                   
                    // Place the sign
                    signLoc.getBlock().setType(Material.BIRCH_WALL_SIGN);
                   
                    // Try to rotate the sign (work in progress)
                    Sign s = (Sign) signLoc.getBlock().getState();
                    Rotatable signRotatable = (Rotatable) signLoc.getBlock().getBlockData();
                    signRotatable.setRotation(BlockFace.EAST);
                    signLoc.getBlock().setBlockData(signRotatable);
                   
                    // Set the sign text
                    s.setLine(1, sourceName);
                    s.update();
     
  10. Offline

    timtower Administrator Administrator Moderator

  11. Offline

    wand555

    You need to use org.bukkit.block.data.type.Sign, instead of org.bukkit.block.Sign because that might be the problem.
     
  12. I'll try @wand555's suggestion and report back. In the meantime, my imports:

    Code:
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.Block;
    import org.bukkit.block.BlockFace;
    import org.bukkit.block.BlockState;
    import org.bukkit.block.Sign;
    import org.bukkit.block.data.BlockData;
    import org.bukkit.block.data.Rotatable;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;
    import org.bukkit.plugin.java.JavaPlugin;
    
    import net.md_5.bungee.api.ChatColor;
    I'm not sure I understand @wand555's suggestion. "org.bukkit.block.data.type.Sign" doesn't have the setLine() and update() methods which, from what I gathered, are needed in order to set the sign text.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Apr 7, 2020
  13. Offline

    wand555

    Using org.bukkit.block.Sign is for the 'block properties' (read here). However org.bukkit.block.data.type.Sign is for rotating (read here, see how it extends Rotatable).
     
  14. Not sure how to go about it. If I try to cast the object explicitly like this, I get "cannot convert from org.bukkit.block.Sign to org.bukkit.block.data.type.Sign".

    Code:
    org.bukkit.block.data.type.Sign signBlock = (Sign)signLoc.getBlock();
     
  15. Offline

    wand555

    That's what I meant.
    Code:
    org.bukkit.block.Sign signBlock = (org.bukkit.block.Sign) block.getState();
    signBlock.setLine(...);
    org.bukkit.data.type.Sign signRotatable = (org.bukkit.data.type.Sign) block.getBlockData();
    signRotatable.setRotation(...);
    block.setBlockData(signRotatable);
     
  16. Thank you very much for your patience. Your code results in no warnings in Eclipse, however your third line causes:

    "class org.bukkit.craftbukkit.v1_15_R1.block.impl.CraftWallSign cannot be cast to class org.bukkit.block.data.type.Sign (org.bukkit.craftbukkit.v1_15_R1.block.impl.CraftWallSign and org.bukkit.block.data.type.Sign are in unnamed module of loader 'app'"

    You will notice I changed your "org.bukkit.data.type.Sign" to "org.bukkit.block.data.type.Sign" which unless I'm very wrong is the class you had in mind.
     
  17. Offline

    wand555

    If the block is a wallsign, try "org.bukkit.block.data.type.WallSign" instead.
    And yes, I meant what you corrected.
     
  18. Well, quoting Eclipse here, org.bukkit.block.data.type.WallSign doesn't implement setRotation(). And apprently you can't cast from org.bukkit.block.data.type.WallSign to org.bukkit.block.data.type.Sign.

    Possibly the most annoying aspect of this API to bump into as a beginner :)
     
  19. Offline

    wand555

    No, youre completely missunderstanding it.
    Code:
    //code blabla...
    if(block.getBlockData() instanceof org.bukkit.block.data.type.Sign)
    (org.bukkit.block.data.type.Sign) signRotatable = (org.bukkit.block.data.type.Sign) block.getBlockData();
    signRotatable.setRotation(..);
    block.setBlockData(signRotatable);
    
    if(block.getBlockData() instanceof org.bukkit.block.data.type.WallSign)
    (org.bukkit.block.data.type.WallSign) signRotatable = (org.bukkit.block.data.type.WallSign) block.getBlockData();
    signRotatable.setRotation(..);
    block.setBlockData(signRotatable);
    To avoid this collision altogether you can just cast it to Rotatable.
    Code:
    Rotatable signRotatable = (org.bukkit.block.data.Rotatable) block.getBlockData();
    signRotatable.setRotation(...);
    block.setBlockData(signRotatable);
    EDIT: The above code is wrong. @Altostratus
    If the sign is a normal (not wall) sign, then do what I wrote above. However when it's a wall sign, it no longer implements Rotatable. Instead it implements Directional (org.bukkit.block.data.Directional). There you don't use #setRotation(...) and instead use #setFacing(...).
    Mb.
     
    Last edited: Apr 7, 2020
  20. I'm sorry, I've followed your example as closely as possible. I've checked and the BlockData is indeed an instance of "
    org.bukkit.block.data.type.WallSign" (rather than *.Sign). So I've tried both of your suggestions.

    Scenario 1, your first code example:

    I've tried the three lines that follow the instance check in your code. But I can't do this:

    Code:
    (org.bukkit.block.data.type.WallSign) signRotatable = (org.bukkit.block.data.type.WallSign) block.getBlockData();
    I can only do this (without the parenthesis in the variable type):

    Code:
    org.bukkit.block.data.type.WallSign signRotatable = (org.bukkit.block.data.type.WallSign) signLoc.getBlock().getBlockData();
    In this case, signRotatable doesn't have a setRotation() method. It only has a setFacing() method. I've tried it. There is no runtime error but the sign's rotation isn't modified.

    Scenario 2, your second code example:

    This results in a runtime error, "org.bukkit.craftbukkit.v1_15_R1.block.impl.CraftWallSign cannot be cast to class org.bukkit.block.data.Rotatable".
     
  21. Offline

    wand555

    @Altostratus Check the edit on the post, I made a mistake.
     
  22. Thanks for the correction, but aren't you referring to what I wrote just above? The significant part is in bold...

     
  23. Offline

    wand555

    Did you #setBlockData()?
     
  24. Yes, of course. Here's the current code (no errors, but no modification either). I should clean this up so that I don't get the block each time through the location, but you get the idea.

    Code:
    org.bukkit.block.data.type.WallSign signRotatable = (org.bukkit.block.data.type.WallSign) signLoc.getBlock().getBlockData();
                    signRotatable.setFacing(BlockFace.EAST);
                    signLoc.getBlock().setBlockData(signRotatable);
                    signLoc.getBlock().getState().update();
    EDIT: Any ideas?
     
    Last edited: Apr 8, 2020
  25. Offline

    wand555

    I don't know anymore at this point, sorry.
    Maybe it has something to do with the block it's attached to but I'm just guessing...
     
  26. OK. Maybe it can't be done... Anyway, thanks a lot again for your help and patience.
     
  27. Offline

    timtower Administrator Administrator Moderator

    @Altostratus Could you post an image of the sign that you are trying to change?
     
Thread Status:
Not open for further replies.

Share This Page