Class MapleClient

java.lang.Object
client.MapleClient
All Implemented Interfaces:
Serializable

public class MapleClient extends Object implements Serializable
單一玩家連線的會話、帳號狀態與登入狀態機。

本類別代表一條由 MapleServerHandler 在連線建立時掛上 CLIENT_KEY channel 屬性的 Netty 連線, 持有底層 Channel、收送兩組 MapleAESOFB 加解密器,以及帳號/封鎖/頻道等狀態; 其登入狀態以 LOGIN_*CASH_SHOP_TRANSITIONCHANGE_CHANNELbyte 常數構成狀態機, 跨 LoginServerChannelServerCashShopServer 流轉。職責與主要協作者:

  • 持有目前操控的 MapleCharacterplayer),並提供登入/登出、頻道轉移與斷線清理。
  • 快取 per-client 的 GraalJS 腳本引擎(enginesScriptEngine)與 reactor 腳本鎖,供 scripting.* 管理器取用。
  • 透過 DatabaseConnection 載入/更新帳號資料,並以 npc_mutexlogin_mutex 等鎖序列化關鍵互動。
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    protected static final class 
    角色名稱與編號的不可變配對。
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final byte
    登入狀態機常數:正轉移至商城伺服器的過渡狀態。
    static final byte
    登入狀態機常數:自商城伺服器返回的過渡狀態。
    static final byte
    登入狀態機常數:頻道切換中的過渡狀態。
    static final io.netty.util.AttributeKey<MapleClient>
    Netty channel 屬性鍵:連線建立時由 MapleServerHandler 將對應的 MapleClient 掛在此鍵上,後續封包處理皆透過 channel.attr(CLIENT_KEY) 取回本連線。
    static final int
    帳號預設可建立的角色欄位數量。
    static final byte
    登入狀態機常數:停留在角色選擇畫面。
    static final byte
    登入狀態機常數:已登入商城伺服器。
    static final byte
    登入狀態機常數:已完成登入並進入遊戲頻道。
    static final byte
    登入狀態機常數:已登入交易(MTS)伺服器。
    static final byte
    登入狀態機常數:尚未登入(初始狀態)。
    static final byte
    登入狀態機常數:登入伺服器與頻道伺服器之間的過渡狀態(轉移期間視為未完全登入)。
    static final byte
    登入狀態機常數:停留在伺服器(世界)選擇畫面。
    short
     
    static final byte
    登入狀態機常數:正轉移至交易(MTS)伺服器的過渡狀態。
    static final byte
    登入狀態機常數:自交易(MTS)伺服器返回的過渡狀態。
  • Constructor Summary

    Constructors
    Constructor
    Description
    MapleClient(MapleAESOFB send, MapleAESOFB receive, io.netty.channel.Channel session)
    建立一條玩家連線,綁定收送兩組加解密器與底層 Netty channel。
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    封鎖本帳號目前所有已知的 MAC 位址。
    static final void
    banMacs(String[] macs)
    將給定的多個 MAC 位址寫入 MAC 封鎖名單(macbans)。
    static boolean
    將單一 MAC 位址加入封鎖名單。
    boolean
    判斷距離上次點擊 NPC 是否已超過防連點冷卻(500 毫秒)。
    boolean
    canMakeCharacter(int serverId)
    判斷本帳號在指定世界是否仍有空餘角色欄位可建立新角色。
    final boolean
    checkBirthDate(int date)
    比對輸入日期是否與本帳號記錄的生日相符。
    final boolean
    驗證本連線目前的來源 IP 是否與資料庫記錄的 SessionIP 相符,且帳號未被封鎖。
    boolean
    驗證輸入的二次密碼是否正確,並在需要時就地升級其雜湊格式。
    void
    清除本連線快取的帳號相關資訊,重置為未登入的初始狀態。
    void
    createdChar(int id)
    將新建立的角色編號加入本連線的授權清單,使其可立即被選用登入。
    final void
    將本連線的偵錯資訊附加到給定的字串建構器。
    final int
    deleteCharacter(int cid)
    刪除指定角色,並一併清除其在各資料表中的所有關聯資料。
    final void
    disconnect(boolean RemoveInChannelServer, boolean fromCS)
    中斷本連線(非伺服器關閉情境)。
    final void
    disconnect(boolean RemoveInChannelServer, boolean fromCS, boolean shutdown)
    完整中斷本連線:存檔角色、清理跨伺服器協作狀態並釋放資源。
    static final int
    依角色名稱查詢其所屬的帳號編號。
    int
    Returns 0 on success, a state to be used for LoginPacket.getLoginFailed(int) otherwise.
    static boolean
    Fullyunban(String charname)
    對指定角色的帳號執行完整解封(帳號封鎖 + IP/MAC 封鎖)。
    boolean
    為此帳號在目前世界增加一個角色欄位。
    int
    取得本連線綁定的帳號編號。
    final String
    取得此連線的登入帳號名稱。
    byte
    取得本帳號的封鎖原因代碼。
    final int
    取得此連線目前所在的頻道編號。
    取得此連線目前所在頻道對應的 ChannelServer 實例。
    int
    取得此帳號在目前世界可用的角色欄位數。
    final String
    取得此連線記錄的主要網卡 MAC 位址。
    boolean
    判斷此連線是否已被標記為待關閉。
    final Timestamp
    查詢此帳號的建立時間。
    final byte
    取得此帳號的性別。
    取得此連線目前的閒置逾時排程任務。
    final long
    取得最後一次送出 ping 封包的時間戳記。
    final long
    取得最後一次收到客戶端 pong 回應的時間戳記。
    final int
    取得最近一次量測的網路延遲。
    final Lock
    取得此 client 的通用互斥鎖(公平鎖)。
    final byte
    自資料庫查詢本帳號的即時登入狀態,並一併更新生日快取與本機 loggedIn 旗標。
    static final String
    產生一則含角色身分前綴的記錄訊息(無參數格式化)。
    static final String
    getLogMessage(MapleCharacter cfor, String message, Object... parms)
    產生一則含角色身分前綴並套用參數格式化的記錄訊息。
    static final String
    產生一則含連線身分前綴的記錄訊息(無參數格式化)。
    static final String
    getLogMessage(MapleClient cfor, String message, Object... parms)
    產生一則含連線身分前綴並套用參數格式化的記錄訊息(其餘多載皆收斂至此)。
    final Set<String>
    取得此連線回報的所有 MAC 位址集合。
    final Lock
    取得此 client 的 NPC 互動鎖。
    取得此連線目前操控的角色實體。
    getReactorScriptLock(int reactorId)
    Track B C4:取得此 client 對應 reactorIdReentrantLock
    取得接收封包用的 AES-OFB 解密器。
    int
    查詢並回傳此帳號的獎勵等級。
    依名稱取得先前快取的 ScriptEngine
    final String
    取得此帳號的第二組密碼(防護密碼)。
    取得送出封包用的 AES-OFB 加密器。
    final io.netty.channel.Channel
    取得此連線底層的 Netty channel。
    final String
    取得本連線對端的 IP 位址(去除埠號部分)。
    取得本帳號暫時封鎖(tempban)的到期時間。
    取得此連線的暫存 IP 位址。
    int
    查詢並回傳此帳號的 VIP 等級。
    final int
    取得此連線所屬的世界(World)編號。
    boolean
    檢查目前連線來源 IP 是否在 IP 封鎖名單(ipbans)中。
    boolean
    檢查本連線蒐集到的任一 MAC 位址是否在 MAC 封鎖名單(macbans)中。
    boolean
    判斷此帳號是否處於被封鎖狀態。
    boolean
    檢查指定 IP 是否在 IP 封鎖名單(ipbans)中。
    boolean
    檢查指定的 MAC 位址是否在 MAC 封鎖名單(macbans)中。
    boolean
    判斷此連線是否已登入商城伺服器。
    final boolean
    判斷此帳號是否為 GM(管理員)帳號。
    boolean
    判斷伺服器是否以本機(localhost)模式運行。
    boolean
    判斷此連線目前是否處於已登入狀態(依本機快取旗標,不查詢資料庫)。
    boolean
    判斷此連線是否處於「已登入但尚未進入遊戲」階段(伺服器選擇或角色選擇畫面)。
    boolean
    判斷此連線是否處於受監控狀態。
    boolean
    判斷此連線目前是否接受處理封包。
    void
    loadAccountData(int accountID)
    自資料庫載入指定帳號的基本資料並填入本連線狀態。
    loadCharacterNames(int serverId)
    載入本帳號在指定世界下所有角色的名稱清單。
    static List<String>
    載入指定帳號底下所有角色的名稱。
    loadCharacters(int serverId)
    載入本帳號在指定世界(伺服器)下的所有角色,供角色選擇畫面使用。
    void
    依角色編號回填此連線所需的基本帳號資訊。
    int
    login(String login, String pwd, boolean ipMacBanned)
    以帳號與密碼進行登入驗證,並依結果更新本連線的帳號狀態。
    final boolean
    login_Auth(int id)
    檢查指定角色是否屬於本連線的授權清單(防止以非本帳號角色登入)。
    final void
    標記已收到客戶端的 pong 回應,更新最後 pong 時間為目前時間。
    final void
    removalTask(boolean shutdown)
    在玩家離線前對其角色執行清理與狀態收尾。
    void
    清除上次 NPC 點擊時間,立即解除防連點冷卻。
    final void
    從快取移除指定名稱的 ScriptEngine
    void
    sendPacket(byte[] packet)
    將已組裝完成的封包寫入此連線並送出。
    final void
    送出 ping 封包並排程逾時檢查。
    void
    setAccID(int id)
    設定本連線綁定的帳號編號。
    final void
    setAccountName(String accountName)
    設定此連線的登入帳號名稱。
    final void
    setChannel(int channel)
    設定此連線目前所在的頻道編號。
    void
    記錄一次 NPC 點擊時間,啟動防連點冷卻。
    final void
    setGender(byte gender)
    設定此帳號的性別。
    final void
    設定此連線的閒置逾時排程任務。
    void
    setMacs(String macData)
    設定本連線目前的 MAC 位址(僅更新記憶體欄位,不寫入資料庫)。
    void
    setMonitored(boolean m)
    設定此連線是否受監控。
    void
    設定此連線目前操控的角色實體。
    void
    setReceiving(boolean m)
    設定此連線是否接受處理封包。
    void
    setRewardLevel(int reward)
    設定此帳號的獎勵等級並立即寫回資料庫。
    final void
    以指定名稱快取一個 ScriptEngine
    final void
    setSecondPassword(String secondPassword)
    設定此帳號的第二組密碼(防護密碼)。
    void
    設定此連線的暫存 IP 位址。
    void
    setVip(int vip)
    設定此帳號的 VIP 等級並立即寫回資料庫。
    final void
    setWorld(int world)
    設定此連線所屬的世界(World)編號。
    static final byte
    unban(String charname)
    依角色名稱解除其所屬帳號的封鎖。
    static byte
    unbanIP(String charname)
    依角色名稱解除其帳號最近登入 IP 的封鎖。
    static final byte
    unbanIPMacs(String charname)
    依角色名稱解除其帳號的 IP 與 MAC 封鎖。
    static final byte
    unHellban(String charname)
    依角色名稱解除其帳號的「地獄封鎖」(hellban)。
    final void
    因帳號於他處重複登入而強制斷線並關閉連線。
    final void
    依據指定的「欄位位置 → 角色編號」對應,重建本帳號的角色卡(character cards)資料表。
    final void
    將本連線目前的性別欄位寫回資料庫(accounts.gender)。
    final void
    updateLoginState(int newstate, String SessionID)
    更新本帳號的登入狀態,並同步寫回資料庫與本機快取旗標。
    void
    updateMacs(String macData)
    將本帳號的 MAC 清單寫回資料庫(accounts.macs)。
    final void
    將本連線目前的二次密碼以加鹽 SHA-512 重新雜湊後寫回資料庫(accounts.2ndpasswordsalt2)。

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Field Details

    • LOGIN_NOTLOGGEDIN

      public static final transient byte LOGIN_NOTLOGGEDIN
      登入狀態機常數:尚未登入(初始狀態)。寫入 accounts.loggedin 並透過 updateLoginState(int, String) 切換。
      See Also:
    • LOGIN_SERVER_TRANSITION

      public static final transient byte LOGIN_SERVER_TRANSITION
      登入狀態機常數:登入伺服器與頻道伺服器之間的過渡狀態(轉移期間視為未完全登入)。
      See Also:
    • LOGIN_LOGGEDIN

      public static final transient byte LOGIN_LOGGEDIN
      登入狀態機常數:已完成登入並進入遊戲頻道。
      See Also:
    • LOGIN_SERVERLIST

      public static final transient byte LOGIN_SERVERLIST
      登入狀態機常數:停留在伺服器(世界)選擇畫面。
      See Also:
    • CASH_SHOP_TRANSITION

      public static final transient byte CASH_SHOP_TRANSITION
      登入狀態機常數:正轉移至商城伺服器的過渡狀態。
      See Also:
    • LOGIN_CS_LOGGEDIN

      public static final transient byte LOGIN_CS_LOGGEDIN
      登入狀態機常數:已登入商城伺服器。
      See Also:
    • CHANGE_CHANNEL

      public static final transient byte CHANGE_CHANNEL
      登入狀態機常數:頻道切換中的過渡狀態。
      See Also:
    • MAPLE_TRADE_TRANSITION

      public static final transient byte MAPLE_TRADE_TRANSITION
      登入狀態機常數:正轉移至交易(MTS)伺服器的過渡狀態。
      See Also:
    • LOGIN_MTS_LOGGEDIN

      public static final transient byte LOGIN_MTS_LOGGEDIN
      登入狀態機常數:已登入交易(MTS)伺服器。
      See Also:
    • CASH_SHOP_TRANSITION_LEAVE

      public static final transient byte CASH_SHOP_TRANSITION_LEAVE
      登入狀態機常數:自商城伺服器返回的過渡狀態。
      See Also:
    • MAPLE_TRADE_TRANSITION_LEAVE

      public static final transient byte MAPLE_TRADE_TRANSITION_LEAVE
      登入狀態機常數:自交易(MTS)伺服器返回的過渡狀態。
      See Also:
    • LOGIN_CHARLIST

      public static final transient byte LOGIN_CHARLIST
      登入狀態機常數:停留在角色選擇畫面。
      See Also:
    • DEFAULT_CHARSLOT

      public static final int DEFAULT_CHARSLOT
      帳號預設可建立的角色欄位數量。
      See Also:
    • CLIENT_KEY

      public static final io.netty.util.AttributeKey<MapleClient> CLIENT_KEY
      Netty channel 屬性鍵:連線建立時由 MapleServerHandler 將對應的 MapleClient 掛在此鍵上,後續封包處理皆透過 channel.attr(CLIENT_KEY) 取回本連線。
    • loginAttempt

      public transient short loginAttempt
  • Constructor Details

    • MapleClient

      public MapleClient(MapleAESOFB send, MapleAESOFB receive, io.netty.channel.Channel session)
      建立一條玩家連線,綁定收送兩組加解密器與底層 Netty channel。

      MapleServerHandler 於連線建立時建構,隨後完成 AES-OFB 握手並將本實例 掛上 CLIENT_KEY channel 屬性。

      Parameters:
      send - 送出封包用的 MapleAESOFB 加密器
      receive - 接收封包用的 MapleAESOFB 解密器
      session - 底層的 Netty Channel
  • Method Details

    • getReceiveCrypto

      public final MapleAESOFB getReceiveCrypto()
      取得接收封包用的 AES-OFB 解密器。
      Returns:
      解密用的 MapleAESOFB
    • getSendCrypto

      public final MapleAESOFB getSendCrypto()
      取得送出封包用的 AES-OFB 加密器。
      Returns:
      加密用的 MapleAESOFB
    • getSession

      public final io.netty.channel.Channel getSession()
      取得此連線底層的 Netty channel。
      Returns:
      底層的 Channel
    • getLock

      public final Lock getLock()
      取得此 client 的通用互斥鎖(公平鎖)。

      用於序列化對本連線狀態的關鍵存取,呼叫端負責 lock()unlock() 配對。

      Returns:
      本連線的通用 Lock
    • getNPCLock

      public final Lock getNPCLock()
      取得此 client 的 NPC 互動鎖。

      scripting.NPCScriptManager 以此序列化同一玩家的 NPC 對話進入,避免並發進入 per-client 腳本引擎。呼叫端負責 lock()unlock() 配對。

      Returns:
      本連線的 NPC 互動 Lock
    • getReactorScriptLock

      public final ReentrantLock getReactorScriptLock(int reactorId)
      Track B C4:取得此 client 對應 reactorIdReentrantLock。GraalJS 下 ReactorScriptManager.act(MapleClient, MapleReactor) 以此序列化「同一 (client, reactorId)」的並發進入 (per-client 引擎共用一條 GraalJS Context、禁多執行緒並發);不同 reactorId 仍並行。回傳 ReentrantLock 以容忍腳本同執行緒重入 act()(rm.hitReactor 同 rid 回呼,目前無腳本如此,純防禦)。
    • getPlayer

      public MapleCharacter getPlayer()
      取得此連線目前操控的角色實體。
      Returns:
      目前的 MapleCharacter;在角色選擇完成之前(仍在登入/角色清單階段)為 null
    • setPlayer

      public void setPlayer(MapleCharacter player)
      設定此連線目前操控的角色實體。
      Parameters:
      player - 欲綁定的 MapleCharacter,可為 null(例如斷線清理時清空)
    • createdChar

      public void createdChar(int id)
      將新建立的角色編號加入本連線的授權清單,使其可立即被選用登入。
      Parameters:
      id - 新建立角色的角色編號
    • login_Auth

      public final boolean login_Auth(int id)
      檢查指定角色是否屬於本連線的授權清單(防止以非本帳號角色登入)。
      Parameters:
      id - 欲驗證的角色編號
      Returns:
      若該角色已在授權清單中則為 true
    • loadCharacters

      public final List<MapleCharacter> loadCharacters(int serverId)
      載入本帳號在指定世界(伺服器)下的所有角色,供角色選擇畫面使用。

      會自資料庫讀取每個角色(MapleCharacter.loadCharFromDB(int, MapleClient, boolean))並載入其角色卡資料, 同時把每個角色的等級/職業記入 charInfo 以供更新角色卡,並補登未在授權清單中的角色。 屬於高成本操作(逐角色查詢,原始碼註記待最佳化)。

      Parameters:
      serverId - 世界(伺服器)編號
      Returns:
      該帳號於此世界的 MapleCharacter 清單;無角色時為空清單
    • updateCharacterCards

      public final void updateCharacterCards(Map<Integer,Integer> cids)
      依據指定的「欄位位置 → 角色編號」對應,重建本帳號的角色卡(character cards)資料表。

      會寫入資料庫(透過共用的 ThreadLocal 連線):先刪除本帳號既有的所有角色卡列, 再逐一插入新對應;只插入符合 CharacterCardFactory.canHaveCard(int, int) 條件的角色。 若 charInfo 為空(無角色)則直接返回。SQL 失敗僅記錄而不拋出。

      Parameters:
      cids - 欄位位置(key)對應到角色編號(value)的對照表
    • canMakeCharacter

      public boolean canMakeCharacter(int serverId)
      判斷本帳號在指定世界是否仍有空餘角色欄位可建立新角色。

      會查詢資料庫取得該世界現有角色數,與 getCharacterSlots() 比較。

      Parameters:
      serverId - 世界(伺服器)編號
      Returns:
      現有角色數小於可用欄位數時為 true
    • loadCharacterNames

      public List<String> loadCharacterNames(int serverId)
      載入本帳號在指定世界下所有角色的名稱清單。

      會查詢資料庫(透過共用的 ThreadLocal 連線)。

      Parameters:
      serverId - 世界(伺服器)編號
      Returns:
      角色名稱清單;無角色時為空清單
    • isLoggedIn_beforeInGame

      public boolean isLoggedIn_beforeInGame()
      判斷此連線是否處於「已登入但尚未進入遊戲」階段(伺服器選擇或角色選擇畫面)。

      會經由 getLoginState() 查詢資料庫取得即時登入狀態。

      Returns:
      登入狀態為 LOGIN_SERVERLISTLOGIN_CHARLIST 時為 true
    • isCSLoggedIn

      public boolean isCSLoggedIn()
      判斷此連線是否已登入商城伺服器。

      會經由 getLoginState() 查詢資料庫取得即時登入狀態。

      Returns:
      登入狀態為 LOGIN_CS_LOGGEDIN 時為 true
    • isLoggedIn

      public boolean isLoggedIn()
      判斷此連線目前是否處於已登入狀態(依本機快取旗標,不查詢資料庫)。
      Returns:
      loggedIn 旗標為真且已綁定有效帳號編號(accId >= 0)時為 true
    • getTempBanCalendar

      public Calendar getTempBanCalendar()
      取得本帳號暫時封鎖(tempban)的到期時間。
      Returns:
      暫封到期的 Calendar;未暫封或尚未自資料庫載入時為 null
    • getBanReason

      public byte getBanReason()
      取得本帳號的封鎖原因代碼。
      Returns:
      封鎖原因代碼(byte
    • hasBannedIP

      public boolean hasBannedIP()
      檢查目前連線來源 IP 是否在 IP 封鎖名單(ipbans)中。

      會查詢資料庫,比對方式為前綴 LIKE(涵蓋以該 IP 開頭的封鎖網段)。

      Returns:
      該 IP 命中任一封鎖規則時為 true;查詢失敗時為 false
    • isBannedMac

      public boolean isBannedMac(String mac)
      檢查指定的 MAC 位址是否在 MAC 封鎖名單(macbans)中。

      會查詢資料庫。全零位址(00-00-00-00-00-00)或長度不為 17 的字串視為無效,直接回傳 false

      Parameters:
      mac - 欲查驗的 MAC 位址字串
      Returns:
      該 MAC 命中封鎖名單時為 true;無效或查詢失敗時為 false
    • hasBannedMac

      public boolean hasBannedMac()
      檢查本連線蒐集到的任一 MAC 位址是否在 MAC 封鎖名單(macbans)中。

      以本連線目前的 macs 集合動態組出 IN (...) 查詢比對資料庫; 集合為空時直接回傳 false

      Returns:
      任一 MAC 命中封鎖名單時為 true;無 MAC 或查詢失敗時為 false
    • banMacs

      public void banMacs()
      封鎖本帳號目前所有已知的 MAC 位址。

      必要時先自資料庫載入帳號的 MAC 清單(accounts.macs),再委派 banMacs(String[]) 寫入 macbans。SQL 失敗僅記錄而不拋出。

    • banMacs

      public static final void banMacs(String[] macs)
      將給定的多個 MAC 位址寫入 MAC 封鎖名單(macbans)。

      會寫入資料庫:先讀取 macfilters 過濾規則,凡符合任一過濾正則的 MAC 會被跳過、 其餘逐筆插入;因 UNIQUE 鍵造成的重複插入失敗會被刻意忽略。SQL 失敗僅記錄而不拋出。

      Parameters:
      macs - 欲封鎖的 MAC 位址陣列
    • isBannedIP

      public boolean isBannedIP(String ip)
      檢查指定 IP 是否在 IP 封鎖名單(ipbans)中。

      會查詢資料庫,比對方式為前綴 LIKE(涵蓋以該 IP 開頭的封鎖網段)。

      Parameters:
      ip - 欲查驗的 IP 位址字串
      Returns:
      該 IP 命中任一封鎖規則時為 true;查詢失敗時為 false
    • finishLogin

      public int finishLogin()
      Returns 0 on success, a state to be used for LoginPacket.getLoginFailed(int) otherwise.
      Returns:
      The state of the login.
    • clearInformation

      public void clearInformation()
      清除本連線快取的帳號相關資訊,重置為未登入的初始狀態。

      重設帳號名稱、帳號編號、二次密碼/鹽值、GM 旗標、登入旗標、封鎖原因、暫封時間與性別, 並清空 charInfo。僅作用於本機記憶體狀態,不寫入資料庫。

    • loadAccountData

      public void loadAccountData(int accountID)
      自資料庫載入指定帳號的基本資料並填入本連線狀態。

      會查詢 accounts 表,讀取 VIP、二次密碼/鹽值、GM 旗標、性別與 MAC; 若存在二次密碼則以 LoginCrypto.rand_r 還原。例外僅記錄而不拋出。

      Parameters:
      accountID - 欲載入的帳號編號
    • login

      public int login(String login, String pwd, boolean ipMacBanned)
      以帳號與密碼進行登入驗證,並依結果更新本連線的帳號狀態。

      會查詢並可能寫入資料庫:自 accounts 讀取密碼雜湊、鹽值、封鎖狀態、VIP、GM、性別等, 支援多種雜湊格式(legacy、SHA-1、加鹽 SHA-512)並在需要時就地升級密碼雜湊(UPDATE accounts); 已封鎖(非 GM)回傳封鎖碼,banned == -1 時呼叫 unban() 解封。 驗證成功(0)時會先 ChannelServer.forceRemovePlayerByAccId(MapleClient, int) 踢除既有連線並把登入狀態重設為 LOGIN_NOTLOGGEDIN,最後設定 loggedIn 為真。

      Parameters:
      login - 帳號名稱
      pwd - 明文密碼
      ipMacBanned - 呼叫端先行判定的 IP/MAC 封鎖旗標
      Returns:
      登入結果代碼:0 成功;3 已封鎖;4 密碼/Session 不符;5 帳號不存在(預設值)
    • CheckSecondPassword

      public boolean CheckSecondPassword(String in)
      驗證輸入的二次密碼是否正確,並在需要時就地升級其雜湊格式。

      支援 legacy、SHA-1 與加鹽 SHA-512 三種格式;若命中前兩者會以加鹽 SHA-512 重新雜湊並 寫回 accounts(資料庫寫入)。升級寫入失敗時回傳 false

      Parameters:
      in - 玩家輸入的明文二次密碼
      Returns:
      二次密碼正確時為 true
    • unban

      public static final byte unban(String charname)
      依角色名稱解除其所屬帳號的封鎖。

      會查詢資料庫由 characters 反查 accountid,再對 accounts 執行解封(banned = 0、清空 banreason)。

      Parameters:
      charname - 角色名稱
      Returns:
      0 解封成功;-1 查無此角色;-2 過程發生 SQL 例外
    • setMacs

      public void setMacs(String macData)
      設定本連線目前的 MAC 位址(僅更新記憶體欄位,不寫入資料庫)。
      Parameters:
      macData - MAC 位址字串
    • updateMacs

      public void updateMacs(String macData)
      將本帳號的 MAC 清單寫回資料庫(accounts.macs)。

      SQL 失敗僅記錄而不拋出。

      Parameters:
      macData - 欲儲存的 MAC 清單字串
    • setAccID

      public void setAccID(int id)
      設定本連線綁定的帳號編號。
      Parameters:
      id - 帳號編號
    • getAccID

      public int getAccID()
      取得本連線綁定的帳號編號。
      Returns:
      帳號編號;尚未登入時為 -1
    • updateLoginState

      public final void updateLoginState(int newstate, String SessionID)
      更新本帳號的登入狀態,並同步寫回資料庫與本機快取旗標。

      會寫入資料庫:更新 accountsloggedinSessionIPlastlogin。 隨後重算本機 loggedInserverTransition 旗標——各種「過渡」狀態 (商城/交易/頻道切換/登入伺服器轉移)會將 serverTransition 設為真且 loggedIn 設為偽; LOGIN_NOTLOGGEDIN 則兩者皆清為偽。SQL 失敗僅記錄而不拋出。

      Parameters:
      newstate - 新的登入狀態(LOGIN_*/過渡常數之一)
      SessionID - 寫入的 Session IP 字串
    • updateGender

      public final void updateGender()
      將本連線目前的性別欄位寫回資料庫(accounts.gender)。

      SQL 失敗僅記錄而不拋出。

    • updateSecondPassword

      public final void updateSecondPassword()
      將本連線目前的二次密碼以加鹽 SHA-512 重新雜湊後寫回資料庫(accounts.2ndpasswordsalt2)。

      SQL 失敗僅記錄而不拋出。

    • getLoginState

      public final byte getLoginState()
      自資料庫查詢本帳號的即時登入狀態,並一併更新生日快取與本機 loggedIn 旗標。

      會讀取 accountsloggedinlastloginbanned 與生日; 若帳號不存在或已封鎖,會關閉底層 session 並拋出 DatabaseException。 對於各種「過渡」狀態,若 lastlogin 距今逾 20 秒(連線逾時)會就地把狀態重設為 LOGIN_NOTLOGGEDIN(含一次 updateLoginState(int, String) 寫入)。

      Returns:
      目前的登入狀態(byte
      Throws:
      DatabaseException - 帳號不存在、已封鎖,或查詢發生 SQL 例外時
    • checkBirthDate

      public final boolean checkBirthDate(int date)
      比對輸入日期是否與本帳號記錄的生日相符。

      比對的是 birthday 快取欄位,其值在最近一次 getLoginState() 查詢時被更新。

      Parameters:
      date - 欲比對的生日(整數格式,如 yyyyMMdd
      Returns:
      與帳號生日相符時為 true
    • removalTask

      public final void removalTask(boolean shutdown)
      在玩家離線前對其角色執行清理與狀態收尾。

      此方法會大量變更角色與地圖狀態:取消所有 buff/debuff;處理進行中的結婚任務狀態; 在監獄地圖時結算剩餘監禁秒數;標記角色待移除;通知所屬活動實例玩家離線;關閉或保留其玩家商店; 清空通訊員;針對特定 Boss 地圖在伺服器關閉時重置任務時間、或在仍存活時登記斷線;最後將角色自地圖移除。 任何 Throwable 皆會被攔截並寫入錯誤檔,不向外拋出。

      Parameters:
      shutdown - 是否為伺服器關閉流程觸發;影響商店關閉方式與 Boss 地圖任務時間的處理
    • disconnect

      public final void disconnect(boolean RemoveInChannelServer, boolean fromCS)
      中斷本連線(非伺服器關閉情境)。

      等同於以 shutdown = false 呼叫 disconnect(boolean, boolean, boolean)

      Parameters:
      RemoveInChannelServer - 是否一併自頻道伺服器(或商城)的玩家儲存中移除此玩家
      fromCS - 是否由商城伺服器情境發起
    • disconnect

      public final void disconnect(boolean RemoveInChannelServer, boolean fromCS, boolean shutdown)
      完整中斷本連線:存檔角色、清理跨伺服器協作狀態並釋放資源。

      此方法有大量副作用與跨伺服器效果:先呼叫 removalTask(boolean) 收尾並 saveToDB 存檔; 透過 World 處理通訊員離開、組隊上下線與隊長轉移、好友上下線、公會與家族的線上狀態; 依 fromCSWorld.Find.findChannel(int) 結果判斷實際所在伺服器(必要時改以正確情境重呼自己); 視 RemoveInChannelServer 自頻道伺服器或商城玩家儲存移除此玩家;最後在非過渡且仍登入時把登入狀態 重設為 LOGIN_NOTLOGGEDIN,並清空 per-client 腳本引擎快取(engines.clear())。 例外會被攔截、記錄並寫入錯誤檔。

      Parameters:
      RemoveInChannelServer - 是否一併自頻道伺服器(或商城)的玩家儲存中移除此玩家
      fromCS - 是否由商城伺服器情境發起
      shutdown - 是否為伺服器關閉流程觸發;為真時僅存檔並提前返回,略過跨伺服器離線處理
    • getSessionIPAddress

      public final String getSessionIPAddress()
      取得本連線對端的 IP 位址(去除埠號部分)。
      Returns:
      對端 IP 位址字串
    • CheckIPAddress

      public final boolean CheckIPAddress()
      驗證本連線目前的來源 IP 是否與資料庫記錄的 SessionIP 相符,且帳號未被封鎖。

      會查詢資料庫(accounts)。未綁定帳號(accId < 0)時回傳 false; 已封鎖則判定不可登入。查詢發生 SQL 例外時保守地回傳 true(記錄但不阻擋)。

      Returns:
      IP 相符且未封鎖時為 true;不符或已封鎖時為 false
    • DebugMessage

      public final void DebugMessage(StringBuilder sb)
      將本連線的偵錯資訊附加到給定的字串建構器。

      輸出對端位址、連線是否啟用/開啟、是否已設定 CLIENT_KEY 屬性、是否已登入,以及是否已綁定角色。

      Parameters:
      sb - 用於收集偵錯文字的 StringBuilder
    • getChannel

      public final int getChannel()
      取得此連線目前所在的頻道編號。
      Returns:
      頻道編號;登入伺服器階段或尚未指派時為初始值
    • getChannelServer

      public final ChannelServer getChannelServer()
      取得此連線目前所在頻道對應的 ChannelServer 實例。
      Returns:
      對應頻道的 ChannelServer;若該頻道未啟動則可能為 null
    • deleteCharacter

      public final int deleteCharacter(int cid)
      刪除指定角色,並一併清除其在各資料表中的所有關聯資料。

      此方法會大量寫入資料庫(透過共用的 ThreadLocal 連線):先驗證角色屬於本帳號 (accountid 必須相符),處理公會與家族的離隊,接著對 characters 及 二十餘張關聯表(道具、技能、好友、任務、傳送點、拍賣等)逐一執行刪除。 視伺服器設定 log_delchr 寫入刪除角色記錄檔,並在玩家開啟 GM 監聽時對所有 GM 廣播封包。

      Parameters:
      cid - 欲刪除角色的角色編號
      Returns:
      結果代碼:0 表示刪除成功;9 表示查無此角色(或不屬於本帳號); 22 表示角色為公會會長無法刪除;10 表示刪除過程發生例外
    • getGender

      public final byte getGender()
      取得此帳號的性別。
      Returns:
      性別代碼(0 為男性、1 為女性)
    • setGender

      public final void setGender(byte gender)
      設定此帳號的性別。
      Parameters:
      gender - 性別代碼(0 為男性、1 為女性)
    • getSecondPassword

      public final String getSecondPassword()
      取得此帳號的第二組密碼(防護密碼)。
      Returns:
      第二組密碼;未設定時可能為 null
    • setSecondPassword

      public final void setSecondPassword(String secondPassword)
      設定此帳號的第二組密碼(防護密碼)。
      Parameters:
      secondPassword - 欲設定的第二組密碼
    • getAccountName

      public final String getAccountName()
      取得此連線的登入帳號名稱。
      Returns:
      帳號名稱;尚未登入時可能為 null
    • setAccountName

      public final void setAccountName(String accountName)
      設定此連線的登入帳號名稱。
      Parameters:
      accountName - 帳號名稱
    • setChannel

      public final void setChannel(int channel)
      設定此連線目前所在的頻道編號。
      Parameters:
      channel - 頻道編號
    • getWorld

      public final int getWorld()
      取得此連線所屬的世界(World)編號。
      Returns:
      世界編號
    • setWorld

      public final void setWorld(int world)
      設定此連線所屬的世界(World)編號。
      Parameters:
      world - 世界編號
    • getLatency

      public final int getLatency()
      取得最近一次量測的網路延遲。

      以最後一次收到 pong 的時間減去最後一次送出 ping 的時間計算; 若結果為負值(pong 尚未回應),sendPing() 排程的逾時檢查會將連線斷開。

      Returns:
      延遲毫秒數
    • getLastPong

      public final long getLastPong()
      取得最後一次收到客戶端 pong 回應的時間戳記。
      Returns:
      時間戳記(System.currentTimeMillis() 毫秒)
    • getLastPing

      public final long getLastPing()
      取得最後一次送出 ping 封包的時間戳記。
      Returns:
      時間戳記(System.currentTimeMillis() 毫秒)
    • pongReceived

      public final void pongReceived()
      標記已收到客戶端的 pong 回應,更新最後 pong 時間為目前時間。

      由處理 pong 封包的處理器呼叫,用於計算 網路延遲

    • sendPing

      public final void sendPing()
      送出 ping 封包並排程逾時檢查。

      記錄送出時間後,立即將 ping 封包寫入連線;接著於 PingTimer 排程一個延遲約兩分鐘的任務, 屆時若 延遲 仍為負值(代表尚未收到 pong),便斷開並關閉此連線。 注意客戶端的閒置時間會額外疊加在此逾時之上。

    • getLogMessage

      public static final String getLogMessage(MapleClient cfor, String message)
      產生一則含連線身分前綴的記錄訊息(無參數格式化)。
      Parameters:
      cfor - 來源連線;為 null 時不附加身分前綴
      message - 記錄訊息內容
      Returns:
      已附加角色/帳號前綴的記錄字串
    • getLogMessage

      public static final String getLogMessage(MapleCharacter cfor, String message)
      產生一則含角色身分前綴的記錄訊息(無參數格式化)。
      Parameters:
      cfor - 來源角色;為 null 時不附加身分前綴
      message - 記錄訊息內容
      Returns:
      已附加角色/帳號前綴的記錄字串
    • getLogMessage

      public static final String getLogMessage(MapleCharacter cfor, String message, Object... parms)
      產生一則含角色身分前綴並套用參數格式化的記錄訊息。

      訊息中的每個 {} 佔位符會依序以 parms 的字串值取代。

      Parameters:
      cfor - 來源角色;為 null 時不附加身分前綴
      message - 含 {} 佔位符的記錄訊息範本
      parms - 依序填入佔位符的參數
      Returns:
      已附加角色/帳號前綴且完成格式化的記錄字串
    • getLogMessage

      public static final String getLogMessage(MapleClient cfor, String message, Object... parms)
      產生一則含連線身分前綴並套用參數格式化的記錄訊息(其餘多載皆收斂至此)。

      cfor 不為 null 時,依序附加角色名稱與 cid(角色存在時)以及帳號名稱 (帳號存在時)作為前綴;接著將訊息中的每個 {} 佔位符依序以 parms 的字串值取代。

      Parameters:
      cfor - 來源連線;為 null 時不附加身分前綴
      message - 含 {} 佔位符的記錄訊息範本
      parms - 依序填入佔位符的參數
      Returns:
      已附加角色/帳號前綴且完成格式化的記錄字串
    • findAccIdForCharacterName

      public static final int findAccIdForCharacterName(String charName)
      依角色名稱查詢其所屬的帳號編號。

      透過共用 ThreadLocal 連線查詢 characters 資料表。

      Parameters:
      charName - 角色名稱
      Returns:
      對應的帳號編號;查無此角色或查詢發生例外時回傳 -1
    • getClientMac

      public final String getClientMac()
      取得此連線記錄的主要網卡 MAC 位址。
      Returns:
      MAC 位址字串;尚未取得時可能為 null
    • getMacs

      public final Set<String> getMacs()
      取得此連線回報的所有 MAC 位址集合。
      Returns:
      不可修改的 MAC 位址集合(Collections.unmodifiableSet(Set)
    • isBanned

      public boolean isBanned()
      判斷此帳號是否處於被封鎖狀態。

      GM 帳號永遠視為未封鎖。

      Returns:
      若帳號被封鎖且非 GM 則為 true,否則為 false
    • isGm

      public final boolean isGm()
      判斷此帳號是否為 GM(管理員)帳號。
      Returns:
      為 GM 則回傳 true
    • setScriptEngine

      public final void setScriptEngine(String name, ScriptEngine e)
      以指定名稱快取一個 ScriptEngine

      腳本引擎以每個 MapleClient 為單位快取,供 NPC/任務等腳本重複使用; 連線斷開時整個快取會被清空。

      Parameters:
      name - 快取鍵(通常為腳本路徑)
      e - 欲快取的腳本引擎
    • getScriptEngine

      public final ScriptEngine getScriptEngine(String name)
      依名稱取得先前快取的 ScriptEngine
      Parameters:
      name - 快取鍵(通常為腳本路徑)
      Returns:
      對應的腳本引擎;若尚未快取則為 null
    • removeScriptEngine

      public final void removeScriptEngine(String name)
      從快取移除指定名稱的 ScriptEngine

      用於令下次取用該腳本時重新讀取檔案(例如指令腳本的自動刷新)。

      Parameters:
      name - 快取鍵(通常為腳本路徑)
    • getIdleTask

      public final ScheduledFuture<?> getIdleTask()
      取得此連線目前的閒置逾時排程任務。
      Returns:
      閒置任務的 ScheduledFuture;未排程時可能為 null
    • setIdleTask

      public final void setIdleTask(ScheduledFuture<?> idleTask)
      設定此連線的閒置逾時排程任務。
      Parameters:
      idleTask - 閒置任務的 ScheduledFuture
    • unLockDisconnect

      public final void unLockDisconnect()
      因帳號於他處重複登入而強制斷線並關閉連線。

      會向客戶端送出重複登入提示封包(serverNotice),呼叫 disconnect,並另起一條執行緒延遲約三秒後關閉 session (讓提示有時間送達)。

    • getCharacterSlots

      public int getCharacterSlots()
      取得此帳號在目前世界可用的角色欄位數。

      GM 帳號固定回傳 15。一般帳號優先回傳已快取的值以節省查詢;快取為預設值時才查詢 character_slots 資料表,若該帳號/世界尚無紀錄則順帶寫入一筆預設列(資料庫副作用)。

      Returns:
      可建立的角色欄位數
    • gainCharacterSlot

      public boolean gainCharacterSlot()
      為此帳號在目前世界增加一個角色欄位。

      當前欄位數達上限 15 時不予增加。成功時會更新 character_slots 資料表 (資料庫副作用)。

      Returns:
      成功增加回傳 true;已達上限或更新失敗回傳 false
    • unbanIP

      public static byte unbanIP(String charname)
      依角色名稱解除其帳號最近登入 IP 的封鎖。

      查出該角色的帳號與 sessionIP 後,自 ipbans 資料表移除對應 IP(資料庫副作用)。

      Parameters:
      charname - 角色名稱
      Returns:
      成功解除的 IP 筆數(通常為 1);查無角色或帳號回傳 -1; 發生 SQL 例外回傳 -2
    • unbanIPMacs

      public static final byte unbanIPMacs(String charname)
      依角色名稱解除其帳號的 IP 與 MAC 封鎖。

      查出該角色帳號的 sessionIPmacs 後,分別自 ipbansmacbans 資料表移除對應紀錄(資料庫副作用);macs", " 分隔,逐一處理。

      Parameters:
      charname - 角色名稱
      Returns:
      已解除的項目類別數(IP 與 MAC 各計一);查無角色或帳號回傳 -1; 發生 SQL 例外回傳 -2
    • unHellban

      public static final byte unHellban(String charname)
      依角色名稱解除其帳號的「地獄封鎖」(hellban)。

      查出該角色帳號的 emailsessionIP 後,將 accounts 資料表中 凡 email 相符(或 IP 相符,若有)的所有帳號之 bannedbanreason 清除 (資料庫副作用,可能影響多個共用 email/IP 的帳號)。

      Parameters:
      charname - 角色名稱
      Returns:
      成功回傳 0;查無角色或帳號回傳 -1;發生 SQL 例外回傳 -2
    • isMonitored

      public boolean isMonitored()
      判斷此連線是否處於受監控狀態。
      Returns:
      受監控則回傳 true
    • setMonitored

      public void setMonitored(boolean m)
      設定此連線是否受監控。
      Parameters:
      m - true 表示啟用監控
    • isReceiving

      public boolean isReceiving()
      判斷此連線目前是否接受處理封包。
      Returns:
      接受封包則回傳 true
    • setReceiving

      public void setReceiving(boolean m)
      設定此連線是否接受處理封包。

      斷線流程會先設為 false 以停止後續封包處理。

      Parameters:
      m - true 表示接受封包
    • canClickNPC

      public boolean canClickNPC()
      判斷距離上次點擊 NPC 是否已超過防連點冷卻(500 毫秒)。
      Returns:
      已可再次點擊 NPC 則回傳 true
    • setClickedNPC

      public void setClickedNPC()
      記錄一次 NPC 點擊時間,啟動防連點冷卻。
      See Also:
    • removeClickedNPC

      public void removeClickedNPC()
      清除上次 NPC 點擊時間,立即解除防連點冷卻。
      See Also:
    • getCreated

      public final Timestamp getCreated()
      查詢此帳號的建立時間。

      透過共用 ThreadLocal 連線查詢 accounts 資料表的 createdat 欄位。

      Returns:
      帳號建立時間;查無此帳號時回傳 null
      Throws:
      DatabaseException - 查詢過程發生 SQL 例外時
    • getTempIP

      public String getTempIP()
      取得此連線的暫存 IP 位址。
      Returns:
      暫存 IP 字串;未設定時可能為 null
    • setTempIP

      public void setTempIP(String s)
      設定此連線的暫存 IP 位址。
      Parameters:
      s - 暫存 IP 字串
    • isLocalhost

      public boolean isLocalhost()
      判斷伺服器是否以本機(localhost)模式運行。
      Returns:
      取自 ServerConstants.Use_Localhost 的設定值
    • setVip

      public void setVip(int vip)
      設定此帳號的 VIP 等級並立即寫回資料庫。

      更新 accounts 資料表的 vip 欄位(資料庫副作用);寫入失敗僅記錄除錯訊息、不拋出例外。

      Parameters:
      vip - VIP 等級
    • getVip

      public int getVip()
      查詢並回傳此帳號的 VIP 等級。

      每次呼叫都會查詢 accounts 資料表並更新內部快取欄位;查詢失敗時回傳上次的快取值。

      Returns:
      VIP 等級
    • setRewardLevel

      public void setRewardLevel(int reward)
      設定此帳號的獎勵等級並立即寫回資料庫。

      更新 accounts 資料表的 reward 欄位(資料庫副作用);寫入失敗僅記錄除錯訊息、不拋出例外。

      Parameters:
      reward - 獎勵等級
    • getRewardLevel

      public int getRewardLevel()
      查詢並回傳此帳號的獎勵等級。

      每次呼叫都會查詢 accounts 資料表並更新內部快取欄位;查詢失敗時回傳上次的快取值。

      Returns:
      獎勵等級
    • sendPacket

      public void sendPacket(byte[] packet)
      將已組裝完成的封包寫入此連線並送出。

      透過底層 Netty session 的 writeAndFlush 立即送出(編碼/加密由 MaplePacketEncoder 處理)。

      Parameters:
      packet - 由 tools.packet.* 建構器組裝出的封包位元組
    • banSingleMacs

      public static boolean banSingleMacs(String macData)
      將單一 MAC 位址加入封鎖名單。

      會校驗格式:全零位址 00-00-00-00-00-00 或長度非 17 的字串一律拒絕;通過後寫入 macbans 資料表(資料庫副作用)。

      Parameters:
      macData - 欲封鎖的 MAC 位址(格式 XX-XX-XX-XX-XX-XX
      Returns:
      成功封鎖回傳 true;格式無效或寫入失敗回傳 false
    • Fullyunban

      public static boolean Fullyunban(String charname)
      對指定角色的帳號執行完整解封(帳號封鎖 + IP/MAC 封鎖)。

      依序呼叫 unban(String)unbanIPMacs(String),兩者皆達預期結果才視為成功 (unban 須為 0unbanIPMacs 須為 12)。

      Parameters:
      charname - 角色名稱
      Returns:
      全部解封成功回傳 true,否則回傳 false
    • getCloseSession

      public boolean getCloseSession()
      判斷此連線是否已被標記為待關閉。
      Returns:
      已標記關閉則回傳 true
    • loadCharacterNamesByAccId

      public static List<String> loadCharacterNamesByAccId(int accId)
      載入指定帳號底下所有角色的名稱。

      透過共用 ThreadLocal 連線查詢 characters 資料表。

      Parameters:
      accId - 帳號編號
      Returns:
      角色名稱清單;查無角色或發生例外時為空清單(永不為 null
    • loadNecessaryAccInfoByCharID

      public void loadNecessaryAccInfoByCharID(int charid)
      依角色編號回填此連線所需的基本帳號資訊。

      分兩次查詢(皆透過共用 ThreadLocal 連線):先由 characters 取得 accountid, 再由 accounts 取得帳號名稱與 MAC,並就地更新本物件的 accIdaccountNamemac 欄位(狀態變更)。任一階段查詢失敗僅記錄錯誤、不拋出例外。

      Parameters:
      charid - 角色編號(cid)