Solved SignChangeEvent#getBlock() returns a block which is not a sign?

Discussion in 'Spigot Plugin Development' started by Sataniel, Jun 14, 2018.

  1. [​IMG]

    A weird error occurred to a user of one of my plugins and I have no idea how it is possible that this happens.

    Code (Text):
    14.06 12:58:45 [Server] ERROR Could not pass event SignChangeEvent to DungeonsXL v0.16-RC-01
    14.06 12:58:45 [Server] INFO org.bukkit.event.EventException: null
    14.06 12:58:45 [Server] INFO at$1.execute( ~[Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at org.bukkit.plugin.RegisteredListener.callEvent( ~[Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at org.bukkit.plugin.SimplePluginManager.fireEvent( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at org.bukkit.plugin.SimplePluginManager.callEvent( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.PlayerConnection.a( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.PacketPlayInUpdateSign.a(SourceFile:44) [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.PacketPlayInUpdateSign.a(SourceFile:10) [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.PlayerConnectionUtils$ [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at java.util.concurrent.Executors$ [?:1.8.0_131]
    14.06 12:58:45 [Server] INFO at [?:1.8.0_131]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.SystemUtils.a(SourceFile:46) [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.MinecraftServer.D( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.DedicatedServer.D( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at net.minecraft.server.v1_12_R1.MinecraftServer.C( [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at [Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO at [?:1.8.0_131]
    14.06 12:58:45 [Server] INFO Caused by: java.lang.ClassCastException: org.bukkit.craftbukkit.v1_12_R1.block.CraftBlockState cannot be cast to org.bukkit.block.Sign
    14.06 12:58:45 [Server] INFO at de.erethon.dungeonsxl.sign.DSignListener.onSignChange( ~[?:?]
    14.06 12:58:45 [Server] INFO at sun.reflect.GeneratedMethodAccessor1298.invoke(Unknown Source) ~[?:?]
    14.06 12:58:45 [Server] INFO at sun.reflect.DelegatingMethodAccessorImpl.invoke( ~[?:1.8.0_131]
    14.06 12:58:45 [Server] INFO at java.lang.reflect.Method.invoke( ~[?:1.8.0_131]
    14.06 12:58:45 [Server] INFO at$1.execute( ~[Spigot.jar:git-Spigot-2086bb0-8cc5a7e]
    14.06 12:58:45 [Server] INFO ... 15 more
    Code of DSignListener:
    Code (Java):
        public void onSignChange(SignChangeEvent event) {
            String[] lines = event.getLines();
            Player player = event.getPlayer();
            Block block = event.getBlock();
            Sign sign = (Sign) block.getState();//<- Line 82
    I am of course aware that I could easily fix this with an instanceof check, but I'd like to understand how and when it is possible that the block of a SignChangeEvent isn't a sign. Do you have any ideas?

    Have a nice day,
    ~ Sataniel
  2. JanTuck


    Good question i would assume he has some other plugin interfering, just add that instanceOf check though.
  3. Can you replicate this problem? Any chance you could go and check what the type of the block is if it's not a sign?
    • Agree Agree x 1
  4. I can't. This has never happened to me, I just got a stacktrace without any explanation from a user.

    That some other plugin interferes was my guess, too. But it still doesn't really make sense to me. Why would another plugin do something like firing a custom SignChangeEvent with a block that isn't a sign or change the type of the block in a SignChangeEvent without cancelling it?
  5. I guess some other plugin has changed the sign block to something else (maybe removed the sign) while handling the event.

    I even have a published plugin which does exactly that: The plugin allows the editing of signs by placing another sign in front of it. Once the player has finished editing (once there is a SignChangeEvent), the plugin cancels the event, removes the sign from the world, and applies its text to the sign behind it. An alternative would be to always delay the removal of the sign by 1 tick, or split the event handling into 2 parts: Cancel the event at priority 'high' and remove the block at priority 'monitor'.
    Currently that specific plugin reacts to the event at 'high' priority, so most other plugins are run before it (priority 'normal' and below), but there is still room to react to it afterwards with priority 'highest'.

    Other ways of fixing such an incompatibility would be to adjust the event priority, so that your event handling always runs before that other plugin, or ignore the event if it got cancelled, or first check if the block is still a sign, or don't depend on the block still being a sign. The way to go depends on what the specific other plugin, that is causing the incompatibility here, is doing and how you want to react to the various possible actions of other plugins.
    #5 blablubbabc, Jun 15, 2018
    Last edited: Jun 15, 2018
    • Informative Informative x 1
  6. Your problem is that you're casting the Block's BlockState to org.bukkit.block.Sign. Cast it to org.bukkit.material.Sign instead. That's unequivocally the problem. You'll likely have to either change your import, or cast it directly "(org.bukkit.material.Sign) block.getState().getData();"

    Edit: you guys above really overthought this one.

    Edit 2: also, forgot to mention, and just added to the code above (bolded) use .getData() as well.
    #6 Velariyel, Jun 15, 2018
    Last edited: Jun 15, 2018
  7. Choco


    You underthought it. You cannot cast a MaterialData implementation to a BlockState implementation. org.bukkit.block.Sign is correct. Any attempt to cast to org.bukkit.material.Sign would result in a ClassCastException. In addition to that, the org.bukkit.material.Sign does not provide methods to modify the lines of a sign because it's a material... not a block state...

    Note the differences:
    org.bukkit.block.Sign: BlockState implementation
    org.bukkit.material.Sign: MaterialData implementation
    • Agree Agree x 1
  8. electronicboy

    IRC Staff

    The fact that it's a CraftBlockMeta and not a Sign (Well, CraftSign) indicates that there isn't a sign there, common causes I've seen from this kinda thing is plugins like FAWE which modify the world async leaving the world in an inconsistent manner (either due to not performing the updates properly, or due to there being a change to the block during the event handling), i.e. there is a TileEntitySign there, but the block data there isn't actually a sign
  9. Guess it depends on if he wants to set the lines or not. I assumed he'd wanted the direction for some reason. My bad there.
  10. Everyone of you, thank you very much, your comments have been very helpful :)
    Checking if the block is still a sign seems to be the solution that matchs my needs best. I'll look for such a sign changer plugin or just create a dummy plugin myself to see how I can make it compatible.