AsyncPlayerChatEvent
AsyncPlayerChatEvent
=> org.bukkit.event.Event
=> org.bukkit.event.player.PlayerEvent
=> org.bukkit.event.player.AsyncPlayerChatEvent
This event will sometimes fire synchronously, depending on how it wastriggered.The constructor provides a boolean to indicate if the event was firedsynchronously or asynchronously. When asynchronous, this event can becalled from any thread, sans the main thread, and has limited access to theAPI.If a player is the direct cause of this event by an incoming packet, thisevent will be asynchronous. If a plugin triggers this event by compelling aplayer to chat, this event will be synchronous.Care should be taken to check {@link #isAsynchronous()} and treat the eventappropriately.当玩家发送聊天消息时触发。本事件虽然名为AsyncPlayerChatEvent
(异步玩家聊天事件),但也有可能同步触发,主要视触发原因而定。本事件构造器提供了一个布尔值,用于确定本事件是异步还是同步触发的。如果本事件是异步触发的,那么它可能运行在任何一个异步线程上,主线程除外。在异步线程中,对Bukkit API
的访问 是有所限制的。如果是玩家客户端发来数据包,触发了本事件,那么本事件是异步的。如果是一个插件强迫玩家发送聊天消息,那么本事件是同步的。应当注意通过isAsynchronous()
方法检查本事件是否为异步触发,并视情况以妥善方法处理本事件。译注:Mojang
在架构本游戏时,几乎没有考虑线程安全问题。整个服务端中的大部分集合、大部分方法,都没有考虑异步操作。在服务端内部,如生成生物、如破坏方块等方法,都运行在主线程上,虽然效率低,但是这样一来也没有线程安全问题。一旦让插件在异步线程中操作实体、方块、物品栏等对象,就可能出现并发修改等错误。整个服务端里几乎没有什么方法是线程安全的。除非某方法仅涉及到数据包的发送,否则它就不能运行在异步线程里。比如发送消息给玩家,这个方法仅仅 涉及到玩家聊天消息相关数据包的发送,因此它可以异步运行。本事件的文档一再警告用户检查事件是否异步触发,就是为了防止用户在异步线程中调用Bukkit API
里那些线程不安全的方法。如果本事件是在主线程里被触发的(同步触发),那么本事件的所有监听器也都运行在主线程里,访问Bukkit API
里的方法就没有线程安全问题。当插件强迫玩家发送聊天消息(比如通过Player#chat(String)
方法)时,本事件是同步触发的。在org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer#chat(String)
方法中,调用了net.minecraft.server.v1_16_R3.PlayerConnection#chat(String s, boolean async)
方法,其中boolean async
参数的值为false
。在该方法中触发本事件时,向本事件构造器内传入了false
,因此可见本事件是同步的。这种情况下访问Bukkit API
里的方法安全无虞。如果本事件是在异步线程里被触发的(异步触发),那么本事件的所有监听器也都运行在异步线程里。此处所谓“异步线程”可以是任何线程。当玩家客户端通过数据包发来聊天消息时,本事件是异步触发的。因为PlayerConnection#a(PacketPlayInChat)
方法中调用了PlayerConnection#c(String s)
方法,然后调用chat(String s, boolean async)
方法,参数boolean async
为true
。在该方法中触发本事件时,向本事件构造器内传入了true
,故此,本事件是异步触发的。在这种情况下,Bukkit API
中的绝大多数方法都不可以被调用。如必欲为之,可用BukkitScheduler
调度一个同步任务,使代码在主线程内运行。
方法声明: public String getMessage()
方法签名: ()Ljava/lang/String;
Gets the message that the player is attempting to send. This messagewill be used with {@link #getFormat()}.@return Message the player is attempting to send该方法用于获取涉事玩家尝试发送的消息字符串。这一字符串将会在getFormat()
方法中被用到。@return 涉事玩家尝试发送的消息字符串。
方法声明: public void setMessage(@NotNull String message)
方法签名: (Ljava/lang/String;)V
Sets the message that the player will send. This message will be usedwith {@link #getFormat()}.@param message New message that the player will send该方法用于设置涉事玩家尝试发送的消息字符串。这一字符串将会在getFormat()
方法中被用到。@param message 涉事玩家尝试发送的消息字符串。
方法声明: public String getFormat()
方法签名: ()Ljava/lang/String;
Gets the format to use to display this chat message.When this event finishes execution, the first format parameter is the{@link Player#getDisplayName()} and the second parameter is {@link#getMessage()}@return {@link String#format(String, Object...)} compatible formatstring该方法用于获取聊天消息格式。当本事件触发以后,格式中的第一个参数将会被替换为Player#getDisplayName()
,第二个参数则替换为本事件getMessage()
方法的返回值。@return 兼容String#format(String, Object...)
方法的格式字符串。译注:所谓“格式字符串”,指服务端将会以类似如下的方式处理消息:String message = String.format(event.getFormat(), new Object[] { event.getPlayer().getDisplayName(), event.getMessage() });
,因此这个方法的返回值要能作为JDK
标准里的String.format(String, Object...)
方法的第一个参数来使用。这个格式字符串的写法应当参阅Java
官方文档中的说明 https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Formatter.html#syntax 。格式字符串默认是<%1$s> %2$s
,其中%1$
指第一个参数(也就是getDisplayName()
字符串),而s
则是conversion
的一种。由于提供的参数是字符串而不是某个实现了java.util.Formattable
的类的实例,因此仅对参数调用toString()
方法(此处没有任何意义)。第二个参数同理。所以原版的消息就是<发送者玩家名> 消息
这样的格式。
方法声明: public void setFormat(@NotNull final String format) throws IllegalFormatException, NullPointerException
方法签名: (Ljava/lang/String;)V
Sets the format to use to display this chat message.When this event finishes execution, the first format parameter is the{@link Player#getDisplayName()} and the second parameter is {@link#getMessage()}@param format {@link String#format(String, Object...)} compatibleformat string@throws IllegalFormatException if the underlying API throws theexception@throws NullPointerException if format is null@see String#format(String, Object...)该方法用于设置聊天消息格式。当本事件触发以后,格式中的第一个参数将会被替换为Player#getDisplayName()
,第二个参数则替换为本事件getMessage()
方法的返回值。@param format 兼容String#format(String, Object...)
方法的格式字符串。@throws 如果本方法底层的方法抛出了IllegalFormatException
,则本方法继续向上抛出之。@throws 如果传入参数为null
则抛出NullPointerException
。@see 参见String#format(String, Object...)
方法文档。译注:关于格式字符串的相关说明见上。本方法会调用一次String.format(String, Object...)
方法来测试传入的格式字符串是不是合法,如果传入的格式字符串并不符合JDK
标准的定义,则会抛出IllegalFormatException
异常。
方法声明: public Set getRecipients()
方法签名: ()Ljava/util/Set;
Gets a set of recipients that this chat message will be displayed to.The set returned is not guaranteed to be mutable and may auto-populateon access. Any listener accessing the returned set should be aware thatit may reduce performance for a lazy set implementation.Listeners should be aware that modifying the list may throw {@linkUnsupportedOperationException} if the event caller provides anunmodifiable set.@return All Players who will see this chat message该方法用于获取一个Set
,其间存储有全部将要接收该条聊天消息的对象。本方法返回的Set
不一定是可以修改的,而且有可能在用户从其间取出值时进行自动填充。使用本方法返回的Set
时需要注意,如果该Set
实现类采用某种Lazy Set
实现,则监听器的性能可能会受影响。使用时还需要注意,如果触发本事件的开发者提供了一个不可修改集合,则对返回的Set
作修改会抛出UnsupportedOperationException
。@return 全部将要接收该条聊天消息的玩家对象。译注:如果本事件并非服务端所触发,而是插件所触发,则就本方法返回的集合而言,Bukkit API
只能保证其实现类是java.util.Set
的子类,但不能保证它可否修改,也不能保证它是不是Lazy Set
。所谓不可修改集合,即不能向其中添加删除元素。监听器只能遍历其中元素,不能通过修改Set
的方式添加或删除一个接收聊天消息的对象,否则会抛出UnsupportedOperationException
。所谓Lazy Set
,一般来说,指的是该Set
对于性能低、开销大的方法尽可能地推迟运 行。比如向Set
中添加元素的方法(假设它名为add
方法),开销比较大(有时候集合元素已满,需要对集合进行扩容才能装得下新元素,这种操作开销非常大)。那么在调用add
方法时,不去立刻添加元素,而是先暂存新元素,推迟运行添加操作。等到要遍历集合的时候,由于再作推迟会影响读取结果,所以不得不把元素添加进集合。而文档中所指的Lazy Set
主要是指org.bukkit.craftbukkit.v1_16_R3.util.LazyPlayerSet
类。这个Set
实现专门用于存储服务器内的全体玩家。但是遍历服务器内全体玩家列表的操作开销比较大。所以这个类的设计是只要读写Set
的方法不被调用,就不向Set
里添加任何玩家对象,让Set
保持空白。一旦要读取时,就当场遍历服务器的在线玩家列表,把全体玩家对象写入该Set
。这种设计适应了本事件的应用场景。正因为本方法返回的Set
不一 定是可以修改的,所以开发者未必能通过本方法的返回值来实现消息屏蔽等功能。故此,这个方法的用处就显得很小,往往不被使用。如果所有事件监听器都不去读取这个Set
,那么就可以让Set
的内容一直留空,不需要遍历服务器在线玩家列表,节约服务器开销。
方法声明: public boolean isCancelled()
方法签名: ()Z
方法声明: public void setCancelled(boolean cancel)
方法签名: (Z)V
方法声明: public HandlerList getHandlers()
方法签名: ()Lorg/bukkit/event/HandlerList;
方法声明: public static HandlerList getHandlerList()
方法签名: ()Lorg/bukkit/event/HandlerList;
Last modified 1mo ago