--- a/net/minecraft/server/TileEntitySign.java
+++ b/net/minecraft/server/TileEntitySign.java
@@ -3,7 +3,7 @@
 import com.mojang.brigadier.exceptions.CommandSyntaxException;
 import javax.annotation.Nullable;
 
-public class TileEntitySign extends TileEntity {
+public class TileEntitySign extends TileEntity implements ICommandListener { // CraftBukkit - implements
 
     public final IChatBaseComponent[] lines = new IChatBaseComponent[]{new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText(""), new ChatComponentText("")};
     public boolean isEditable = true;
@@ -26,6 +26,12 @@
             nbttagcompound.setString("Text" + (i + 1), s);
         }
 
+        // CraftBukkit start
+        if (Boolean.getBoolean("convertLegacySigns")) {
+            nbttagcompound.setBoolean("Bukkit.isConverted", true);
+        }
+        // CraftBukkit end
+
         nbttagcompound.setString("Color", this.color.b());
         return nbttagcompound;
     }
@@ -36,18 +42,38 @@
         super.load(nbttagcompound);
         this.color = EnumColor.a(nbttagcompound.getString("Color"), EnumColor.BLACK);
 
+        // CraftBukkit start - Add an option to convert signs correctly
+        // This is done with a flag instead of all the time because
+        // we have no way to tell whether a sign is from 1.7.10 or 1.8
+
+        boolean oldSign = Boolean.getBoolean("convertLegacySigns") && !nbttagcompound.getBoolean("Bukkit.isConverted");
+
         for (int i = 0; i < 4; ++i) {
             String s = nbttagcompound.getString("Text" + (i + 1));
-            IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(s.isEmpty() ? "\"\"" : s);
+            if (s != null && s.length() > 2048) {
+                s = "\"\"";
+            }
+
+            try {
+                IChatBaseComponent ichatbasecomponent = IChatBaseComponent.ChatSerializer.a(s.isEmpty() ? "\"\"" : s);
 
-            if (this.world instanceof WorldServer) {
-                try {
-                    this.lines[i] = ChatComponentUtils.filterForDisplay(this.a((EntityPlayer) null), ichatbasecomponent, (Entity) null, 0);
-                } catch (CommandSyntaxException commandsyntaxexception) {
+                if (oldSign) {
+                    lines[i] = org.bukkit.craftbukkit.util.CraftChatMessage.fromString(s)[0];
+                    continue;
+                }
+                // CraftBukkit end
+
+                if (this.world instanceof WorldServer) {
+                    try {
+                        this.lines[i] = ChatComponentUtils.filterForDisplay(this.a((EntityPlayer) null), ichatbasecomponent, (Entity) null, 0);
+                    } catch (CommandSyntaxException commandsyntaxexception) {
+                        this.lines[i] = ichatbasecomponent;
+                    }
+                } else {
                     this.lines[i] = ichatbasecomponent;
                 }
-            } else {
-                this.lines[i] = ichatbasecomponent;
+            } catch (com.google.gson.JsonParseException jsonparseexception) {
+                this.lines[i] = new ChatComponentText(s);
             }
 
             this.g[i] = null;
@@ -108,11 +134,37 @@
         return true;
     }
 
+    // CraftBukkit start
+    @Override
+    public void sendMessage(IChatBaseComponent ichatbasecomponent) {}
+
+    @Override
+    public org.bukkit.command.CommandSender getBukkitSender(CommandListenerWrapper wrapper) {
+        return wrapper.getEntity() != null ? wrapper.getEntity().getBukkitSender(wrapper) : new org.bukkit.craftbukkit.command.CraftBlockCommandSender(wrapper, this);
+    }
+
+    @Override
+    public boolean shouldSendSuccess() {
+        return false;
+    }
+
+    @Override
+    public boolean shouldSendFailure() {
+        return false;
+    }
+
+    @Override
+    public boolean shouldBroadcastCommands() {
+        return false;
+    }
+    // CraftBukkit end
+
     public CommandListenerWrapper a(@Nullable EntityPlayer entityplayer) {
         String s = entityplayer == null ? "Sign" : entityplayer.getDisplayName().getString();
         Object object = entityplayer == null ? new ChatComponentText("Sign") : entityplayer.getScoreboardDisplayName();
 
-        return new CommandListenerWrapper(ICommandListener.DUMMY, new Vec3D((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D), Vec2F.a, (WorldServer) this.world, 2, s, (IChatBaseComponent) object, this.world.getMinecraftServer(), entityplayer);
+        // CraftBukkit - this
+        return new CommandListenerWrapper(this, new Vec3D((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D), Vec2F.a, (WorldServer) this.world, 2, s, (IChatBaseComponent) object, this.world.getMinecraftServer(), entityplayer);
     }
 
     public EnumColor getColor() {
@@ -123,7 +175,7 @@
         if (enumcolor != this.getColor()) {
             this.color = enumcolor;
             this.update();
-            this.world.notify(this.getPosition(), this.getBlock(), this.getBlock(), 3);
+            if (this.world != null) this.world.notify(this.getPosition(), this.getBlock(), this.getBlock(), 3); // CraftBukkit - skip notify if world is null (SPIGOT-5122)
             return true;
         } else {
             return false;
