55import dev .architectury .event .events .common .ChatEvent ;
66import dev .architectury .event .events .common .LifecycleEvent ;
77import dev .architectury .event .events .common .PlayerEvent ;
8+ import net .minecraft .ChatFormatting ;
9+ import net .minecraft .network .chat .ClickEvent ;
810import net .minecraft .network .chat .Component ;
911import net .minecraft .server .level .ServerPlayer ;
12+ import net .minecraft .network .chat .MutableComponent ;
13+ import net .minecraft .network .chat .TextColor ;
1014import net .minecraft .server .MinecraftServer ;
1115import net .stonebound .simpleircbridge .utils .MircColors ;
1216import org .slf4j .Logger ;
1317
1418import static net .stonebound .simpleircbridge .simpleircbridge .Config .channel ;
1519import static net .stonebound .simpleircbridge .simpleircbridge .Config .timestop ;
20+ import java .net .URI ;
21+ import java .net .URISyntaxException ;
22+ import java .util .regex .Matcher ;
23+ import java .util .regex .Pattern ;
1624
1725
1826public class SimpleIRCBridgeCommon {
@@ -32,13 +40,12 @@ public SimpleIRCBridgeCommon() {
3240 PlayerEvent .PLAYER_JOIN .register ((Playerjoin ) -> eventHandler .playerLoggedIn (Playerjoin ));
3341 PlayerEvent .PLAYER_QUIT .register ((Playerquit ) -> eventHandler .playerLoggedOut (Playerquit ));
3442
35- ChatEvent .RECEIVED .register ((ServerPlayer player , Component chat ) -> {
36- eventHandler .serverChat (player , chat );
37- return EventResult .pass ();
38- });
43+
3944 LifecycleEvent .SERVER_STARTING .register (this ::serverStarting );
4045 LifecycleEvent .SERVER_STOPPING .register (this ::serverStopping );
4146 LifecycleEvent .SERVER_STOPPED .register (this ::serverStopped );
47+ ChatEvent .DECORATE .register (eventHandler ::serverChat );
48+
4249
4350 }
4451
@@ -79,10 +86,82 @@ static Logger log() {
7986 }
8087 }
8188
82- /* package-private */
83- void sendToMinecraft (String line ){
84- if (this .mcServer != null ) {
85- this .mcServer .getPlayerList ().getPlayers ().forEach (player -> player .sendSystemMessage (Component .literal (line )));
86- }
87- }
89+
90+ /* package-private */
91+ void sendToMinecraft (String line ) {
92+ if (this .mcServer != null ) {
93+ this .mcServer .getPlayerList ().getPlayers ().forEach (player -> player .sendSystemMessage (newChatWithLinks (line , true )));
94+ }
95+ }
96+
97+ /*
98+ * Copyright (c) Forge Development LLC and contributors
99+ * SPDX-License-Identifier: LGPL-2.1-only
100+ */
101+ static final Pattern URL_PATTERN = Pattern .compile (
102+ // schema ipv4 OR namespace port path ends
103+ // |-----------------| |-------------------------| |-------------------------| |---------| |--| |---------------|
104+ "((?:[a-z0-9]{2,}:\\ /\\ /)?(?:(?:[0-9]{1,3}\\ .){3}[0-9]{1,3}|(?:[-\\ w_]{1,}\\ .[a-z]{2,}?))(?::[0-9]{1,5})?.*?(?=[!\" \u00A7 \n ]|$))" ,
105+ Pattern .CASE_INSENSITIVE );
106+
107+ public static Component newChatWithLinks (String string , boolean allowMissingHeader ) {
108+ // Includes ipv4 and domain pattern
109+ // Matches an ip (xx.xxx.xx.xxx) or a domain (something.com) with or
110+ // without a protocol or path.
111+ MutableComponent ichat = null ;
112+ Matcher matcher = URL_PATTERN .matcher (string );
113+ int lastEnd = 0 ;
114+
115+ // Find all urls
116+ while (matcher .find ()) {
117+ int start = matcher .start ();
118+ int end = matcher .end ();
119+
120+ // Append the previous left overs.
121+ String part = string .substring (lastEnd , start );
122+ if (!part .isEmpty ()) {
123+ if (ichat == null )
124+ ichat = Component .literal (part );
125+ else
126+ ichat .append (part );
127+ }
128+ lastEnd = end ;
129+ String url = string .substring (start , end );
130+ MutableComponent link = Component .literal (url );
131+
132+ try {
133+ // Add schema so client doesn't crash.
134+ if ((new URI (url )).getScheme () == null ) {
135+ if (!allowMissingHeader ) {
136+ if (ichat == null )
137+ ichat = Component .literal (url );
138+ else
139+ ichat .append (url );
140+ continue ;
141+ }
142+ url = "http://" + url ;
143+ }
144+ } catch (URISyntaxException e ) {
145+ // Bad syntax bail out!
146+ if (ichat == null ) ichat = Component .literal (url );
147+ else ichat .append (url );
148+ continue ;
149+ }
150+
151+ // Set the click event and append the link.
152+ ClickEvent click = new ClickEvent (ClickEvent .Action .OPEN_URL , url );
153+ link .setStyle (link .getStyle ().withClickEvent (click ).withUnderlined (true ).withColor (TextColor .fromLegacyFormat (ChatFormatting .BLUE )));
154+ if (ichat == null )
155+ ichat = Component .literal ("" );
156+ ichat .append (link );
157+ }
158+
159+ // Append the rest of the message.
160+ String end = string .substring (lastEnd );
161+ if (ichat == null )
162+ ichat = Component .literal (end );
163+ else if (!end .isEmpty ())
164+ ichat .append (Component .literal (string .substring (lastEnd )));
165+ return ichat ;
166+ }
88167}
0 commit comments