四、代码测试
接下来对所写的Java项目进行测试,首先是一个金币为0且关卡数为0的空白存档,在修改文件时先将植物大战僵尸关闭,因为在修改数据文件时如果植物大战僵尸游戏开着,虽然将数据文件的内容做了修改,但是在关闭植物大战僵尸后,游戏仍然会将当前游戏内的数据信息覆盖到dat文件中,因此就相当于没有进行任何修改。
现在关闭植物大战僵尸游戏并且在IntelliJ IDEA中将主类启动
1. 读取数据文件
首先读取数据文件,查看第4列的数据是否为01(默认第一关)与第8列的数据是否为0(默认金币为0)
读取到的数据文件内容正确
2. 修改关卡位置
现在将关卡修改到第42关,即5-2关
再读取数据文件,查看第四列的值是否为2a
现在进入到游戏中查看关卡是否改变
关卡的数据和我们修改的内容一样,现在再查看商店中的金币数量是否为0
金币数量也为0,说明只修改了关卡信息
3. 修改金币数量
此时进行修改金币的数量
再读取数据文件,查看第八列的数据是否为e8,第九列的数据是否为03
进入游戏中查看金币是否发生了变化
此时金币数量修改为了10000
4. 退出修改器
5. 输入参数错误情况
在项目启动时输入的内容不是修改器的功能选项时
关卡位置和金币数量越界情况
成功以JSON格式输出,至此Java项目测试完毕
五、源码
1. 项目结构
2. 项目代码
①. ResultInfo类,位于pojo包
packagecom.shijimo.game.pojo; importlombok.AllArgsConstructor; importlombok.Data; importlombok.NoArgsConstructor; importlombok.experimental.Accessors; /*** @author Dream_飞翔* @date 2021/10/26* @time 18:32* @email 1072876976@qq.com** 输出提示信息*/chain=true) (publicclassResultInfo { privateIntegercode; // 状态码privateStringmsg; // 提示信息}
②. EditorService类,位于service包
packagecom.shijimo.game.service; importcom.shijimo.game.util.ReadUtil; importjava.util.List; /*** @author Dream_飞翔* @date 2021/10/26* @time 18:29* @email 1072876976@qq.com*/publicclassEditorService { /*** 修改关卡数据** @param result 要修改的关卡(十六进制)*/publicvoidwriteFileCheckPoint(Stringresult) { // 进行文件的读取List<Integer>dataList=ReadUtil.readFile(); // 将修改关卡列上的数据dataList.set(4, Integer.valueOf(result, 16)); ReadUtil.writeFile(dataList); dataList.removeAll(dataList); System.out.println("关卡数据写入完成!"); } /*** 修改金币数据** @param result 要修改的金币数量(十六进制)*/publicvoidwriteFileMoney(Stringresult) { // 进行文件的读取List<Integer>dataList=ReadUtil.readFile(); // 将传来的字符长度进行除2运算intcount=result.length() >>1; if (count>1) { // 以两位为单位长度进行截取,一共有两个数据StringfirstStr=result.substring(0, 2); StringsecondStr=result.substring(2, 4); dataList.set(8, Integer.valueOf(secondStr, 16)); dataList.set(9, Integer.valueOf(firstStr, 16)); // 将修改后的金币数据数据写入文件ReadUtil.writeFile(dataList); System.out.println("金币数据写入完成!"); dataList.removeAll(dataList); } else { // 将修改关卡列上的数据dataList.set(8, Integer.valueOf(result, 16)); // 当进入这里时第九位一定为0,如果从高位改到低位的话要确保第九位为0dataList.set(9, 0); ReadUtil.writeFile(dataList); System.out.println("金币数据写入完成!"); dataList.removeAll(dataList); } } }
③. NumUtil类,位于util包
packagecom.shijimo.game.util; /*** @author Dream_飞翔* @date 2021/10/26* @time 16:32* @email 1072876976@qq.com* <p>* 本类用于将整数进行进制的转换*/publicclassNumUtil { /*** 将十进制整数转换为16进制的字符串** @param num 传来的整数* @return 转换为16进制后的字符串*/publicstaticStringintToHex(intnum) { // 如果传来的整数为0则直接返回if (num==0) { return"0"; } // 使用到StringBuilder效率会更高StringBuilderbuilder=newStringBuilder(); // 定义16进制下的所有数字char[] hexChar= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; // 如果传来的数不为0则一直进行除法运算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反转并返回returnbuilder.reverse().toString(); } }
⑥. ReadUtil类,位于util包
packagecom.shijimo.game.util; importjava.io.*; importjava.util.ArrayList; importjava.util.List; /*** @author Dream_飞翔* @date 2021/10/26* @time 10:11* @email 1072876976@qq.com* <p>* 该类用于读取二进制文件中的数据,植物大战僵尸的本地数据文件中只有一行数据* 1. 通过InputStream进行二进制方式读取* 2. 对每一行的内容进行特殊的处理*/publicclassReadUtil { // 定义文件的路径staticStringfilePath="C:\\ProgramData\\PopCap Games\\PlantsVsZombies\\userdata\\user1.dat"; // 定义整型集合类用于存储staticList<Integer>nums=newArrayList<>(); /*** 读取文件内容并将读取到的内容以List集合的格式返回** @return 数据的List集合*/publicstaticList<Integer>readFile() { try { // 声明文件对象Filefile=newFile(filePath); // 将文件内容读取到文件读取流当中InputStreamin; // 将读取的流进行封装in=newFileInputStream(file); // 定义整数对象用于存储读取到的内容intcontent; // 一次读取一行,直到读入的内容为null时读取文件的过程结束while ((content=in.read()) !=-1) { // 将读取到的内容存储到List集合中nums.add(content); } // 关闭流in.close(); } catch (Exceptione) { e.printStackTrace(); } returnnums; } /*** 将文件内容写入到user1.dat文件中,可以进行修改关卡和修改金币数量** @param dataList 传来的整型数组*/publicstaticvoidwriteFile(List<Integer>dataList) { // 声明要输出到的文件对象Filefile=newFile(filePath); try { // 定义数据输出流DataOutputStreamout=newDataOutputStream(newFileOutputStream(file)); // 遍历传来的List集合for (Integerinteger : dataList) { // 将List集合中的数据写入到user1.dat文件中out.write(integer); // 刷新输出流out.flush(); } // 刷新输出流out.flush(); // 关闭输出流out.close(); } catch (Exceptione) { e.printStackTrace(); } } /*** 将指定的文件内容输出到控制台上*/publicstaticvoidprintFile() { // 定义整型集合类用于存储List<Integer>dataList=newArrayList<>(); try { // 声明文件对象Filefile=newFile(filePath); // 将文件内容读取到文件读取流当中InputStreamin; // 将读取的流进行封装in=newFileInputStream(file); // 定义整数对象用于存储读取到的内容intcontent; // 一次读取一行,直到读入的内容为null时读取文件的过程结束while ((content=in.read()) !=-1) { // 将读取到的内容存储到List集合中dataList.add(content); } // 关闭流in.close(); } catch (Exceptione) { e.printStackTrace(); } // 定义标志变量的初值为0intcount=0; // 将读取到的内容输出System.out.println("00\t01\t02\t03\t04\t05\t06\t07\t08\t09\t0a\t0b\t0c\t0d\t0e\t0f"); // 遍历读取到的整数集合for (Integerdata : dataList) { // 如果标志变量的值对16做取余运算为0的话则进行换行操作if (count%16==0) System.out.println(); System.out.print(NumUtil.intToHex(data) +"\t"); count++; } } }
⑦. EditorApplication主启动类,位于项目的最外层包中
packagecom.shijimo.game; importcom.alibaba.fastjson.JSONObject; importcom.shijimo.game.pojo.ResultInfo; importcom.shijimo.game.service.EditorService; importcom.shijimo.game.util.NumUtil; importcom.shijimo.game.util.ReadUtil; importjava.util.Scanner; /*** @author Dream_飞翔* @date 2021/10/26* @time 17:01* @email 1072876976@qq.com*/publicclassEditorApplication { publicstaticvoidmain(String[] args) { // 将业务处理对象实例化EditorServiceeditorService=newEditorService(); 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 老张写的植物大战僵尸修改器 version: 1.0"); while (true) { System.out.println("**********************************************************"); System.out.println("* =======> 1. 修改关卡位置 <======= *"); System.out.println("* =======> 2. 修改金币数量 <======= *"); System.out.println("* =======> 3. 读取数据文件 <======= *"); System.out.println("* =======> 4. 退出此修改器 <======= *"); System.out.println("**********************************************************"); System.out.print("请输入您的选择:"); // 定义Scanner对象用于接收从键盘上输入的数字Scannerscanner=newScanner(System.in); intchoose=scanner.nextInt(); switch (choose) { case1: { Scannereditor=newScanner(System.in); System.out.println("您的选择是 => 选择修改关卡的位置"); System.out.print("请输入您想要跳到的关卡位置(最高50关):"); // 接收关卡的位置数据intcheckPoint=editor.nextInt(); // 进行越界判断if (checkPoint<=0||checkPoint>50) { System.out.println(JSONObject.toJSONString(newResultInfo(500, "输入数据有误"))); break; } // 将提示信息输出System.out.println("正在修改关卡数据..."); // 如果输入的数据合法,将其转换为十六进制数据Stringresult=NumUtil.intToHex(checkPoint); // 调用业务层的方法进行修改内容editorService.writeFileCheckPoint(result); System.out.println("关卡数据修改成功!"); } break; case2: { System.out.println("您的选择是 => 修改游戏的金币数量"); System.out.print("请输入您想要修改的金币数量(最高655350个):"); // 声明输入对象Scannereditor=newScanner(System.in); // 接收输入的金币数量intmoney=editor.nextInt(); // 进行越界判断if (money>655350||money<0) { System.out.println(JSONObject.toJSONString(newResultInfo(500, "输入数据有误"))); break; } // 如果输入的数据合法,将其转换为十六进制数据Stringresult=NumUtil.intToHex(money/10); // 调用业务层的方法进行修改数据并返回修改结果editorService.writeFileMoney(result); // 如果金币数量修改成功System.out.println("金币数据修改成功!"); } break; case3: { System.out.println("您的选择是 => 读取游戏的数据文件"); System.out.println("开始读取数据文件..."); // 读取数据文件并打印ReadUtil.printFile(); // 换行System.out.println(); // 输出提示信息System.out.println("数据文件读取成功!"); } break; case4: { System.out.println("感谢您的使用,期待下次再见!"); System.exit(0); } default: System.out.println(JSONObject.toJSONString(newResultInfo(500, "输入指令有误"))); } } } }
⑧. pom.xml文件
<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="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><!--导入alibaba的Json依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency><!--导入lombok工具--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><!--导入单元测试依赖--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version></dependency></dependencies></project>
总结
以上便是使用Java修改植物大战僵尸数据文件的过程,还记得当时在使用工具去修改游戏存档数据时都感觉到不可思议与不敢相信!而此时通过Java代码进行修改文件数据时都没有一丝的感觉自己做不成功,对于自己来说,不仅仅是技术上的提高,更重要的是心态上的变化。当自己独立写过一定数量的代码时内心就会变得很有底气,变得更加相信自己,可能就像很多人说的那样,真正的技术一定是相当数量的代码堆叠起来的!
自然界没有风风雨雨,大地就不会春华秋实。若不尝试着做些本事之外的事,就永远不会成长!人生的价值并不在于成功后的荣光,而在于追求的本身,在于信念的树立与坚持的过程。坚守信念,犹如在内心撒下一颗种子,只要在适宜的条件下,种子自会生根发芽破土而出,总会有收获果实的期望。有时需要外力辅助才可取得成果,但最终还要靠自我去完成,因为任何人也不可能把信念深植于你的心中。所以,我们要坚守自我的信念,播下期望的种子。做一名自信者,牢牢把住自我生命的罗盘!