Class PlayerEventManager

java.lang.Object
com.ultikits.ultitools.manager.PlayerEventManager
All Implemented Interfaces:
org.bukkit.event.Listener

@Internal public class PlayerEventManager extends Object implements org.bukkit.event.Listener
玩家事件管理器 处理玩家相关事件的WebSocket消息
  • Field Details

    • webSocketClient

      private volatile UltiPanelWebSocketClient 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

      public void initialize(UltiPanelWebSocketClient client)
      初始化玩家事件管理器

      整个方法与 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

      private void sendPlayerEvent(UltiPanelWebSocketClient client, com.google.gson.JsonObject data)
      发送玩家事件到UltiPanel

      客户端由调用方传入,而不是在这里重新读 webSocketClient:三个事件处理器各自 把那个 volatile 字段单次读进局部变量再一路传下来。否则「检查连接」与「真正发送」读到 的会是两个不同的值——shutdown() 恰好挤在中间的话,这里就是一个 NPE。

      Parameters:
      client - 事件处理器进入时读到的那个客户端
      data - 事件数据
    • getServerId

      private String getServerId(UltiPanelWebSocketClient client)
      获取服务器ID

      取建连时交给客户端的那份 UUID(即 CommonUtils.getUltiToolsUUID(),见 PluginInitiationUtils.getPanelWebsocketClient),与所有兄弟 manager 一致。 在 #180 之前这里返回的是一个写死的占位字符串,意味着全球每台服务器发出的 player_event 都自称同一身份,面板侧无法路由。

      Returns:
      服务器ID