Class PlayerEventManager
- All Implemented Interfaces:
org.bukkit.event.Listener
-
Field Summary
FieldsModifier and TypeFieldDescriptionprivate boolean是否已经把自己挂进 Bukkit 事件系统。private UltiPanelWebSocketClient必须 volatile。写在 WebSocket 的 onOpen 线程上(initialize(com.ultikits.ultitools.websocket.UltiPanelWebSocketClient)),读在 Bukkit 主线程上 (三个事件处理器)。只给写方加synchronized不构成发布——读方从不获取同一把监视器, 两边之间没有 happens-before 边。 -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate StringgetServerId(UltiPanelWebSocketClient client) 获取服务器IDvoidinitialize(UltiPanelWebSocketClient client) 初始化玩家事件管理器(package private) boolean供测试断言幂等守卫的状态。voidonPlayerChat(org.bukkit.event.player.PlayerChatEvent event) 处理玩家聊天事件voidonPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) 处理玩家加入事件voidonPlayerQuit(org.bukkit.event.player.PlayerQuitEvent event) 处理玩家退出事件private void幂等地注册事件监听器:已注册过就直接返回。private voidsendPlayerEvent(UltiPanelWebSocketClient client, com.google.gson.JsonObject data) 发送玩家事件到UltiPanelvoidshutdown()注销事件监听器并断开与客户端的关联。
-
Field Details
-
webSocketClient
必须 volatile。写在 WebSocket 的 onOpen 线程上(initialize(com.ultikits.ultitools.websocket.UltiPanelWebSocketClient)),读在 Bukkit 主线程上 (三个事件处理器)。只给写方加synchronized不构成发布——读方从不获取同一把监视器, 两边之间没有 happens-before 边。这一点在加幂等守卫之前是被意外掩盖的:那时每次
initialize都会调registerEvents,而 Bukkit 注册监听器会写HandlerList的 volatile 字段, 事件分发再读它,于是本字段的写被顺带发布了出去。守卫跳过注册之后这条捎带链就断了, 重连后处理器可能一直看着旧的、已断开的客户端,玩家事件被静默丢弃。见 PR #264 的评审。 -
listenerRegistered
private volatile boolean listenerRegistered是否已经把自己挂进 Bukkit 事件系统。initialize(UltiPanelWebSocketClient)由initializeManagers()调用,而后者挂在 WebSocket 的onConnectHandler上——每次 onOpen 都会跑一遍。没有这个守卫的话, 断线重连 N 次就会注册 N 份监听器,同一个玩家事件被发 N 遍。见 issue #180。
-
-
Constructor Details
-
PlayerEventManager
public PlayerEventManager()
-
-
Method Details
-
initialize
初始化玩家事件管理器整个方法与
shutdown()互斥。只让registerEvents单独同步是不够的: 「赋值客户端」与「注册监听器」之间会留下一个窗口,logout 恰好挤进去的话,shutdown()摘掉的监听器会被紧随其后的registerEvents又装回去, 于是disableCloud()之后listenerRegistered仍为 true。见 PR #264 的评审。- Parameters:
client- WebSocket客户端
-
registerEvents
private void registerEvents()幂等地注册事件监听器:已注册过就直接返回。调用方必须已持有本对象的监视器锁(目前唯一调用方是
initialize(com.ultikits.ultitools.websocket.UltiPanelWebSocketClient))。 -
shutdown
public void shutdown()注销事件监听器并断开与客户端的关联。由
PluginInitiationUtils.disableCloud()调用——即/ulticloud logout之后。 注意与「断线重连」区分:那种断开之后initialize还会被调回来,注销了反而要重注册; 这里处理的是「云功能被显式关掉」,监听器应当真的摘掉。 -
isListenerRegistered
boolean isListenerRegistered()供测试断言幂等守卫的状态。 -
onPlayerJoin
public void onPlayerJoin(org.bukkit.event.player.PlayerJoinEvent event) 处理玩家加入事件 -
onPlayerQuit
public void onPlayerQuit(org.bukkit.event.player.PlayerQuitEvent event) 处理玩家退出事件 -
onPlayerChat
public void onPlayerChat(org.bukkit.event.player.PlayerChatEvent event) 处理玩家聊天事件 -
sendPlayerEvent
发送玩家事件到UltiPanel客户端由调用方传入,而不是在这里重新读
webSocketClient:三个事件处理器各自 把那个 volatile 字段单次读进局部变量再一路传下来。否则「检查连接」与「真正发送」读到 的会是两个不同的值——shutdown()恰好挤在中间的话,这里就是一个 NPE。- Parameters:
client- 事件处理器进入时读到的那个客户端data- 事件数据
-
getServerId
获取服务器ID取建连时交给客户端的那份 UUID(即
CommonUtils.getUltiToolsUUID(),见PluginInitiationUtils.getPanelWebsocketClient),与所有兄弟 manager 一致。 在 #180 之前这里返回的是一个写死的占位字符串,意味着全球每台服务器发出的player_event都自称同一身份,面板侧无法路由。- Returns:
- 服务器ID
-