-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
0006-Adventure.patch
5545 lines (5332 loc) · 232 KB
/
0006-Adventure.patch
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Riley Park <rileysebastianpark@gmail.com>
Date: Fri, 29 Jan 2021 17:21:55 +0100
Subject: [PATCH] Adventure
Co-authored-by: zml <zml@stellardrift.ca>
Co-authored-by: Jake Potrebic <jake.m.potrebic@gmail.com>
Co-authored-by: Yannick Lamprecht <yannicklamprecht@live.de>
diff --git a/build.gradle.kts b/build.gradle.kts
index 1a80f7c7e7f4ae9857b732897dc2ee7cfc187626..f57827e724bff2bf586b468cc4e5ba6a1901bd57 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -11,6 +11,18 @@ java {
val annotationsVersion = "24.1.0"
val bungeeCordChatVersion = "1.20-R0.2"
+val adventureVersion = "4.17.0"
+val apiAndDocs: Configuration by configurations.creating {
+ attributes {
+ attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
+ attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
+ attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
+ attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
+ }
+}
+configurations.api {
+ extendsFrom(apiAndDocs)
+}
// Paper start - configure mockito agent that is needed in newer java versions
val mockitoAgent = configurations.register("mockitoAgent")
@@ -28,7 +40,11 @@ dependencies {
// api dependencies are listed transitively to API consumers
api("com.google.guava:guava:32.1.2-jre")
api("com.google.code.gson:gson:2.10.1")
- api("net.md-5:bungeecord-chat:$bungeeCordChatVersion")
+ // Paper start - adventure
+ api("net.md-5:bungeecord-chat:$bungeeCordChatVersion-deprecated+build.18") {
+ exclude("com.google.guava", "guava")
+ }
+ // Paper - adventure
api("org.yaml:snakeyaml:2.2")
api("org.joml:joml:1.10.5")
// Paper start
@@ -36,6 +52,13 @@ dependencies {
isTransitive = false // includes junit
}
api("it.unimi.dsi:fastutil:8.5.6")
+ apiAndDocs(platform("net.kyori:adventure-bom:$adventureVersion"))
+ apiAndDocs("net.kyori:adventure-api")
+ apiAndDocs("net.kyori:adventure-text-minimessage")
+ apiAndDocs("net.kyori:adventure-text-serializer-gson")
+ apiAndDocs("net.kyori:adventure-text-serializer-legacy")
+ apiAndDocs("net.kyori:adventure-text-serializer-plain")
+ apiAndDocs("net.kyori:adventure-text-logger-slf4j")
// Paper end
compileOnly("org.apache.maven:maven-resolver-provider:3.9.6")
@@ -113,15 +136,32 @@ tasks.withType<Javadoc> {
"https://guava.dev/releases/32.1.2-jre/api/docs/",
"https://javadoc.io/doc/org.yaml/snakeyaml/2.2/",
"https://javadoc.io/doc/org.jetbrains/annotations/$annotationsVersion/", // Paper - we don't want Java 5 annotations
- "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/",
+ // "https://javadoc.io/doc/net.md-5/bungeecord-chat/$bungeeCordChatVersion/", // Paper - don't link to bungee chat
// Paper start - add missing javadoc links
"https://javadoc.io/doc/org.joml/joml/1.10.5/index.html",
"https://www.javadoc.io/doc/com.google.code.gson/gson/2.10.1",
"https://jspecify.dev/docs/api/",
// Paper end
+ // Paper start
+ "https://jd.advntr.dev/api/$adventureVersion/",
+ "https://jd.advntr.dev/key/$adventureVersion/",
+ "https://jd.advntr.dev/text-minimessage/$adventureVersion/",
+ "https://jd.advntr.dev/text-serializer-gson/$adventureVersion/",
+ "https://jd.advntr.dev/text-serializer-legacy/$adventureVersion/",
+ "https://jd.advntr.dev/text-serializer-plain/$adventureVersion/",
+ "https://jd.advntr.dev/text-logger-slf4j/$adventureVersion/",
+ // Paper end
)
options.tags("apiNote:a:API Note:")
+ inputs.files(apiAndDocs).ignoreEmptyDirectories().withPropertyName(apiAndDocs.name + "-configuration")
+ doFirst {
+ options.addStringOption(
+ "sourcepath",
+ apiAndDocs.resolvedConfiguration.files.joinToString(separator = File.pathSeparator, transform = File::getPath)
+ )
+ }
+
// workaround for https://github.com/gradle/gradle/issues/4046
inputs.dir("src/main/javadoc").withPropertyName("javadoc-sourceset")
doLast {
diff --git a/src/main/java/io/papermc/paper/chat/ChatRenderer.java b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..1288cdeafe587e9e78e7c5087742ea054ba8423d
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ChatRenderer.java
@@ -0,0 +1,70 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * A chat renderer is responsible for rendering chat messages sent by {@link Player}s to the server.
+ */
+@NullMarked
+@FunctionalInterface
+public interface ChatRenderer {
+
+ /**
+ * Renders a chat message. This will be called once for each receiving {@link Audience}.
+ *
+ * @param source the message source
+ * @param sourceDisplayName the display name of the source player
+ * @param message the chat message
+ * @param viewer the receiving {@link Audience}
+ * @return a rendered chat message
+ */
+ @ApiStatus.OverrideOnly
+ Component render(Player source, Component sourceDisplayName, Component message, Audience viewer);
+
+ /**
+ * Create a new instance of the default {@link ChatRenderer}.
+ *
+ * @return a new {@link ChatRenderer}
+ */
+ static ChatRenderer defaultRenderer() {
+ return new ViewerUnawareImpl.Default((source, sourceDisplayName, message) -> Component.translatable("chat.type.text", sourceDisplayName, message));
+ }
+
+ @ApiStatus.Internal
+ sealed interface Default extends ChatRenderer, ViewerUnaware permits ViewerUnawareImpl.Default {
+ }
+
+ /**
+ * Creates a new viewer-unaware {@link ChatRenderer}, which will render the chat message a single time,
+ * displaying the same rendered message to every viewing {@link Audience}.
+ *
+ * @param renderer the viewer unaware renderer
+ * @return a new {@link ChatRenderer}
+ */
+ static ChatRenderer viewerUnaware(final ViewerUnaware renderer) {
+ return new ViewerUnawareImpl(renderer);
+ }
+
+ /**
+ * Similar to {@link ChatRenderer}, but without knowledge of the message viewer.
+ *
+ * @see ChatRenderer#viewerUnaware(ViewerUnaware)
+ */
+ interface ViewerUnaware {
+
+ /**
+ * Renders a chat message.
+ *
+ * @param source the message source
+ * @param sourceDisplayName the display name of the source player
+ * @param message the chat message
+ * @return a rendered chat message
+ */
+ @ApiStatus.OverrideOnly
+ Component render(Player source, Component sourceDisplayName, Component message);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..e1ef23b4bdf60a299d6d37099cef777b3de13ac7
--- /dev/null
+++ b/src/main/java/io/papermc/paper/chat/ViewerUnawareImpl.java
@@ -0,0 +1,38 @@
+package io.papermc.paper.chat;
+
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Internal
+@NullMarked
+sealed class ViewerUnawareImpl implements ChatRenderer, ChatRenderer.ViewerUnaware permits ViewerUnawareImpl.Default {
+ private final ViewerUnaware unaware;
+ private @Nullable Component message;
+
+ ViewerUnawareImpl(final ViewerUnaware unaware) {
+ this.unaware = unaware;
+ }
+
+ @Override
+ public Component render(final Player source, final Component sourceDisplayName, final Component message, final Audience viewer) {
+ return this.render(source, sourceDisplayName, message);
+ }
+
+ @Override
+ public Component render(final Player source, final Component sourceDisplayName, final Component message) {
+ if (this.message == null) {
+ this.message = this.unaware.render(source, sourceDisplayName, message);
+ }
+ return this.message;
+ }
+
+ static final class Default extends ViewerUnawareImpl implements ChatRenderer.Default {
+ Default(final ViewerUnaware unaware) {
+ super(unaware);
+ }
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..9a95d203151d2c91b0eec494e3674f0facfaa305
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AbstractChatEvent.java
@@ -0,0 +1,122 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.player.PlayerEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * An abstract implementation of a chat event, handling shared logic.
+ */
+@ApiStatus.NonExtendable
+@NullMarked
+public abstract class AbstractChatEvent extends PlayerEvent implements Cancellable {
+
+ private final Set<Audience> viewers;
+ private final Component originalMessage;
+ private final SignedMessage signedMessage;
+ private ChatRenderer renderer;
+ private Component message;
+
+ private boolean cancelled;
+
+ AbstractChatEvent(final boolean async, final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+ super(player, async);
+ this.viewers = viewers;
+ this.renderer = renderer;
+ this.message = message;
+ this.originalMessage = originalMessage;
+ this.signedMessage = signedMessage;
+ }
+
+ /**
+ * Gets a set of {@link Audience audiences} that this chat message will be displayed to.
+ *
+ * <p>The set returned may auto-populate on access. Any listener accessing the returned set should be aware that
+ * it may reduce performance for a lazy set implementation.</p>
+ *
+ * @return a mutable set of {@link Audience audiences} who will receive the chat message
+ */
+ public final Set<Audience> viewers() {
+ return this.viewers;
+ }
+
+ /**
+ * Sets the chat renderer.
+ *
+ * @param renderer the chat renderer
+ * @throws NullPointerException if {@code renderer} is {@code null}
+ */
+ public final void renderer(final ChatRenderer renderer) {
+ this.renderer = requireNonNull(renderer, "renderer");
+ }
+
+ /**
+ * Gets the chat renderer.
+ *
+ * @return the chat renderer
+ */
+ public final ChatRenderer renderer() {
+ return this.renderer;
+ }
+
+ /**
+ * Gets the user-supplied message.
+ * The return value will reflect changes made using {@link #message(Component)}.
+ *
+ * @return the user-supplied message
+ */
+ public final Component message() {
+ return this.message;
+ }
+
+ /**
+ * Sets the user-supplied message.
+ *
+ * @param message the user-supplied message
+ * @throws NullPointerException if {@code message} is {@code null}
+ */
+ public final void message(final Component message) {
+ this.message = requireNonNull(message, "message");
+ }
+
+ /**
+ * Gets the original and unmodified user-supplied message.
+ * The return value will <b>not</b> reflect changes made using
+ * {@link #message(Component)}.
+ *
+ * @return the original user-supplied message
+ */
+ public final Component originalMessage() {
+ return this.originalMessage;
+ }
+
+ /**
+ * Gets the signed message.
+ * Changes made in this event will <b>not</b> update
+ * the signed message.
+ *
+ * @return the signed message
+ */
+ public final SignedMessage signedMessage() {
+ return this.signedMessage;
+ }
+
+ @Override
+ public final boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ @Override
+ public final void setCancelled(final boolean cancel) {
+ this.cancelled = cancel;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..ddd4c90f83b5cb8f069ff53760abb3c4adfd1168
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatCommandDecorateEvent.java
@@ -0,0 +1,29 @@
+package io.papermc.paper.event.player;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+@ApiStatus.Experimental
+@NullMarked
+public class AsyncChatCommandDecorateEvent extends AsyncChatDecorateEvent {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ @ApiStatus.Internal
+ public AsyncChatCommandDecorateEvent(final @Nullable Player player, final Component originalMessage) {
+ super(player, originalMessage);
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..9e5ea0cd006bd9744b84923620841f07fa40c2cb
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatDecorateEvent.java
@@ -0,0 +1,105 @@
+package io.papermc.paper.event.player;
+
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.server.ServerEvent;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * This event is fired when the server decorates a component for chat purposes. This is called
+ * before {@link AsyncChatEvent} and the other chat events. It is recommended that you modify the
+ * message here, and use the chat events for modifying receivers and later the chat type. If you
+ * want to keep the message as "signed" for the clients who get it, be sure to include the entire
+ * original message somewhere in the final message.
+ * <br>
+ * See {@link AsyncChatCommandDecorateEvent} for the decoration of messages sent via commands
+ */
+@ApiStatus.Experimental
+@NullMarked
+public class AsyncChatDecorateEvent extends ServerEvent implements Cancellable {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ private final @Nullable Player player;
+ private final Component originalMessage;
+ private Component result;
+
+ private boolean cancelled;
+
+ @ApiStatus.Internal
+ public AsyncChatDecorateEvent(final @Nullable Player player, final Component originalMessage) {
+ super(true);
+ this.player = player;
+ this.originalMessage = originalMessage;
+ this.result = originalMessage;
+ }
+
+ /**
+ * Gets the player (if available) associated with this event.
+ * <p>
+ * Certain commands request decorations without a player context
+ * which is why this is possibly null.
+ *
+ * @return the player or {@code null}
+ */
+ public @Nullable Player player() {
+ return this.player;
+ }
+
+ /**
+ * Gets the original decoration input
+ *
+ * @return the input
+ */
+ public Component originalMessage() {
+ return this.originalMessage;
+ }
+
+ /**
+ * Gets the decoration result. This may already be different from
+ * {@link #originalMessage()} if some other listener to this event
+ * changed the result.
+ *
+ * @return the result
+ */
+ public Component result() {
+ return this.result;
+ }
+
+ /**
+ * Sets the resulting decorated component.
+ *
+ * @param result the result
+ */
+ public void result(final Component result) {
+ this.result = result;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return this.cancelled;
+ }
+
+ /**
+ * A cancelled decorating event means that no changes to the result component
+ * will have any effect. The decorated component will be equal to the original
+ * component.
+ */
+ @Override
+ public void setCancelled(final boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..50c3e117dec63811823b4e6395bf4f090692ee8c
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/AsyncChatEvent.java
@@ -0,0 +1,44 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An event fired when a {@link Player} sends a chat message to the server.
+ * <p>
+ * This event will sometimes fire synchronously, depending on how it was
+ * triggered.
+ * <p>
+ * If a player is the direct cause of this event by an incoming packet, this
+ * event will be asynchronous. If a plugin triggers this event by compelling a
+ * player to chat, this event will be synchronous.
+ * <p>
+ * Care should be taken to check {@link #isAsynchronous()} and treat the event
+ * appropriately.
+ */
+@NullMarked
+public final class AsyncChatEvent extends AbstractChatEvent {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ @ApiStatus.Internal
+ public AsyncChatEvent(final boolean async, final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+ super(async, player, viewers, renderer, message, originalMessage, signedMessage);
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/event/player/ChatEvent.java b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
new file mode 100644
index 0000000000000000000000000000000000000000..42a82ce2316a4aad2883d24c7e2ff95d95f5881a
--- /dev/null
+++ b/src/main/java/io/papermc/paper/event/player/ChatEvent.java
@@ -0,0 +1,40 @@
+package io.papermc.paper.event.player;
+
+import io.papermc.paper.chat.ChatRenderer;
+import java.util.Set;
+import net.kyori.adventure.audience.Audience;
+import net.kyori.adventure.chat.SignedMessage;
+import net.kyori.adventure.text.Component;
+import org.bukkit.Warning;
+import org.bukkit.entity.Player;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NullMarked;
+
+/**
+ * An event fired when a {@link Player} sends a chat message to the server.
+ *
+ * @deprecated Listening to this event forces chat to wait for the main thread, delaying chat messages.
+ * It is recommended to use {@link AsyncChatEvent} instead, wherever possible.
+ */
+@Deprecated
+@Warning(reason = "Listening to this event forces chat to wait for the main thread, delaying chat messages.")
+@NullMarked
+public final class ChatEvent extends AbstractChatEvent {
+
+ private static final HandlerList HANDLER_LIST = new HandlerList();
+
+ @ApiStatus.Internal
+ public ChatEvent(final Player player, final Set<Audience> viewers, final ChatRenderer renderer, final Component message, final Component originalMessage, final SignedMessage signedMessage) {
+ super(false, player, viewers, renderer, message, originalMessage, signedMessage);
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return HANDLER_LIST;
+ }
+
+ public static HandlerList getHandlerList() {
+ return HANDLER_LIST;
+ }
+}
diff --git a/src/main/java/io/papermc/paper/text/PaperComponents.java b/src/main/java/io/papermc/paper/text/PaperComponents.java
new file mode 100644
index 0000000000000000000000000000000000000000..934d1d3ca490a8e25c438bc8c57eb6bde50e0147
--- /dev/null
+++ b/src/main/java/io/papermc/paper/text/PaperComponents.java
@@ -0,0 +1,180 @@
+package io.papermc.paper.text;
+
+import java.io.IOException;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.flattener.ComponentFlattener;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
+import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
+import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Entity;
+import org.jspecify.annotations.NullMarked;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Paper API-specific methods for working with {@link Component}s and related.
+ */
+@NullMarked
+public final class PaperComponents {
+
+ private PaperComponents() {
+ throw new RuntimeException("PaperComponents is not to be instantiated!");
+ }
+
+ /**
+ * Resolves a component with a specific command sender and subject.
+ * <p>
+ * Note that in Vanilla, elevated permissions are usually required to use
+ * '@' selectors in various component types, but this method should not
+ * check such permissions from the sender.
+ * <p>
+ * A {@link CommandSender} argument is required to resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+ * </ul>
+ * A {@link Entity} argument is optional to help resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * </ul>
+ * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+ *
+ * @param input the component to resolve
+ * @param context the command sender to resolve with
+ * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject) throws IOException {
+ return resolveWithContext(input, context, scoreboardSubject, true);
+ }
+
+ /**
+ * Resolves a component with a specific command sender and subject.
+ * <p>
+ * Note that in Vanilla, elevated permissions are required to use
+ * '@' selectors in various component types. If the boolean {@code bypassPermissions}
+ * argument is {@code false}, the {@link CommandSender} argument will be used to query
+ * those permissions.
+ * <p>
+ * A {@link CommandSender} argument is required to resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.NBTComponent}</li>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * <li>{@link net.kyori.adventure.text.SelectorComponent}</li>
+ * </ul>
+ * A {@link Entity} argument is optional to help resolve:
+ * <ul>
+ * <li>{@link net.kyori.adventure.text.ScoreComponent}</li>
+ * </ul>
+ * {@link net.kyori.adventure.text.TranslatableComponent}s don't require any extra arguments.
+ *
+ * @param input the component to resolve
+ * @param context the command sender to resolve with
+ * @param scoreboardSubject the scoreboard subject to use (for use with {@link net.kyori.adventure.text.ScoreComponent}s)
+ * @param bypassPermissions true to bypass permissions checks for resolving components
+ * @return the resolved component
+ * @throws IOException if a syntax error tripped during resolving
+ */
+ @SuppressWarnings("deprecation") // using unsafe as a bridge
+ public static Component resolveWithContext(final Component input, final @Nullable CommandSender context, final @Nullable Entity scoreboardSubject, final boolean bypassPermissions) throws IOException {
+ return Bukkit.getUnsafe().resolveWithContext(input, context, scoreboardSubject, bypassPermissions);
+ }
+
+ /**
+ * Return a component flattener that can use game data to resolve extra information about components.
+ *
+ * @return a component flattener
+ */
+ @SuppressWarnings("deprecation") // using unsafe as a bridge
+ public static ComponentFlattener flattener() {
+ return Bukkit.getUnsafe().componentFlattener();
+ }
+
+ /**
+ * Get a serializer for {@link Component}s that will convert components to
+ * a plain-text string.
+ *
+ * <p>Implementations may provide a serializer capable of processing any
+ * information that requires access to implementation details.</p>
+ *
+ * @return a serializer to plain text
+ * @deprecated will be removed in adventure 5.0.0, use {@link PlainTextComponentSerializer#plainText()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.1")
+ public static PlainComponentSerializer plainSerializer() {
+ return Bukkit.getUnsafe().plainComponentSerializer();
+ }
+
+ /**
+ * Get a serializer for {@link Component}s that will convert components to
+ * a plain-text string.
+ *
+ * <p>Implementations may provide a serializer capable of processing any
+ * information that requires access to implementation details.</p>
+ *
+ * @return a serializer to plain text
+ * @deprecated use {@link PlainTextComponentSerializer#plainText()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static PlainTextComponentSerializer plainTextSerializer() {
+ return Bukkit.getUnsafe().plainTextSerializer();
+ }
+
+ /**
+ * Get a serializer for {@link Component}s that will convert to and from the
+ * standard JSON serialization format using Gson.
+ *
+ * <p>Implementations may provide a serializer capable of processing any
+ * information that requires implementation details, such as legacy
+ * (pre-1.16) hover events.</p>
+ *
+ * @return a json component serializer
+ * @deprecated use {@link GsonComponentSerializer#gson()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static GsonComponentSerializer gsonSerializer() {
+ return Bukkit.getUnsafe().gsonComponentSerializer();
+ }
+
+ /**
+ * Get a serializer for {@link Component}s that will convert to and from the
+ * standard JSON serialization format using Gson, downsampling any RGB colors
+ * to their nearest {@link NamedTextColor} counterpart.
+ *
+ * <p>Implementations may provide a serializer capable of processing any
+ * information that requires implementation details, such as legacy
+ * (pre-1.16) hover events.</p>
+ *
+ * @return a json component serializer
+ * @deprecated use {@link GsonComponentSerializer#colorDownsamplingGson()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static GsonComponentSerializer colorDownsamplingGsonSerializer() {
+ return Bukkit.getUnsafe().colorDownsamplingGsonComponentSerializer();
+ }
+
+ /**
+ * Get a serializer for {@link Component}s that will convert to and from the
+ * legacy component format used by Bukkit. This serializer uses the
+ * {@link LegacyComponentSerializer.Builder#useUnusualXRepeatedCharacterHexFormat()}
+ * option to match upstream behavior.
+ *
+ * <p>This legacy serializer uses the standard section symbol to mark
+ * formatting characters.</p>
+ *
+ * <p>Implementations may provide a serializer capable of processing any
+ * information that requires access to implementation details.</p>
+ *
+ * @return a section serializer
+ * @deprecated use {@link LegacyComponentSerializer#legacySection()}
+ */
+ @Deprecated(forRemoval = true, since = "1.18.2")
+ public static LegacyComponentSerializer legacySectionSerializer() {
+ return Bukkit.getUnsafe().legacyComponentSerializer();
+ }
+}
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index a485d590242b814e85aa2a1d54f0926017cfd2b5..fed32ada5a5ba6906c0f8e854ad71fd691fe5811 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -423,7 +423,9 @@ public final class Bukkit {
*
* @param message the message
* @return the number of players
+ * @deprecated in favour of {@link Server#broadcast(net.kyori.adventure.text.Component)}
*/
+ @Deprecated // Paper
public static int broadcastMessage(@NotNull String message) {
return server.broadcastMessage(message);
}
@@ -1227,6 +1229,19 @@ public final class Bukkit {
server.shutdown();
}
+ // Paper start
+ /**
+ * Broadcast a message to all players.
+ * <p>
+ * This is the same as calling {@link #broadcast(net.kyori.adventure.text.Component,
+ * java.lang.String)} with the {@link Server#BROADCAST_CHANNEL_USERS} permission.
+ *
+ * @param message the message
+ * @return the number of players
+ */
+ public static int broadcast(net.kyori.adventure.text.@NotNull Component message) {
+ return server.broadcast(message);
+ }
/**
* Broadcasts the specified message to every user with the given
* permission name.
@@ -1236,6 +1251,21 @@ public final class Bukkit {
* permissibles} must have to receive the broadcast
* @return number of message recipients
*/
+ public static int broadcast(net.kyori.adventure.text.@NotNull Component message, @NotNull String permission) {
+ return server.broadcast(message, permission);
+ }
+ // Paper end
+ /**
+ * Broadcasts the specified message to every user with the given
+ * permission name.
+ *
+ * @param message message to broadcast
+ * @param permission the required permission {@link Permissible
+ * permissibles} must have to receive the broadcast
+ * @return number of message recipients
+ * @deprecated in favour of {@link #broadcast(net.kyori.adventure.text.Component, String)}
+ */
+ @Deprecated // Paper
public static int broadcast(@NotNull String message, @NotNull String permission) {
return server.broadcast(message, permission);
}
@@ -1497,6 +1527,7 @@ public final class Bukkit {
return server.createInventory(owner, type);
}
+ // Paper start
/**
* Creates an empty inventory with the specified type and title. If the type
* is {@link InventoryType#CHEST}, the new inventory has a size of 27;
@@ -1522,6 +1553,38 @@ public final class Bukkit {
* @see InventoryType#isCreatable()
*/
@NotNull
+ public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, net.kyori.adventure.text.@NotNull Component title) {
+ return server.createInventory(owner, type, title);
+ }
+ // Paper end
+
+ /**
+ * Creates an empty inventory with the specified type and title. If the type
+ * is {@link InventoryType#CHEST}, the new inventory has a size of 27;
+ * otherwise the new inventory has the normal size for its type.<br>
+ * It should be noted that some inventory types do not support titles and
+ * may not render with said titles on the Minecraft client.
+ * <br>
+ * {@link InventoryType#WORKBENCH} will not process crafting recipes if
+ * created with this method. Use
+ * {@link Player#openWorkbench(Location, boolean)} instead.
+ * <br>
+ * {@link InventoryType#ENCHANTING} will not process {@link ItemStack}s
+ * for possible enchanting results. Use
+ * {@link Player#openEnchanting(Location, boolean)} instead.
+ *
+ * @param owner The holder of the inventory; can be null if there's no holder.
+ * @param type The type of inventory to create.
+ * @param title The title of the inventory, to be displayed when it is viewed.
+ * @return The new inventory.
+ * @throws IllegalArgumentException if the {@link InventoryType} cannot be
+ * viewed.
+ * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
+ *
+ * @see InventoryType#isCreatable()
+ */
+ @Deprecated // Paper
+ @NotNull
public static Inventory createInventory(@Nullable InventoryHolder owner, @NotNull InventoryType type, @NotNull String title) {
return server.createInventory(owner, type, title);
}
@@ -1540,6 +1603,7 @@ public final class Bukkit {
return server.createInventory(owner, size);
}
+ // Paper start
/**
* Creates an empty inventory of type {@link InventoryType#CHEST} with the
* specified size and title.
@@ -1552,10 +1616,30 @@ public final class Bukkit {
* @throws IllegalArgumentException if the size is not a multiple of 9
*/
@NotNull
+ public static Inventory createInventory(@Nullable InventoryHolder owner, int size, net.kyori.adventure.text.@NotNull Component title) throws IllegalArgumentException {
+ return server.createInventory(owner, size, title);
+ }
+ // Paper end
+
+ /**
+ * Creates an empty inventory of type {@link InventoryType#CHEST} with the
+ * specified size and title.
+ *
+ * @param owner the holder of the inventory, or null to indicate no holder
+ * @param size a multiple of 9 as the size of inventory to create
+ * @param title the title of the inventory, displayed when inventory is
+ * viewed
+ * @return a new inventory
+ * @throws IllegalArgumentException if the size is not a multiple of 9
+ * @deprecated in favour of {@link #createInventory(InventoryHolder, InventoryType, net.kyori.adventure.text.Component)}
+ */
+ @Deprecated // Paper
+ @NotNull
public static Inventory createInventory(@Nullable InventoryHolder owner, int size, @NotNull String title) throws IllegalArgumentException {
return server.createInventory(owner, size, title);
}
+ // Paper start
/**
* Creates an empty merchant.
*
@@ -1563,7 +1647,20 @@ public final class Bukkit {
* when the merchant inventory is viewed
* @return a new merchant
*/
+ public static @NotNull Merchant createMerchant(net.kyori.adventure.text.@Nullable Component title) {
+ return server.createMerchant(title);
+ }
+ // Paper start
+ /**
+ * Creates an empty merchant.
+ *
+ * @param title the title of the corresponding merchant inventory, displayed
+ * when the merchant inventory is viewed
+ * @return a new merchant
+ * @deprecated in favour of {@link #createMerchant(net.kyori.adventure.text.Component)}
+ */
@NotNull
+ @Deprecated // Paper
public static Merchant createMerchant(@Nullable String title) {
return server.createMerchant(title);
}
@@ -1680,12 +1777,43 @@ public final class Bukkit {
return server.isPrimaryThread();
}
+ // Paper start
+ /**
+ * Gets the message that is displayed on the server list.
+ *
+ * @return the server's MOTD
+ */
+ @NotNull public static net.kyori.adventure.text.Component motd() {
+ return server.motd();
+ }
+
+ /**
+ * Set the message that is displayed on the server list.
+ *
+ * @param motd The message to be displayed
+ */
+ public static void motd(final net.kyori.adventure.text.@NotNull Component motd) {
+ server.motd(motd);
+ }
+
+ /**
+ * Gets the default message that is displayed when the server is stopped.
+ *
+ * @return the shutdown message
+ */
+ public static net.kyori.adventure.text.@Nullable Component shutdownMessage() {
+ return server.shutdownMessage();
+ }
+ // Paper end
+
/**
* Gets the message that is displayed on the server list.
*
* @return the servers MOTD
+ * @deprecated in favour of {@link #motd()}
*/
@NotNull
+ @Deprecated // Paper
public static String getMotd() {
return server.getMotd();
}
@@ -1694,7 +1822,9 @@ public final class Bukkit {
* Set the message that is displayed on the server list.
*
* @param motd The message to be displayed
+ * @deprecated in favour of {@link #motd(net.kyori.adventure.text.Component)}
*/
+ @Deprecated // Paper
public static void setMotd(@NotNull String motd) {
server.setMotd(motd);
}
@@ -1714,8 +1844,10 @@ public final class Bukkit {
* Gets the default message that is displayed when the server is stopped.
*
* @return the shutdown message
+ * @deprecated in favour of {@link #shutdownMessage()}
*/
@Nullable
+ @Deprecated // Paper
public static String getShutdownMessage() {
return server.getShutdownMessage();
}
diff --git a/src/main/java/org/bukkit/ChatColor.java b/src/main/java/org/bukkit/ChatColor.java
index e3f185dc982d1c38195a4e01ddd485c13ffa58c0..918a045165cdcde264bc24082b7afebb407271de 100644
--- a/src/main/java/org/bukkit/ChatColor.java