diff --git a/src/tel/eden/mod/chat/ChatText.java b/src/tel/eden/mod/chat/ChatText.java index bd201f4..750c241 100644 --- a/src/tel/eden/mod/chat/ChatText.java +++ b/src/tel/eden/mod/chat/ChatText.java @@ -41,6 +41,13 @@ private ChatText() { * already a valid username, else null. */ static String resolveRealName(Component message, String displayed) { + if (displayed == null) { + return null; + } + if (message == null) { + String cleaned = displayed.replaceAll("[^a-zA-Z0-9_]", ""); + return IGN.matcher(cleaned).matches() ? cleaned : null; + } List chars = flatten(message); String text = text(chars); int start = text.indexOf(displayed); @@ -74,6 +81,9 @@ static String resolveRealName(Component message, String displayed) { * no metadata is present (caller falls back to the displayed name). */ public static String resolveRealNameAnywhere(Component message, String displayed) { + if (message == null) { + return null; + } Set candidates = new LinkedHashSet<>(); message.visit((style, fragment) -> { String hover = hoverRealName(style); @@ -113,6 +123,9 @@ public static String resolveRealNameAnywhere(Component message, String displayed } private static List flatten(Component message) { + if (message == null) { + return List.of(); + } List out = new ArrayList<>(); message.visit((style, fragment) -> { String hover = hoverRealName(style); diff --git a/src/tel/eden/mod/chat/PlayerNameResolver.java b/src/tel/eden/mod/chat/PlayerNameResolver.java index 7a21774..7fe2624 100644 --- a/src/tel/eden/mod/chat/PlayerNameResolver.java +++ b/src/tel/eden/mod/chat/PlayerNameResolver.java @@ -31,12 +31,15 @@ public static void reset() { /** Resolve a rendered player name and retain any hover-provided nickname mapping. */ public static String resolve(Component message, String displayed) { + if (displayed == null) { + return null; + } // A reward/bank line can contain several player names. Resolving from every // hover in the whole component made the receiver inherit the giver's hover // (e.g. a catboy/FadeDave hover turned every recipient into FadeDave). Only // accept metadata attached to this displayed span; a previously learned alias // is the safe fallback when the server omits that span's hover. - String resolved = ChatText.resolveRealName(message, displayed); + String resolved = message != null ? ChatText.resolveRealName(message, displayed) : null; if (resolved == null) { resolved = canonicalize(displayed); } diff --git a/test/tel/eden/mod/chat/ChatTextTest.java b/test/tel/eden/mod/chat/ChatTextTest.java index f17c416..1ace502 100644 --- a/test/tel/eden/mod/chat/ChatTextTest.java +++ b/test/tel/eden/mod/chat/ChatTextTest.java @@ -82,6 +82,18 @@ void resolveRealNameAnywhereReturnsNullWithNoHoverOrInsertionMetadata() { assertNull(ChatText.resolveRealNameAnywhere(message, "plain")); } + @Test + void resolveRealNameHandlesNullMessageSafely() { + assertEquals("Asthae", ChatText.resolveRealName(null, "Asthae")); + assertNull(ChatText.resolveRealName(null, "some weird nickname")); + assertNull(ChatText.resolveRealName(null, null)); + } + + @Test + void resolveRealNameAnywhereHandlesNullMessageSafely() { + assertNull(ChatText.resolveRealNameAnywhere(null, "plain")); + } + @Test void resolveClickTargetNamePeelsANickSlashRealSuffix() { Component message = Component.literal("catboy/FadeDave"); diff --git a/test/tel/eden/mod/chat/PlayerNameResolverTest.java b/test/tel/eden/mod/chat/PlayerNameResolverTest.java index 45ff1ff..a29c9b2 100644 --- a/test/tel/eden/mod/chat/PlayerNameResolverTest.java +++ b/test/tel/eden/mod/chat/PlayerNameResolverTest.java @@ -87,4 +87,10 @@ void learnFromTextIgnoresUnknownWordsAndLearnsKnownPlayer() { PlayerNameResolver.learnFromText("KnownUser/NickBuddy joined!"); assertEquals(Optional.of("KnownUser"), PlayerNameResolver.resolveKnown("NickBuddy")); } + + @Test + void resolveHandlesNullMessageSafely() { + assertEquals("SomePlayer", PlayerNameResolver.resolve(null, "SomePlayer")); + assertEquals(null, PlayerNameResolver.resolve(null, null)); + } }