基礎 LPC

作者: Descartes of Borg
第一版: 23 april 1993
第二版: 16 june 1993

第二章: LPC 程式

2.1 關於程式

這一章的名字取得不怎麼好, 因為沒有人用 LPC 寫程式. 寫 LPC 程式的人寫的是物件 (objects). 這兩種說法有啥差別 ? 好吧, 就我們現在的目標來說, 差別在於兩者檔案執行的方式不同. 當你「跑」一個程式的時候, 都是從程式中固定的地方開始執行. 換句話說, 就是所有的程式開始執行的時候, 一定有個地方寫清楚要從那裡開始. 另外, 程式有一個固定的終止點, 所以執行程式只要執行到該終止點, 程式就中止執行. 總之, 程式從固定的開頭跑到固定的結尾. LPC 物件就不是這麼一回事.

在 mud 裡面, LPC 物件只是遊戲 (driver) C 程式中, 顯而易見的部分. 換句話說, mud 程式在 driver 裡面開始與結束執行. 但是實際上, 對於創造你玩的 mud 世界來說, driver 並沒有做多少事. 反之, driver 相當依賴 LPC 碼, 並需要執行物件中的程式碼. 所以 LPC 物件不需要有起始點, 也不需要有固定的終止點.

就像其他的程式語言, LPC 「程式」可以由一個或一個以上的檔案組成. 很簡單, 程式要先載入 driver 的記憶體. driver  會根據本手冊所教的結構, 讀取物件中一行行的程式. 有一件重要的事你要先搞清楚, 就是 LPC  物件執行時沒有開頭也沒有終止.


2.2 diiver-mudlib 之間的互動

我先前提過, driver 是在主機上執行的 C 程式. 它讓你連上遊戲, 並執行 LPC 碼. 注意, 這是 mud 程式設計的一個理論而已, 也不需要比其他的方法好. 整個 mud 遊戲可以全部用 C 來寫. 這樣遊戲的執行速度快上很多, 卻讓 mud 缺乏可塑性, 使巫師在遊戲正在執行的時候無法加入新東西. DikuMUD 就是全部用 C 寫成的. 相反的, LPMUD 的理論就是 driver 不該決定遊戲內容, 而遊戲內容應該決定於遊戲中的個別事物, 並能夠在遊戲執行時加上東西. 這就是為什麼 LPMUD 使用 LPC 程式語言. 它能讓你用 LPC 定義遊戲內容, 交給 driver 依需要讀取並執行. 況且學 LPC 要比 C 容易得多, 這樣讓更多人能加入創造世界的過程.

一旦你用 LPC 寫了一個檔案 (假設是用正確的 LPC),  它只是躺在你主機的硬碟裡不動, 直到遊戲中有東西參考 (reference) 它. 當遊戲中有東西終於參考到它時, 這個檔案就會被複製一份到記憶體裡面, 並且呼叫這個物件中一個特殊的函式 (function). 呼叫這個函式的目的是初始化 (initialize) 這個物件中的變數. 現在, 別管你腦袋裡才看到的上兩句話, 因為一個對程式設計完全陌生的人來說, 哪裡會知道函式或變數到底是啥東西. 現在重要的是要知道 driver 讀取主機硬碟裡面的物件檔案, 複製一份之後扔進記憶體儲存 (既然是複本, 也就可以有許多不同的版本 ). 你稍後會知道什麼是函式、什麼是變數, 並搞清楚到底遊戲中的一些東西是怎麼參考你的物件的.


2.3 將一個物件載入記憶體

雖然一個物件裡面並沒有規定要從一個固定的地方開始執行程式, driver 卻要先找到一個固定的地方並執行之, 才能初始化一個物件. 在精簡模式的 driver 上, 這是一個叫作 reset()  的函式. 在原始模式 mud  中, 則是 create().

LPC 物件是由變數 (variable) 所組成的 (會更改的值) 而函式是處理這些變數的程式. 函式經由 LPC  語法結構來處理變數, 語法結構包括: 呼叫其他函式、使用外部定義函式 (externally defined functions, efuns)、基本的 LPC 運算式 (expression) 和流程控制 (flow control mechanism).

前面這些聽起來亂七八糟的吧 ?  讓我們從變數開始著手. 拿「等級」變數來說吧, 等級可以隨情形不同而改變它的數值, 而不同的事物也使用玩家的等級數字作出不同的事. 舉個例: 如果你是等級十九級的玩家, 則等級變數的數值就是 19 . 如果你的 mud 是舊的 LPMud 2.4.5 系統, 等級 1 到 19 級是玩家, 20 級以上是巫師, 則會有許多事物會詢問你的等級變數值, 判斷你能不能使用巫師的動作. 基本上, 任何 LPC 物件就是一堆會隨時間不同而改變的變數組成的. 發生在物件身上的事, 都基於該物件的各個變數裡頭的數值. 而常常也有許多事會更改變數.

所以無論何時, 一個 LPC 撰寫的物件被其他在記憶體的物件拿來參考時, driver 就尋找這物件裡面所要找的值在哪裡 (但是現在還沒有任何數值) . driver 找過之後, 就呼叫物件中的 reset() 或 create() 函式  (視不同 driver 而定) , 來設定該物件一開始的變數值. 就這樣, 經由「呼叫」「函式」處理變數.

雖然絕大多數的 LPC  程式碼都從 create() 或 reset()  開始執行, 此處卻不是 LPC  程式碼開頭的地方. 事實上, 沒有這兩個函式也沒關係. 如果你的物件一開始所有的值都是 NULL  (虛無) 指標 (在此, 虛無指標我們先當它是 0  吧) , 那你就不需要 create() 或 reset() 函式. 所以, 每個物件開始執行程式碼的地方都可能完全不同.

現在讓我們搞清楚這整章在講些什麼. 問題是: 一個完整的 LPC 到底是由哪些東西組成的 ? 好, 一個 LPC 物件簡單來說, 就是一個或一個以上的函式組合起來, 處理一個以上的變數 (或是不處理變數也行) . 各個函式之間完全不用管它們擺的先後順序. 換句話說:

-----
void init() { add_action("smile", "smile"); }

void create() { return; }

int smile(string str) { return 0; }
-----

跟底下的一樣:

-----

void create() { return; }

int smile(string str) { return 0; }

void init() { add_action("smile", "smile"); }

_____

另外有個很重要的事提醒你, 下面這個物件只有:

-----

void nonsense() {}
-----

這樣也可以, 但是這種微不足道的物件, 它大概不會與你的 mud 中的其他物件作出正確的互動關係, 因為這樣的物件沒有重量、看不到......以此類推.


2.4 本章總結

LPC 碼沒有起點或終點, 因為 LPC 碼是用來創造 driver 程式使用的物件, 而非單獨的程式. LPC 物件包括一個或多個函式, 其間先後順序毫無關係, 而這些函式之中, 處理多個變數 (或根本沒有任何變數) . LPC 物件只是躺在主機的硬碟裡面, 等著遊戲中其他的物件參考它 (換言之, 它們實際上不存在) . 一旦一個物件被參考到, 它會被載入記憶體中, 並且它所有的變數都是零. 精簡模式 mud 呼叫此物件的 reset() 而原始模式 mud 呼叫 create()  (如果此物件有這些函式的話 ), 讓這些函式來指定一些變數的初始值. 物件中的其他函式由 driver 或遊戲中其他的物件使用之, 讓物件之間達到互動並處理 LPC 變數.

reset() 和 create() 的說明:

只有原始模式的 mud 使用 create() (請見本手冊的 Introduction 一章, 有關原始模式和精簡模式的介紹). 此函式僅用來初始化剛被參考的物件.

原始模式及精簡模式的 mud 都使用 reset() 函式. 在精簡模式 mud 中, reset() 有兩個功能. 第一, 它用來初始化剛被參考的物件. 第二, 在精簡模式的 mud 中, reset() 用來重新設定物件. 也就是說, 讓物件回到最初的狀態. 這樣可以讓一個房間內的怪物重生, 或把一道門關回去......以此類推. 原始模式的 mud 只用 reset() 作第二種功能 (就跟 reset 的意思一樣).
所以在 LP 式的 mud 中有兩件重要的事情讓 driver 呼叫物件中的函式. 第一件事是創造物件. 此時, driver  呼叫物件中的一個函式來初始化物件的變數值. 在精簡模式的 mud  裡, 由 reset() 做此工作 (要加上 0 參數, 後面的章節再討論參數是啥). 原始模式的 mud 下, 由 create() 做此工作.

第二件事是把房間重新設定回某些基本的狀況. 這些基本的設定可能會與一開始的初始值不同, 也可能相同, 而你當然也不想花時間一直去重覆做某些事 (像是重新設定一些不會更改的變數) . 精簡模式的 mud 用 reset() 函式來創造和重新設定物件. 而原始模式的 mud 用 create() 創造物件, 用 reset() 重新設定物件. 但是精簡模式也不會失去所有的變數值, 因為有個方法能區分是創造物件還是重新設定物件. 在精簡模式要重新設定, 則 driver 傳入 1 或重新設定的數字當作 reset() 的參數. 現在這個對你來說沒啥意義, 但是要記住, 你在精簡模式實際上是可以把兩種情形區分開來的. 另外也要記住, reset() 在創造物件時傳入的參數是 0, 而重新設定物件時傳入非零值.


翻譯: Spock of Final Frontier  98.Jan.16.

回到上一頁