Minecraft模組系列寫到這裡,對我自己來說也是一個挑戰 (我玩Minecraft的資歷非常淺);有很多功能都是一邊玩,一邊寫;如果發現了有趣的功能,就自己嘗試動手做看看。
但我們今天先停下來,重新審視一下現有程式碼,當功能越來越多的時候,把現有程式精簡化也是很重要的一環。
這裡的重新開始不會叫你把現有程式都砍光光的...別緊張。
我們來回顧一下到目前為止做了哪些功能:
功能很多,我們在接下來開發新的功能之前,要先回過頭來看一下我們的目標:設計遊戲
那麼,我們現在的程式碼有什麼樣的問題呢?
if (event.entity.worldObj.isRemote)
之類的方式來限制執行事件的只能是客戶端或伺服器端,但這有點不直覺且麻煩。在Forge模組開發有一個叫做Proxy
的功能可以幫助我們把這些繁雜的工作完成。接下來,我們就動工吧?
別緊張,讓我們先了解什麼是Proxy
...之前,不知道會不會有人跟我一樣有一個疑問:到底Minecraft Forge在啟動遊戲的時候是怎麼知道要從哪裡開始執行,以及是如何執行的?
Run
執行的訊息視窗看到類似下面的訊息:
...
[Client thread/INFO] [FML]: Found 0 mods from the command line. Injecting into mod discoverer
[Client thread/INFO] [FML]: Searching D:\minecraft_forge\myproject\run\mods for mods
[Client thread/INFO] [myFancyMods]: Mod myFancyMods is missing the required element 'name'. Substituting myFancyMods
...
這裡Forge是透過java的一種程式碼映射(Reflection)的功能,這個功能可以在不知道名稱的情況下,透過指定的條件(例如我們的標註@Mod
)來去尋找所有滿足條件的介面、類別、方法等。@Mod
找到主程式後,Forge接下來會去找尋是否有Proxy(下面會說)的定義存在;如果有,會透過Proxy
去初始化並設定必要的變數。@EventHandler
標註的方法來建立所有在啟動前需要完成的事情,這個標註會有三個階段分別完成不同的事:
那讓我們來了解一下到底什麼是Proxy:
在Minecraft設計裡,可以同時有多名玩家一起遊玩。所以很明顯的,玩家端會有畫面的產生、控制滑鼠與鍵盤的動作等等,就是客戶端(Client);而伺服器端(Server)就是負責世界的產生、處理不同事件等等。
我們在開發時雖然只有一台電腦,但他其實同時扮演著Client與Server的角色。
Client與Server都有自己專屬的程式碼,但他們可以執行相同的方法(這也是為什麼我們在前面幾天會需要透過IF
判斷的方式來處理不同端的請求)。
這個時候,Forge聽到開發者的心聲,Proxy就是用來幫忙開發者方便分辨誰要處理Client與Server。
而使用的方法,我想你已經猜到了,就是使用標註:@SideProxy
。
注意:以下我們要做的事情是重構程式碼,假如你是直接從這一篇開始看的人,請自行參考對應的[DayXXX]
在[Day3]我們建立了一個主程式Main.java檔案,在Forge的模組開發中,一般都會習慣將主程式命名與套件名稱相同。因此請在com.ithome.mymod
這個套件下建立一個新的Class名為MyMod
,並將Main這個類別內的@Mod
標註拿掉,這樣之後啟動就不會找到這個模組了:
原本的Main.java檔案內容,把@Mod加上註解。
// 透過註釋@Mod告訴Forge這個是模組的主要檔案
//@Mod(modid = Main.MODID, version = Main.VERSION)
public class Main {
...(以下略)
打開MyMod
檔案,跟[Day3]一樣,定義Modid
、name
與version
:
package com.ithome.mymod;
import com.ithome.mymod.configs.Constants;
import net.minecraftforge.fml.common.Mod;
@Mod(modid = Constants.MOD_ID, name = Constants.MOD_NAME, version = Constants.VERSION)
public class MyMod {
}
建立一個新的Constaints
檔案在com.ithome.mymod.configs
套件下,這個檔案是用來定義所有會用到的字串(包含我們前一個步驟的modid
, name
, version
)。
package com.ithome.mymod.configs;
public class Constants {
// Mod info
public static final String MOD_ID = "mymod";
public static final String MOD_NAME = "My Mod";
public static final String VERSION = "1.0";
}
注意這裡我們使用小寫來定義模組ID,不像Main
這個檔案有用大小寫。理由我在[Day14]的註冊方塊一節有說明過了。
接著把Proxy
功能加上。記得我們在前面說過,要將不同功能的類別分類,比較好的方式就是建立獨立的目錄。我們先在MyMod
檔案內,加上@SideProxy
標註與proxy變數:
package com.ithome.mymod;
import com.ithome.mymod.configs.Constants;
import com.ithome.mymod.proxy.IProxy;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.SidedProxy;
@Mod(modid = Constants.MOD_ID, name = Constants.MOD_NAME, version = Constants.VERSION)
public class MyMod {
@SidedProxy(serverSide = Constants.SERVER_PROXY, clientSide = Constants.CLIENT_PROXY)
public static IProxy proxy;
}
然後把Client與Server的類別名稱完整字串放入到Constants
類別中:
package com.ithome.mymod.configs;
public class Constants {
// Mod info
public static final String MOD_ID = "mymod";
public static final String MOD_NAME = "My Mod";
public static final String VERSION = "1.0";
// Proxy
public static final String CLIENT_PROXY = "com.ithome.mymod.proxy.ClientProxy";
public static final String SERVER_PROXY = "com.ithome.mymod.proxy.ServerProxy";
}
最後再建立三個檔案:
IProxy.java
package com.ithome.mymod.proxy;
public interface IProxy {
}
ClientProxy.java
package com.ithome.mymod.proxy;
public class ClientProxy implements IProxy {
}
ServerProxy.java
package com.ithome.mymod.proxy;
public class ServerProxy implements IProxy {
}
存檔進入遊戲,如同[Day3]一樣,現在你應該要看到我們新的模組mymod
出現了!