長文:Minecraft 的多人遊戲是如何發展起來的?(一)

尋夢新聞LINE@每日推播熱門推薦文章,趣聞不漏接❤️

加入LINE好友

文/Tiger Tang

這個問題太想自問自答了,因為這段血淚史完全可以寫成精彩紛呈的長篇小說!作為在 Minecraft 業界打滾多年的人,必須得給大家侃侃背後的故事!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

// Survival Multiplayer 時代(2010)

讓時光回溯到五年前的 8 月 9 日的凌晨。我們的故事主角兼 Minecraft 創始人,Markus ‘Notch’ Persson,正二十四個小時宅在家里,撐著雙眼死瞪電腦螢幕,雙手則 迅速地敲著鍵盤,廢寢忘食地調試著程序。再過一個小時就是 8 月 10 日了,Minecraft 生存多人遊戲(Survival Multiplayer,SMP)正式發布的日子。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

你看 Notch 這妖魅的小眼神

Minecraft SMP 的名字聽上去很高大上,但其實就是一個叫做 minecraft_server.jar 的文件罷了,小巧綠色又便攜。使用方法也非常簡單,雙擊打開,它就會自動在默認端口上設置好一個 Minecraft 的服務器,別人只需憑你的 IP 即可進入。理所當然地,一些基本的命令也包含在其中:/kick 用來踢人,/gamemode 用來從生存轉創造…

現在看來,第一個版本的 SMP 相當簡陋,但玩家們正沉醉於和朋友一起玩生存的樂趣里,再簡陋也讚不絕口。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

SMP 的發布,正是迎合 Minecraft 迅猛上升的用戶註冊量。截至 2010 年 5 月,Minecraft 的付費用戶已經達到兩萬,YouTube 上以 Minecraft 作為關鍵字的視頻日益增長,而此時,這個遊戲還只是在 Alpha 階段!

而 SMP 的出現,更讓 Minecraft 的知名度登上又一巔峰:什麼!?可以和好基友在開放式的 LEGO 世界里生存、探險、搞基(?);還可以開創造起個鬥獸場戰個痛或者堆滿 TNT 然後炸地圖;最給力的是神似編程的紅石系統,直接令 Minecraft 一躍成為遊戲開發工具!哪個 AAA 級遊戲有這麼爽的體驗?!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

即使從五年後看來,SMP 的第一個版本也有相當高的遊戲性

在 SMP 發布僅僅兩個月後,Minecraft 的付費用戶就翻了個 1.5 倍,兩個月就賺了一百多萬!SMP 的巨大成功並沒有讓 Notch 怠慢,沒過多久就向玩家們宣布了 Beta 版本的到來。而 Notch 也正式註冊了 Mojang AB 的商標,為之後發行遊戲鋪路。

// hMod 時代(2010 ~ 2011)

SMP 好玩歸好玩,可是不能在上面裝 mod 這一點讓不少玩家很苦惱。當然了,可以通過反編譯 minecraft_server.jar 修改里面的代碼,比如調整一下玩家的默認速度什麼的,然後每個玩家一走起路來就跑十公里遠,上天入海不是夢。畢竟 Mojang 也沒有做什麼簽名驗證,也沒什麼坑爹的全程聯網驗證(育碧:…),要修改幾個變量然後重新編譯,理論上來講不難啊。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

可行歸可行,問題是修改起來太麻煩:代碼全部被混淆(obfuscated)了!

什麼叫代碼混淆呢?舉個栗子,比如說原本的代碼是這樣的:

private String playerName = "你爸爸"; // 定義玩家名稱
private double health = 20.0D; // 定義玩家血量
private float walkSpeed = 1.2F; // 定義玩家速度
public void chat(String message) { // 定義一個說話的函數
 Server.broadcastMessage(this, message); // 向服務器里的函數傳遞參數
}

沒學過 Java 是不是也很清晰明了?這修改起來還不容易,簡直就是填空嘛,小學生都會。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

問題是在編譯的時候,代碼被 Mojang 事先混淆了,可能到你手里的時候就變成這樣了:

String a = Base64.decodeFromBase64("5L2g54i454i4");
double b = 20.0D;
float c = 1.2F;
public void d(String a) {
 bl.aE(this, a);
}

尼瑪這叫一個狠哪,若是沒有原本的代碼,你看得懂嗎?

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

你或許說,上面這幾行,我也能猜出個大概吧?嗯,b 是血量,因為玩家血量最高就是 20,然後 c 是… bl 是… aE 是…

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

別忙著翻桌,我們再來看看真實個例,下面是 Minecraft 1.8 里面的 aap 類:

public class aap extends um {
 private static final Logger b = ;
 public float a = (float) (Math.random() * 3.141592653589793D * 2.0D);
 private int c;
 private int d;
 private int e = 5;
 private String f;
 private String g;
 public aap(amp paramamp, double paramDouble1, double paramDouble2, double paramDouble3) {
 super(paramamp);
 a(0.25F, 0.25F);
 b(paramDouble1, paramDouble2, paramDouble3);
 this.y = ((float) (Math.random() * 360.0D));
 this.v = ((float) (Math.random() * 0.2000000029802322D - 0.1000000014901161D));
 this.w = 0.2000000029802322D;
 this.x = ((float) (Math.random() * 0.2000000029802322D - 0.1000000014901161D));
 }
 public aap(amp paramamp, double paramDouble1, double paramDouble2, double paramDouble3, aio paramaio) {
 this(paramamp, paramDouble1, paramDouble2, paramDouble3);
 a(paramaio);
 }
 public aap(amp paramamp) {
 super(paramamp);
 a(0.25F, 0.25F);
 a(new aio(apg.a, 0));
 }
 protected boolean q_() {
 return false;
 }
 protected void g() {
 F().a(10, 5);
 }
 public void j() {
 if (k() == null) {
 H();
 return;
 }
 super.j();
 if ((this.d > 0) && (this.d != 32767)) {
 this.d -= 1;
 }
 this.p = this.s;
 this.q = this.t;
 this.r = this.u;
 this.w -= 0.03999999910593033D;
 this.T = j(this.s, (aL().b + aL().e) / 2.0D, this.u);
 d(this.v, this.w, this.x);
 int i = ((int) this.p != (int) this.s) || ((int) this.q != (int) this.t) || ((int) this.r != (int) this.u) ? 1 : 0;
 if ((i != 0) || (this.W % 25 == 0)) {
 if (this.o.p(new dl(this)).c().r() == big.i) {
 this.w = 0.2000000029802322D;
 this.v = ((this.V.nextFloat() - this.V.nextFloat()) * 0.2F);
 this.x = ((this.V.nextFloat() - this.V.nextFloat()) * 0.2F);
 a("random.fizz", 0.4F, 2.0F + this.V.nextFloat() * 0.4F);
 }
 if (!this.o.C) {
 v();
 }
 }
 float f1 = 0.98F;
 if (this.C) {
 f1 = this.o.p(new dl(sr.c(this.s), sr.c(aL().b) - 1, sr.c(this.u))).c().K * 0.98F;
 }
 this.v *= f1;
 this.w *= 0.9800000190734863D;
 this.x *= f1;
 if (this.C) {
 this.w *= -0.5D;
 }
 if (this.c != -32768) {
 this.c += 1;
 }
 if ((!this.o.C) && (this.c >= 6000)) {
 H();
 }
 }
 private void v() {
 for (aap localaap : this.o.a(aap.class, aL().b(0.5D, 0.0D, 0.5D))) {
 a(localaap);
 }
 }
 private boolean a(aap paramaap) {
 if (paramaap == this) {
 return false;
 }
 if ((!paramaap.ad()) || (!ad())) {
 return false;
 }
 aio localaio1 = k();
 aio localaio2 = paramaap.k();
 if ((this.d == 32767) || (paramaap.d == 32767)) {
 return false;
 }
 if ((this.c == -32768) || (paramaap.c == -32768)) {
 return false;
 }
 if (localaio2.b() != localaio1.b()) {
 return false;
 }
 if ((localaio2.n() ^ localaio1.n())) {
 return false;
 }
 if ((localaio2.n()) && (!localaio2.o().equals(localaio1.o()))) {
 return false;
 }
 if (localaio2.b() == null) {
 return false;
 }
 if ((localaio2.b().k()) && (localaio2.i() != localaio1.i())) {
 return false;
 }
 if (localaio2.b < localaio1.b) {
 return paramaap.a(this);
 }
 if (localaio2.b + localaio1.b > localaio2.c()) {
 return false;
 }
 localaio2.b += localaio1.b;
 paramaap.d = Math.max(paramaap.d, this.d);
 paramaap.c = Math.min(paramaap.c, this.c);
 paramaap.a(localaio2);
 H();
 return true;
 }
 public void i() {
 this.c = 4800;
 }
 public boolean T() {
 return this.o.a(aL(), big.h, this);
 }
 protected void f(int paramInt) {
 a(ua.a, paramInt);
 }
 public boolean a(ua paramua, float paramFloat) {
 if (b(paramua)) {
 return false;
 }
 if ((k() != null) && (k().b() == aip.bU) && (paramua.c())) {
 return false;
 }
 X();
 this.e = ((int) (this.e - paramFloat));
 if (this.e <= 0) {
 H();
 }
 return false;
 }
 public void b(eu parameu) {
 parameu.a("Health", (short) (byte) this.e);
 parameu.a("Age", (short) this.c);
 parameu.a("PickupDelay", (short) this.d);
 if (m() != null) {
 parameu.a("Thrower", this.f);
 }
 if (l() != null) {
 parameu.a("Owner", this.g);
 }
 if (k() != null) {
 parameu.a("Item", k().b(new eu()));
 }
 }
 public void a(eu parameu) {
 this.e = (parameu.e("Health") & 0xFF);
 this.c = parameu.e("Age");
 if (parameu.c("PickupDelay")) {
 this.d = parameu.e("PickupDelay");
 }
 if (parameu.c("Owner")) {
 this.g = parameu.j("Owner");
 }
 if (parameu.c("Thrower")) {
 this.f = parameu.j("Thrower");
 }
 eu localeu = parameu.m("Item");
 a(aio.a(localeu));
 if (k() == null) {
 H();
 }
 }
 public void d(adq paramadq) {
 if (this.o.C) {
 return;
 }
 aio localaio = k();
 int i = localaio.b;
 if ((this.d == 0) && ((this.g == null) || (6000 - this.c <= 200) || (this.g.equals(paramadq.b_()))) && (paramadq.bg.a(localaio))) {
 if (localaio.b() == ahw.a(apg.r)) {
 paramadq.b(rl.g);
 }
 if (localaio.b() == ahw.a(apg.s)) {
 paramadq.b(rl.g);
 }
 if (localaio.b() == aip.aA) {
 paramadq.b(rl.t);
 }
 if (localaio.b() == aip.i) {
 paramadq.b(rl.w);
 }
 if (localaio.b() == aip.bq) {
 paramadq.b(rl.A);
 }
 if ((localaio.b() == aip.i) && (m() != null)) {
 adq localadq = this.o.a(m());
 if ((localadq != null) && (localadq != paramadq)) {
 localadq.b(rl.x);
 }
 }
 this.o.a(paramadq, "random.pop", 0.2F, ((this.V.nextFloat() - this.V.nextFloat()) * 0.7F + 1.0F) * 2.0F);
 paramadq.a(this, i);
 if (localaio.b <= 0) {
 H();
 }
 }
 }
 public String b_() {
 if (i_()) {
 return aG();
 }
 return eq.a("item." + k().a());
 }
 public boolean az() {
 return false;
 }
 public void c(int paramInt) {
 super.c(paramInt);
 if (!this.o.C) {
 v();
 }
 }
 public aio k() {
 aio localaio = F().f(10);
 if (localaio == null) {
 if (this.o != null) {
 b.error("Item entity " + D() + " has no item?!");
 }
 return new aio(apg.b);
 }
 return localaio;
 }
 public void a(aio paramaio) {
 F().b(10, paramaio);
 F().h(10);
 }
 public String l() {
 return this.g;
 }
 public void a(String paramString) {
 this.g = paramString;
 }
 public String m() {
 return this.f;
 }
 public void c(String paramString) {
 this.f = paramString;
 }
 public void o() {
 this.d = 10;
 }
 public void p() {
 this.d = 0;
 }
 public void q() {
 this.d = 32767;
 }
 public void a(int paramInt) {
 this.d = paramInt;
 }
 public boolean r() {
 return this.d > 0;
 }
 public void t() {
 this.c = -6000;
 }
 public void u() {
 q();
 this.c = 5999;
 }}

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

能猜得出來算你狠。

於是,雖然 SMP 的第三方修改成為可能,但基本沒有服主會閒的蛋疼去玩這個。除了代碼被混淆之外,由於 Minecraft 長期都是 Notch 一個人開發,所以內部的業務邏輯也寫得很亂,或者說實在太有 Notch 特立獨行的代碼風格了,窩們實在猜不粗來呀!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

Notch 表示:「你丫反編譯我的代碼還瞎逼逼」(設計對白)

不過就是有些人點錯天賦了,就在 SMP 發布後沒多久的 2010 年年底,一位叫 hey0 的大神在自己的個人網站上發布了 hMod。hMod 一出,激起千層浪,眾人紛紛驚呼:民間奇才!

hMod 是個什麼玩意兒?我盡量簡單地解釋一下。以往的 SMP modding 模式(也就是上面提到的,直接修改源代碼),我們畫個流程圖出來:

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

hMod 的原理,就是將那些不可讀的代碼,通過 hey0 君敏銳的觀察能力,「翻譯」成可讀而清晰明了的東西。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

還記得剛才那堆亂七八糟的代碼嗎?有興趣的同學可以自行閱讀「翻譯」過後的代碼。

(「翻譯」這詞實際上並不準確,實際上 hMod 是對 SMP 的半封裝,詳細的技術細節在此略過。)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

這實在太偉大了!要在服務器上加入自己原創的內容,頓時簡單了起來。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

不過如此偉大的 hMod 更新了幾個月,原作者就突然潛水,小道消息是說回老家結婚去了,然後由另一位現已就職 Mojang 的大神 Dinnerbone 繼續填坑。還沒填到一半 Dinnerbone 就不幹了:靠,代碼真亂!於是拉上幾個志同道合的同志一起推翻重做,扛起「翻譯」的任務,Bukkit 計劃就這麼誕生了。

// Bukkit 時代(2011 ~ 2014)

Bukkit 計劃實際上分為兩部分:Bukkit API 和 CraftBukkit。廢話不多說,我們再畫個流程圖:

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

原理和 hMod 是一樣的,但 Bukkit API 寫得更好之餘,最重要的成就就是加入了事件系統,不過這個話題說下去完全可以另起爐灶了,所以咱們暫且跳過。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

好了我知道你們都在吐槽上面的魔法是什麼鬼,那麼我盡量簡單講一講,沒有面向對象編程基礎的同學可以跳過下面這幾段。

Bukkit API 里全部都是抽象的類與方法,打個比方有個方法叫 getOnlinePlayers(),返回當前玩家數量。為什麼要抽象?為什麼我們不直接整合做到(implementation)?比如我發現下面這行代碼就可以返回當前玩家數量,這不搞定了嗎,分兩步幹嘛。

aJ.e();

問題是我們的這行代碼的基礎,是通過破解 Minecraft SMP 的源代碼對吧?更準確的說,是通過破解 Minecraft SMP 當前版本的源代碼作為基礎。而代碼混淆這個過程,是每個版本都會重新進行一次的。上面那行代碼或許在 Minecraft SMP 1.7 能用,但到 1.8,可能就完全報錯了。因為或許在 1.8 里,要獲取當前玩家數量的代碼是這樣的:

b.aX();

所以,在 Bukkit API 的部分里,這個方法是抽象的,留給相應版本的 CraftBukkit 去做到。並且這麼一來,有了抽象的接口作為參考,新版本的 SMP 發布時,Bukkit 團隊也能更方便地更新 CraftBukkit。在這里也順便吐槽一下,常常見到有人說用 Bukkit 開服,其實是錯的 —— Bukkit 里全是抽象的接口而已,開個鬼啊。正確的說法是用 CraftBukkit 開服(其他服務器端另計)。所以下次你見到誰跟你炫耀說「我會用 Bukkit 開服務器你造嗎」,記得高大上的回他一句:「乖,那個叫 CraftBukkit。跟我讀,科阿哇夫特巴可以特。」

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

好了話題扯遠了,那麼有了 Bukkit 能做些什麼呢?能做的事太多了!比如用 Bukkit API 的自定義命令功能,加個叫 /launch 的命令,然後輸入 /launch <誰誰誰> 就將目標玩家噴上天,這無論在原生 Minecraft 里或者 SMP 里都是做不到的!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

效果請參見左下角~ 這些基於 Bukkit API 的小程序被統稱為插件(plugin)

好了,我知道你們又要吐槽了。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

當然不是!這種插件實在太膚淺了,Bukkit API 真正最廣泛的應用是用來開發小遊戲(minigame)。你沒聽錯,在遊戲里開發遊戲!只要有足夠的人力物力,依靠著 Bukkit API,要弄出個 Minecraft 版《無主之地》或者《使命召喚》是絕對可行的!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

國外知名服務器 Hypixel 最近推出的新遊戲 Warlords,武器到裝備的模型都是完全自制的。Warlords 的核心玩法其實就是搶旗,但又加入了武器收集,附魔系統和角色系統等等,目前平均在線玩家 2000+,稱其為小型 PvP 網遊也絕不為過。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

毫不誇張地說,Warlords 甚至要比 Steam 上不少免費的 FPS 好玩;光是收集要素就足夠吸引了!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

另一知名大服 Wynncraft,則主打 RPG 玩法,照搬了當今網路上 MMORPG 的很多元素:饒有趣味的任務,廣闊宏偉的地圖,專門刷經驗升級的地城… 倒也弄得趣味橫生。

我們還是先回到 2011 年,回到 Bukkit 剛剛發展起來的時候吧:那時大眾對 Minecraft 多人遊戲的概念,還只是停留在與好基友一起玩生存的程度。真正將 Bukkit 計劃推向大眾視野的,當屬在 2011 年發展起來的 MCSG(Minecraft Survival Games)服務器。聽過《饑餓遊戲》吧?熟悉里面的設定吧?而 MCSG,就是饑餓遊戲在 Minecraft 的翻版:24 個玩家在開放式的地圖里生存,到處開箱尋找物資,誰生存到最後就贏。SMP 可能也弄得出來,但是想做複雜一點,將箱子的物品完全隨機化,或者將玩家數據保存在 mySQL 數據庫里,又或者加入一堆炫目的技能,那是 SMP 絕不可及的。而以上這一切,利用 Bukkit API,小 case 啦。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

當然 Survival Games 遠遠沒有如今的 Warlords 吸引,但是在當年卻可謂掀起了一陣 Minecraft 潮。在 YouTube 上實況大型服務器里遊戲的實況主越來越多,即使是如今已經超過千萬訂閱的 SkyDoesMinecraft —— 就是那個玩了 Flappy Bird 的小夥 —— 也是玩 Minecraft 發家的(好吧,這個看名字就知道)。

好了,我知道你看到這里有點無聊了。所以下面重點來了!!v(。・ω・。)ィェィ♪

因為也是在 2011 年,一個名為 Buycraft 的東東進入了服主們的視野。Buycraft 是一個 CMS 系統,服務器的玩家可以通過在前端用 Paypal 或者信用卡購買東西,來獲得服務器上的增值服務。用 Minecraft 賺錢不再是夢!

真是爆炸性的大新聞。

……

(超燃 BGM 響起)

這豈止是爆炸性,簡直是歷史性!!!這不正是現在手遊最喜歡加入的課金系統嗎?!!

你嫌你的裝備太差嗎??

你覺得打怪升級太慢嗎???

你羨慕那些滿身神裝的高富帥嗎????

快來買 VIP 會員吧!!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

一個月只需 10 美刀,即可讓你得到最尊貴的享受!!!!!!!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

雙倍金錢!!!

三倍經驗!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

換裝系統!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

寵物陪伴!!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

房間防踢!!!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

非 VIP 說話全是灰色的!!!!買了 VIP 你就算罵髒話我們也幫你加個白色高亮!!!!!!還有高端洋氣上檔次 VIP 字樣的前綴!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

別人死了最多就一句死亡信息!!!!!!!!你死掉我們在你死亡的地點放個七彩煙花!!!!!!!!!讓整個世界都知道你死了!!!!!!!!!!!

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

於是,Minecraft 的多人遊戲到這里已經完全發展起輕工業來了,首先是開發難度低兼成本小,插件還不一定要自己開發,網上現成的一堆,實在不行開價讓別人來做;然後就等著收錢吧,五十美刀一個月的 VIP 照樣有人買!

Bukkit 計劃也從此聲名大噪,越來越多的熱心人士加入了開發行列,每一個更新都是無數服主歡呼的時刻。和 SMP 一樣簡單的開服流程也讓服務器越來越多,保守可能也有幾十萬 —— 甚至有了 Minecraft Server List,Minecraft Servers 這樣的網站,只是列出互聯網上公開的服務器地址,同時暗中通過競價為某些服務器提升排名,一個月就能賺上萬。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

吃驚吧?更恐怖的在後頭呢。剛才提到過的,2012 年迅速崛起的小遊戲服務器 Hypixel,也是通過增值內容付費的方式,賺了個盆滿缽滿!根據我一位認識 Hypixel 開發者的外國朋友的可信消息,高峰期的 Hypixel,日均 30000+ 玩家,一年的淨利潤 $1000000+。除以 12,一個月九萬多美元,也即五十七萬人民幣。

一個月五十七萬。

一個月五十七萬。

一個月五十七萬。

一個月五十七萬。

一個月五十七萬。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

可能有人會質疑以上的數字,那麼我曬曬親身經歷吧:學生黨一枚,有多年編程基礎,閒著沒事也寫寫插件。我曾經在 2013 年 9 月在一家名為 Minecade 屬下的 SkyDoesMinecraft 服務器工作過三個月,月薪一千。2014 年年尾幫 ArkhamNetwork 服務器做過外包項目,六百。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

還有一些零零碎碎的小項目,在此不表。而無論是 Minecade 還是 ArkhamNetwork,甚至還擠不進大服務器的行列。可以想像得見一線服務器的員工們,一個月能賺多少了!

所以說如今 Minecraft 的多人遊戲完全是一條成熟的產業線,服主帶著充足的資金聘請員工,為服務器開發高質量內容吸引玩家;玩家則購買增值內容甚至主動捐款來令服務器盈利。這樣的良性循環在整個遊戲界來講都是很難得的。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

與此同時,單人遊戲的體驗也在穩速提高,1.5 的紅石更新,1.6 的馬匹更新,1.7 的世界觀更新,而勤快追上步伐的 Bukkit 計劃又讓服務器們得以爭先搶後地在 CraftBukkit 新版本發布的第一時間更新服務器上的內容,為的就是吸引人流。

原本就蓬勃的 Minecraft 遊戲界在 2013 年進入了黃金時代。有充足的利潤打底,服務器們開發的新內容一次比一次高質量,Quakecraft、Hide and Seek、Prison、Factions、Arcade 等遊戲模式的名字已經深入民心,玩家們也樂意付錢,於是兩邊和樂融融,近年來遊戲業流行的 Freemium 的模式竟然在他們身上得到最好的做到。Minecraft 在這一年突破 1000 萬銷量,很大程度上要歸功於辛勤的服主們。

黃金時代終究是要過去的。狂喜的人們似乎沒有發現,一朵烏雲已經慢慢逼近……

// 輝煌背後

Bukkit 時代看似輝煌,但實際上有不少隱患出現:

第一是 Bukkit 本身的衰落。2012 年 2 月,Bukkit 的開發團隊(Dinnerbone,EvilSeph,Grum,Tahg)收到來自 Mojang 的 offer,於是欣然應邀加盟 Mojang;作為條件,他們不能再開發 Bukkit,而是負責開發新版本的 SMP 和其他與 Minecraft 有關的工作,比如編寫 Plugin API。

Dinnerbone 和 Grum 這兩位可以說是對整個 Bukkit 計劃貢獻最大的人,反編譯和反混淆由 Grum 全權負責,然後 Dinnerbone 則接過代碼坐在電腦桌前除了上廁所外不停歇地碼上二三十個小時(這就是愛啊 <3),為的就是以最快的速度將新版本的 Bukkit API 和 CraftBukkit 呈現在大眾面前。如今他們走了,雖然有人接班,但是他們都沒有了 Dinnerbone 和 Grum 的那份旁人難以理解的激情,更新對他們來說更像是一份義務而不是責任。這也不能怪他們,但伴之而來的就是 CraftBukkit 的更新越來越慢,當初兩天就能更新完,現在要花上兩個月;而Bukkit 在 1.5 後鮮有再加入新的 API,意思就是上文提到的「翻譯」活越來越少人肯去做,導致許多 SMP 的新功能都無法單純地利用 Bukkit API 做到,必須還得配合之前提到的那種直接修改源代碼的蛋疼方法…

作為過來人,我可以肯定地告訴你們:閱讀 Minecraft 的源代碼太蛋疼了…

第二是收費泛濫。服務器們收費的方式推陳出新,以 Hypixel 為例,VIP 出完了出 VIP+,VIP+ 出完了出 MVP,MVP 出完了再出 MVP+…

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

幾十美金幾十美金地收… 國內一線 MMO都沒這麼貴啊

就算玩家們樂意,他們的家長也不樂意呀!不少熊孩子一個月花了幾千美刀在 Minecraft 上,而家長們又怎會了解 Bukkit 服務器們的商業模式,於是出現了家長們憤怒地在推特上向 Notch 投訴並要求全額退款,否則要將 Mojang 告上法庭的啼笑皆非的情況。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

Mojang 躺著也中槍:關我屁事啊!?

第三是版權問題。CraftBukkit 內置了 Minecraft 反編譯過後的源代碼,無形中已經侵犯了 Mojang 的版權;更搞笑的是,Bukkit 計劃採用的是 GPL 協議!一個開源計劃里卻包含了反編譯過的商業代碼,這一點本身能夠不被大眾口誅筆伐實屬幸運。

Mojang 當然知道 Bukkit 計劃是怎麼回事,不過他們對這些第三方服務器端也就是睜一只眼閉一只眼,只要你不把 Minecraft 重新打包一次就拿出去賣,你改成 Q 塊世界我也不管你。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

我知道你們還沒完全消化這幾段的內容 (o´Д`)=з 但敬請記住這幾個關鍵詞吧:Bukkit 衰落、收費泛濫和版權糾紛。

因為故事要進入高潮階段了。

長文:Minecraft 的多人遊戲是如何發展起來的?(一)

淡定,淡定

(待續……)

About 尋夢園
尋夢園是台灣最大的聊天室及交友社群網站。 致力於發展能夠讓會員們彼此互動、盡情分享自我的平台。 擁有數百間不同的聊天室 ,讓您隨時隨地都能找到志同道合的好友!