diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
index 7018bad74..ba0ea3781 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenFontTest.java
@@ -1,10 +1,10 @@
 package net.minecraft.client.gui;
 
 import net.minecraft.client.render.font.Alignment;
+import net.minecraft.client.render.font.FontFile;
 import net.minecraft.client.render.font.Fonts;
 import net.minecraft.core.net.ChatEmotes;
 import net.minecraft.core.util.collection.NamespaceID;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.lwjgl.input.Mouse;
@@ -35,7 +35,7 @@ public class ScreenFontTest extends Screen{
         for (int i = 0; i < 1000; i++) {
             builder.append("a");
         }
-        builder.append("\n").append(ChatAllowedCharacters.ALLOWED_CHARACTERS);
+        builder.append("\n").append(FontFile.CHARS);
         builder.append("\n").append("§rItalic test: §oTEST STRING");
         builder.append("\n").append("§rBold test: §lTEST STRING");
         builder.append("\n").append("§rUnderline test: §nTEST STRING");
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenLabelEditor.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenLabelEditor.java
index f0f276e61..104cb33c2 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenLabelEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenLabelEditor.java
@@ -54,6 +54,10 @@ public class ScreenLabelEditor extends Screen implements ITextField {
             return;
         }
         if (button.id == 0) {
+            if (!this.mc.thePlayer.gamemode.allowColorFormattingInDyeableText()) {
+                this.text = TextFormatting.removeAllColorFormatting(this.text);
+            }
+
             this.item.setCustomName(this.text);
             this.mc.displayScreen(null);
 
diff --git a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
index c9d89fa4e..30f412f04 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/ScreenSignEditor.java
@@ -19,7 +19,7 @@ import net.minecraft.core.lang.I18n;
 import net.minecraft.core.net.packet.PacketSignUpdate;
 import net.minecraft.core.sound.SoundCategory;
 import net.minecraft.core.util.Nullability;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
+import net.minecraft.core.util.helper.NetCharacters;
 import net.minecraft.core.util.helper.DyeColor;
 import org.jetbrains.annotations.NotNull;
 import org.joml.Math;
@@ -28,11 +28,6 @@ import org.lwjgl.input.Keyboard;
 import java.util.Objects;
 
 public class ScreenSignEditor extends Screen {
-    private static final @NotNull String ALLOWED_CHARACTERS;
-    static {
-        ALLOWED_CHARACTERS = ChatAllowedCharacters.ALLOWED_CHARACTERS;
-    }
-
     protected @NotNull String screenTitle;
 
     private final @NotNull TileEntitySign entitySign;
@@ -96,7 +91,10 @@ public class ScreenSignEditor extends Screen {
         Keyboard.enableRepeatEvents(false);
         // I have no idea how the hell the world is null sometimes
         // but thaboard was getting a crash from the world being null
-        if (this.mc.currentWorld != null && this.mc.currentWorld.isClientSide) {
+        if (this.mc.currentWorld == null) return;
+        if (!this.mc.currentWorld.isClientSide) {
+            this.entitySign.finalizeEditor(this.mc.thePlayer);
+        } else {
             Objects.requireNonNull(this.mc.getSendQueue())
                 .addToSendQueue(
                     new PacketSignUpdate(
@@ -140,7 +138,7 @@ public class ScreenSignEditor extends Screen {
         if (eventKey == Keyboard.KEY_BACK && !this.entitySign.signText[this.editLine].isEmpty()) {
             this.entitySign.signText[this.editLine] = this.entitySign.signText[this.editLine].substring(0, this.entitySign.signText[this.editLine].length() - 1);
         }
-        if ((ALLOWED_CHARACTERS.indexOf(eventCharacter) >= 0 || Character.isLetterOrDigit(eventCharacter)) && this.entitySign.signText[this.editLine].length() < TileEntitySign.MAX_LINE_SIZE) {
+        if (NetCharacters.isAllowed(eventCharacter) && this.entitySign.signText[this.editLine].length() < TileEntitySign.MAX_LINE_SIZE) {
             this.entitySign.signText[this.editLine] += eventCharacter;
         }
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/keyboard/ScreenKeyboard.java b/game/client/src/main/java/net/minecraft/client/gui/keyboard/ScreenKeyboard.java
index fd3e0b839..7c5a5d105 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/keyboard/ScreenKeyboard.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/keyboard/ScreenKeyboard.java
@@ -9,7 +9,6 @@ import net.minecraft.client.input.controller.ControllerInput;
 import net.minecraft.client.render.renderer.GLRenderer;
 import net.minecraft.client.render.texture.stitcher.TextureRegistry;
 import net.minecraft.core.sound.SoundCategory;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import net.minecraft.core.util.helper.MathHelper;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -389,10 +388,6 @@ public class ScreenKeyboard extends Screen implements ITextField {
         return Integer.MAX_VALUE;
     }
 
-    public boolean isCharacterAllowed(char c) {
-        return ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) >= 0 || Character.isLetterOrDigit(c);
-    }
-
     public interface KeyAction {
         void apply(char c, int key, String workingString);
     }
diff --git a/game/client/src/main/java/net/minecraft/client/gui/text/ITextField.java b/game/client/src/main/java/net/minecraft/client/gui/text/ITextField.java
index b98eca3d7..d721cc48a 100644
--- a/game/client/src/main/java/net/minecraft/client/gui/text/ITextField.java
+++ b/game/client/src/main/java/net/minecraft/client/gui/text/ITextField.java
@@ -1,6 +1,6 @@
 package net.minecraft.client.gui.text;
 
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
+import net.minecraft.core.util.helper.NetCharacters;
 
 public interface ITextField
 {
@@ -12,6 +12,6 @@ public interface ITextField
 
 	default boolean isCharacterAllowed(char c)
 	{
-		return ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) >= 0 || Character.isLetterOrDigit(c);
+		return NetCharacters.isAllowed(c);
 	}
 }
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
index 33aae017e..a4cffe434 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontDefault.java
@@ -13,13 +13,13 @@ public class FontDefault extends FontFile {
 
     @Override
     public boolean canRenderChar(final char c) {
-        return c == ' ' || (ALLOWED_CHARACTER_INDICES[c] >= 0 && textureForChar(c) != 0);
+        return c == ' ' || (CHARACTER_INDICES[c] >= 0 && textureForChar(c) != 0);
 //        return false;
     }
 
     @Override
     public int textureForChar(final char c) {
-        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
+        final int fontIndex = CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
         final int index = fontIndex / GLYPHS_PER_TEXTURE;
         if (this.texHandleCache[index] != TEXTURE_UNASSIGNED) return this.texHandleCache[index];
@@ -35,7 +35,7 @@ public class FontDefault extends FontFile {
     @Override
     public double charWidthDouble(final char c) {
         if (c == ' ') return 4;
-        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
+        final int fontIndex = CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
 
         final int upper = this.charWidth[fontIndex] >> 4;
@@ -49,7 +49,7 @@ public class FontDefault extends FontFile {
     @Override
     public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z) {
         if (c == ' ') return 4;
-        final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
+        final int fontIndex = CHARACTER_INDICES[c];
         if (fontIndex < 0) return 0;
 
         final int upper = this.charWidth[fontIndex] >> 4;
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
index 49aa6d5b8..47026d5c5 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontFile.java
@@ -1,38 +1,56 @@
 package net.minecraft.client.render.font;
 
-import it.unimi.dsi.fastutil.objects.ObjectArrayList;
 import net.minecraft.client.Minecraft;
 import net.minecraft.client.render.tessellator.TessellatorFont;
 import net.minecraft.client.render.texture.Texture;
 import net.minecraft.client.render.texturepack.TexturePackList;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
+import net.minecraft.core.util.helper.NetCharacters;
+
 import org.jetbrains.annotations.NotNull;
 
+import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
-import java.util.List;
 import java.util.Random;
 
 public class FontFile implements Font {
-    /**
-     * Some characters somehow are recognized as "default" characters while their rendering is not supported.
-     * Have to delete this once workarounds are found.
-     */
-    protected static final List<Character> DEFAULT_BLACKLIST = new ObjectArrayList<>();
-    static {
-        DEFAULT_BLACKLIST.addAll(Arrays.asList('\uff0c', '\u3002', '\u3001', '\u3010', '\u3011', '\uff1b', '\u2019', '\u2018', '\u201c', '\u201d', '\uff1f', '\uff01', '\u00b7'));
-    }
-    public static final int[] ALLOWED_CHARACTER_INDICES = new int[Character.MAX_VALUE];
+    public static final String CHARS = parseCharactersFromFile();
+    public static final int[] CHARACTER_INDICES = new int[Character.MAX_VALUE];
     static {
-        for (char c = 0; c < ALLOWED_CHARACTER_INDICES.length; c++) {
-            ALLOWED_CHARACTER_INDICES[c] = ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c);
-        }
-        for (var c : DEFAULT_BLACKLIST) {
-            ALLOWED_CHARACTER_INDICES[c] = -1;
+        for (var c = 0; c < CHARACTER_INDICES.length; c++) {
+            CHARACTER_INDICES[c] = CHARS.indexOf(c);
         }
+        // Some characters somehow are recognized as "default" characters while their rendering is not supported.
+        // Have to delete this once workarounds are found.
+        final char[] blacklist = {
+            '\uff0c', '\u3002', '\u3001', '\u3010', '\u3011', '\uff1b', '\u2019', '\u2018', 
+            '\u201c', '\u201d', '\uff1f', '\uff01', '\u00b7'
+        };
+        for (var c : blacklist) CHARACTER_INDICES[c] = -1;
     }
 
+    @NotNull
+    private static String parseCharactersFromFile() {
+        final var buf = new StringBuilder();
+        try (final var bufferedReader = new BufferedReader(new InputStreamReader(
+            FontFile.class.getResourceAsStream("/font.txt"),
+            StandardCharsets.UTF_8
+        ))) {
+            while (true) {
+                final var line = bufferedReader.readLine();
+                if (line == null) break;
+                if (line.startsWith("#")) continue;
+                buf.append(line);
+            }
+        } catch (Exception ignored) {} 
+        // empty file will result in every character fallback to unicode, which should be acceptable
+
+        return buf.toString();
+    }
+    
     protected static final int ITALICS_SHIFT = 1;
     protected static final int GLYPHS_PER_TEXTURE = 256;
     protected static final int TEXTURE_UNASSIGNED = -1;
@@ -89,17 +107,15 @@ public class FontFile implements Font {
 
     @Override
     public char getObfuscatedChar(final char c, final Random random) {
-        if (ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) < 0) return c;
-        char randomChar;
-        int counter = 0;
-        do {
-            randomChar = ChatAllowedCharacters.ALLOWED_CHARACTERS.charAt(random.nextInt(ChatAllowedCharacters.ALLOWED_CHARACTERS.length()));
-            if (counter++ > 1000) {
-                randomChar = c;
-                break;
-            }
-        } while (charWidth(c) != charWidth(randomChar) || !canRenderChar(randomChar));
-        return randomChar;
+        if (!NetCharacters.isAllowed(c)) return c;
+        for (int i = 0; i < 1000; i++) {
+            final char randomChar = (char)(short)random.nextInt(Short.MAX_VALUE);
+            if (!NetCharacters.isAllowed(randomChar)) continue;
+            if (charWidth(randomChar) != charWidth(randomChar)) continue;
+            if (!canRenderChar(randomChar)) continue;
+            return randomChar;
+        }
+        return c;
     }
 
     @Override
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
index 086a3df02..e96032cf6 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontMonospace.java
@@ -21,7 +21,7 @@ public class FontMonospace extends FontFile{
 
     @Override
     public boolean canRenderChar(final char c) {
-        return ALLOWED_CHARACTER_INDICES[c] != -1 && (c == ' ' || textureForChar(c) != 0);
+        return CHARACTER_INDICES[c] != -1 && (c == ' ' || textureForChar(c) != 0);
     }
 
     @Override
@@ -32,7 +32,7 @@ public class FontMonospace extends FontFile{
     @Override
     public double renderChar(final TessellatorFont tessellator, final char c, final double x, final double y, final double z) {
         if (c != ' ') {
-            final int fontIndex = ALLOWED_CHARACTER_INDICES[c];
+            final int fontIndex = CHARACTER_INDICES[c];
             if (fontIndex < 0) return 0;
 
             final int upper = this.charWidth[fontIndex] >> 4;
diff --git a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
index bdd99e294..c0060d244 100644
--- a/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
+++ b/game/client/src/main/java/net/minecraft/client/render/font/FontRendererDefault.java
@@ -177,7 +177,7 @@ public class FontRendererDefault extends FontRenderer {
         tessellator.setShadow(shadow);
         tessellator.setConfig(this.currentConfig);
         double shift;
-        final int fontIndex = FontDefault.ALLOWED_CHARACTER_INDICES[c];
+        final int fontIndex = FontDefault.CHARACTER_INDICES[c];
         if (SF.isObfuscated(this.currentConfig) && c != ' ' && fontIndex > 0) {
             shift = font.charWidth(c);
             final char randomChar = font.getObfuscatedChar(c, this.random);
diff --git a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
index b1a5c079d..fb64b5fa6 100644
--- a/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
+++ b/game/core/src/main/java/net/minecraft/core/block/entity/TileEntitySign.java
@@ -123,11 +123,20 @@ public class TileEntitySign extends TileEntity
     public void setLocked(boolean locked) {
         this.locked = locked;
     }
-    
+
+    public void finalizeEditor(@NotNull Player editor) {
+        finalizeEditor(editor.gamemode.allowColorFormattingInDyeableText());
+    }
+
+    public void finalizeEditor(boolean allowColorCode) {
+        if (!allowColorCode) for(int i = 0; i < 4; i++) {
+            signText[i] = TextFormatting.removeAllColorFormatting(signText[i]);
+        }
+    }
+
     @Override
 	public Packet getDescriptionPacket()
     {
         return new PacketTileEntityData(this);
     }
-
 }
diff --git a/game/core/src/main/java/net/minecraft/core/net/command/TextFormatting.java b/game/core/src/main/java/net/minecraft/core/net/command/TextFormatting.java
index 25825a9b9..b94457bea 100644
--- a/game/core/src/main/java/net/minecraft/core/net/command/TextFormatting.java
+++ b/game/core/src/main/java/net/minecraft/core/net/command/TextFormatting.java
@@ -5,6 +5,9 @@ import org.jetbrains.annotations.Nullable;
 public class TextFormatting {
 	public static final String FORMAT_CHARS = "0123456789abcdefklmnopr+-";
 	public static final String FORMAT_REGEX = "§[0123456789abcdefklmnopr+-]|§<(.*?)>";
+
+    public static final String COLOR_FORMAT_REGEX = "§[0123456789abcdef]|§<(.*?)>";
+
 	public static final TextFormatting[] FORMATTINGS = new TextFormatting[FORMAT_CHARS.length()];
 	
 	public static final TextFormatting WHITE = new TextFormatting('0').setNames("White");
@@ -75,6 +78,10 @@ public class TextFormatting {
 		return name.replaceAll(FORMAT_REGEX, "");
 	}
 
+    public static String removeAllColorFormatting(String name) {
+        return name.replaceAll(COLOR_FORMAT_REGEX, "");
+    }
+
     public static String formatted(String string, TextFormatting... formattings) {
         StringBuilder sb = new StringBuilder();
         for (TextFormatting formatting : formattings) {
@@ -98,7 +105,7 @@ public class TextFormatting {
 	public String[] getNames() {
 		return names;
 	}
-	
+
 	@Override
 	public String toString() {
 		return "" + SPECIAL_CHAR + code;
diff --git a/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemode.java b/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemode.java
index 681a78a03..562494605 100644
--- a/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemode.java
+++ b/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemode.java
@@ -27,11 +27,12 @@ public final class Gamemode {
     private final boolean properToolNeededToMine;
     private final boolean canInteract;
     private final boolean permaDeath;
+    private final boolean colorFormattingInDyeableText;
     private final float blockReachDistance;
     private final float entityReachDistance;
-
+    
 	/* package-private */ Gamemode(
-		final @NotNull String languageKey,
+        final @NotNull String languageKey,
 		final @NotNull Function<@NotNull ContainerInventory, @NotNull MenuInventory> inventoryMenuSupplier,
 		final boolean invulnerablePlayer,
 		final boolean playerFlight,
@@ -45,6 +46,7 @@ public final class Gamemode {
 		final boolean properToolNeededToMine,
 		final boolean canInteract,
 		final boolean permaDeath,
+        final boolean colorFormattingInDyeableText,
 		final float blockReachDistance,
 		final float entityReachDistance
 	) {
@@ -62,6 +64,7 @@ public final class Gamemode {
 		this.properToolNeededToMine = properToolNeededToMine;
 		this.canInteract = canInteract;
 		this.permaDeath = permaDeath;
+		this.colorFormattingInDyeableText = colorFormattingInDyeableText;
 		this.blockReachDistance = blockReachDistance;
 		this.entityReachDistance = entityReachDistance;
     }
@@ -186,4 +189,11 @@ public final class Gamemode {
     public boolean hasPermaDeath() {
         return this.permaDeath;
     }
+
+    /**
+     * If true, the player is allowed to apply color formatting code on dyeable texts fields, e.g. signs and labels
+     */
+    public boolean allowColorFormattingInDyeableText() {
+        return this.colorFormattingInDyeableText;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/gamemode/GamemodeBuilder.java b/game/core/src/main/java/net/minecraft/core/player/gamemode/GamemodeBuilder.java
index 8ecbcb55a..de808f350 100644
--- a/game/core/src/main/java/net/minecraft/core/player/gamemode/GamemodeBuilder.java
+++ b/game/core/src/main/java/net/minecraft/core/player/gamemode/GamemodeBuilder.java
@@ -25,6 +25,7 @@ public class GamemodeBuilder {
     private boolean properToolNeededToMine = false;
     private boolean canInteract = true;
     private boolean permaDeath = false;
+    private boolean colorFormattingInDyeableText = false;
     private float blockReachDistance = 4.5f;
     private float entityReachDistance = 3.0f;
 
@@ -52,6 +53,7 @@ public class GamemodeBuilder {
             this.properToolNeededToMine,
             this.canInteract,
             this.permaDeath,
+            this.colorFormattingInDyeableText,
             this.blockReachDistance,
             this.entityReachDistance
         );
@@ -182,4 +184,13 @@ public class GamemodeBuilder {
         this.permaDeath = true;
         return this;
     }
+
+    /**
+     * Sets that the player can apply color formatting code on dyeable texts fields.
+     * @see Gamemode#allowColorFormattingInDyeableText()
+     */
+    public @NotNull GamemodeBuilder withColorFormattingInDyeableText() {
+        this.colorFormattingInDyeableText = true;
+        return this;
+    }
 }
diff --git a/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemodes.java b/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemodes.java
index 5ab6818c3..8ac71ebb9 100644
--- a/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemodes.java
+++ b/game/core/src/main/java/net/minecraft/core/player/gamemode/Gamemodes.java
@@ -31,6 +31,7 @@ public final class Gamemodes {
             .withPlayerFlight()
             .withFireImmunity()
             .withInstantPortalTravel()
+            .withColorFormattingInDyeableText()
             .withBlockReachDistance(6.0f)
             .withEntityReachDistance(4.5f)
             .build()
diff --git a/game/core/src/main/java/net/minecraft/core/util/helper/ChatAllowedCharacters.java b/game/core/src/main/java/net/minecraft/core/util/helper/ChatAllowedCharacters.java
deleted file mode 100644
index 157e92e50..000000000
--- a/game/core/src/main/java/net/minecraft/core/util/helper/ChatAllowedCharacters.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package net.minecraft.core.util.helper;
-
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-
-public class ChatAllowedCharacters
-{
-
-    public ChatAllowedCharacters()
-    {
-    }
-
-    private static String getAllowedCharacters()
-    {
-        StringBuilder s = new StringBuilder();
-        try (BufferedReader bufferedReader = new BufferedReader(
-                new InputStreamReader(
-                        ChatAllowedCharacters.class.getResourceAsStream("/font.txt"),
-                        StandardCharsets.UTF_8
-                )
-        )) {
-            do {
-                String s2 = bufferedReader.readLine();
-                if (s2 == null) {
-                    break;
-                }
-                if (!s2.startsWith("#")) {
-                    s.append(s2);
-                }
-            } while (true);
-        } catch (Exception ignored) {}
-        return s.toString();
-    }
-
-    public static final String ALLOWED_CHARACTERS = getAllowedCharacters();
-    public static final char[] ALLOWED_CHARACTERS_ARRAY = {
-        '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', 
-        '<', '>', '|', '"', ':'
-    };
-
-}
diff --git a/game/core/src/main/java/net/minecraft/core/util/helper/NetCharacters.java b/game/core/src/main/java/net/minecraft/core/util/helper/NetCharacters.java
new file mode 100644
index 000000000..d83149464
--- /dev/null
+++ b/game/core/src/main/java/net/minecraft/core/util/helper/NetCharacters.java
@@ -0,0 +1,37 @@
+package net.minecraft.core.util.helper;
+
+public abstract class NetCharacters {
+    private NetCharacters() {}
+
+    private static final short C0_MASK = ~(short)0x1F;
+    private static final short ASCII_MASK = ~(short)0x7F;
+
+    public static boolean isAllowed(String message) {
+        for (int i = 0; i < message.length(); i++) {
+            if (!isAllowed(message.charAt(i))) return false;
+        }
+        return true;
+    }
+
+    public static boolean isAllowed(char ch) {
+        final var u16 = (short)ch;
+
+        // fast path for ascii
+        if ((u16 & C0_MASK) == 0) return false;
+        if ((u16 & ASCII_MASK) == 0) {
+            if (u16 == 0x7F) return false; // DEL
+            return true;
+        }
+
+        switch (Character.getType(ch)) {
+            case Character.CONTROL:
+            case Character.FORMAT:
+            case Character.SURROGATE:
+            case Character.PRIVATE_USE:
+            case Character.UNASSIGNED:
+                return false;
+            default:
+                return true;
+        }
+    }
+}
diff --git a/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java b/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
index fd116a7a4..6c735c023 100644
--- a/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
+++ b/game/core/src/main/java/net/minecraft/core/world/settings/WorldConfiguration.java
@@ -5,7 +5,6 @@ import net.minecraft.core.data.gamerule.GameRuleCollection;
 import net.minecraft.core.lang.I18n;
 import net.minecraft.core.player.gamemode.Gamemode;
 import net.minecraft.core.player.gamemode.Gamemodes;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.world.Dimension;
 import net.minecraft.core.world.config.spawning.SpawnerConfig;
@@ -36,6 +35,8 @@ public class WorldConfiguration implements WorldConfigurationReader {
     private final @NotNull Map<@NotNull Dimension, @NotNull SettingNodesValues> worldSettings = new Object2ObjectArrayMap<>();
     private @Nullable ChangeListener changeListener;
 
+    private static String DISALLOWED_CHARS_REGEX = "[\\/\\n\\r\\t\\x00\\f`\\?\\*\\\\<>|\":]";
+
     public WorldConfiguration() {
         for (final @NotNull Dimension dimension : Dimension.getDimensionList().values()) {
             this.worldSettings.put(dimension, new SettingNodesValues(WorldSettingNodes.getRootNode(), dimension));
@@ -184,15 +185,12 @@ public class WorldConfiguration implements WorldConfigurationReader {
     }
 
     public @NotNull String getFolderName(final @NotNull ISaveFormat saveFormat) {
-        final char @NotNull [] ac = ChatAllowedCharacters.ALLOWED_CHARACTERS_ARRAY;
         @NotNull String folderName = this.worldName.trim();
         if (MathHelper.stringNullOrLengthZero(folderName)) {
             folderName = I18n.getInstance().translateKey("gui.create_world.label.new_world");
         }
 
-        for (final char c : ac) {
-            folderName = folderName.replace(c, '_');
-        }
+        folderName = folderName.replaceAll(DISALLOWED_CHARS_REGEX, "_");
 
         final @NotNull String fixedFolderName = folderName;
 
diff --git a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
index 7a03a7989..e8cf4ceeb 100644
--- a/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
+++ b/game/server/src/main/java/net/minecraft/server/net/handler/PacketHandlerServer.java
@@ -30,7 +30,7 @@ import net.minecraft.core.player.inventory.menu.MenuFlag;
 import net.minecraft.core.player.inventory.menu.MenuInventoryCreative;
 import net.minecraft.core.player.inventory.slot.Slot;
 import net.minecraft.core.util.helper.AES;
-import net.minecraft.core.util.helper.ChatAllowedCharacters;
+import net.minecraft.core.util.helper.NetCharacters;
 import net.minecraft.core.util.helper.Direction;
 import net.minecraft.core.util.helper.MathHelper;
 import net.minecraft.core.util.helper.RestHandler;
@@ -494,18 +494,14 @@ public class PacketHandlerServer extends PacketHandler
         }
 
         if (message.length() > 256) {
-            message = message.substring(0, 255);
+            message = message.substring(0, 256);
         }
 
         message = message.trim();
-//		message = message.replace('§', '$');
 
-        for (int i = 0; i < message.length(); i++) {
-            char c = message.charAt(i);
-            if (ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(c) < 0) {
-                sendPacket(new PacketChat(String.valueOf(TextFormatting.GRAY) + TextFormatting.ITALIC + "[SERVER] Illegal characters in chat message."));
-                return;
-            }
+        if (!NetCharacters.isAllowed(message)) {
+            sendPacket(new PacketChat(String.valueOf(TextFormatting.GRAY) + TextFormatting.ITALIC + "[SERVER] Illegal characters in chat message."));
+            return;
         }
 
         if (message.startsWith("/")) {
@@ -676,46 +672,35 @@ public class PacketHandlerServer extends PacketHandler
     @Override
     public void handleSignUpdate(@NotNull PacketSignUpdate packetSignUpdate) {
         if (!this.playerEntity.getGamemode().canInteract() || !this.playerEntity.isAlive()) return;
-        WorldServer worldserver = this.mcServer.getDimensionWorld(this.playerEntity.dimension);
-        if (worldserver.isBlockLoaded(packetSignUpdate.xPosition, packetSignUpdate.yPosition, packetSignUpdate.zPosition)) {
-            TileEntity tileEntity = worldserver.getTileEntity(packetSignUpdate.xPosition, packetSignUpdate.yPosition, packetSignUpdate.zPosition);
-            if (tileEntity instanceof TileEntitySign sign) {
-                if (!sign.isEditableBy(this.playerEntity)) {
-                    LOGGER.warn("Player {} just tried to change non-editable sign", this.playerEntity.username);
-                    return;
-                }
-            }
-            for (int i = 0; i < 4; i++) {
-                boolean isLineValid = true;
-                if (packetSignUpdate.signLines[i].length() > 15) {
-                    isLineValid = false;
-                } else {
-                    packetSignUpdate.signLines[i] = packetSignUpdate.signLines[i].replaceAll("§", "$");
-                    for (int l = 0; l < packetSignUpdate.signLines[i].length(); l++) {
-                        if (ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(packetSignUpdate.signLines[i].charAt(l)) < 0) {
-                            isLineValid = false;
-                            break;
-                        }
-                    }
-                }
-                if (!isLineValid) {
-                    packetSignUpdate.signLines[i] = "!?";
-                }
-            }
+        final var worldserver = this.mcServer.getDimensionWorld(this.playerEntity.dimension);
+        final var pos = new TilePos(packetSignUpdate.xPosition, packetSignUpdate.yPosition, packetSignUpdate.zPosition);
 
-            if (tileEntity instanceof TileEntitySign sign) {
-                int x = packetSignUpdate.xPosition;
-                int y = packetSignUpdate.yPosition;
-                int z = packetSignUpdate.zPosition;
-                System.arraycopy(packetSignUpdate.signLines, 0, sign.signText, 0, 4);
+        if (!worldserver.isBlockLoaded(pos)) return;
+        if (!(worldserver.getTileEntity(pos) instanceof TileEntitySign sign)) return;
 
-                sign.setColor(TextFormatting.FORMATTINGS[packetSignUpdate.color]);
-                sign.setPicture(EnumSignPicture.values()[packetSignUpdate.picture]);
+        if (!sign.isEditableBy(this.playerEntity)) {
+            LOGGER.warn("Player {} just tried to change non-editable sign", this.playerEntity.username);
+            return;
+        }
 
-                sign.setChanged();
-                worldserver.markBlockNeedsUpdate(x, y, z);
+        for (int i = 0; i < packetSignUpdate.signLines.length; i++) {
+            final String line = packetSignUpdate.signLines[i];
+            assert line.length() <= TileEntitySign.MAX_LINE_SIZE; // PacketSignUpdate.read guarantees
+            
+            if (!NetCharacters.isAllowed(line)) {
+                packetSignUpdate.signLines[i] = "!?";
+                continue;
             }
         }
+
+        System.arraycopy(packetSignUpdate.signLines, 0, sign.signText, 0, 4);
+        sign.finalizeEditor(this.playerEntity);
+
+        sign.setColor(TextFormatting.FORMATTINGS[packetSignUpdate.color]);
+        sign.setPicture(EnumSignPicture.values()[packetSignUpdate.picture]);
+
+        sign.setChanged();
+        worldserver.markBlockNeedsUpdate(pos);
     }
 
     @Override
@@ -738,6 +723,9 @@ public class PacketHandlerServer extends PacketHandler
     public void handleSetItemName(@NotNull PacketSetItemName packet) {
         String name = packet.name;
         if (name.length() > 16) name = packet.name.substring(0, 16);
+        if (!this.playerEntity.gamemode.allowColorFormattingInDyeableText()) {
+            name = TextFormatting.removeAllColorFormatting(name);
+        }
         this.playerEntity.inventory.getItem(packet.slot).setCustomName(name);
     }
 
