iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 7
1
Software Development

從讀遊戲原始碼學做連線遊戲系列 第 7

Day 07 - 指令系統 - Unlight 的指令處理(二)

為了要知道 @func_list 是在哪裡被定義,我們繼續在原始碼裡面找到 #post_init 這個方法,內容大致上如下

# 接続時
def post_init
  @error_count = 0                 # 無効なコマンドが送られてきた数
  # ...
  @command_list =[]
  @func_list = []
  @@receive_cmd.method_list.each do |c|
    @func_list << method(c)
  end
end

我們會發現當使用者「連上」伺服器的時候,會使用 @@receive_cmd 這個物件裡面的 method_list 將可呼叫的指令放到 @func_list 裡面。

在 Ruby 中使用 @ 開頭的是 Instance Variable 使用 @@ 開頭的則是 Class Variable,而 #method 方法可以將某個方法轉成類似 Function Pointer 的形式讓其他人可以呼叫。

不過我們在 src/protocol/ulserver.rb 裡面反而找不到 @@receive_cmd 是在哪裡被賦予數值的,這是因為不同的伺服器會使用不同的指令組合,因此我們可以個別伺服器的協定(Protocol)檔案找到這些定義,下面就以登入伺服器(src/protocol/authserver.rb)作為例子

# クラスの初期化
def self.setup
  super
  Player.auth_off_all
  # コマンドクラスをつくる
  @@receive_cmd=Command.new(self,:Auth)
  # 暗号化クラスを作る
  @@srp = SRP.new()
  @@invited_id_set = []
end

基本上每一個伺服器都會有一個叫做 .setup 的方法,這是當伺服器啟動的時候會先呼叫這個方法將伺服器相關的設定初始化,而 @@receive_cmd 也是其中一個被初始化的數值,用來定義每個伺服器可以處理的指令。

因此,我們需要了解 Command 這個物件是負責哪些任務,才能夠繼續針對指令系統做深入的討論。

# コンストラクタ(Cはコマンドを追加するクラス)
def initialize(c,type)
  @klass = c
  @cmd_val = Struct.new(:name, :type, :size)
  @method_list = []
  #      p type
  # サーバータイプによって生成するコマンドを切り換える
  case type
  when :Auth
    require 'protocol/command/authcommand'
  # ...
  end
  init_receive(RECEIVE_COMMANDS)
  init_send(SEND_COMMANDS)
end

Command 處初始化中,我們可以從前面 @@receive_cmd=Command.new(self,:Auth) 這段程式碼了解到幾件事情。

  1. @klass 會等於 AuthServer 這個物件(在 Class Method 時 self 表示物件自身的實例)
  2. 依照傳入的 type 不同載入不同的指令設定,以這邊為例子就是 src/protocol/command/authcommand.rb 這個檔案

另外也定義了指令的數值結構應該要是 name (名稱)加上 type(類型)和 size(大小)這三個資訊,不過目前還無法清楚的推論出用途,先繼續往下看。

當載入完畢對應的指令設定後,會呼叫 #init_receive#init_send 這兩個方法,簡單說就是對應「接收」跟「發送」兩個情境。

打開 src/protocol/command/authcommand.rb 來看,會發現到 RECEIVE_COMMANDSSEND_COMMANDS 兩個常數。

# 受信コマンド一覧
# 名称:型:サイズ(0は可変)
RECEIVE_COMMANDS =
  [
    # プレイヤー登録
    [:register,
     [# Name, Type, Size
      ["name", :String, 0],
      ["email", :String, 0],
      ["salt", :String, 0],
      ["verifire", :String, 0],
      ["server_type", :int, 4],
     ]
    ],
# ...

以 AuthServer 使用的指令來看,依照前面我們知道的邏輯可以推論以下幾件事情。

  1. 指令的編號應該等同於這邊的 Index 值,例如 register 指令編號是 0
  2. @cmd_val 結構對應的是參數,以 register 指令的第一個參數來說會是 name="name", type=:String, size=0
  3. 前面我們雖然解析出指令封包結構,但是封包內容的結構還不確定,在這邊可以知道應該是依照該指令的參數結構來解析內容

不過如何將接收到的資料處理成可以執行的指令,到目前為止還沒辦法完全確定下來,因此我們需要再繼續了解 #init_receive 方法所了什麼。

我的個人部落格是弦而時習之平常會把自己發現的一些新技巧紀錄在上面,也歡迎大家來逛逛。


上一篇
Day 06 - 指令系統 - Unlight 的指令處理(一)
下一篇
Day 08 - 指令系統 - Unlight 的指令處理(三)
系列文
從讀遊戲原始碼學做連線遊戲33

尚未有邦友留言

立即登入留言