After downloading an open source project licensed with GPL, it costs you a whole weekend setting up and having all configuration done, and finally being online on service. Others then come and use your network bandwidth, CPU, and memory resources for free, and some even try to hack your service. You paid the bills to electricity company and ISP for your system and look what you got in return: …… I can hardly name one, maybe the “respects” from free users?

So why not have your service GET PAID?

MaNGOS is yet another open source project implementing an educational and experimental MMORPG (Massive Multiplayer Online Role-Playing Game) server. Its title stands for the short of Massive Network Game Object Server and the development team stronly argued that “MaNGOS is not an emulator for any kind of existing MMOROG server”. I have no problem with their arguement, even though their Term of Service explicitly states that no Blizzard-related personnel is permitted to access any content of the project. What I actually concern is: we are using your so-called general purpose MMORPG server framework, so why can’t we charge the gamers like all gaming companies do, as it’s only a program handling basic network I/O with multithreading and nothing more?

Before telling you how to modify the code of MaNGOS to support accounting features, I must forewarn you that holding a public server using MaNGOS does lead you to the violation of the Term of Service and you won’t receive any supports from the official team as a result. Also, providing your own server for some copyrighted MMORPG clients will get you into a lawsuit. So kids, don’t do this at home nor at school.

The core problem to implement this functionality is to knwo when we should send accounting information to the client in a login session. Then something interests me: is it possible for every realm server to maintain its own accounting information? If the answer is yes, it could imply that once we have a realm farm, we can let gamers pay for only playing on some specific realm servers, probably with a special discount. To achieve this, we have to send something to the client between the authentication success and character enumeration on a realm server. I watched the log produced by mangosd.exe and found out that the realm server sent a SMSG_AUTH_RESPONSE message to the client with the authentication result and some other parameters. I think this is the right moment for a server to include the accounting information in these parameters if the message is to notify the client that the login is successful. Then I traced the code to find what MaNGOS would send in a successful SMSG_AUTH_RESPONSE and found this in src/game/World.cpp (of rev 6767):

void World::AddSession_ (WorldSession* s) {

WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_OK);
packet << uint32 (0); // unknown random value…
packet << uint8 (0);
packet << uint32 (0);
packet << uint8 (s->Expansion()); // must be set in database manually for each account
s->SendPacket (&packet);

As you can see, MaNGOS tells the client that the authentication is successful and whether or not the client is allowed to use expansion features. Some might notice that there are additional three fields for unknown use, in which one of them is commented strangely: “unknown random value…”. Fine then, if it is unknown and random, why not use it to contain our accounting information?! We can put a value here that indicates how long the user’s subscription is left, and maybe another additional value pointing out what kind of subscription now the user is using. Many commercial MMORPG’s here in Taiwan provide two kinds of subscription: 1) the fee is based on the time you stay online, or 2) the period you want to play without caring your online status. Now I am going to send the accounting information in the server message as following:

WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_OK);
packet << uint32 (timeLeft);
packet << uint8 (subscription);

packet << uint32 (0);
packet << uint8 (s->Expansion()); // must be set in database manually for each account
s->SendPacket (&packet);

The variable timeLeft means how much time available left in unit of minutes (note that it should be little-endian), and subscription indicates which kind of subscription the user has paid for and they may be one of the followings:

#define SUB_PERIOD 0×00 // the user can play all day long in this period of time
#define SUB_FREE 0×04 // the user is not charged to play!
#define SUB_POINTCARD 0×10 // the user can be online in this amount of minutes
#define SUB_BOTH 0×20 // the user paid for both SUB_PERIOD and SUB_POINTCARD

Now we are done with the core problem. The rest of the work including querying and updating user accounting information in a database is left to you to finish, since they are no big deal for ones who write in C++ language. What I showed here is only the concept to add some maybe additional but indeed useful information to the message that server sent. As MaNGOS is targeting to be a universal MMORPG server framework, my method provides much extensibility to other developers. You can add many related cool features into the framework based my modification, including different accounting in various realm servers as we earlier mentioned, more subscription types like personal IGR plan or so.

For the last words, this article only reveals a way to have MaNGOS support players’ personal accountings. The target is as clear as MaNGOS’: to create and improve a universal MMORPG server framework. So I will NOT take any responsibility of any actions made by readers with knowledge gained from this article, including figuring out how to apply the described method to some specific game clients.

 
Taiwanese Translation 台文翻譯:

在下載一個使用 GPL 授權的開放原始碼專案之後,你花了一整個周末的時間在安裝程式、調整組態設定,最後才終於把軟體搞定上線供人使用。接著其他人湧入你的系統,啃食著你的網路頻寬、CPU 運算資源、還有記憶體空間,更過分的是他們居然都沒有付你半毛錢!每個月你都要按時繳費給那些電力公司以及網路公司,回頭看看你得到了些甚麼?我想不太出來,也許是來自那些免費使用者的尊敬

既然這樣幹嘛不讓開始幫你提供的服務收費

MaNGOS 是另一個實作「多人連線角色扮演遊戲」的開放原始碼專案,而且還是一個定位自己成具教育與實驗性質的實驗作品。它的名 稱其實是「Massive Network Game Object Server」的縮寫,並且開發團隊不斷地鄭重聲明「MaNGOS 不是任何現存的線上遊戲的模擬器」。如果他們堅持要這樣聲明我是沒有甚麼意見啦,雖然在專案官方網站的服務使用規範當中直接了當排除任何與 Blizzard 有聘僱或合作關係的一切個人和團體對於 MaNGOS 網站的任何內容進行存取行為。其實我真正關心的是:好啊你們是個萬用型的線上遊戲框架,也就是說只是一個單純收發網路封包的空殼程式,那為什麼我們不能拿 它來收費呢?所有原創的線上遊戲公司都在做這件事耶~

在說明如何稍加修改 MaNGOS 的程式碼就以支援付費機制之前,我還是必須先警告你一些事情。使用 MaNGOS 架設公開伺服器是被服務使用規範所禁止的,如果你違反了這項規範,你將不會從開發團隊獲得任何的技術支援。另外,架設任何能夠讓現存而且有版權的線上遊戲客戶端程式使用你的服務的伺服器會讓你被告上法院。所以孩子們千萬不要嘗試在家裡做這種事情喔,在學校也不可以。

在實作這個機制的當中,最核心的問題應該是在於,伺服器該何時送出帳單資訊給客戶端?其實有一件事情一直引起我的興趣,那就是在大部份的線上遊戲當中我們付一次費用,爾後在任意的伺服器上所扣的點數/時間數都是同一個。如果我們可以讓不同伺服器的帳單分開算,那這樣會很有趣,譬如說讓使用者只能玩幾個固定的伺服器,但是讓他付費的時候擁有誘人的折扣,這樣還可以解決伺服器人數不平衡的問題。如果要做到這樣的功能,我們一定要從個別伺服器上發送付費資訊,最好是在個別伺服器登入完成、列出角色清單之間做完這件事。所以我觀察了 mangosd.exe 所產生的 log 檔案,並且發現了伺服器有個 SMSG_AUTH_RESPONSE 訊息非常值得注意,它會通知客戶端有關身分認證的結果以及一些其他的額外資訊。這看似是個很好介入的著力點,因為緊接著這個訊息之後就是客戶端送出列出角色的要求了。所以我 trace 了 MaNGOS 有關於 SMSG_AUTH_RESPONSE 的程式碼,並發現了在 src/game/World.cpp(版本 rev 6767)檔案當中的這段:

void World::AddSession_ (WorldSession* s) {

WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_OK);
packet << uint32 (0); // 未知的隨機數值…
packet << uint8 (0);
packet << uint32 (0);
packet << uint8 (s->Expansion()); // 在資料庫裡必須每個帳號都要確實設定此值
s->SendPacket (&packet);

如你所見,這段程式碼是在處理認證成功後發出的伺服器訊息,其中還帶有了使用者是否有購買資料片的資訊。聰明的你是否注意到了呢?有三個奇怪的參數全被填上零不知道要被用來幹嘛,而且還有一個被特別註解「未知的隨機數值」。好吧,如果是未知而且又是隨機的話,拿它來記錄付費資訊又有甚麼不可以?!我們可以拿一個較長的欄位來存放使用者剩下多少時間,也許還需要另一個較小的欄位來表示使用者購買的是點卡還是月卡,資本主義真的是很有創意。於是我對這段程式稍微做了些改動:

WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_OK);
packet << uint32 (timeLeft);
packet << uint8 (subscription);

packet << uint32 (0);
packet << uint8 (s->Expansion()); // 在資料庫裡必須每個帳號都要確實設定此值
s->SendPacket (&packet);

我加了兩個變數,其中 timeLeft 代表的是使用者還有多少剩餘的時間可以使用伺服器(注意,封包中的欄位數值必須是 little-endian),subsciption 則指出使用者購買的點數型態,他們會是下列的其中一者:

#define SUB_PERIOD 0×00 // 月卡
#define SUB_FREE 0×04 // 免費期間!
#define SUB_POINTCARD 0×10 // 點卡
#define SUB_BOTH 0×20 // 同時儲值了月卡和點卡

現在最核心的問題已經被我們解決了,伺服器可以送出帶有付費資訊的訊息給成功登入的客戶端。剩下來的問題就相對變得簡單很多,像是從資料庫當中讀取點數資訊、還有每隔一段時間去更新資料庫內的點數數值,這些東西對於會寫 C++ 的人來說都是非常容易的,特別是針對 MaNGOS 這種開放原始碼的專案,所以這些工作就留給你來完成吧。這篇文章所敘述的只是一個很單純的概念,可以讓伺服器送出一些額外且具有價值的資訊給客戶端應用程式。由於 MaNGOS 的目標是成為一個泛用型的伺服器框架,我在本文中對於程式碼的修改也因此留下了非常大的延伸空間給其他開發者。基於這個修改,你可以很輕易地加入更多酷炫的功能進伺服器框架當中,像是前文有提到的個別伺服器分開計算使用者剩餘的點數/時間、或是加入個人 IGR 計畫之類的更多付費方案等。

最後我還是要說明一番,這篇文章所呈現的只是諸多可以讓 MaNGOS 支援付費機制的方法之一而已,我的目的和 MaNGOS 的一樣清楚:建立並不斷改良一個泛用型的多人連線遊戲伺服器框架。所以我無法也不會為任何透過這篇文章獲得相關知識的讀者的任何行為所負責像是如果你發現了如何將我的方法應用在某些特定線上遊戲客戶端程式上,這些都不會是我的責任。

Post a Comment

*
*