通過(guò)Java修改游戲存檔的實(shí)現(xiàn)思路
前言
植物大戰(zhàn)僵尸的數(shù)據(jù)文件是存儲(chǔ)在本地的dat文件當(dāng)中,修改在本地的dat文件就可以修改到游戲中的數(shù)據(jù)。之前使用二進(jìn)制編碼工具Hex Editor Neo實(shí)現(xiàn)了修改植物大戰(zhàn)僵尸的本地游戲數(shù)據(jù),現(xiàn)在嘗試不使用Hex Editor Neo二進(jìn)制工具編輯游戲存檔,使用Java程序來(lái)編輯游戲在本地存儲(chǔ)的數(shù)據(jù)。在經(jīng)歷了幾次失敗以后成功的實(shí)現(xiàn)了在Java程序中修改植物大戰(zhàn)僵尸的本地?cái)?shù)據(jù),在這里將實(shí)現(xiàn)的過(guò)程以及思路和錯(cuò)誤記錄下來(lái),便于以后返回溫習(xí)。
使用Hex Editor Neo修改游戲數(shù)據(jù)博客鏈接:C1任務(wù)01-修改游戲存檔
提示:以下是本篇文章正文內(nèi)容,下面案例可供參考
一、實(shí)現(xiàn)思路
不論是做大型的項(xiàng)目或者只是實(shí)現(xiàn)一個(gè)小的功能,都要先明確實(shí)現(xiàn)的思路,哪一步要做什么要事先明確,不然就會(huì)像無(wú)頭蒼蠅一樣不知所措。
Java版本:JDK1.8
使用工具:IntelliJ IDEA 2021.1.2
項(xiàng)目管理:maven
idea2021最新激活碼:
http://chabaoo.cn/article/196349.htm
https://www.yuque.com/docs/share/b996d27e-c888-45f2-bb1e-f6db5efe2485?#
實(shí)現(xiàn)思路相對(duì)簡(jiǎn)單,因?yàn)橹参锎髴?zhàn)僵尸游戲的數(shù)據(jù)文件存儲(chǔ)在本地的存儲(chǔ)位置是已知的,因此我們可以將實(shí)現(xiàn)過(guò)程拆分為以下三個(gè)步驟:
將.dat數(shù)據(jù)文件抽象為File對(duì)象,使用IO流將數(shù)據(jù)讀取到Java程序當(dāng)中將相應(yīng)位置的數(shù)據(jù)修改為用戶輸入的數(shù)據(jù)最后將Java程序中存儲(chǔ)的數(shù)據(jù)通過(guò)IO流寫回到本地的dat數(shù)據(jù)文件中
這里可以覆蓋回?cái)?shù)據(jù)文件中也可以修改指定位置的數(shù)據(jù),在這里我采用的方法是覆蓋原文件的數(shù)據(jù)。
二、項(xiàng)目準(zhǔn)備
在正式編寫代碼之前要先做一些準(zhǔn)備工作
1. 創(chuàng)建maven工程
因?yàn)楸旧聿⒉恍枰跒g覽器端展示數(shù)據(jù),因此創(chuàng)建一個(gè)空的maven工程即可
到這里一個(gè)maven工程就創(chuàng)建完畢
2. 導(dǎo)入依賴
①. JSON依賴
在這個(gè)Java項(xiàng)目中如果出現(xiàn)異?;蚱渌e(cuò)誤情況,我是以JSON形式輸出到控制臺(tái),因此在這里我導(dǎo)入了阿里巴巴開(kāi)發(fā)的fastjson
依賴
<!-- 導(dǎo)入alibaba的Json依賴 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency>
②. Lombok依賴
導(dǎo)入lombok
依賴的原因是為了減少實(shí)體類中的代碼量,使代碼更簡(jiǎn)潔,可讀性更高
<!-- 導(dǎo)入lombok工具 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency>
③. Junit4單元測(cè)試
在寫代碼時(shí)確保所寫方法沒(méi)有問(wèn)題的一種方式就是使用單元測(cè)試,在這里我導(dǎo)入了Junit4
單元測(cè)試框架
<!-- 導(dǎo)入單元測(cè)試依賴 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency>
至此maven工程中的依賴全部導(dǎo)入完成
三、核心代碼
1. 使用的對(duì)象
在讀取dat數(shù)據(jù)文件中要使用到以下幾個(gè)Java對(duì)象,在此進(jìn)行簡(jiǎn)單的介紹
- InputStream: 該抽象類是所有的類表示字節(jié)輸入流的父類
- FileInputStream:從文件系統(tǒng)中的文件中獲得輸入的字節(jié)
- DataOutputStream:將數(shù)據(jù)寫入到指定的基本輸出流中
2. 讀取數(shù)據(jù)文件
我在讀取數(shù)據(jù)文件時(shí)將文件的存儲(chǔ)路徑定義成了全局變量,便于在每個(gè)方法中進(jìn)行調(diào)用
因?yàn)榇鎯?chǔ)植物大戰(zhàn)僵尸的數(shù)據(jù)文件user1.dat中數(shù)據(jù)是以二進(jìn)制的方式進(jìn)行存儲(chǔ),因此我們?cè)谧x取文件內(nèi)容時(shí)也要使用二進(jìn)制的方式進(jìn)行讀取。
如果使用字符的方式進(jìn)行讀取的話會(huì)出現(xiàn)讀取出的數(shù)據(jù)只有幾個(gè)字符的情況,用記事本打開(kāi)dat文件就會(huì)發(fā)現(xiàn)在二進(jìn)制數(shù)據(jù)文件中的內(nèi)容只有一行并且很多字符都是以空格形式存在的,因此使用字符讀入的方式就只能讀取到一行數(shù)據(jù),并且空格數(shù)據(jù)會(huì)被當(dāng)成null進(jìn)行處理,所以顯示的結(jié)果就只有幾個(gè)字符。
將讀取到的整數(shù)數(shù)據(jù)存儲(chǔ)到泛型約束為Integer
類型的List
集合當(dāng)中,進(jìn)行存儲(chǔ)
/** * 讀取文件內(nèi)容并將讀取到的內(nèi)容以List集合的格式返回 * * @return 數(shù)據(jù)的List集合 */ public static List<Integer> readFile() { try { // 聲明文件對(duì)象 File file = new File(filePath); // 將文件內(nèi)容讀取到文件讀取流當(dāng)中 InputStream in; // 將讀取的流進(jìn)行封裝 in = new FileInputStream(file); // 定義整數(shù)對(duì)象用于存儲(chǔ)讀取到的內(nèi)容 int content; // 一次讀取一行,直到讀入的內(nèi)容為null時(shí)讀取文件的過(guò)程結(jié)束 while ((content = in.read()) != -1) { // 將讀取到的內(nèi)容存儲(chǔ)到List集合中 nums.add(content); } // 關(guān)閉流 in.close(); } catch (Exception e) { e.printStackTrace(); } return nums; }
2. 修改關(guān)卡信息
在之前修改游戲存檔數(shù)據(jù)時(shí)就明白,在user1.dat
文件中,第4列中的十六進(jìn)制內(nèi)容代表著關(guān)卡信息,因此在修改游戲的關(guān)卡信息時(shí)就要指定List
集合中下標(biāo)為4的集合數(shù)據(jù)值為用戶輸入的值。在這里用戶輸入的數(shù)據(jù)雖然是十進(jìn)制的數(shù)據(jù),但是在將數(shù)據(jù)寫入user1.dat
文件時(shí)不需要再進(jìn)行十進(jìn)制到十六進(jìn)制的轉(zhuǎn)換了,因?yàn)?strong>最后在文件中存儲(chǔ)的形式都是二進(jìn)制的0和1的形式進(jìn)行存儲(chǔ)的。
/** * 修改關(guān)卡數(shù)據(jù) * * @param result 要修改的關(guān)卡(十六進(jìn)制) */ public void writeFileCheckPoint(String result) { // 進(jìn)行文件的讀取 List<Integer> dataList = ReadUtil.readFile(); // 將修改關(guān)卡列上的數(shù)據(jù) dataList.set(4, Integer.valueOf(result, 16)); ReadUtil.writeFile(dataList); dataList.removeAll(dataList); System.out.println("關(guān)卡數(shù)據(jù)寫入完成!"); }
將數(shù)據(jù)輸出到數(shù)據(jù)文件的方法
/** * 將文件內(nèi)容寫入到user1.dat文件中,可以進(jìn)行修改關(guān)卡和修改金幣數(shù)量 * * @param dataList 傳來(lái)的整型數(shù)組 */ public static void writeFile(List<Integer> dataList) { // 聲明要輸出到的文件對(duì)象 File file = new File(filePath); try { // 定義數(shù)據(jù)輸出流 DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); // 遍歷傳來(lái)的List集合 for (Integer integer : dataList) { // 將List集合中的數(shù)據(jù)寫入到user1.dat文件中 out.write(integer); // 刷新輸出流 out.flush(); } // 關(guān)閉輸出流 out.close(); } catch (Exception e) { e.printStackTrace(); } }
3. 修改金幣信息
在這里還要注意到,雖然第八列和第九列的內(nèi)容代表著金幣信息,但是在這里的第九列的數(shù)據(jù)為高位,并不是按照慣性思維從第八列開(kāi)始依次排列,因此在存儲(chǔ)金幣信息時(shí)要進(jìn)行單獨(dú)的處理。
具體的處理方法就是,將十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù)時(shí)如果轉(zhuǎn)換后的十六進(jìn)制數(shù)的長(zhǎng)度為3位(在Java中十進(jìn)制數(shù)轉(zhuǎn)換為十六進(jìn)制數(shù)時(shí),十六進(jìn)制數(shù)是以String類型進(jìn)行存儲(chǔ)和顯示的),則在轉(zhuǎn)換后的字符串的起始位置加0,這樣做的原因是要進(jìn)行截取兩位,讓高位的數(shù)據(jù)在高位存儲(chǔ),低位的數(shù)據(jù)在低位存儲(chǔ)。
例如:
十進(jìn)制數(shù)據(jù)轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù)的轉(zhuǎn)換方法如下:
/** * 將十進(jìn)制整數(shù)轉(zhuǎn)換為16進(jìn)制的字符串 * * @param num 傳來(lái)的十進(jìn)制整數(shù) * @return 轉(zhuǎn)換為16進(jìn)制后的字符串 */ public static String intToHex(int num) { // 如果傳來(lái)的整數(shù)為0則直接返回 if (num == 0) { return "0"; } // 使用到StringBuilder效率會(huì)更高 StringBuilder builder = new StringBuilder(); // 定義16進(jìn)制下的所有數(shù)字 char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; // 如果傳來(lái)的數(shù)不為0則一直進(jìn)行除法運(yùn)算 while (num != 0) { builder = builder.append(hexChar[num % 16]); num = num / 16; } if (builder.length() == 1 || builder.length() % 2 != 0) { return "0" + builder.reverse(); } // 最后將builder反轉(zhuǎn)并返回 return builder.reverse().toString(); }
修改金幣數(shù)量的方法:
/** * 修改金幣數(shù)據(jù) * * @param result 要修改的金幣數(shù)量(十六進(jìn)制) */ public void writeFileMoney(String result) { // 進(jìn)行文件的讀取 List<Integer> dataList = ReadUtil.readFile(); // 將傳來(lái)的字符長(zhǎng)度進(jìn)行除2運(yùn)算 int count = result.length() >> 1; if (count > 1) { // 以兩位為單位長(zhǎng)度進(jìn)行截取,一共有兩個(gè)數(shù)據(jù) String firstStr = result.substring(0, 2); // 低位數(shù)據(jù) String secondStr = result.substring(2, 4); // 高位數(shù)據(jù) // 設(shè)置低位的數(shù)據(jù) dataList.set(8, Integer.valueOf(secondStr, 16)); // 設(shè)置高位的數(shù)據(jù) dataList.set(9, Integer.valueOf(firstStr, 16)); // 將修改后的金幣數(shù)據(jù)數(shù)據(jù)寫入文件 ReadUtil.writeFile(dataList); System.out.println("金幣數(shù)據(jù)寫入完成!"); // 清空集合中的數(shù)據(jù) dataList.removeAll(dataList); } else { // 將修改關(guān)卡列上的數(shù)據(jù) dataList.set(8, Integer.valueOf(result, 16)); // 當(dāng)進(jìn)入這里時(shí)第九位一定為0,當(dāng)從很多的金幣修改到很少的金幣時(shí)要確保第九位為0 dataList.set(9, 0); // 將修改后的整型List集合寫入到dat文件中 ReadUtil.writeFile(dataList); System.out.println("金幣數(shù)據(jù)寫入完成!"); // 清空集合中的數(shù)據(jù) dataList.removeAll(dataList); } }
四、代碼測(cè)試
接下來(lái)對(duì)所寫的Java項(xiàng)目進(jìn)行測(cè)試,首先是一個(gè)金幣為0且關(guān)卡數(shù)為0的空白存檔,在修改文件時(shí)先將植物大戰(zhàn)僵尸關(guān)閉,因?yàn)?strong>在修改數(shù)據(jù)文件時(shí)如果植物大戰(zhàn)僵尸游戲開(kāi)著,雖然將數(shù)據(jù)文件的內(nèi)容做了修改,但是在關(guān)閉植物大戰(zhàn)僵尸后,游戲仍然會(huì)將當(dāng)前游戲內(nèi)的數(shù)據(jù)信息覆蓋到dat文件中,因此就相當(dāng)于沒(méi)有進(jìn)行任何修改。
現(xiàn)在關(guān)閉植物大戰(zhàn)僵尸游戲并且在IntelliJ IDEA中將主類啟動(dòng)
1. 讀取數(shù)據(jù)文件
首先讀取數(shù)據(jù)文件,查看第4列的數(shù)據(jù)是否為01(默認(rèn)第一關(guān))與第8列的數(shù)據(jù)是否為0(默認(rèn)金幣為0)
讀取到的數(shù)據(jù)文件內(nèi)容正確
2. 修改關(guān)卡位置
現(xiàn)在將關(guān)卡修改到第42關(guān),即5-2關(guān)
再讀取數(shù)據(jù)文件,查看第四列的值是否為2a
現(xiàn)在進(jìn)入到游戲中查看關(guān)卡是否改變
關(guān)卡的數(shù)據(jù)和我們修改的內(nèi)容一樣,現(xiàn)在再查看商店中的金幣數(shù)量是否為0
金幣數(shù)量也為0,說(shuō)明只修改了關(guān)卡信息
3. 修改金幣數(shù)量
此時(shí)進(jìn)行修改金幣的數(shù)量
再讀取數(shù)據(jù)文件,查看第八列的數(shù)據(jù)是否為e8,第九列的數(shù)據(jù)是否為03
進(jìn)入游戲中查看金幣是否發(fā)生了變化
此時(shí)金幣數(shù)量修改為了10000
4. 退出修改器
5. 輸入?yún)?shù)錯(cuò)誤情況
在項(xiàng)目啟動(dòng)時(shí)輸入的內(nèi)容不是修改器的功能選項(xiàng)時(shí)
關(guān)卡位置和金幣數(shù)量越界情況
成功以JSON格式輸出,至此Java項(xiàng)目測(cè)試完畢
五、源碼
1. 項(xiàng)目結(jié)構(gòu)
2. 項(xiàng)目代碼
①. ResultInfo類,位于pojo包
package com.shijimo.game.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * @author Dream_飛翔 * @date 2021/10/26 * @time 18:32 * @email 1072876976@qq.com * * 輸出提示信息 */ @Data @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor public class ResultInfo { private Integer code; // 狀態(tài)碼 private String msg; // 提示信息 }
②. EditorService類,位于service包
package com.shijimo.game.service; import com.shijimo.game.util.ReadUtil; import java.util.List; /** * @author Dream_飛翔 * @date 2021/10/26 * @time 18:29 * @email 1072876976@qq.com */ public class EditorService { /** * 修改關(guān)卡數(shù)據(jù) * * @param result 要修改的關(guān)卡(十六進(jìn)制) */ public void writeFileCheckPoint(String result) { // 進(jìn)行文件的讀取 List<Integer> dataList = ReadUtil.readFile(); // 將修改關(guān)卡列上的數(shù)據(jù) dataList.set(4, Integer.valueOf(result, 16)); ReadUtil.writeFile(dataList); dataList.removeAll(dataList); System.out.println("關(guān)卡數(shù)據(jù)寫入完成!"); } /** * 修改金幣數(shù)據(jù) * * @param result 要修改的金幣數(shù)量(十六進(jìn)制) */ public void writeFileMoney(String result) { // 進(jìn)行文件的讀取 List<Integer> dataList = ReadUtil.readFile(); // 將傳來(lái)的字符長(zhǎng)度進(jìn)行除2運(yùn)算 int count = result.length() >> 1; if (count > 1) { // 以兩位為單位長(zhǎng)度進(jìn)行截取,一共有兩個(gè)數(shù)據(jù) String firstStr = result.substring(0, 2); String secondStr = result.substring(2, 4); dataList.set(8, Integer.valueOf(secondStr, 16)); dataList.set(9, Integer.valueOf(firstStr, 16)); // 將修改后的金幣數(shù)據(jù)數(shù)據(jù)寫入文件 ReadUtil.writeFile(dataList); System.out.println("金幣數(shù)據(jù)寫入完成!"); dataList.removeAll(dataList); } else { // 將修改關(guān)卡列上的數(shù)據(jù) dataList.set(8, Integer.valueOf(result, 16)); // 當(dāng)進(jìn)入這里時(shí)第九位一定為0,如果從高位改到低位的話要確保第九位為0 dataList.set(9, 0); ReadUtil.writeFile(dataList); System.out.println("金幣數(shù)據(jù)寫入完成!"); dataList.removeAll(dataList); } } }
③. NumUtil類,位于util包
package com.shijimo.game.util; /** * @author Dream_飛翔 * @date 2021/10/26 * @time 16:32 * @email 1072876976@qq.com * <p> * 本類用于將整數(shù)進(jìn)行進(jìn)制的轉(zhuǎn)換 */ public class NumUtil { /** * 將十進(jìn)制整數(shù)轉(zhuǎn)換為16進(jìn)制的字符串 * * @param num 傳來(lái)的整數(shù) * @return 轉(zhuǎn)換為16進(jìn)制后的字符串 */ public static String intToHex(int num) { // 如果傳來(lái)的整數(shù)為0則直接返回 if (num == 0) { return "0"; } // 使用到StringBuilder效率會(huì)更高 StringBuilder builder = new StringBuilder(); // 定義16進(jìn)制下的所有數(shù)字 char[] hexChar = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; // 如果傳來(lái)的數(shù)不為0則一直進(jìn)行除法運(yùn)算 while (num != 0) { builder = builder.append(hexChar[num % 16]); num = num / 16; } if (builder.length() == 1 || builder.length() % 2 != 0) { return "0" + builder.reverse(); } // else if (builder.length() >= 1 && builder.length() % 2 != 0) { // return builder.reverse() + "0"; // } // 最后將builder反轉(zhuǎn)并返回 return builder.reverse().toString(); } }
⑥. ReadUtil類,位于util包
package com.shijimo.game.util; import java.io.*; import java.util.ArrayList; import java.util.List; /** * @author Dream_飛翔 * @date 2021/10/26 * @time 10:11 * @email 1072876976@qq.com * <p> * 該類用于讀取二進(jìn)制文件中的數(shù)據(jù),植物大戰(zhàn)僵尸的本地?cái)?shù)據(jù)文件中只有一行數(shù)據(jù) * 1. 通過(guò)InputStream進(jìn)行二進(jìn)制方式讀取 * 2. 對(duì)每一行的內(nèi)容進(jìn)行特殊的處理 */ public class ReadUtil { // 定義文件的路徑 static String filePath = "C:\\ProgramData\\PopCap Games\\PlantsVsZombies\\userdata\\user1.dat"; // 定義整型集合類用于存儲(chǔ) static List<Integer> nums = new ArrayList<>(); /** * 讀取文件內(nèi)容并將讀取到的內(nèi)容以List集合的格式返回 * * @return 數(shù)據(jù)的List集合 */ public static List<Integer> readFile() { try { // 聲明文件對(duì)象 File file = new File(filePath); // 將文件內(nèi)容讀取到文件讀取流當(dāng)中 InputStream in; // 將讀取的流進(jìn)行封裝 in = new FileInputStream(file); // 定義整數(shù)對(duì)象用于存儲(chǔ)讀取到的內(nèi)容 int content; // 一次讀取一行,直到讀入的內(nèi)容為null時(shí)讀取文件的過(guò)程結(jié)束 while ((content = in.read()) != -1) { // 將讀取到的內(nèi)容存儲(chǔ)到List集合中 nums.add(content); } // 關(guān)閉流 in.close(); } catch (Exception e) { e.printStackTrace(); } return nums; } /** * 將文件內(nèi)容寫入到user1.dat文件中,可以進(jìn)行修改關(guān)卡和修改金幣數(shù)量 * * @param dataList 傳來(lái)的整型數(shù)組 */ public static void writeFile(List<Integer> dataList) { // 聲明要輸出到的文件對(duì)象 File file = new File(filePath); try { // 定義數(shù)據(jù)輸出流 DataOutputStream out = new DataOutputStream(new FileOutputStream(file)); // 遍歷傳來(lái)的List集合 for (Integer integer : dataList) { // 將List集合中的數(shù)據(jù)寫入到user1.dat文件中 out.write(integer); // 刷新輸出流 out.flush(); } // 刷新輸出流 out.flush(); // 關(guān)閉輸出流 out.close(); } catch (Exception e) { e.printStackTrace(); } } /** * 將指定的文件內(nèi)容輸出到控制臺(tái)上 */ public static void printFile() { // 定義整型集合類用于存儲(chǔ) List<Integer> dataList = new ArrayList<>(); try { // 聲明文件對(duì)象 File file = new File(filePath); // 將文件內(nèi)容讀取到文件讀取流當(dāng)中 InputStream in; // 將讀取的流進(jìn)行封裝 in = new FileInputStream(file); // 定義整數(shù)對(duì)象用于存儲(chǔ)讀取到的內(nèi)容 int content; // 一次讀取一行,直到讀入的內(nèi)容為null時(shí)讀取文件的過(guò)程結(jié)束 while ((content = in.read()) != -1) { // 將讀取到的內(nèi)容存儲(chǔ)到List集合中 dataList.add(content); } // 關(guān)閉流 in.close(); } catch (Exception e) { e.printStackTrace(); } // 定義標(biāo)志變量的初值為0 int count = 0; // 將讀取到的內(nèi)容輸出 System.out.println("00\t01\t02\t03\t04\t05\t06\t07\t08\t09\t0a\t0b\t0c\t0d\t0e\t0f"); // 遍歷讀取到的整數(shù)集合 for (Integer data : dataList) { // 如果標(biāo)志變量的值對(duì)16做取余運(yùn)算為0的話則進(jìn)行換行操作 if (count % 16 == 0) System.out.println(); System.out.print(NumUtil.intToHex(data) + "\t"); count++; } } }
⑦. EditorApplication主啟動(dòng)類,位于項(xiàng)目的最外層包中
package com.shijimo.game; import com.alibaba.fastjson.JSONObject; import com.shijimo.game.pojo.ResultInfo; import com.shijimo.game.service.EditorService; import com.shijimo.game.util.NumUtil; import com.shijimo.game.util.ReadUtil; import java.util.Scanner; /** * @author Dream_飛翔 * @date 2021/10/26 * @time 17:01 * @email 1072876976@qq.com */ public class EditorApplication { public static void main(String[] args) { // 將業(yè)務(wù)處理對(duì)象實(shí)例化 EditorService editorService = new EditorService(); System.out.println("**********************************************************"); System.out.println(" ,---._ \n" + " .-- -.' \\ \n" + " | | : \n" + " : ; | \n" + " : | .---. \n" + " | : : ,--.--. /. ./| ,--.--. \n" + " : / \\ .-' . ' | / \\ \n" + " | ; | .--. .-. | /___/ \\: | .--. .-. | \n" + " ___ l \\__\\/: . . . \\ ' . \\__\\/: . . \n" + " / /\\ J : ,\" .--.; | \\ \\ ' ,\" .--.; | \n" + "/ ../ `..- , / / ,. | \\ \\ / / ,. | \n" + "\\ \\ ; ; : .' \\ \\ \\ | ; : .' \\ \n" + " \\ \\ ,' | , .-./ '---\" | , .-./ \n" + " \"---....--' `--`---' `--`---' "); System.out.println("\n 老張寫的植物大戰(zhàn)僵尸修改器 version: 1.0"); while (true) { System.out.println("**********************************************************"); System.out.println("* =======> 1. 修改關(guān)卡位置 <======= *"); System.out.println("* =======> 2. 修改金幣數(shù)量 <======= *"); System.out.println("* =======> 3. 讀取數(shù)據(jù)文件 <======= *"); System.out.println("* =======> 4. 退出此修改器 <======= *"); System.out.println("**********************************************************"); System.out.print("請(qǐng)輸入您的選擇:"); // 定義Scanner對(duì)象用于接收從鍵盤上輸入的數(shù)字 Scanner scanner = new Scanner(System.in); int choose = scanner.nextInt(); switch (choose) { case 1: { Scanner editor = new Scanner(System.in); System.out.println("您的選擇是 => 選擇修改關(guān)卡的位置"); System.out.print("請(qǐng)輸入您想要跳到的關(guān)卡位置(最高50關(guān)):"); // 接收關(guān)卡的位置數(shù)據(jù) int checkPoint = editor.nextInt(); // 進(jìn)行越界判斷 if (checkPoint <= 0 || checkPoint > 50) { System.out.println(JSONObject.toJSONString(new ResultInfo(500, "輸入數(shù)據(jù)有誤"))); break; } // 將提示信息輸出 System.out.println("正在修改關(guān)卡數(shù)據(jù)..."); // 如果輸入的數(shù)據(jù)合法,將其轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù) String result = NumUtil.intToHex(checkPoint); // 調(diào)用業(yè)務(wù)層的方法進(jìn)行修改內(nèi)容 editorService.writeFileCheckPoint(result); System.out.println("關(guān)卡數(shù)據(jù)修改成功!"); } break; case 2: { System.out.println("您的選擇是 => 修改游戲的金幣數(shù)量"); System.out.print("請(qǐng)輸入您想要修改的金幣數(shù)量(最高655350個(gè)):"); // 聲明輸入對(duì)象 Scanner editor = new Scanner(System.in); // 接收輸入的金幣數(shù)量 int money = editor.nextInt(); // 進(jìn)行越界判斷 if (money > 655350 || money < 0) { System.out.println(JSONObject.toJSONString(new ResultInfo(500, "輸入數(shù)據(jù)有誤"))); break; } // 如果輸入的數(shù)據(jù)合法,將其轉(zhuǎn)換為十六進(jìn)制數(shù)據(jù) String result = NumUtil.intToHex(money / 10); // 調(diào)用業(yè)務(wù)層的方法進(jìn)行修改數(shù)據(jù)并返回修改結(jié)果 editorService.writeFileMoney(result); // 如果金幣數(shù)量修改成功 System.out.println("金幣數(shù)據(jù)修改成功!"); } break; case 3: { System.out.println("您的選擇是 => 讀取游戲的數(shù)據(jù)文件"); System.out.println("開(kāi)始讀取數(shù)據(jù)文件..."); // 讀取數(shù)據(jù)文件并打印 ReadUtil.printFile(); // 換行 System.out.println(); // 輸出提示信息 System.out.println("數(shù)據(jù)文件讀取成功!"); } break; case 4: { System.out.println("感謝您的使用,期待下次再見(jiàn)!"); System.exit(0); } default: System.out.println(JSONObject.toJSONString(new ResultInfo(500, "輸入指令有誤"))); } } } }
⑧. pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>GameEditor</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- 導(dǎo)入alibaba的Json依賴 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency> <!-- 導(dǎo)入lombok工具 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <!-- 導(dǎo)入單元測(cè)試依賴 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> </dependency> </dependencies> </project>
總結(jié)
以上便是使用Java修改植物大戰(zhàn)僵尸數(shù)據(jù)文件的過(guò)程,還記得當(dāng)時(shí)在使用工具去修改游戲存檔數(shù)據(jù)時(shí)都感覺(jué)到不可思議與不敢相信!而此時(shí)通過(guò)Java代碼進(jìn)行修改文件數(shù)據(jù)時(shí)都沒(méi)有一絲的感覺(jué)自己做不成功,對(duì)于自己來(lái)說(shuō),不僅僅是技術(shù)上的提高,更重要的是心態(tài)上的變化。當(dāng)自己獨(dú)立寫過(guò)一定數(shù)量的代碼時(shí)內(nèi)心就會(huì)變得很有底氣,變得更加相信自己,可能就像很多人說(shuō)的那樣,真正的技術(shù)一定是相當(dāng)數(shù)量的代碼堆疊起來(lái)的!
自然界沒(méi)有風(fēng)風(fēng)雨雨,大地就不會(huì)春華秋實(shí)。若不嘗試著做些本事之外的事,就永遠(yuǎn)不會(huì)成長(zhǎng)!人生的價(jià)值并不在于成功后的榮光,而在于追求的本身,在于信念的樹(shù)立與堅(jiān)持的過(guò)程。堅(jiān)守信念,猶如在內(nèi)心撒下一顆種子,只要在適宜的條件下,種子自會(huì)生根發(fā)芽破土而出,總會(huì)有收獲果實(shí)的期望。有時(shí)需要外力輔助才可取得成果,但最終還要靠自我去完成,因?yàn)槿魏稳艘膊豢赡馨研拍钌钪灿谀愕男闹?。所以,我們要?jiān)守自我的信念,播下期望的種子。做一名自信者,牢牢把住自我生命的羅盤!
到此這篇關(guān)于通過(guò)Java修改游戲存檔的文章就介紹到這了,更多相關(guān)Java修改游戲存檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring boot 實(shí)現(xiàn)配置多個(gè)DispatcherServlet最簡(jiǎn)單方式
這篇文章主要介紹了spring boot 實(shí)現(xiàn)配置多個(gè)DispatcherServlet最簡(jiǎn)單方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01SpringBoot中Mybatis注解一對(duì)多和多對(duì)多查詢實(shí)現(xiàn)示例
這篇文章主要介紹了SpringBoot中Mybatis注解一對(duì)多和多對(duì)多查詢的實(shí)現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程
這篇文章主要介紹了從零搭建Spring Boot腳手架整合OSS作為文件服務(wù)器的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08ThreadPoolExecutor線程池原理及其execute方法(詳解)
下面小編就為大家?guī)?lái)一篇ThreadPoolExecutor線程池原理及其execute方法(詳解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06Java實(shí)現(xiàn)excel大數(shù)據(jù)量導(dǎo)入
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)excel大數(shù)據(jù)量導(dǎo)入,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-08-08Java獲取Prometheus監(jiān)控?cái)?shù)據(jù)的方法實(shí)現(xiàn)
本文主要介紹了Java獲取Prometheus監(jiān)控?cái)?shù)據(jù)的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12Idea工具中創(chuàng)建 SpringBoot工程及入門詳解
這篇文章主要介紹了Idea工具中創(chuàng)建 SpringBoot工程及入門分析詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-02-02