【乾貨】《我的世界》Mod開發教程13:網絡


3樓貓 發佈時間:2024-12-20 04:42:41 作者:甄別 Language

【乾貨】《我的世界》Mod開發教程13:網絡-第0張

本期教程我們將探討網絡通信。網絡用於服務器與客戶端之間的數據同步。有時,我們無法直接從遊戲中獲取數據,比如上次提到的覆蓋層(overlay)。覆蓋層的參數沒有直接獲取遊戲內相關信息的接口。這些數據與遊戲邏輯無關,因為覆蓋層類是遊戲客戶端的屬性,數據存儲在服務器端,因此無法直接獲取。

上次我們想到了一個解決辦法,即使用靜態變量保存數據,在數據發生更改時更新。雖然我沒有詳細描述,但提到了每次數據更改時都要更新。這樣,我們可以直接使用靜態變量,但會引發一個問題:如果連接的是服務器,數據仍存儲在服務器上,而客戶端不運行邏輯,因此數據始終為零。這導致覆蓋層在服務器環境下始終顯示為零。要解決這個問題,需要通過網絡將服務器端數據同步到客戶端。

【乾貨】《我的世界》Mod開發教程13:網絡-第1張

在此之前,我們還注意到 Parchment MC 和 New Forge 需要更新。Parchment MC 的地圖版本是 8 月 13 日版,New Forge 的版本是 47.1.70。我們可以順便更新一下,操作起來不會太麻煩。將地圖版本改為 8.13,將 Forge 版本改為 47.1.70,然後加載 GRADLE 進行更改,出現“build successful”提示後,更新完成。

更新完成後,我們來實現網絡通信。首先創建一個軟件包,然後創建一個 Java 類,命名為 Channel。稍後我們會繼續編寫該類。接著創建另一個軟件包,命名為 packet.paci,其中創建一個 Java 類,命名為 FarmXP,用於同步服務端數據到客戶端。

【乾貨】《我的世界》Mod開發教程13:網絡-第2張

首先,在packet包下創建一個新的軟件包,命名為"server to player"。然後將 FarmXPPacket.java 文件拖拽到該包中。

現在,我們需要自己實現一些方法來處理這個packet。首先,我們需要定義 packet 中要使用的參數。由於我們只需要一個參數,即農場經驗,因此我們可以定義一個私有的 final int 變量,命名為 farmXP。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

接下來,我們創建構造函數。這裡我們需要兩個方法,首先是解碼器。我們可以直接編寫一個構造函數,命名為 public FarmXPPacket,參數為一個 friendly bad Buff。這個 Buff 是 Minecraft 提供的一種專門用於網絡傳輸的工具,裡面包含了許多現成可用的方法。我們可以直接使用這些方法,例如,我們可以寫 farmXP = Buff.re() 來讀取數據。除了我們目前需要的 read 方法外,如果我們想傳輸更復雜的數據,Buff 中還有其他有用的方法,比如 readBlogPost 和 readNBT 等。但在這裡,我們只需使用 read 方法即可。

接下來,我們需要一個編碼器。我們編寫一個名為 public void encode 的方法,參數同樣是一個 friendly bad Buff,但這次我們需要將數據寫入 Buff 中。我們使用 Buff.right 方法來寫入 int 類型的 farmXP。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

這兩個方法編寫完成後,我們還需要一個處理數據的方法。我們編寫一個名為 public void handle 的方法,參數為一個 supplier,其泛型為 network event 的 context。在這個方法中,我們可以對數據包進行處理。首先,我們可以通過 context.getPlayer() 來獲取玩家對象。然後,context 中還包含了一些其他有用的方法,比如 getPlayer()。但由於我們是從服務端發送數據包,因此服務端只有一個,所以我們不需要使用 getPlayer() 方法。

另外,還有一點需要注意的是,這個網絡系統是在另一個線程上運行的。它和MINECRAFT客戶端或邏輯並不在同一個線程上。是的,它是多線程的。因此,我們不能直接與遊戲的某些信息進行交互。但是,許多人都有這樣的需求。因此,我們提供了一個名為"in cool work"的解決方案。通過傳入一個RUNNABLE,在下一個遊戲主線程的時刻執行。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

比如,"Player fx p provider"執行"點XPCLINT等於放XP",然後調用"context點set pack handled",最後直接返回true。因為在這裡,我們沒有複雜的邏輯,所以不需要判斷操作是否成功。如果這種簡單邏輯失敗的可能性較小。當然,如果你的邏輯可能出錯,就需要使用try-catch進行包裹,或者定義一些判斷條件。最後,如果失敗,就返回false。handle部分就完成了。

現在,我們需要在channel這一部分進行操作。我們可以看到文檔裡提供了許多關於network的內容。我們直接複製這些內容,並導入。對於"resource location",我們直接使用我們自己的mod地。因此,我們將其命名為"FMXP"。接下來,我們使用public static void register方法。我們需要註冊我們的頻道,所以我們必須對頻道進行初始化,包括註冊我們的packet。我們使用instance來註冊消息。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

這裡有一些參數,比如Message、Encoder、Decoder、Consumer和network direction。但是,這種寫法可讀性較差,有時候會忘記參數的順序。因此,我們可以使用另一種方法,即使用message builder。使用message builder可以增強可讀性。首先,第一個參數是我們的firm xp packet的class。然後,我們在這裡定義一個id,讓其初始值為零。每次使用時,id都會自增,這樣就不會重複。

第三個參數是網絡方向,lay to clint。然後是decoder,即我們之前寫的構造函數。我們將其實例化為cos函數。編碼器是我們之前編寫的。最後,點consumer,這裡使用了之前講過的"my sweet network sweet"。還有一些自動生成的方法可供使用,比如method network three和farm x p take it handle。最後,點擊ADD即可完成註冊。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

下面是我們自定義的方法。在這裡,我們繼續編寫public static void sendToPlayer方法。關於參數,我們可以看到這個instance,在這個方法中我們可以發送任何內容。因此,我們可以直接仿照這個方法編寫。這個方法中的message是在這個位置定義的,所以我們也在這裡定義一個message參數。接著我們需要另一個參數,即要發送給誰。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

因此我們需要添加一個player參數。在這裡,我們可以根據文檔上的packet Distribute,點選player,然後將message發送給對應的玩家。對於farm xp overlay,我們可以在這裡添加player、FarmXPProvider,然後點選XPCLIENT,將文字改為farm xp:。回到我們的farmXP類,我們需要在XP發生更改時更新這個數值。我們可以找一個合適的時機,在這個時機同步這個數據。如果找不到我們的這個event listener,我們可以在使用XP的時候進行同步。因為在使用之後就會發生更改。

在這裡,我們可以編寫channel.sendToPlayer(newBMXPitXP, player),然後從event中獲取玩家並轉換成服務器玩家。當然,在這裡可能會遇到一些潛在的問題。同時,player clone也需要同步。我們可以直接複製這個內容。同樣地,在玩家加入服務器時也需要同步這個數據。可以使用PlayerLoginEvent,我們之前應該已經寫過了。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

完成了這些工作之後,我們需要將其註冊進去。在FMLCommonSetupEvent中,調用channel.register方法註冊。完成這一系列步驟後,我們進行測試。由於這次測試涉及到網絡,所以我們需要運行一個服務器。服務器啟動完成後,我們運行lint,然後在多人遊戲中連接到本地主機。現在我們的XP是零。點擊右鍵後,程序崩潰了。但不用擔心,這是意料之中的。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

在這一段中,我們要探討一個重要的知識點,即如何根據錯誤報告來識別問題。當我們在處理代碼時,有時會遇到類似於 "net.minecraft"、"clean"、"player"、"local there" 這樣的提示信息。這些信息表明在客戶端和服務端都存在一些差異。在客戶端,"player" 實際上是指本地玩家,而在服務端,它指的是服務器上的玩家。因此,有些代碼只能在服務端執行,而在客戶端則不應該執行。如果錯誤的代碼在客戶端運行,程序就會崩潰。我們可以通過一些方法來確定當前是在哪個端,比如使用 "level.isClientSide" 或者 "Dist.isClient" 這樣的方法。

【乾貨】《我的世界》Mod開發教程13:網絡-第3張

一般來說,我們優先使用前兩種方法,而最後一種方法只是在前兩種方法無法使用時才考慮。在代碼中,我們可以這樣寫:如果事件發生在客戶端,那麼就不執行特定的代碼,只有在服務端才執行。同樣的邏輯也適用於其他情況。通過這樣的方式,我們可以確保代碼在不同端的正確執行。在本段的最後,我們還介紹瞭如何在獲取經驗時同步數據,以及如何在客戶端和服務端之間發送數據的方法。那麼本期教程就到此結束。


© 2022 3樓貓 下載APP 站點地圖 廣告合作:asmrly666@gmail.com