小談Online game伺服器端設計(4)

2022-06-22 17:48:10 字數 3610 閱讀 4645

在這一章節,我想談談關於伺服器端的指令碼的相關設計。因為在上一章節裡面,談npc智慧型相關的時候已經接觸到一些指令碼相關的東東了。還是先來談談指令碼的作用吧。

在基於編譯的伺服器端程式中,是無法在程式的執行過程中構建一些東西的,那麼這個時候就需要指令碼語言的支援了,由於指令碼語言涉及到邏輯判斷,所以光提供一些函式介面是沒用的,還需要提供一些簡單的語法和文法解析的功能。其實說到底,任何的事件都可以看成兩個部分:第一是對自身,或者別的物件的數值的改變,另外乙個就是將該事件以文字或者圖形的方式廣播出去。那麼,這裡牽扯到乙個很重要的話題,就是對某一物件進行定址。恩,談到這,我想將本章節分為三個部分來談,首先是伺服器如何來管理動態建立出來的物件(伺服器記憶體管理),第二是如何對某一物件進行定址,第三則是指令碼語言的組織和解釋。其實之所以到第四章再來談伺服器的記憶體管理是因為在前幾章談這個的話,大家對其沒有乙個感性的認識,可能不知道伺服器的記憶體管理究竟有什麼用。

4.1、伺服器記憶體管理

對於伺服器記憶體管理我們將採用記憶體池的方法,也稱為靜態記憶體管理。其概念為在伺服器初始化的時候,申請一塊非常大的記憶體,稱為記憶體池(memory pool),同時也申請一小塊記憶體空間,稱為垃圾**站(garbage recollecting station)。其大體思路如下:當程式需要申請記憶體的時候,首先檢查垃圾**站是否為空,如果不為空的話,則從垃圾**站中找一塊可用的記憶體位址,在記憶體池中根據位址找到相應的空間,分配給程式用,如果垃圾**站是空的話,則直接從記憶體池的當前指標位置申請一塊記憶體;當程式釋放空間的時候,給那塊記憶體打上已經釋放掉的標記,然後把那塊記憶體的位址放入垃圾**站。

下面具體談談該方法的詳細設計,首先,我們將採用類似於作業系統的段頁式系統來管理記憶體,這樣的好處是可以充分的利用記憶體池,其缺點是管理起來比較麻煩。嗯,下面來具體看看我們怎麼樣來定義頁和段的結構:

typedef struct m_segment_s

m_segment_t;

typedef struct m_page_s

m_page_t;

static m_page_t *all_pages;

// total_size是總共要申請的記憶體數,num_pages是總共打算建立多少個頁面。

void initialize_memory_pool( int total_size, int num_pages )

// 設定最後乙個頁面的大小。

if ( (last_size = total_size % num_pages) != 0 )

all_pages[i].size = last_size;

}下面看看垃圾**站怎麼設計:

int **garbage_station;

void init_garbage_station( int num_pages, int page_size )

}也許這樣的貼**會讓大家覺得很不明白,嗯,我的**水平確實不怎麼樣,那麼下面我來用文字方式來敘說一下大體的概念吧。對於段頁式記憶體管理,首先分成n個頁面,這個是固定的,而對於每個頁面內的段則是動態的,段的大小事先是不知道的,那麼我們需要**的不僅僅是頁面的記憶體,還包括段的記憶體,那麼我們就需要乙個二維陣列來儲存是哪個頁面的那塊段的位址被釋放了。然後對於申請記憶體的時候,則首先檢查需要申請記憶體的大小,如果不夠乙個頁面大小的話,則在垃圾**站裡面尋找可用的段空間分配,如果找不到,則申請乙個新的頁面空間。

這樣用記憶體池的方法來管理整個遊戲世界的記憶體可以有效的減少記憶體碎片,一定程度的提高遊戲執行的穩定性和效率。

4.2、遊戲中物件的定址

第乙個問題,我們為什麼要定址?加入了指令碼語言的概念之後,遊戲中的一些邏輯物件,比如說npc,某個item之類的都是由指令碼語言在遊戲執行的過程中動態生成的,那麼我們通過什麼樣的方法來對這些物件進行索引呢?說得簡單一點,就是如何找到他們呢?有個很簡單的方法,全部遍歷一次。當然,這是個簡單而有效的方法,但是效率上的消耗是任何一台伺服器都吃不消的,特別是在遊戲的規模比較大之後。

那麼,我們怎麼來在遊戲世界中很快的尋找這些物件呢?我想在談這個之前,說一下hash table這個資料結構,它叫雜湊表,也有人叫它雜湊表,其工作原理是不是順序訪問,也不是隨機訪問,而是通過乙個雜湊函式對其key進行計算,算出在記憶體中這個key對應的value的位址,而對其進行訪問。好處是不管面對多大的資料,只需要一次計算就能找到其位址,非常的快捷,那麼弊端是什麼呢?當兩個key通過雜湊函式計算出來的位址是同乙個位址的時候,麻煩就來了,會產生碰撞,其的解決方法非常的麻煩,這裡就不詳細談其解決方法了,否則估計再寫個四,五章也未必談得清楚,不過如果大家對其感興趣的話,歡迎討論。

嗯,我們將用雜湊表來對遊戲中的物件進行索引,具體怎麼做呢?首先,在記憶體池中申請一塊兩倍大於遊戲中物件總數的記憶體,為什麼是兩倍大呢?防止雜湊表碰撞。然後我們選用物件的名稱作為雜湊表的索引key,然後就可以開始設計雜湊函式了。下面來看個例子:

static int t =

;// s是需要進行索引的字串指標,maxn是字串可能的最大長度,返回值是相對位址。

inline int whashstr(char *s, int maxn)

return (oh << 8) + h;

}具體的演算法就不說了,上面的那一大段東西不要問我為什麼,這個演算法的出處是cacm 33-6中的乙個叫peter k.pearson的鬼子寫的**中介紹的演算法,據說速度非常的快。有了這個雜湊函式,我們就可以通過它來對世界裡面的任意物件進行非常快的定址了。

4.3、指令碼語言解釋

在設計指令碼語言之前,我們首先需要明白,我們的指令碼語言要實現什麼樣的功能?否則隨心所欲的做下去寫出個c的直譯器之類的也說不定。我們要實現的功能只是簡單的邏輯判斷和迴圈,其他所有的功能都可以由事先提供好的函式來完成。嗯,這樣我們就可以列出一張工作量的表單:設計物件在底層的儲存結構,提供指令碼和底層間的訪問介面,設計支援邏輯判斷和迴圈的直譯器。

下面先來談談物件在底層的儲存結構。具體到每種不同屬性的物件,需要採用不同的結構,當然,如果你願意的話,你可以所有的物件都採同同樣的結構,然後在結構裡面設計乙個雜湊表來儲存各種不同的屬性。但這並不是乙個好方法,過分的依賴雜湊表會讓你的遊戲的邏輯變得繁雜不清。所以,盡量的區分每種不同的物件採用不同的結構來設計。但是有一點值得注意的是,不管是什麼結構,有一些東西是統一的,就是我們所說的物件頭,那麼我們怎麼來設計這樣乙個物件頭呢?

typedef struct object_head_s

object_head_t;

其中name是在雜湊表中這個物件的索引號,prog則是指令碼直譯器需要解釋的程式內容。下面我們就以npc為例來設計乙個結構:

typedef struct npc_s

npc_t;

ok,結構設計完成,那麼我們怎麼來設計指令碼直譯器呢?這裡有兩種法,一種是用虛擬機器的模式來解析指令碼語言,另外一中則是用類似組合語言的那種結構來設計,設定一些條件跳轉和迴圈就可以實現邏輯判斷和迴圈了,比如:

set name, "路人甲";

choose: random_choose_personality;

// 隨機選擇npc的個性

compare hp, 100;

// 比較氣血,比較出的值可以放在乙個固定的變數裡面

ifless less;

// hp < 100的話,則返回。

jump choose;

// 否則繼續選擇,只到選到乙個hp < 100的。

less: return success;

小談Online game伺服器端設計(3)

下面我想來談談關於伺服器上npc的設計以及npc智慧型等一些方面涉及到的問題。首先,我們需要知道什麼是npc,npc需要做什麼。npc的全稱是 non player character 很顯然,他是乙個character,但不是玩家,那麼從這點上可以知道,npc的某些行為是和玩家類似的,他可以行走,...

小談Online game伺服器端設計(3)

下面我想來談談關於伺服器上npc的設計以及npc智慧型等一些方面涉及到的問題。首先,我們需要知道什麼是npc,npc需要做什麼。npc的全稱是 non player character 很顯然,他是乙個character,但不是玩家,那麼從這點上可以知道,npc的某些行為是和玩家類似的,他可以行走,...

小談Online game伺服器端設計(4)

在這一章節,我想談談關於伺服器端的指令碼的相關設計。因為在上一章節裡面,談npc智慧型相關的時候已經接觸到一些指令碼相關的東東了。還是先來談談指令碼的作用吧。在基於編譯的伺服器端程式中,是無法在程式的執行過程中構建一些東西的,那麼這個時候就需要指令碼語言的支援了,由於指令碼語言涉及到邏輯判斷,所以光...