├── .github └── workflows │ └── build.yaml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── pom.xml ├── src ├── main │ ├── java │ │ └── com │ │ │ └── dnf │ │ │ ├── DnfApplication.java │ │ │ ├── GuiApplication.java │ │ │ ├── config │ │ │ └── AppConfig.java │ │ │ ├── constant │ │ │ └── IniConstant.java │ │ │ ├── driver │ │ │ ├── ReadWriteMemory.java │ │ │ ├── api │ │ │ │ └── ApiMemory.java │ │ │ ├── ltq │ │ │ │ ├── IoCode.java │ │ │ │ ├── LtqMemory.java │ │ │ │ └── ReadWrite.java │ │ │ └── tan │ │ │ │ └── TanMemory.java │ │ │ ├── entity │ │ │ ├── CoordinateType.java │ │ │ ├── GameMapType.java │ │ │ ├── GlobalData.java │ │ │ ├── MapDataType.java │ │ │ ├── MapNodeType.java │ │ │ └── MapTraversalType.java │ │ │ ├── game │ │ │ ├── Address.java │ │ │ ├── AutoThread.java │ │ │ ├── Base.java │ │ │ ├── GameCall.java │ │ │ ├── GameMap.java │ │ │ ├── Initialize.java │ │ │ ├── MapData.java │ │ │ ├── Screen.java │ │ │ ├── SendPack.java │ │ │ ├── Task.java │ │ │ └── Traverse.java │ │ │ └── helper │ │ │ ├── Button.java │ │ │ ├── Bytes.java │ │ │ ├── FileUtils.java │ │ │ ├── IniUtils.java │ │ │ ├── NumberUtils.java │ │ │ ├── Process.java │ │ │ ├── Strings.java │ │ │ └── Timer.java │ └── resources │ │ ├── application.yml │ │ ├── helper.ini │ │ └── qq.png └── test │ └── java │ └── com │ └── dnf │ ├── BufferTest.java │ ├── ReadWriteTest.java │ └── helper │ └── ButtonTest.java └── start.bat /.github/workflows/build.yaml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python 3 | 4 | name: Java application 5 | 6 | on: 7 | push: 8 | branches: 9 | - "master" 10 | tags: 11 | - 'v*' 12 | pull_request: 13 | branches: 14 | - "master" 15 | 16 | jobs: 17 | build: 18 | 19 | runs-on: windows-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: Set up JDK 17 25 | uses: actions/setup-java@v3 26 | with: 27 | java-version: '17' 28 | distribution: 'temurin' 29 | 30 | - name: Build with Maven 31 | run: mvn clean compile package 32 | 33 | - name: Upload Release 34 | uses: softprops/action-gh-release@v1 35 | if: startsWith(github.ref, 'refs/tags/') 36 | with: 37 | token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} 38 | files: | 39 | target/*.jar 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | .idea 40 | !src/main/resources/helper.ini 41 | helper.ini -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | qiuapeng921 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 如何启动 2 | 3 | ```markdown 4 | 1. 下载并安装jdk17 5 | https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_windows-x64_bin.zip) 6 | 2. 下载maven https://dlcdn.apache.org/maven/maven-3/3.9.5/binaries/apache-maven-3.9.5-bin.zip 7 | 3. 配置好jdk环境变量和maven环境变量 8 | 4. git clone https://mirror.ghproxy.com/https://github.com/SunnyEmbrace/DnfHelper-Java.git 9 | 5. maven package 10 | 4. 自己找破图标驱动或者进qq群下载破图标驱动,启动图标驱动 11 | 5. 复制src/main/resources/helper.ini 到target目录下 12 | 6. *** 管理员方式运行 java -jar target/Dnfhelper-Java-1.0.0.jar *** 13 | 7. 功能热键 F1.全屏打怪 End.自动刷图 14 | ``` 15 | ### 点击链接加入群聊【毒奶粉研究院】:https://t.me/+D3V4dfGWE_JjYzU1 16 | 17 | ## Stargazers over time 18 | 19 | [![Stargazers over time](https://starchart.cc/qiuapeng921/DnfHelper-Java.svg)](https://starchart.cc/SunnyEmbrace/DnfHelper-Java) 20 | 21 | 22 | ## 免责声明 23 | 24 | 程序是免费开源的产品,仅用于学习交流使用! 25 | 不可用于任何违反`中华人民共和国(含台湾省)`或`使用者所在地区`法律法规的用途。 26 | 因为作者即本人仅完成代码的开发和开源活动`(开源即任何人都可以下载使用)`,从未参与用户的任何运营和盈利活动。 27 | 且不知晓用户后续将`程序源代码`用于何种用途,故用户使用过程中所带来的任何法律责任即由用户自己承担。 28 | 29 | ## JetBrains 支持的项目 30 | 31 | 非常感谢 Jetbrains 友好地为我提供了一个许可,让我可以从事这个项目和其他开源项目。 32 | 33 | [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue) 34 | 35 | ## License 36 | 37 | MIT 38 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | |---------|--------------------| 10 | | 5.1.x | :white_check_mark: | 11 | | 5.0.x | :x: | 12 | | 4.0.x | :white_check_mark: | 13 | | < 4.0 | :x: | 14 | 15 | ## Reporting a Vulnerability 16 | 17 | Use this section to tell people how to report a vulnerability. 18 | 19 | Tell them where to go, how often they can expect to get an update on a 20 | reported vulnerability, what to expect if the vulnerability is accepted or 21 | declined, etc. 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.springframework.boot 8 | spring-boot-starter-parent 9 | 3.0.10 10 | 11 | 12 | 13 | Dnfhelper-Java 14 | 1.0.0 15 | 16 | 17 | 17 18 | 17 19 | 3.0.10 20 | 2.4.5 21 | 1.7.36 22 | 5.8.21 23 | 5.13.0 24 | 0.5.4 25 | 2.2 26 | 27 | 28 | 29 | 30 | 31 | io.github.lunasaw 32 | luna-common 33 | ${luna-common.version} 34 | 35 | 36 | 37 | org.slf4j 38 | slf4j-simple 39 | ${slf4j-simple.version} 40 | 41 | 42 | cn.hutool 43 | hutool-all 44 | ${hutool.version} 45 | 46 | 47 | net.java.dev.jna 48 | jna 49 | ${jna.version} 50 | 51 | 52 | org.ini4j 53 | ini4j 54 | ${ini4j.version} 55 | 56 | 57 | org.yaml 58 | snakeyaml 59 | ${snakeyaml.version} 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter 68 | 69 | 70 | org.yaml 71 | snakeyaml 72 | 73 | 74 | 75 | 76 | org.yaml 77 | snakeyaml 78 | 79 | 80 | org.springframework.boot 81 | spring-boot-starter-test 82 | test 83 | 84 | 85 | org.projectlombok 86 | lombok 87 | true 88 | 89 | 90 | 91 | 92 | org.slf4j 93 | slf4j-simple 94 | 95 | 96 | 97 | io.github.lunasaw 98 | luna-common 99 | 100 | 101 | 102 | 103 | net.java.dev.jna 104 | jna 105 | 106 | 107 | 108 | 109 | cn.hutool 110 | hutool-all 111 | 112 | 113 | 114 | 115 | org.ini4j 116 | ini4j 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.springframework.boot 125 | spring-boot-maven-plugin 126 | 127 | 128 | 129 | org.projectlombok 130 | lombok 131 | 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/DnfApplication.java: -------------------------------------------------------------------------------- 1 | package com.dnf; 2 | 3 | import com.dnf.game.Initialize; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.boot.Banner; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | import org.springframework.context.ConfigurableApplicationContext; 9 | 10 | /** 11 | * @author 情歌 12 | */ 13 | @SpringBootApplication 14 | @Slf4j 15 | public class DnfApplication { 16 | public static void main(String[] args) { 17 | SpringApplication application = new SpringApplication(DnfApplication.class); 18 | // 禁止打印banner 19 | application.setBannerMode(Banner.Mode.OFF); 20 | 21 | ConfigurableApplicationContext applicationContext = application.run(args); 22 | 23 | // 注册关闭钩子 24 | Runtime.getRuntime().addShutdownHook(new Thread(() -> log.info("服务关闭"))); 25 | 26 | // 从容器获取实例对象 27 | Initialize initialize = applicationContext.getBean(Initialize.class); 28 | initialize.init(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/GuiApplication.java: -------------------------------------------------------------------------------- 1 | package com.dnf; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.Banner; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.boot.SpringApplication; 7 | import org.springframework.boot.autoconfigure.SpringBootApplication; 8 | 9 | import java.awt.*; 10 | import java.awt.event.ActionEvent; 11 | import java.awt.event.ActionListener; 12 | import java.awt.event.WindowAdapter; 13 | import java.awt.event.WindowEvent; 14 | 15 | //@SpringBootApplication 16 | //@Slf4j 17 | public class GuiApplication implements CommandLineRunner { 18 | public static void main(String[] args) { 19 | SpringApplication application = new SpringApplication(GuiApplication.class); 20 | // 禁止打印banner 21 | application.setBannerMode(Banner.Mode.OFF); 22 | application.run(args); 23 | } 24 | 25 | public void run(String... args) { 26 | // 设置 DISPLAY 环境变量 27 | System.setProperty("java.awt.headless", "false"); 28 | 29 | EventQueue.invokeLater(() -> { 30 | Frame frame = new Frame("贪吃蛇"); 31 | 32 | //设置布局 33 | frame.setLayout(null); 34 | //坐标 35 | frame.setBounds(0, 0, 300, 400); 36 | frame.setBackground(new Color(0x00281F)); 37 | 38 | //创建面板 39 | Panel panel = new Panel(); 40 | //panel设置坐标,相对于frame 41 | panel.setBounds(50, 30, 200, 300); 42 | panel.setBackground(new Color(0x480050)); 43 | 44 | Button button = new Button(); 45 | button.setLabel("click"); 46 | 47 | //构造一个ActionListener,去满足addActionListener()监听事件的需求 48 | MyActionListener listener = new MyActionListener(); 49 | button.addActionListener(listener); 50 | 51 | panel.add(button); 52 | 53 | //窗口添加画板 54 | frame.add(panel); 55 | frame.setVisible(true); 56 | 57 | //监听关闭窗口事件 58 | frame.addWindowListener(new WindowAdapter() { 59 | //关闭窗口 60 | @Override 61 | public void windowClosing(WindowEvent e) { 62 | System.out.println("windowClosing"); 63 | System.exit(0); 64 | } 65 | }); 66 | }); 67 | } 68 | } 69 | 70 | class MyActionListener implements ActionListener { 71 | 72 | @Override 73 | public void actionPerformed(ActionEvent e) { 74 | System.out.println("aaa"); 75 | } 76 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/config/AppConfig.java: -------------------------------------------------------------------------------- 1 | package com.dnf.config; 2 | 3 | import com.dnf.constant.IniConstant; 4 | import com.dnf.driver.ReadWriteMemory; 5 | import com.dnf.driver.api.ApiMemory; 6 | import com.dnf.helper.IniUtils; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | /** 11 | * @author 情歌 12 | */ 13 | @Configuration 14 | public class AppConfig { 15 | /** 16 | * 初始化读写类 17 | * 18 | * @return ReadWriteMemory 19 | */ 20 | @Bean 21 | public ReadWriteMemory readWrite() { 22 | return new ApiMemory(); 23 | } 24 | 25 | /** 26 | * 辅助配置文件初始化 27 | * 28 | * @return IniUtils 29 | */ 30 | @Bean 31 | public IniUtils iniUtils() { 32 | IniUtils iniUtils = new IniUtils(); 33 | iniUtils.setFilename(IniConstant.HELPER); 34 | return iniUtils; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/constant/IniConstant.java: -------------------------------------------------------------------------------- 1 | package com.dnf.constant; 2 | 3 | /** 4 | * @author 情歌 5 | */ 6 | public class IniConstant { 7 | 8 | public static final String CONFIG = "C:\\config.ini"; 9 | 10 | public static final String HELPER = "helper.ini"; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/ReadWriteMemory.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author 情歌 7 | */ 8 | public interface ReadWriteMemory { 9 | void setProcessId(int processId); 10 | 11 | long allocate(int size); 12 | 13 | boolean freed(int address); 14 | 15 | int[] readByte(long address, int size); 16 | 17 | short readShort(long address); 18 | 19 | int readInt(long address); 20 | 21 | default long readOffset(long address, List offset) { 22 | long tmpAddr = address; 23 | if (offset.isEmpty()) { 24 | return 0; 25 | } 26 | tmpAddr = readLong(tmpAddr); 27 | 28 | for (int i = 1; i < offset.size(); i++) { 29 | tmpAddr = tmpAddr + offset.get(i); 30 | tmpAddr = readLong(tmpAddr); 31 | } 32 | 33 | return tmpAddr; 34 | } 35 | 36 | long readLong(long address); 37 | 38 | float readFloat(long address); 39 | 40 | double readDouble(long address); 41 | 42 | boolean writeByte(long address, int[] data); 43 | 44 | boolean writeShort(long address, short value); 45 | 46 | boolean writeInt(long address, int value); 47 | 48 | boolean writeLong(long address, long value); 49 | 50 | boolean writeFloat(long address, float value); 51 | 52 | boolean writeDouble(long address, double value); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/api/ApiMemory.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver.api; 2 | 3 | import com.dnf.driver.ReadWriteMemory; 4 | import com.sun.jna.Memory; 5 | import com.sun.jna.Pointer; 6 | import com.sun.jna.platform.win32.BaseTSD; 7 | import com.sun.jna.platform.win32.Kernel32; 8 | import com.sun.jna.platform.win32.WinNT; 9 | import com.sun.jna.ptr.IntByReference; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | 13 | /** 14 | * @author 情歌 15 | */ 16 | public class ApiMemory implements ReadWriteMemory { 17 | final Logger logger = LoggerFactory.getLogger(ApiMemory.class.getName()); 18 | private final Kernel32 kernel32; 19 | private int processId; 20 | 21 | public ApiMemory() { 22 | kernel32 = Kernel32.INSTANCE; 23 | } 24 | 25 | @Override 26 | public void setProcessId(int processId) { 27 | this.processId = processId; 28 | } 29 | 30 | public WinNT.HANDLE openProcess() { 31 | WinNT.HANDLE handle = kernel32.OpenProcess(Kernel32.PROCESS_ALL_ACCESS, false, processId); 32 | if (handle == null) { 33 | logger.error("openProcess pid = {} , error = {}", processId, kernel32.GetLastError()); 34 | return null; 35 | } 36 | 37 | return handle; 38 | } 39 | 40 | @Override 41 | public long allocate(int size) { 42 | WinNT.HANDLE handle = openProcess(); 43 | if (handle == null) { 44 | return 0; 45 | } 46 | 47 | // 分配内存 48 | int allocationType = Kernel32.MEM_COMMIT | Kernel32.MEM_RESERVE; 49 | int protect = Kernel32.PAGE_EXECUTE_READWRITE; 50 | Pointer pointer = Kernel32.INSTANCE.VirtualAllocEx(handle, null, new BaseTSD.SIZE_T(size), allocationType, protect); 51 | return Pointer.nativeValue(pointer); 52 | } 53 | 54 | @Override 55 | public boolean freed(int address) { 56 | WinNT.HANDLE handle = openProcess(); 57 | if (handle == null) { 58 | return false; 59 | } 60 | return kernel32.VirtualFreeEx(handle, new Pointer(address), new BaseTSD.SIZE_T(0), WinNT.MEM_RELEASE); 61 | } 62 | 63 | private Memory readMemory(long address, int size) { 64 | WinNT.HANDLE handle = openProcess(); 65 | if (handle == null) { 66 | return null; 67 | } 68 | 69 | try { 70 | // 创建缓冲区,并将数据放入其中 71 | IntByReference bytesWritten = new IntByReference(); 72 | Memory buffer = new Memory(size); 73 | boolean result = kernel32.ReadProcessMemory(handle, new Pointer(address), buffer, size, bytesWritten); 74 | if (!result) { 75 | throw new Exception(String.format("%d", kernel32.GetLastError())); 76 | } 77 | return buffer; 78 | } catch (Exception e) { 79 | logger.error("readByteMemory address = {} , error = {}", address, e.getStackTrace()); 80 | return null; 81 | } finally { 82 | kernel32.CloseHandle(handle); 83 | } 84 | } 85 | 86 | @Override 87 | public int[] readByte(long address, int size) { 88 | Memory memory = readMemory(address, size); 89 | if (memory == null) { 90 | return null; 91 | } 92 | int[] memoryValues = new int[size]; 93 | for (int i = 0; i < size; i++) { 94 | memoryValues[i] = Byte.toUnsignedInt(memory.getByte(i)); 95 | } 96 | return memoryValues; 97 | } 98 | 99 | @Override 100 | public short readShort(long address) { 101 | Memory memory = readMemory(address, 2); 102 | if (memory == null) { 103 | return 0; 104 | } 105 | return memory.getShort(0); 106 | } 107 | 108 | @Override 109 | public int readInt(long address) { 110 | Memory memory = readMemory(address, 4); 111 | if (memory == null) { 112 | return 0; 113 | } 114 | return memory.getInt(0); 115 | } 116 | 117 | @Override 118 | public long readLong(long address) { 119 | Memory memory = readMemory(address, 8); 120 | if (memory == null) { 121 | return 0; 122 | } 123 | return memory.getLong(0); 124 | } 125 | 126 | @Override 127 | public float readFloat(long address) { 128 | Memory memory = readMemory(address, 4); 129 | if (memory == null) { 130 | return 0; 131 | } 132 | return memory.getFloat(0); 133 | } 134 | 135 | @Override 136 | public double readDouble(long address) { 137 | Memory memory = readMemory(address, 8); 138 | if (memory == null) { 139 | return 0; 140 | } 141 | return memory.getDouble(0); 142 | } 143 | 144 | 145 | private boolean writeMemory(long address, Memory memory, int size) { 146 | WinNT.HANDLE handle = openProcess(); 147 | if (handle == null) { 148 | return false; 149 | } 150 | 151 | try { 152 | // 写入数据 153 | boolean result = kernel32.WriteProcessMemory(handle, new Pointer(address), memory, size, null); 154 | if (!result) { 155 | throw new Exception(String.format("%d", kernel32.GetLastError())); 156 | } 157 | return true; 158 | } catch (Exception e) { 159 | logger.error("writeByteMemory address = {} , error = {}", address, e.getStackTrace()); 160 | return false; 161 | } finally { 162 | kernel32.CloseHandle(handle); 163 | } 164 | } 165 | 166 | @Override 167 | public boolean writeByte(long address, int[] data) { 168 | Memory buffer = new Memory(data.length); 169 | 170 | for (int i = 0; i < data.length; i++) { 171 | byte b = (byte) (data[i] & 0xFF); // 取低8位字节 172 | buffer.setByte(i, b); 173 | } 174 | 175 | return writeMemory(address, buffer, data.length); 176 | } 177 | 178 | @Override 179 | public boolean writeShort(long address, short value) { 180 | Memory buffer = new Memory(2); 181 | buffer.setShort(0, value); 182 | return writeMemory(address, buffer, 2); 183 | } 184 | 185 | @Override 186 | public boolean writeInt(long address, int value) { 187 | Memory buffer = new Memory(4); 188 | buffer.setInt(0, value); 189 | return writeMemory(address, buffer, 4); 190 | } 191 | 192 | @Override 193 | public boolean writeLong(long address, long value) { 194 | Memory buffer = new Memory(8); 195 | buffer.setLong(0, value); 196 | return writeMemory(address, buffer, 8); 197 | } 198 | 199 | @Override 200 | public boolean writeFloat(long address, float value) { 201 | Memory buffer = new Memory(4); 202 | buffer.setFloat(0, value); 203 | return writeMemory(address, buffer, 4); 204 | } 205 | 206 | @Override 207 | public boolean writeDouble(long address, double value) { 208 | Memory buffer = new Memory(8); 209 | buffer.setDouble(0, value); 210 | return writeMemory(address, buffer, 8); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/ltq/IoCode.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver.ltq; 2 | 3 | /** 4 | * @author 情歌 5 | */ 6 | public class IoCode { 7 | public static final int CHECK_CODE = 2236424; 8 | public static final int READ_CODE = 2236428; 9 | public static final int WRITE_CODE = 2236432; 10 | public static final int MODULE_CODE = 2236436; 11 | public static final int ALLOC_CODE = 2236440; 12 | public static final int FREE_CODE = 2236444; 13 | public static final int OPEN_PROTECT_CODE = 2236448; 14 | public static final int OFF_PROTECT_CODE = 2236452; 15 | public static final int MODULEFUNC_CODE = 2236456; 16 | public static final int HIDE_PROCESS_CODE = 2236420; 17 | public static final int SHOW_PROCESS_CODE = 2236424; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/ltq/LtqMemory.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver.ltq; 2 | 3 | import com.dnf.driver.ReadWriteMemory; 4 | import com.sun.jna.Memory; 5 | import com.sun.jna.platform.win32.Kernel32; 6 | import com.sun.jna.platform.win32.WinNT; 7 | import com.sun.jna.ptr.IntByReference; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | 11 | /** 12 | * @author 情歌 13 | */ 14 | public class LtqMemory implements ReadWriteMemory { 15 | final Logger logger = LoggerFactory.getLogger(LtqMemory.class.getName()); 16 | private final Kernel32 kernel32; 17 | private int processId; 18 | 19 | 20 | private WinNT.HANDLE deviceHandle; 21 | 22 | public LtqMemory() { 23 | kernel32 = Kernel32.INSTANCE; 24 | } 25 | 26 | 27 | @Override 28 | public void setProcessId(int processId) { 29 | this.processId = processId; 30 | } 31 | 32 | @Override 33 | public long allocate(int size) { 34 | return 0; 35 | } 36 | 37 | @Override 38 | public boolean freed(int address) { 39 | return false; 40 | } 41 | 42 | private Memory readMemory(long address, int size) { 43 | Memory buffer = new Memory(size); 44 | 45 | // 准备输入缓冲区 46 | ReadWrite inputData = new ReadWrite(); 47 | inputData.setProcessId(processId); 48 | inputData.setData(buffer); 49 | inputData.setMemoryAddress(address); 50 | inputData.setSize(size); 51 | inputData.setKey(""); 52 | 53 | try { 54 | // 调用 DeviceIoControl 函数 55 | boolean success = kernel32.DeviceIoControl(deviceHandle, IoCode.READ_CODE, inputData.getPointer(), 40, inputData.getData(), size, new IntByReference(0), null); 56 | if (!success) { 57 | throw new Exception(String.format("%d", kernel32.GetLastError())); 58 | } 59 | return buffer; 60 | } catch (Exception e) { 61 | logger.error("readMemory address = {} , error = {}", address, e.getStackTrace()); 62 | return null; 63 | } 64 | } 65 | 66 | @Override 67 | public int[] readByte(long address, int size) { 68 | Memory memory = readMemory(address, size); 69 | if (memory == null) { 70 | return null; 71 | } 72 | int[] memoryValues = new int[size]; 73 | for (int i = 0; i < size; i++) { 74 | memoryValues[i] = Byte.toUnsignedInt(memory.getByte(i)); 75 | } 76 | return memoryValues; 77 | } 78 | 79 | @Override 80 | public short readShort(long address) { 81 | return 0; 82 | } 83 | 84 | @Override 85 | public int readInt(long address) { 86 | return 0; 87 | } 88 | 89 | @Override 90 | public long readLong(long address) { 91 | return 0; 92 | } 93 | 94 | @Override 95 | public float readFloat(long address) { 96 | return 0; 97 | } 98 | 99 | @Override 100 | public double readDouble(long address) { 101 | return 0; 102 | } 103 | 104 | private boolean writeMemory(long address, Memory memory, int size) { 105 | // 准备输入缓冲区 106 | ReadWrite inputData = new ReadWrite(); 107 | inputData.setProcessId(processId); 108 | inputData.setData(memory); 109 | inputData.setMemoryAddress(address); 110 | inputData.setSize(size); 111 | inputData.setKey(""); 112 | 113 | return kernel32.DeviceIoControl(deviceHandle, IoCode.WRITE_CODE, inputData.getPointer(), 40 + size, null, 0, new IntByReference(0), null); 114 | } 115 | 116 | @Override 117 | public boolean writeByte(long address, int[] data) { 118 | Memory buffer = new Memory(data.length); 119 | 120 | for (int i = 0; i < data.length; i++) { 121 | byte b = (byte) (data[i] & 0xFF); // 取低8位字节 122 | buffer.setByte(i, b); 123 | } 124 | 125 | return writeMemory(address, buffer, data.length); 126 | } 127 | 128 | @Override 129 | public boolean writeShort(long address, short value) { 130 | return false; 131 | } 132 | 133 | @Override 134 | public boolean writeInt(long address, int value) { 135 | return false; 136 | } 137 | 138 | @Override 139 | public boolean writeLong(long address, long value) { 140 | return false; 141 | } 142 | 143 | @Override 144 | public boolean writeFloat(long address, float value) { 145 | return false; 146 | } 147 | 148 | @Override 149 | public boolean writeDouble(long address, double value) { 150 | return false; 151 | } 152 | 153 | public boolean installDrive() { 154 | return true; 155 | } 156 | 157 | public boolean uninstallDrive() { 158 | return true; 159 | } 160 | 161 | public long getModuleAddr() { 162 | return 0; 163 | } 164 | 165 | public long getModuleFuncAddr() { 166 | return 0; 167 | } 168 | 169 | public long getFuncAddr() { 170 | return 0; 171 | } 172 | 173 | public boolean processProtectOn() { 174 | return true; 175 | } 176 | 177 | public boolean processProtectOff() { 178 | return true; 179 | } 180 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/ltq/ReadWrite.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver.ltq; 2 | 3 | import com.sun.jna.Pointer; 4 | import com.sun.jna.Structure; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | 8 | /** 9 | * @author 情歌 10 | */ 11 | @EqualsAndHashCode(callSuper = true) 12 | @Data 13 | public class ReadWrite extends Structure { 14 | public long processId; 15 | public long memoryAddress; 16 | public long size; 17 | public Pointer data; 18 | public String key; 19 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/driver/tan/TanMemory.java: -------------------------------------------------------------------------------- 1 | package com.dnf.driver.tan; 2 | 3 | import com.dnf.driver.ReadWriteMemory; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | /** 8 | * @author 情歌 9 | */ 10 | public class TanMemory implements ReadWriteMemory { 11 | Logger logger = LoggerFactory.getLogger(TanMemory.class.getName()); 12 | 13 | private int processId; 14 | 15 | @Override 16 | public void setProcessId(int processId) { 17 | this.processId = processId; 18 | } 19 | 20 | 21 | @Override 22 | public long allocate(int size) { 23 | return 0; 24 | } 25 | 26 | @Override 27 | public boolean freed(int address) { 28 | return false; 29 | } 30 | 31 | @Override 32 | public int[] readByte(long address, int size) { 33 | return new int[0]; 34 | } 35 | 36 | @Override 37 | public short readShort(long address) { 38 | return 0; 39 | } 40 | 41 | @Override 42 | public int readInt(long address) { 43 | return 0; 44 | } 45 | 46 | @Override 47 | public long readLong(long address) { 48 | return 0; 49 | } 50 | 51 | @Override 52 | public float readFloat(long address) { 53 | return 0; 54 | } 55 | 56 | @Override 57 | public double readDouble(long address) { 58 | return 0; 59 | } 60 | 61 | @Override 62 | public boolean writeByte(long address, int[] data) { 63 | return false; 64 | } 65 | 66 | @Override 67 | public boolean writeShort(long address, short value) { 68 | return false; 69 | } 70 | 71 | @Override 72 | public boolean writeInt(long address, int value) { 73 | return false; 74 | } 75 | 76 | @Override 77 | public boolean writeLong(long address, long value) { 78 | return false; 79 | } 80 | 81 | @Override 82 | public boolean writeFloat(long address, float value) { 83 | return false; 84 | } 85 | 86 | @Override 87 | public boolean writeDouble(long address, double value) { 88 | return false; 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/CoordinateType.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 地图坐标 7 | * 8 | * @author 情歌 9 | */ 10 | @Data 11 | public class CoordinateType { 12 | public int x; 13 | public int y; 14 | public int z; 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/GameMapType.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 游戏地图 7 | * 8 | * @author 情歌 9 | */ 10 | @Data 11 | public class GameMapType { 12 | public CoordinateType mapCoordinates; // 地图坐标 13 | public boolean left; // 地图左边 14 | public boolean right; // 地图右边 15 | public boolean up; // 地图上边 16 | public boolean down; // 地图下边 17 | public int mapChannel; // 地图通道 18 | public int backgroundColor; // 背景颜色 19 | 20 | public GameMapType() { 21 | this.mapCoordinates = new CoordinateType(); 22 | this.left = false; 23 | this.right = false; 24 | this.up = false; 25 | this.down = false; 26 | this.mapChannel = 0; 27 | this.backgroundColor = 0; 28 | } 29 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/GlobalData.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | /** 4 | * @author 情歌 5 | */ 6 | public class GlobalData { 7 | public static boolean autoSwitch; // 自动开关 8 | public static boolean screenSwitch; // 技能开关 9 | public static boolean doubleSwitch; // 倍攻开关 10 | public static int taskId; // 任务编号 11 | public static int mapId; // 副本编号 12 | public static int mapLevel; // 副本难度 13 | public static int roleCount = 1; // 角色总数 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/MapDataType.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | /** 9 | * 地图数据 10 | * 11 | * @author 情歌 12 | */ 13 | @Data 14 | public class MapDataType { 15 | public String mapName; // 地图名称 16 | public int mapUm; // 地图编号 17 | public List mapChannel; // 地图通道 18 | public CoordinateType startZb; // 起始坐标 19 | public CoordinateType endZb; // 终点坐标 20 | public int width; // 宽 21 | public int height; // 高 22 | public List mapRoute; // 地图走法 23 | public int consumeFatigue; // 消耗疲劳 24 | public int channelNum; // 通道数量 25 | public long tmp; // 临时变量 26 | 27 | public MapDataType() { 28 | this.mapName = ""; 29 | this.mapUm = 0; 30 | this.mapChannel = new ArrayList<>(); 31 | this.startZb = new CoordinateType(); 32 | this.endZb = new CoordinateType(); 33 | this.width = 0; 34 | this.height = 0; 35 | this.mapRoute = new ArrayList<>(); 36 | this.consumeFatigue = 0; 37 | this.channelNum = 0; 38 | this.tmp = 0; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/MapNodeType.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 地图节点 7 | * 8 | * @author 情歌 9 | */ 10 | @Data 11 | public class MapNodeType { 12 | public int f; // 地图F点 13 | public int g; // 地图G点 14 | public int h; // 地图H点 15 | public CoordinateType currentCoordinates; // 当前坐标 16 | public CoordinateType finalCoordinates; // 最终坐标 17 | 18 | public MapNodeType() { 19 | this.f = 0; 20 | this.g = 0; 21 | this.h = 0; 22 | this.currentCoordinates = new CoordinateType(); 23 | this.finalCoordinates = new CoordinateType(); 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/entity/MapTraversalType.java: -------------------------------------------------------------------------------- 1 | package com.dnf.entity; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * 地图遍历 7 | * 8 | * @author 情歌 9 | */ 10 | @Data 11 | public class MapTraversalType { 12 | // 地图遍历类型的属性 13 | public long rwAddr; // 地图地址 14 | public long mapData; // 地图数据 15 | public long start; // 起始位置 16 | public long end; // 结束位置 17 | public long objNum; // 物体数量 18 | public long objTmp; // 物体临时变量 19 | public long objPtr; // 物体指针 20 | public int objCamp; // 物体阵营 21 | public long objBlood; // 物体血量 22 | public int objTypeA; // 物体类型A 23 | public int objTypeB; // 物体类型B 24 | public int objCode; // 物体代码 25 | public String objNameA; // 物体名称A 26 | public String objNameB; // 物体名称B 27 | public CoordinateType rwCoordinate; // 地图坐标类型 28 | public CoordinateType gwCoordinate; // 游戏坐标类型 29 | public CoordinateType wpCoordinate; // 终点坐标类型 30 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Address.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | /** 4 | * @author 情歌 5 | */ 6 | public class Address { 7 | public static final long RwAddr = 0x14C2503F8L; // 新人物基址 8 | public static final long RwAddr1 = 0x14C2503F0L; // 人物基址 9 | public static final long RwAddr2 = 0x14B8E1D08L; // 人物基址B 10 | public static final long RWCallAddr = 0x144CA54B0L; // 人物CALL 11 | public static final long JSDjAddr = 0x14B957BE0L; // 角色等级 12 | public static final long PFAddr = 0x14B971380L; // 评分基址 13 | public static final long GGCsAddr = 0x14C251758L; // 公告参数 14 | public static final long GGCallAddr = 0x144D712C0L; // 公告CALL 15 | public static final long BbJzAddr = 0x14B972668L; // 背包基址 16 | public static final long JSPtrAddr = 0x14B9723B0L; // 角色指针 17 | public static final long CzDqyAddr = 0x14B92F7ACL; // 城镇大区域 18 | public static final long CzXqyAddr = 0x14B92F7B0L; // 城镇小区域 19 | public static final long YXZTAddr = 0x14B4B3F20L; // 游戏状态 20 | public static final long SNBBAddr = 0x14B9726C0L; // 司南背包 21 | public static final long YrBbAddr = 0x14B9726B8L; // 玉荣背包 22 | public static final long BxrBbAddr = 0x14B9726B8L; // 辟邪玉背包 23 | public static final long SnAddCallAddr = 0x141DDF8D0L; // 司南添加CALL 24 | public static final long SnJtRcxAddr = 0x14B920068L; // 司南进图_Rcx 25 | public static final long SnJtCallAddr = 0x141DC2C00L; // 司南进图CALL 26 | public static final long SnAddRcxAddr = 0x145460CF0L; // 取司南添加RCX 27 | public static final long YrlPyAddr = 0x600L; // 玉荣力偏移 28 | public static final long JsYrlAddr = 0x5408L; // 角色玉荣力 29 | public static final long HBCallAddr = 0x13FDC0000L; // 汇编CALL 30 | public static final long TranslateMessage = 0x1477C6CC0L; // TranslateMessage 31 | public static final long GameTimeGetTime = 0x1477C70F8L; // GameTimeGetTime 32 | public static final long JNCallAddr = 0x14480BB70L; // 技能CALL 33 | public static final long JwCallAddr = 0x144AA4D10L; // 聚物CALL 34 | public static final long JwXyAddr = 0xFE24L; // 聚物校验 35 | public static final long TaskAddr = 0x14B972750L; // 任务基址 36 | public static final long JsCallAddr = 0x1440FCD40L; // 接受CALL 37 | public static final long WcCallAddr = 0x1440FD350L; // 完成CALL 38 | public static final long TjCallAddr = 0x1440FCE30L; // 提交CALL 39 | public static final long TgCallAddr = 0x143E98F50L; // 跳过CALL 40 | public static final long AjAddr = 0x14C70EC10L; // 按键基址 41 | public static final long DHAddr = 0x14C2A42E8L; // 对话基址 42 | public static final long DHAddrB = 0x14B7A84C0L; // 对话基址B 43 | public static final long EscDHAddr = 0x14B7A84E0L; // Esc对话基址 44 | public static final long FpAddr = 0x14B972660L; // 翻牌基址 45 | public static final long FbBhAddr = 0x14B957B70L; // 副本编号 46 | public static final long SJAddr = 0x20A050L; // 时间基址 47 | public static final long FJBHAddr = 0x14B972650L; // 房间编号 48 | public static final long MaxPlAddr = 0x14C25032CL; // 最大疲劳 49 | public static final long CutPlAddr = 0x14C25039CL; // 当前疲劳 50 | public static final long QyParamAddr = 0x14C2A9830L; // 区域参数 51 | public static final long QyCallAddr = 0x145AB95C0L; // 区域CALL 52 | public static final long QyPyAddr = 0xA9FA8L; // 区域偏移 53 | public static final long XTuCallAddr = 0x145AF90E0L; // 选图CALL 54 | public static final long JTuCallAddr = 0x145B398F0L; // 进图CALL 55 | public static final long HChengCallAddr = 0x14589EE90L; // 回城CALL 56 | public static final long GtCallAddr = 0x143C32B80L; // 过图CALL 57 | public static final long PyCall1Addr = 0x143A84420L; // 漂移CALL 58 | public static final long PyCall2Addr = 0x145C531D0L; // 漂移CALL2 59 | public static final long BpCallAddr = 0x14403E760L; // 奔跑CALL 60 | public static final long XrNcCallAddr = 0x144CE04F0L; // 写入内存 61 | public static final long BpPyAddr1 = 0x1208L; // 奔跑偏移_1 62 | public static final long BpPyAddr2 = 0x11F0L; // 奔跑偏移_2 63 | public static final long CzSyRdxAddr = 0x14B9435E8L; // 城镇瞬移_Rdx 64 | public static final long CzSyCallAddr = 0x145B001A0L; // 城镇瞬移CALL 65 | public static final long XzJsCallAddr = 0x1404FD580L; // 选择角色CALL 66 | public static final long FhJsCallAddr = 0x144513860L; // 返回角色CALL 67 | public static final long LqCallJudgeAddr = 0x144C91AE0L; // 冷却判断CALL 68 | public static final long CdResetCallAddr = 0x144AF47E0L; // CD重置CALL 69 | public static final long FjCallAddr = 0x1448F0FB0L; // 分解CALL 70 | public static final long ZlCallAddr = 0x1448E73A0L; // 整理CALL 71 | public static final long DqFzAddr = 0x14C2A5B38L; // 当前负重 72 | public static final long ZdFzAddr = 0x2DB8L; // 最大负重 73 | public static final long FbAddr = 0x14C2AA440L; // 发包基址 74 | public static final long HcCallAddr = 0x145B64E70L; // 缓冲CALL 75 | public static final long FbCallAddr = 0x145B65B60L; // 发包CALL 76 | public static final long JmB1CallAddr = 0x145B65CD0L; // 加密包CALL 77 | public static final long JmB2CallAddr = 0x145B66050L; // 加密包CALL2 78 | public static final long JmB3CallAddr = 0x145B65CF0L; // 加密包CALL4 79 | public static final long JmB4CallAddr = 0x145B65D10L; // 加密包CALL8 80 | public static final long SqNcCallAddr = 0x143A59560L; // 申请内存 81 | public static final long BUffMemRcxAddr = 0x14B9725A8L; // BUFF内存_RCX 82 | public static final long BUffMemCallAddr = 0x145B81B80L; // BUFF内存CALL 83 | public static final long DyBuffCall = 0x144CDC830L; // 调用BUFFCALL 84 | public static final long TakeEffectCallAddr = 0x144A19C20L; // 生效CALL 85 | public static final long PutOnCallAddr = 0x144AB7A70L; // 穿上CALL 86 | public static final long TmCallAddr = 0x145B93BA0L; // 透明CALL 87 | public static final long CreateCallAddr = 0x144DBCF00L; // 创建CALL 88 | public static final long WpYdCallAddr = 0x1448DDA40L; // 物品移动CALL 89 | public static final long JnSwAddr = 0x144A605C1L; // 技能三无 90 | public static final long RwMwAddr = 0x11E54L; // 人物名望 91 | public static final long WpMcAddr = 0x40L; // 物品名称 92 | public static final long WpJyLxAddr = 0xA8L; // 物品交易类型 93 | public static final long DzIDAddr = 0x436CL; // 动作ID 94 | public static final long DtKs2 = 0x1B8L; // 地图开始2 95 | public static final long DtJs2 = 0x1C0L; // 地图结束2 96 | public static final long DtPyAddr = 0x168L; // 地图偏移 97 | public static final long LxPyAddr = 0x134L; // 类型偏移 98 | public static final long FxPyAddr = 0x148L; // 方向偏移 99 | public static final long CEPfAddr = 0x88L; // 评分偏移 100 | public static final long FbSqAddr = 0x13CL; // 发包拾取 101 | public static final long GwXlAddr = 0x4F78L; // 怪物血量 102 | public static final long ZyPyAddr = 0xEB8L; // 阵营偏移 103 | public static final long DmWpAddr = 0x2B70L; // 地面物品 104 | public static final long JxWpAddr = 0xF950L; // 脚下物品 105 | public static final long DmPyAddr = 0x868L; // 代码偏移 106 | public static final long McPyAddr = 0x870L; // 名称偏移 107 | public static final long ZbPjAddr = 0x2B8L; // 装备品级 108 | public static final long DtCtAddr = 0x878L; // 地图穿透 109 | public static final long JzCtAddr = 0x87CL; // 建筑穿透 110 | public static final long DqZbAddr = 0x328L; // 读取坐标 111 | public static final long YjRwStartAddr = 0x10L; // 已接任务首地址 112 | public static final long YjRwEndAddr = 0x18L; // 已接任务尾地址 113 | public static final long QbRwStartAddr = 0xA8L; // 全部任务首地址 114 | public static final long QbRwEndAddr = 0xB0L; // 全部任务尾地址 115 | public static final long RwLxAddr = 0x218L; // 任务类型 116 | public static final long RwDxAddr = 0x28L; // 任务大小 117 | public static final long RwTjAddr = 0x4D0L; // 任务条件 118 | public static final long RwDjAddr = 0x328L; // 任务等级 119 | public static final long RwFbAddr = 0x488L; // 任务副本 120 | public static final long SfKmAddr = 0x27CL; // 是否开门 121 | public static final long CutRoomXAddr = 0x1C98L; // 当前房间X 122 | public static final long CutRoomYAddr = 0x1C9CL; // 当前房间Y 123 | public static final long BOSSRoomXAddr = 0x1D98L; // BOSS房间X 124 | public static final long BOSSRoomYAddr = 0x1D9CL; // BOSS房间Y 125 | public static final long GouHuoAddr = 0x1E28L; // 篝火判断 126 | public static final long SyPyAddr = 0x1D8CL; // 索引偏移 127 | public static final long MxPyAddr = 0x128L; // 门型偏移 128 | public static final long KgPyAddr = 0x890L; // 宽高偏移 129 | public static final long SzPyAddr = 0x8B0L; // 数组偏移 130 | public static final long DtMcAddr = 0x418L; // 地图名称 131 | public static final long StPyAddr = 0xC0L; // 顺图偏移 132 | public static final long ZbStPyAddr = 0x3848L; // 坐标顺图 133 | public static final long FxIdAddr = 0xE8L; // 方向ID 134 | public static final long WplAddr = 0xFD98L; // 物品栏 135 | public static final long WplPyAddr = 0xA8L; // 物品栏偏移 136 | public static final long JnlAddr = 0xFD10L; // 技能栏 137 | public static final long JnlPyAddr = 0x90L; // 技能栏偏移 138 | 139 | public static long RwKbAddr = 0; // 人物基址 140 | public static long NcBhKbAddr = 0; // 内存汇编 141 | public static long JnKbAddr = 0; // 技能Call 142 | public static long GtKbAddr = 0; // 过图Call 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/AutoThread.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.GlobalData; 4 | import com.dnf.entity.MapDataType; 5 | import com.dnf.helper.Button; 6 | import com.dnf.helper.IniUtils; 7 | import com.dnf.helper.Strings; 8 | import com.dnf.helper.Timer; 9 | import com.sun.jna.platform.win32.Win32VK; 10 | import jakarta.annotation.Resource; 11 | import org.springframework.stereotype.Component; 12 | 13 | import java.util.Random; 14 | 15 | /** 16 | * @author 情歌 17 | */ 18 | @Component 19 | public class AutoThread extends Base { 20 | // 首次进图 21 | static boolean firstEnterMap; 22 | 23 | // 完成次数 24 | // static int completedNum; 25 | 26 | @Resource 27 | private MapData mapData; 28 | 29 | @Resource 30 | private GameCall gamecall; 31 | 32 | @Resource 33 | private Traverse traverse; 34 | 35 | @Resource 36 | private GameMap gameMap; 37 | 38 | @Resource 39 | private SendPack sendPack; 40 | 41 | @Resource 42 | private IniUtils iniUtils; 43 | 44 | @Resource 45 | private Task task; 46 | 47 | public void autoSwitch() { 48 | if (!GlobalData.autoSwitch) { 49 | Thread thread = new Thread(this::autoThread); 50 | thread.start(); 51 | GlobalData.autoSwitch = true; 52 | logger.info("自动刷图 - [ √ ]"); 53 | } else { 54 | GlobalData.autoSwitch = false; 55 | logger.info("自动刷图 - [ x ]"); 56 | } 57 | } 58 | 59 | private void autoThread() { 60 | while (GlobalData.autoSwitch) { 61 | try { 62 | Timer.sleep(200); 63 | // 对话处理 64 | if (mapData.isDialogA() || mapData.isDialogB()) { 65 | Button.DriveButton(Win32VK.VK_ESCAPE.code, 0, false); 66 | Timer.sleep(100); 67 | Button.DriveButton(Win32VK.VK_SPACE.code, 0, false); 68 | continue; 69 | } 70 | 71 | // 进入城镇 72 | if (mapData.getStat() == 0) { 73 | Timer.sleep(200); 74 | enterTown(); 75 | continue; 76 | } 77 | 78 | // 城镇处理 79 | if (mapData.getStat() == 1 && mapData.isTown()) { 80 | townHandle(); 81 | continue; 82 | } 83 | 84 | // 进入副本 85 | if (mapData.getStat() == 2) { 86 | enterMap(GlobalData.mapId, GlobalData.mapLevel); 87 | continue; 88 | } 89 | 90 | // 在地图内 91 | if (mapData.getStat() == 3) { 92 | if (!firstEnterMap && !mapData.isTown()) { 93 | // 透明call 94 | gamecall.hideCall(gamecall.personPtr()); 95 | // sss评分 96 | Random random = new Random(); 97 | int nextInt = random.nextInt(9999999 - 5201314 + 1) + 5201314; 98 | memory.writeLong(memory.readLong(Address.PFAddr) + Address.CEPfAddr, nextInt); 99 | firstEnterMap = true; 100 | continue; 101 | } 102 | 103 | // 跟随怪物 104 | if (iniUtils.read("自动配置", "跟随打怪", Integer.class) > 0) { 105 | logger.debug("开始跟随怪物"); 106 | traverse.followMonster(); 107 | } 108 | 109 | // 过图 110 | if (mapData.isOpenDoor() && !mapData.isBossRoom()) { 111 | if (traverse.isExistsItem()) { 112 | // 捡物品 113 | logger.debug("过图捡物"); 114 | traverse.packPickup(); 115 | continue; 116 | } 117 | // 过图 118 | passMap(); 119 | continue; 120 | } 121 | 122 | if (mapData.isBossRoom() && mapData.isPass()) { 123 | if (traverse.isExistsItem()) { 124 | logger.debug("boss房捡物"); 125 | traverse.packPickup(); 126 | continue; 127 | } 128 | 129 | // 刷图计次 130 | passBoss(); 131 | // 退出副本 132 | quitMap(); 133 | firstEnterMap = false; 134 | } 135 | } 136 | } catch (Exception exception) { 137 | logger.error("自动线程出错 {}", exception.getMessage()); 138 | } 139 | } 140 | } 141 | 142 | /** 143 | * 进入城镇 144 | */ 145 | private void enterTown() { 146 | Integer roleNumber = iniUtils.read("自动配置", "角色数量", Integer.class); 147 | // 取ini角色数量 148 | if (GlobalData.roleCount > roleNumber) { 149 | logger.info("指定角色完成所有角色"); 150 | logger.info("自动刷图 - [ x ]"); 151 | GlobalData.autoSwitch = false; 152 | return; 153 | } 154 | GlobalData.roleCount++; 155 | 156 | Timer.sleep(200); 157 | sendPack.selectRole(GlobalData.roleCount - 1); 158 | Timer.sleep(500); 159 | logger.info("进入角色 {} ", GlobalData.roleCount); 160 | logger.info("开始第 [ {} ] 个角色,剩余疲劳 [ {} ]", GlobalData.roleCount, mapData.getPl()); 161 | 162 | while (GlobalData.autoSwitch) { 163 | logger.debug("城镇循环"); 164 | Timer.sleep(300); 165 | // 进入城镇跳出循环 166 | if (mapData.getStat() == 1) { 167 | break; 168 | } 169 | } 170 | } 171 | 172 | /** 173 | * 城镇处理 174 | */ 175 | private void townHandle() { 176 | if (mapData.getPl() < 8) { 177 | backToRole(); 178 | return; 179 | } 180 | 181 | Timer.sleep(500); 182 | // 分解装备 183 | traverse.handleEquip(); 184 | 185 | // 1 剧情 2 搬砖 186 | Integer autoModel = iniUtils.read("自动配置", "自动模式", Integer.class); 187 | if (autoModel == 1 && mapData.getRoleLevel() < 110) { 188 | GlobalData.mapId = task.handleTask(); 189 | GlobalData.mapLevel = 0; 190 | } 191 | if (autoModel == 2 && mapData.getRoleLevel() == 110) { 192 | String numbers = iniUtils.read("自动配置", "普通地图", String.class); 193 | int[] mapIds = Strings.splitToIntArray(numbers, ","); 194 | Integer mapLevel = iniUtils.read("自动配置", "地图难度", Integer.class); 195 | Random random = new Random(); 196 | int index = random.nextInt(mapIds.length); 197 | GlobalData.mapId = mapIds[index]; 198 | GlobalData.mapLevel = mapLevel; 199 | } 200 | 201 | if (autoModel == 3) { 202 | logger.info("未央功能未实现"); 203 | return; 204 | } 205 | 206 | if (GlobalData.mapId == 0) { 207 | logger.info("地图编号为空,无法切换区域"); 208 | return; 209 | } 210 | 211 | Timer.sleep(500); 212 | gamecall.areaCall(GlobalData.mapId); 213 | Timer.sleep(500); 214 | selectMap(); 215 | } 216 | 217 | /** 218 | * 选择地图 219 | */ 220 | private void selectMap() { 221 | while (GlobalData.autoSwitch) { 222 | logger.debug("选图循环"); 223 | Timer.sleep(200); 224 | sendPack.selectMap(); 225 | if (mapData.getStat() == 2 || mapData.getStat() == 3) { 226 | break; 227 | } 228 | } 229 | } 230 | 231 | // 返回角色 232 | private void backToRole() { 233 | logger.info("疲劳值不足 · 即将切换角色"); 234 | Timer.sleep(200); 235 | sendPack.returnRole(); 236 | while (GlobalData.autoSwitch) { 237 | logger.debug("返回角色循环"); 238 | Timer.sleep(200); 239 | if (mapData.getStat() == 0) { 240 | break; 241 | } 242 | } 243 | } 244 | 245 | /** 246 | * 进入地图 247 | */ 248 | private void enterMap(int mapId, int mapLevel) { 249 | if (mapLevel == 5) { 250 | for (int i = 4; i >= 0; i--) { 251 | if (mapData.getStat() == 3) { 252 | break; 253 | } 254 | if (mapData.getStat() == 2) { 255 | gamecall.goMapCall(mapId, i); 256 | Timer.sleep(1000); 257 | } 258 | if (mapData.getStat() == 1) { 259 | selectMap(); 260 | } 261 | } 262 | } else { 263 | gamecall.goMapCall(mapId, mapLevel); 264 | } 265 | 266 | while (GlobalData.autoSwitch) { 267 | logger.debug("进入副本循环"); 268 | Timer.sleep(200); 269 | if (mapData.getStat() == 3) { 270 | break; 271 | } 272 | } 273 | } 274 | 275 | /** 276 | * 过图处理 277 | */ 278 | private void passMap() { 279 | if (!mapData.isOpenDoor() || mapData.isBossRoom()) { 280 | return; 281 | } 282 | 283 | int overTheMap = iniUtils.read("自动配置", "过图方式", Integer.class); 284 | if (overTheMap <= 0) { 285 | return; 286 | } 287 | 288 | MapDataType mapDataType = gameMap.mapData(); 289 | int direction = gameMap.getDirection(mapDataType.mapRoute.get(0), mapDataType.mapRoute.get(1)); 290 | if (direction < 0) { 291 | logger.info("方向错误"); 292 | return; 293 | } 294 | 295 | switch (overTheMap) { 296 | case 1: 297 | gamecall.overMapCall(direction); 298 | case 2: 299 | gamecall.driftHandle(direction); 300 | } 301 | } 302 | 303 | /** 304 | * 通过boss 305 | */ 306 | private void passBoss() { 307 | IniUtils iniUtils = new IniUtils().setFilename("C:\\config.ini"); 308 | Integer count = iniUtils.read("default", "count", Integer.class); 309 | iniUtils.write("default", "count", count + 1); 310 | logger.info("{} [ {} ] 剩余疲劳 [ {} ]", mapData.getMapName(), count + 1, mapData.getPl()); 311 | } 312 | 313 | /** 314 | * 退出地图 315 | */ 316 | private void quitMap() { 317 | Timer.sleep(200); 318 | Random random = new Random(); 319 | int num = random.nextInt(4); 320 | sendPack.getIncome(0, num); 321 | 322 | int outMap = iniUtils.read("自动配置", "出图方式", Integer.class); 323 | 324 | while (GlobalData.autoSwitch) { 325 | logger.debug("退出副本-处理"); 326 | Timer.sleep(200); 327 | if (outMap == 0) { 328 | Timer.sleep(3000); 329 | } 330 | // 退出地图 331 | sendPack.leaveMap(); 332 | // 在城镇或者选图跳出循环 333 | if (mapData.getStat() == 1) { 334 | break; 335 | } 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Base.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.driver.ReadWriteMemory; 4 | import jakarta.annotation.Resource; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * @author 情歌 10 | */ 11 | public class Base { 12 | protected final Logger logger = LoggerFactory.getLogger(this.getClass()); 13 | 14 | @Resource 15 | protected ReadWriteMemory memory; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/GameCall.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.helper.Bytes; 4 | import com.dnf.helper.Timer; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author 情歌 9 | */ 10 | @Component 11 | public class GameCall extends Base { 12 | private static boolean compileCallRun; 13 | 14 | public int[] subRsp(int i) { 15 | if (i > 127) { 16 | return Bytes.addBytes(new int[]{72, 129, 236}, Bytes.intToBytes(i)); 17 | } 18 | return Bytes.addBytes(new int[]{72, 131, 236}, new int[]{i}); 19 | } 20 | 21 | public int[] addRsp(int i) { 22 | if (i > 127) { 23 | return Bytes.addBytes(new int[]{72, 129, 196}, Bytes.intToBytes(i)); 24 | } 25 | return Bytes.addBytes(new int[]{72, 131, 196}, new int[]{i}); 26 | } 27 | 28 | public int[] call(long address) { 29 | int[] shellCode = new int[]{255, 21, 2, 0, 0, 0, 235, 8}; 30 | return Bytes.addBytes(shellCode, Bytes.intToBytes(address)); 31 | } 32 | 33 | /** 34 | * 汇编call 35 | * 36 | * @param intArr int[] 37 | */ 38 | public void compileCall(int[] intArr) { 39 | logger.debug("compiling call {}", intArr); 40 | 41 | // 汇编中转, 空白地址, 跳转地址 42 | long assemblyTransit = Address.NcBhKbAddr + 300; 43 | long blankAddress = Address.NcBhKbAddr + 500; 44 | long jumpAddress = blankAddress - 100; 45 | 46 | if (compileCallRun) { 47 | return; 48 | } 49 | 50 | compileCallRun = true; 51 | long hookShell = Address.HBCallAddr; 52 | hookShell = hookShell + 144; 53 | long hookJump = hookShell + 19; 54 | int[] hookData = memory.readByte(hookShell, 19); 55 | int[] hookOldData = hookData.clone(); 56 | 57 | try { 58 | hookData = Bytes.addBytes(hookData, new int[]{72, 184}, Bytes.intToBytes(jumpAddress)); 59 | hookData = Bytes.addBytes(hookData, new int[]{131, 56, 1, 117, 42, 72, 129, 236, 0, 3, 0, 0}); 60 | hookData = Bytes.addBytes(hookData, new int[]{72, 187}, Bytes.intToBytes(blankAddress)); 61 | hookData = Bytes.addBytes(hookData, new int[]{255, 211}); 62 | hookData = Bytes.addBytes(hookData, new int[]{72, 184}, Bytes.intToBytes(jumpAddress)); 63 | hookData = Bytes.addBytes(hookData, new int[]{199, 0, 3, 0, 0, 0}); 64 | hookData = Bytes.addBytes(hookData, new int[]{72, 129, 196, 0, 3, 0, 0}); 65 | hookData = Bytes.addBytes(hookData, new int[]{255, 37, 0, 0, 0, 0}, Bytes.intToBytes(hookJump)); 66 | 67 | if (memory.readInt(assemblyTransit) == 0) { 68 | memory.writeByte(assemblyTransit, hookData); 69 | } 70 | 71 | int[] byteArray = new int[intArr.length]; 72 | System.arraycopy(intArr, 0, byteArray, 0, intArr.length); 73 | 74 | memory.writeByte(blankAddress, Bytes.addBytes(byteArray, new int[]{195})); 75 | int[] hookShellValue = Bytes.addBytes(new int[]{255, 37, 0, 0, 0, 0}, Bytes.intToBytes(assemblyTransit), new int[]{144, 144, 144, 144, 144}); 76 | 77 | memory.writeByte(hookShell, hookShellValue); 78 | 79 | memory.writeInt(jumpAddress, 1); 80 | while (memory.readInt(jumpAddress) == 1) { 81 | Timer.sleep(10); 82 | } 83 | } catch (Exception e) { 84 | logger.error("汇编call执行异常 error = {}", (Object) e.getStackTrace()); 85 | } finally { 86 | memory.writeByte(hookShell, hookOldData); 87 | memory.writeByte(blankAddress, new int[intArr.length + 16]); 88 | compileCallRun = false; 89 | } 90 | } 91 | 92 | 93 | /** 94 | * 取人物指针call 95 | * 96 | * @param address long 97 | * @return long 98 | */ 99 | public long getPerPtrCall(long address) { 100 | // 构造 shellCode 的字节数组 101 | int[] shellCode = Bytes.addBytes(subRsp(100), call(Address.RWCallAddr), new int[]{72, 163}); 102 | shellCode = Bytes.addBytes(shellCode, Bytes.intToBytes(address)); 103 | shellCode = Bytes.addBytes(shellCode, addRsp(100)); 104 | compileCall(shellCode); 105 | return memory.readLong(address); 106 | } 107 | 108 | /** 109 | * 人物指针 110 | * 111 | * @return long 112 | */ 113 | public long personPtr() { 114 | return getPerPtrCall(Address.RwKbAddr); 115 | } 116 | 117 | /** 118 | * 技能call 119 | * 120 | * @param address 触发地址 121 | * @param code 技能代码 122 | * @param harm 技能伤害 123 | * @param x x坐标 124 | * @param y y坐标 125 | * @param z z坐标 126 | * @param size 技能大小 127 | */ 128 | public void skillCall(long address, int code, int harm, int x, int y, int z, float size) { 129 | // 空白地址 130 | long emptyAddress = Address.JnKbAddr; 131 | // 向空白地址写入参数 132 | memory.writeLong(emptyAddress, address); // 触发地址 133 | memory.writeInt(emptyAddress + 16, code); // 技能代码 134 | memory.writeInt(emptyAddress + 20, harm); // 技能伤害 135 | memory.writeInt(emptyAddress + 32, x); // x 坐标 136 | memory.writeInt(emptyAddress + 36, y); // y 坐标 137 | memory.writeInt(emptyAddress + 40, z); // z 坐标 138 | memory.writeFloat(emptyAddress + 140, size); // 技能大小 139 | memory.writeInt(emptyAddress + 144, 65535); // 最大敌人数量 140 | memory.writeInt(emptyAddress + 148, 65535); // 最大敌人数量 141 | // 构造 shell code 142 | int[] shellCode = new int[]{72, 129, 236, 0, 2, 0, 0, 72, 185}; 143 | shellCode = Bytes.addBytes(shellCode, Bytes.intToBytes(emptyAddress)); 144 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}); 145 | shellCode = Bytes.addBytes(shellCode, Bytes.intToBytes(Address.JNCallAddr)); 146 | shellCode = Bytes.addBytes(shellCode, new int[]{255, 208, 72, 129, 196, 0, 2, 0, 0}); 147 | compileCall(shellCode); 148 | } 149 | 150 | /** 151 | * 透明call 152 | * 153 | * @param address long 154 | */ 155 | public void hideCall(long address) { 156 | int[] shellCode = new int[]{72, 129, 236, 0, 2, 0, 0}; 157 | shellCode = Bytes.addBytes(shellCode, new int[]{65, 191, 255, 255, 255, 255}); 158 | shellCode = Bytes.addBytes(shellCode, new int[]{199, 68, 36, 32, 255, 255, 0, 0}); 159 | shellCode = Bytes.addBytes(shellCode, new int[]{65, 185, 1, 0, 0, 0}); 160 | shellCode = Bytes.addBytes(shellCode, new int[]{73, 184, 1, 0, 0, 0, 0, 0, 0, 0}); 161 | shellCode = Bytes.addBytes(shellCode, new int[]{186, 1, 0, 0, 0}); 162 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 185}, Bytes.intToBytes(address)); 163 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.TmCallAddr)); 164 | shellCode = Bytes.addBytes(shellCode, new int[]{255, 208, 72, 129, 196, 0, 2, 0, 0}); 165 | compileCall(shellCode); 166 | } 167 | 168 | /** 169 | * 区域call 170 | * 171 | * @param mapNum long 172 | */ 173 | 174 | public void areaCall(int mapNum) { 175 | long regionAddress = memory.readLong(Address.QyParamAddr); 176 | long tmpRegionCall = Address.QyCallAddr; 177 | int[] shellCode = subRsp(48); 178 | shellCode = Bytes.addBytes(shellCode, Bytes.addBytes(new int[]{65, 184}, Bytes.intToBytes(mapNum))); 179 | shellCode = Bytes.addBytes(shellCode, new int[]{186, 174, 12, 0, 0}); 180 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 184, 255, 255, 255, 255, 0, 0, 0, 0}); 181 | shellCode = Bytes.addBytes(shellCode, Bytes.addBytes(new int[]{72, 185}, Bytes.intToBytes(Address.QyParamAddr))); 182 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 139, 9}); 183 | 184 | shellCode = Bytes.addBytes(shellCode, new int[]{76, 139, 201, 73, 129, 193}); 185 | shellCode = Bytes.addBytes(shellCode, Bytes.intToBytes((int) Address.QyPyAddr), new int[]{73, 131, 233, 64}); 186 | 187 | shellCode = Bytes.addBytes(shellCode, Bytes.addBytes(new int[]{72, 184}, Bytes.intToBytes(tmpRegionCall))); 188 | shellCode = Bytes.addBytes(shellCode, new int[]{255, 208}, addRsp(48)); 189 | compileCall(shellCode); 190 | int maxRegion = memory.readInt(regionAddress + Address.QyPyAddr); 191 | int minRegion = memory.readInt(regionAddress + Address.QyPyAddr + 4); 192 | int townX = memory.readInt(regionAddress + Address.QyPyAddr + 8); 193 | int townY = memory.readInt(regionAddress + Address.QyPyAddr + 12); 194 | moveCall(maxRegion, minRegion, townX, townY); 195 | } 196 | 197 | /** 198 | * 移动Call 199 | * 200 | * @param maxMap int 201 | * @param mixMap int 202 | * @param x int 203 | * @param y int 204 | */ 205 | public void moveCall(int maxMap, int mixMap, int x, int y) { 206 | long rolePtr = memory.readLong(Address.JSPtrAddr); // 角色指针 207 | memory.writeInt(Address.CzSyRdxAddr, maxMap); 208 | memory.writeInt(Address.CzSyRdxAddr + 4, mixMap); 209 | memory.writeInt(Address.CzSyRdxAddr + 8, x); 210 | memory.writeInt(Address.CzSyRdxAddr + 12, y); 211 | 212 | int[] shellCode = subRsp(256); 213 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 186}, Bytes.intToBytes(Address.CzSyRdxAddr)); 214 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 185}, Bytes.intToBytes(rolePtr)); 215 | shellCode = Bytes.addBytes(shellCode, call(Address.CzSyCallAddr)); // 城镇瞬移CALL 216 | shellCode = Bytes.addBytes(shellCode, addRsp(256)); 217 | compileCall(shellCode); 218 | } 219 | 220 | /** 221 | * 过图call 222 | * 223 | * @param fx int 0左 1右 2上 3下 224 | */ 225 | public void overMapCall(int fx) { 226 | long emptyAddr = Address.GtKbAddr; 227 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.StPyAddr); 228 | int[] shellCode = new int[]{65, 185, 255, 255, 255, 255}; 229 | shellCode = Bytes.addBytes(shellCode, new int[]{73, 184}, Bytes.intToBytes(emptyAddr)); 230 | shellCode = Bytes.addBytes(shellCode, new int[]{186}, Bytes.intToBytes(fx)); 231 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 185}, Bytes.intToBytes(roomData)); 232 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.GtCallAddr)); 233 | shellCode = Bytes.addBytes(shellCode, new int[]{255, 208}); 234 | compileCall(shellCode); 235 | } 236 | 237 | 238 | // DriftCall 漂移Callx 239 | public void driftCall(long ptr, int x, int y, int z, int speed) { 240 | coordinateMove(x,y); 241 | return; 242 | // int[] shellCode = new int[]{72, 129, 236, 0, 8, 0, 0}; 243 | // shellCode = Bytes.addBytes(shellCode, new int[]{185, 240, 0, 0, 0}); 244 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.SqNcCallAddr)); 245 | // shellCode = Bytes.addBytes(shellCode, new int[]{255, 208}); 246 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 139, 240, 72, 139, 200}); 247 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.PyCall1Addr)); 248 | // shellCode = Bytes.addBytes(shellCode, new int[]{255, 208}); 249 | // shellCode = Bytes.addBytes(shellCode, new int[]{185}, Bytes.intToBytes(x)); 250 | // shellCode = Bytes.addBytes(shellCode, new int[]{137, 8}); 251 | // shellCode = Bytes.addBytes(shellCode, new int[]{185}, Bytes.intToBytes(y)); 252 | // shellCode = Bytes.addBytes(shellCode, new int[]{137, 72, 4}); 253 | // shellCode = Bytes.addBytes(shellCode, new int[]{185}, Bytes.intToBytes(z)); 254 | // shellCode = Bytes.addBytes(shellCode, new int[]{137, 72, 8, 72, 141, 72, 24}); 255 | // shellCode = Bytes.addBytes(shellCode, new int[]{186}, Bytes.intToBytes(speed)); 256 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.PyCall2Addr)); 257 | // shellCode = Bytes.addBytes(shellCode, new int[]{255, 208}); 258 | // shellCode = Bytes.addBytes(shellCode, new int[]{51, 219, 137, 95, 48, 199, 135, 224, 0, 0, 0, 2, 0, 0, 0, 72, 141, 69, 136, 72, 137, 68, 36, 96, 72, 137, 93, 136, 72, 137, 93, 144, 51, 210, 72, 141, 77, 136}); 259 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 184}, Bytes.intToBytes(Address.XrNcCallAddr)); 260 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 139, 206, 72, 139, 1}); 261 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 139, 6, 137, 92, 36, 64, 72, 137, 92, 36, 56, 72, 137, 92, 36, 48, 137, 92, 36, 40, 72, 141, 77, 136, 72, 137, 76, 36, 32, 69, 51, 201}); 262 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 186}, Bytes.intToBytes(ptr)); 263 | // shellCode = Bytes.addBytes(shellCode, new int[]{73, 184}, Bytes.intToBytes(ptr)); 264 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 139, 206}); 265 | // shellCode = Bytes.addBytes(shellCode, new int[]{255, 144}, Bytes.intToBytes(312)); 266 | // shellCode = Bytes.addBytes(shellCode, new int[]{72, 129, 196, 0, 8, 0, 0}); 267 | // compileCall(shellCode); 268 | } 269 | 270 | 271 | /** 272 | * 进图Call 273 | * 274 | * @param mapId int 地图Id 275 | * @param mapLevel int 地图等级 276 | */ 277 | public void goMapCall(int mapId, int mapLevel) { 278 | long rolePtr = memory.readLong(Address.JSPtrAddr); 279 | int[] shellCode = subRsp(48); 280 | shellCode = Bytes.addBytes(shellCode, new int[]{65, 185, 0, 0, 0, 0}); 281 | shellCode = Bytes.addBytes(shellCode, new int[]{65, 184}, Bytes.intToBytes(mapLevel)); 282 | shellCode = Bytes.addBytes(shellCode, new int[]{72, 185}, Bytes.intToBytes(rolePtr)); 283 | shellCode = Bytes.addBytes(shellCode, new int[]{186}, Bytes.intToBytes(mapId)); 284 | shellCode = Bytes.addBytes(shellCode, new int[]{199, 68, 36, 40, 255, 255, 255, 255, 199, 68, 36, 32, 0, 0, 0, 0}); 285 | shellCode = Bytes.addBytes(shellCode, call(Address.JTuCallAddr)); 286 | shellCode = Bytes.addBytes(shellCode, addRsp(48)); 287 | compileCall(shellCode); 288 | } 289 | 290 | 291 | // driftHandle 漂移顺图 292 | public void driftHandle(int fx) { 293 | long address = personPtr(); 294 | long mapOffset = memory.readLong(address + Address.DtPyAddr); 295 | if (mapOffset == 0) { 296 | return; 297 | } 298 | 299 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.StPyAddr); 300 | 301 | long coordinateStructure = roomData + fx * Address.FxIdAddr + Address.ZbStPyAddr; 302 | int startX = memory.readInt(coordinateStructure); 303 | int startY = memory.readInt(coordinateStructure + 4); 304 | int endX = memory.readInt(coordinateStructure + 8); 305 | int endY = memory.readInt(coordinateStructure + 12); 306 | int x = 0; 307 | int y = 0; 308 | 309 | if (fx == 0) { 310 | x = startX + endX + 20; 311 | y = startY + endY / 2; 312 | } else if (fx == 1) { 313 | x = startX - 20; 314 | y = startY + endY / 2; 315 | } else if (fx == 2) { 316 | x = startX + endX / 2; 317 | y = startY + endY + 20; 318 | } else if (fx == 3) { 319 | x = startX + endX / 2; 320 | y = startY - 20; 321 | } 322 | if (x == 0 || y == 0) { 323 | logger.info("漂移顺图异常"); 324 | return; 325 | } 326 | driftCall(address, x, y, 0, 50); 327 | Timer.sleep(100); 328 | driftCall(address, startX + endX / 2, startY, 0, 50); 329 | } 330 | 331 | 332 | /** 333 | * 接受call 334 | * 335 | * @param taskId int 336 | */ 337 | public void acceptTaskCall(int taskId) { 338 | int[] data = subRsp(64); 339 | data = Bytes.addBytes(data, new int[]{186}, Bytes.intToBytes(taskId)); 340 | data = Bytes.addBytes(data, call(Address.JsCallAddr)); 341 | data = Bytes.addBytes(data, addRsp(64)); 342 | compileCall(data); 343 | } 344 | 345 | /** 346 | * 提交Call 347 | */ 348 | public void submitTaskCall(int taskId) { 349 | int[] data = subRsp(48); 350 | data = Bytes.addBytes(data, new int[]{65, 189, 1, 0, 0, 0}); 351 | data = Bytes.addBytes(data, new int[]{65, 190, 255, 255, 255, 255}); 352 | data = Bytes.addBytes(data, new int[]{65, 139, 205}); 353 | data = Bytes.addBytes(data, new int[]{69, 139, 198}); 354 | data = Bytes.addBytes(data, new int[]{72, 185}, Bytes.intToBytes(Address.TaskAddr)); 355 | data = Bytes.addBytes(data, new int[]{72, 139, 9}); 356 | data = Bytes.addBytes(data, new int[]{186}, Bytes.intToBytes(taskId)); 357 | data = Bytes.addBytes(data, call(Address.TjCallAddr)); 358 | data = Bytes.addBytes(data, addRsp(48)); 359 | compileCall(data); 360 | } 361 | 362 | /** 363 | * 完成任务Call 364 | * 365 | * @param taskId long 366 | */ 367 | public void finishTaskCall(long taskId) { 368 | int[] data = subRsp(512); 369 | data = Bytes.addBytes(data, new int[]{179, 255}); 370 | data = Bytes.addBytes(data, new int[]{68, 15, 182, 203}); 371 | data = Bytes.addBytes(data, new int[]{65, 176, 255}); 372 | data = Bytes.addBytes(data, new int[]{186}, Bytes.intToBytes(taskId)); 373 | data = Bytes.addBytes(data, call(Address.WcCallAddr)); 374 | data = Bytes.addBytes(data, addRsp(512)); 375 | compileCall(data); 376 | } 377 | 378 | /** 379 | * 坐标移动 380 | * @param x 坐标 381 | * @param y y坐标 382 | */ 383 | public void coordinateMove(int x, int y) { 384 | // .常量 人物坐标_1, "328", , , 0x148 创新中心获取 385 | // .常量 人物坐标_2, "69", , , 0x45 创新中心获取 386 | long personPtr = personPtr(); 387 | // 读取坐标指针 388 | long coordinatePtr = memory.readLong(memory.readLong(personPtr + 0x148L) + 8); 389 | // 计算坐标偏移量 390 | long offset = coordinatePtr + 0x45L; 391 | 392 | // 写入坐标值 393 | memory.writeFloat(offset, (float) x); 394 | memory.writeFloat(offset + 4, (float) y); 395 | } 396 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/GameMap.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.CoordinateType; 4 | import com.dnf.entity.GameMapType; 5 | import com.dnf.entity.MapDataType; 6 | import com.dnf.entity.MapNodeType; 7 | import jakarta.annotation.Resource; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | /** 14 | * @author 情歌 15 | */ 16 | @Component 17 | public class GameMap extends Base { 18 | 19 | @Resource 20 | private MapData mapData; 21 | 22 | private static MapNodeType getMapNodeType(CoordinateType mapEnd, CoordinateType waitHandleCoordinate, MapNodeType tmpNode) { 23 | int guessG; 24 | if (waitHandleCoordinate.x == tmpNode.currentCoordinates.x || waitHandleCoordinate.y == tmpNode.currentCoordinates.y) { 25 | guessG = 10; 26 | } else { 27 | guessG = 14; 28 | } 29 | MapNodeType waitHandleNode = new MapNodeType(); 30 | waitHandleNode.setG(tmpNode.g + guessG); 31 | waitHandleNode.setH(Math.toIntExact(Math.toIntExact(mapEnd.x) - Math.toIntExact(waitHandleCoordinate.x * 10L) + (mapEnd.y) - Math.toIntExact(waitHandleCoordinate.y * 10L))); 32 | waitHandleNode.setF(waitHandleNode.g + waitHandleNode.getH()); 33 | waitHandleNode.setCurrentCoordinates(waitHandleCoordinate); 34 | waitHandleNode.setFinalCoordinates(tmpNode.currentCoordinates); 35 | return waitHandleNode; 36 | } 37 | 38 | /** 39 | * 获取方向 40 | * 41 | * @param cutRoom 当前房间 42 | * @param nextRoom 下一个房间 43 | * @return 方向 44 | */ 45 | public int getDirection(CoordinateType cutRoom, CoordinateType nextRoom) { 46 | int direction = 0; 47 | int x = cutRoom.x - nextRoom.x; 48 | int y = cutRoom.y - nextRoom.y; 49 | if (x == 0 && y == 0) { 50 | return 4; 51 | } 52 | if (x == 0) { 53 | if (y == 1) { 54 | direction = 2; 55 | } else { 56 | direction = 3; 57 | } 58 | } else if (y == 0) { 59 | if (x == 1) { 60 | direction = 0; 61 | } else { 62 | direction = 1; 63 | } 64 | } 65 | return direction; 66 | } 67 | 68 | /** 69 | * 寻路_判断方向 70 | * 71 | * @param tx 方向 72 | * @param fx 方向 73 | * @return 是否可走 74 | */ 75 | public boolean judgeDirection(int tx, int fx) { 76 | // 方向数组 77 | int[] directionArr = new int[4]; 78 | // 方向集合 79 | int[][] directionSet = {{0, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 1, 1, 0}, {1, 0, 0, 0}, {1, 1, 0, 0}, {1, 0, 1, 0}, {1, 1, 1, 0}, {0, 0, 0, 1}, {0, 1, 0, 1}, {0, 0, 1, 1}, {0, 1, 1, 1}, {1, 0, 0, 1}, {1, 1, 0, 1}, {1, 0, 1, 1}, {1, 1, 1, 1}}; 80 | if (fx <= 15) { 81 | System.arraycopy(directionSet[tx], 0, directionArr, 0, 4); 82 | } else { 83 | for (int i = 0; i < 4; i++) { 84 | directionArr[i] = 0; 85 | } 86 | } 87 | return directionArr[fx] == 1; 88 | } 89 | 90 | /** 91 | * 整理坐标 92 | * 93 | * @param simulationRoute 模拟路线 94 | * @param realityRoute 真实路线 95 | * @return 消耗疲劳值 96 | */ 97 | public int tidyCoordinate(List simulationRoute, List realityRoute) { 98 | int x, y, k = 0; 99 | for (CoordinateType coordinateType : simulationRoute) { 100 | CoordinateType tempCoordinates = new CoordinateType(); 101 | x = (coordinateType.x + 2) % 3; 102 | y = (coordinateType.y + 2) % 3; 103 | if (x == 0 && y == 0) { 104 | tempCoordinates.x = (coordinateType.x + 2) / 3 - 1; 105 | tempCoordinates.y = (coordinateType.y + 2) / 3 - 1; 106 | realityRoute.add(k, tempCoordinates); 107 | k++; 108 | } 109 | } 110 | return k; 111 | } 112 | 113 | public MapDataType mapData() { 114 | MapDataType data = new MapDataType(); 115 | //(房间编号)+时间地址)+ 116 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.MxPyAddr); 117 | long roomIndex = mapData.decode(roomData + Address.SyPyAddr); 118 | 119 | data.width = memory.readInt(memory.readLong(roomData + Address.KgPyAddr) + roomIndex * 8); 120 | data.height = memory.readInt(memory.readLong(roomData + Address.KgPyAddr) + roomIndex * 8 + 4); 121 | data.tmp = memory.readLong(memory.readLong(roomData + Address.SzPyAddr) + 32 * roomIndex + 8); 122 | data.channelNum = data.width * data.height; 123 | for (int i = 0; i < data.channelNum; i++) { 124 | data.mapChannel.add(i, memory.readInt(data.tmp + i * 4L)); 125 | } 126 | 127 | data.startZb.x = mapData.getCutRoom().x + 1; 128 | data.startZb.y = mapData.getCutRoom().y + 1; 129 | data.endZb.x = mapData.getBossRoom().x + 1; 130 | data.endZb.y = mapData.getBossRoom().y + 1; 131 | 132 | if (data.startZb.x == data.endZb.x && data.startZb.y == data.endZb.y) { 133 | return data; 134 | } 135 | 136 | data.consumeFatigue = getRoute(data.mapChannel, data.width, data.height, data.startZb, data.endZb, data.mapRoute); 137 | return data; 138 | } 139 | 140 | /** 141 | * 获取走法 142 | * 143 | * @param mapChannel 地图通道 144 | * @param width 宽 145 | * @param height 高 146 | * @param mapStart 起始坐标 147 | * @param mapEnd 终点坐标 148 | * @param realityRoute 真实路线 149 | * @return 消耗疲劳值 150 | */ 151 | public int getRoute(List mapChannel, int width, int height, CoordinateType mapStart, CoordinateType mapEnd, List realityRoute) { 152 | CoordinateType startCoordinate = new CoordinateType(); 153 | CoordinateType endCoordinate = new CoordinateType(); 154 | 155 | if (mapStart.x == mapEnd.x && mapStart.y == mapEnd.y) { 156 | return 0; 157 | } 158 | 159 | GameMapType[][] mapArray = genMap(width, height, mapChannel); 160 | GameMapType[][] mapFlag = displayMap(mapArray, width, height); 161 | startCoordinate.x = mapStart.x * 3 - 2; 162 | startCoordinate.y = mapStart.y * 3 - 2; 163 | endCoordinate.x = mapEnd.x * 3 - 2; 164 | endCoordinate.y = mapEnd.y * 3 - 2; 165 | List crossWay = routeCalculate(mapFlag, startCoordinate, endCoordinate, width * 3, height * 3); 166 | return tidyCoordinate(crossWay, realityRoute); 167 | } 168 | 169 | /** 170 | * 生成地图 171 | * 172 | * @param width 宽 173 | * @param height 高 174 | * @param mapChannel 地图通道 175 | * @return 游戏地图 176 | */ 177 | public GameMapType[][] genMap(int width, int height, List mapChannel) { 178 | GameMapType[][] gameMap = new GameMapType[width][height]; 179 | for (int x = 0; x < width; x++) { 180 | for (int y = 0; y < height; y++) { 181 | gameMap[x][y] = new GameMapType(); 182 | } 183 | } 184 | 185 | int i = 0; 186 | for (int y = 0; y < height; y++) { 187 | for (int x = 0; x < width; x++) { 188 | gameMap[x][y].mapCoordinates.x = x; 189 | gameMap[x][y].mapCoordinates.y = y; 190 | gameMap[x][y].mapChannel = mapChannel.get(i); 191 | gameMap[x][y].left = judgeDirection(mapChannel.get(i), 0); 192 | gameMap[x][y].right = judgeDirection(mapChannel.get(i), 1); 193 | gameMap[x][y].up = judgeDirection(mapChannel.get(i), 2); 194 | gameMap[x][y].down = judgeDirection(mapChannel.get(i), 3); 195 | gameMap[x][y].backgroundColor = 0xFFFFFF; 196 | i++; 197 | if (gameMap[x][y].mapChannel == 0) { 198 | gameMap[x][y].backgroundColor = 0x000000; 199 | } 200 | } 201 | } 202 | 203 | return gameMap; 204 | } 205 | 206 | /** 207 | * 显示地图 208 | * 209 | * @param mapArr 地图数组 210 | * @param width 宽 211 | * @param height 高 212 | * @return 游戏地图 213 | */ 214 | public GameMapType[][] displayMap(GameMapType[][] mapArr, int width, int height) { 215 | GameMapType[][] mapLabel = new GameMapType[width * 3][height * 3]; 216 | for (int x = 0; x < width * 3; x++) { 217 | for (int y = 0; y < height * 3; y++) { 218 | mapLabel[x][y] = new GameMapType(); 219 | } 220 | } 221 | 222 | for (int y = 0; y < height; y++) { 223 | for (int x = 0; x < width; x++) { 224 | mapLabel[(x + 1) * 3 - 2][(y + 1) * 3 - 2].backgroundColor = 0xFFFFFF; 225 | if (mapArr[x][y].left) { 226 | mapLabel[(x + 1) * 3 - 3][(y + 1) * 3 - 2].backgroundColor = 0xFFFFFF; 227 | } 228 | if (mapArr[x][y].right) { 229 | mapLabel[(x + 1) * 3 - 1][(y + 1) * 3 - 2].backgroundColor = 0xFFFFFF; 230 | } 231 | if (mapArr[x][y].up) { 232 | mapLabel[(x + 1) * 3 - 2][(y + 1) * 3 - 3].backgroundColor = 0xFFFFFF; 233 | } 234 | if (mapArr[x][y].down) { 235 | mapLabel[(x + 1) * 3 - 2][(y + 1) * 3 - 1].backgroundColor = 0xFFFFFF; 236 | } 237 | } 238 | } 239 | return mapLabel; 240 | } 241 | 242 | /** 243 | * 路径计算 244 | * 245 | * @param mapLabel 地图标签 246 | * @param mapStart 起始坐标 247 | * @param mapEnd 终点坐标 248 | * @param width 宽 249 | * @param height 高 250 | * @return 路径 251 | */ 252 | public List routeCalculate(GameMapType[][] mapLabel, CoordinateType mapStart, CoordinateType mapEnd, int width, int height) { 253 | MapNodeType tmpNode = new MapNodeType(); // 待检测节点, 临时节点 254 | List openList = new ArrayList<>(); // 开放列表 255 | List closeList = new ArrayList<>(); // 关闭列表 256 | 257 | int shortEstNum = 0; // 最短编号 258 | 259 | tmpNode.currentCoordinates.x = mapStart.x; 260 | tmpNode.currentCoordinates.y = mapStart.y; 261 | 262 | try { 263 | mapLabel[Math.toIntExact(mapStart.x)][Math.toIntExact(mapStart.y)].backgroundColor = 0x00FF00; 264 | mapLabel[Math.toIntExact(mapStart.x)][Math.toIntExact(mapStart.y)].backgroundColor = 0x0000FF; 265 | openList.add(0, tmpNode); 266 | } catch (Exception e) { 267 | logger.error(e.getMessage()); 268 | } 269 | List moveArr = new ArrayList<>(); 270 | 271 | do { 272 | int minF = 0; 273 | for (int y = 0; y < openList.size(); y++) { 274 | if (minF == 0) { 275 | minF = openList.get(0).f; 276 | shortEstNum = y; 277 | } 278 | if (openList.get(y).f < minF) { 279 | minF = openList.get(y).f; 280 | shortEstNum = y; 281 | } 282 | } 283 | 284 | try { 285 | tmpNode = openList.get(shortEstNum); 286 | openList.remove(shortEstNum); 287 | closeList.add(0, tmpNode); 288 | } catch (RuntimeException e) { 289 | logger.error(e.getMessage()); 290 | } 291 | if (tmpNode.currentCoordinates.x != mapStart.x || tmpNode.currentCoordinates.y != mapStart.y) { 292 | if (tmpNode.currentCoordinates.x != mapEnd.x || tmpNode.currentCoordinates.y != mapEnd.y) { 293 | mapLabel[Math.toIntExact(tmpNode.currentCoordinates.x)][Math.toIntExact(tmpNode.currentCoordinates.y)].backgroundColor = 0x0080FF; 294 | } 295 | } 296 | for (int y = 0; y < closeList.size(); y++) { 297 | if (closeList.get(y).currentCoordinates.x == mapEnd.x && closeList.get(y).currentCoordinates.y == mapEnd.y) { 298 | MapNodeType waitHandleNode = closeList.get(y); 299 | do { 300 | for (MapNodeType mapNodeType : closeList) { 301 | if (mapNodeType.currentCoordinates.x == waitHandleNode.finalCoordinates.x && mapNodeType.currentCoordinates.y == waitHandleNode.finalCoordinates.y) { 302 | waitHandleNode = mapNodeType; 303 | break; 304 | } 305 | } 306 | if (waitHandleNode.currentCoordinates.x != mapStart.x || waitHandleNode.currentCoordinates.y != mapStart.y) { 307 | mapLabel[Math.toIntExact(waitHandleNode.currentCoordinates.x)][Math.toIntExact(waitHandleNode.currentCoordinates.y)].backgroundColor = 0x00D8D8; 308 | moveArr.add(0, waitHandleNode.currentCoordinates); 309 | } 310 | 311 | } while (waitHandleNode.currentCoordinates.x != mapStart.x || waitHandleNode.currentCoordinates.y != mapStart.y); 312 | moveArr.add(0, mapStart); 313 | moveArr.add(mapEnd); 314 | return moveArr; 315 | } 316 | } 317 | for (int y = 0; y < 4; y++) { 318 | CoordinateType waitHandleCoordinate = new CoordinateType(); // 待检测坐标 319 | if (y == 0) { 320 | waitHandleCoordinate.x = tmpNode.currentCoordinates.x; 321 | waitHandleCoordinate.y = tmpNode.currentCoordinates.y - 1; 322 | } else if (y == 1) { 323 | waitHandleCoordinate.x = tmpNode.currentCoordinates.x - 1; 324 | waitHandleCoordinate.y = tmpNode.currentCoordinates.y; 325 | } else if (y == 2) { 326 | waitHandleCoordinate.x = tmpNode.currentCoordinates.x + 1; 327 | waitHandleCoordinate.y = tmpNode.currentCoordinates.y; 328 | } else { 329 | waitHandleCoordinate.x = tmpNode.currentCoordinates.x; 330 | waitHandleCoordinate.y = tmpNode.currentCoordinates.y + 1; 331 | } 332 | if (waitHandleCoordinate.x < 0 || waitHandleCoordinate.x > (width - 1) || waitHandleCoordinate.y < 0 || waitHandleCoordinate.y > (height - 1)) { 333 | continue; 334 | } 335 | if (mapLabel[Math.toIntExact(waitHandleCoordinate.x)][Math.toIntExact(waitHandleCoordinate.y)].backgroundColor == 0x000000) { 336 | continue; 337 | } 338 | boolean existCloseList = false; 339 | for (MapNodeType nodeType : closeList) { 340 | if (nodeType.currentCoordinates.x == waitHandleCoordinate.x && nodeType.currentCoordinates.y == waitHandleCoordinate.y) { 341 | existCloseList = true; 342 | break; 343 | } 344 | } 345 | if (existCloseList) { 346 | continue; 347 | } 348 | boolean existOpenList = false; 349 | for (MapNodeType mapNodeType : openList) { 350 | if (mapNodeType.currentCoordinates.x == waitHandleCoordinate.x && mapNodeType.currentCoordinates.y == waitHandleCoordinate.y) { 351 | int guessG; 352 | if (waitHandleCoordinate.x != tmpNode.currentCoordinates.x || waitHandleCoordinate.y != tmpNode.currentCoordinates.y) { 353 | guessG = 14; 354 | } else { 355 | guessG = 10; 356 | } 357 | 358 | if (tmpNode.g + guessG < mapNodeType.g) { 359 | mapNodeType.setFinalCoordinates(tmpNode.currentCoordinates); 360 | } 361 | 362 | existOpenList = true; 363 | break; 364 | } 365 | } 366 | if (!existOpenList) { 367 | MapNodeType waitHandleNode = getMapNodeType(mapEnd, waitHandleCoordinate, tmpNode); 368 | openList.add(0, waitHandleNode); 369 | } 370 | 371 | } 372 | } while (!openList.isEmpty()); 373 | return moveArr; 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Initialize.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import com.dnf.constant.IniConstant; 5 | import com.dnf.helper.FileUtils; 6 | import com.dnf.helper.IniUtils; 7 | import com.dnf.helper.Process; 8 | import com.sun.jna.platform.win32.User32; 9 | import com.sun.jna.platform.win32.Win32VK; 10 | import com.sun.jna.platform.win32.WinUser; 11 | import jakarta.annotation.Resource; 12 | import org.springframework.stereotype.Component; 13 | 14 | /** 15 | * @author 情歌 16 | */ 17 | @Component 18 | public class Initialize extends Base { 19 | @Resource 20 | private AutoThread autoThread; 21 | 22 | @Resource 23 | private Screen screen; 24 | 25 | public void init() { 26 | String modelName = "dnf.exe"; 27 | int processId = Process.getProcessId(modelName); 28 | if (processId == 0) { 29 | logger.info("等待游戏运行..."); 30 | do { 31 | processId = Process.getProcessId(modelName); 32 | } while (processId == 0); 33 | } 34 | 35 | // 初始化配置文件 36 | initConfigIni(); 37 | 38 | // 设置全局进程id 39 | memory.setProcessId(processId); 40 | // 判断是否有图标 41 | if (memory.readInt(0x140000000L) != 9460301) { 42 | logger.error("无读写权限"); 43 | return; 44 | } 45 | 46 | initEmptyAddress(); 47 | 48 | logger.info("加载成功-欢迎使用"); 49 | logger.info("当前时间:{}", DateUtil.date(System.currentTimeMillis())); 50 | hotKey(); 51 | } 52 | 53 | /** 54 | * 注册热键 55 | */ 56 | private void hotKey() { 57 | User32 user32 = User32.INSTANCE; 58 | 59 | user32.RegisterHotKey(null, Win32VK.VK_F1.code, 0, Win32VK.VK_F1.code); 60 | user32.RegisterHotKey(null, Win32VK.VK_END.code, 0, Win32VK.VK_END.code); 61 | user32.RegisterHotKey(null, Win32VK.VK_OEM_3.code, 0, Win32VK.VK_OEM_3.code); 62 | 63 | // 消息循环 64 | WinUser.MSG msg = new WinUser.MSG(); 65 | while (user32.GetMessage(msg, null, 0, 0) != 0) { 66 | if (msg.message == WinUser.WM_HOTKEY) { 67 | int hotkeyId = msg.wParam.intValue(); 68 | if (hotkeyId == Win32VK.VK_F1.code) { 69 | screen.screenSwitch(); 70 | } else if (hotkeyId == Win32VK.VK_END.code) { 71 | autoThread.autoSwitch(); 72 | } else if (hotkeyId == Win32VK.VK_OEM_3.code) { 73 | screen.screenKill(); 74 | } 75 | } else { 76 | user32.TranslateMessage(msg); 77 | user32.DispatchMessage(msg); 78 | } 79 | } 80 | } 81 | 82 | 83 | /** 84 | * 初始化空白地址 85 | */ 86 | private void initEmptyAddress() { 87 | Address.RwKbAddr = memory.allocate(4096); 88 | Address.NcBhKbAddr = memory.allocate(4096); 89 | Address.JnKbAddr = memory.allocate(4096); 90 | Address.GtKbAddr = memory.allocate(4096); 91 | 92 | logger.info("人物基址 {}", Long.toHexString(Address.RwKbAddr)); 93 | logger.info("内存汇编 {}", Long.toHexString(Address.NcBhKbAddr)); 94 | logger.info("技能空白 {}", Long.toHexString(Address.JnKbAddr)); 95 | logger.info("过图空白 {}", Long.toHexString(Address.GtKbAddr)); 96 | } 97 | 98 | private void initConfigIni() { 99 | // 判断是否存在全局计次配置文件 100 | FileUtils fileUtils = new FileUtils(IniConstant.CONFIG); 101 | if (!fileUtils.exists()) { 102 | fileUtils.create(); 103 | IniUtils iniUtils = new IniUtils(); 104 | iniUtils.setFilename(IniConstant.CONFIG).write("default", "count", 0); 105 | } 106 | 107 | FileUtils file = new FileUtils(IniConstant.HELPER); 108 | file.exists(); 109 | } 110 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/MapData.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.CoordinateType; 4 | import com.dnf.entity.MapTraversalType; 5 | import com.dnf.helper.Strings; 6 | import jakarta.annotation.Resource; 7 | import org.springframework.stereotype.Component; 8 | 9 | /** 10 | * @author 情歌 11 | */ 12 | @Component 13 | public class MapData extends Base { 14 | @Resource 15 | private GameCall gameCall; 16 | 17 | /** 18 | * 加密 19 | * 20 | * @param address long 内存地址 21 | * @param value int 数值 22 | */ 23 | public void encode(long address, int value) { 24 | memory.writeInt(address, value); 25 | } 26 | 27 | /** 28 | * 解密 29 | * 30 | * @param address long 内存地址 31 | * @return int 32 | */ 33 | public int decode(long address) { 34 | return memory.readInt(address); 35 | } 36 | 37 | /** 38 | * 游戏状态 39 | * 40 | * @return int 0选角 1城镇 2选图 3图内 5选择频道 41 | */ 42 | public int getStat() { 43 | return memory.readInt(Address.YXZTAddr); 44 | } 45 | 46 | 47 | /** 48 | * 是否城镇 49 | * 50 | * @return boolean 51 | */ 52 | public boolean isTown() { 53 | long personPtr = gameCall.personPtr(); 54 | return memory.readInt(personPtr + Address.DtPyAddr) == 0; 55 | } 56 | 57 | 58 | public boolean isOpenDoor() { 59 | long personPtr = gameCall.personPtr(); 60 | long encodeData = memory.readLong(memory.readLong(personPtr + Address.DtPyAddr-8) + 16); 61 | return decode(encodeData + Address.SfKmAddr) == 0; 62 | } 63 | 64 | public boolean isBossRoom() { 65 | CoordinateType cut = getCutRoom(); 66 | CoordinateType boss = getBossRoom(); 67 | return cut.x == boss.x && cut.y == boss.y; 68 | } 69 | 70 | /** 71 | * 获取当前房间坐标 72 | * 73 | * @return CoordinateType 74 | */ 75 | public CoordinateType getCutRoom() { 76 | CoordinateType result = new CoordinateType(); 77 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.MxPyAddr); 78 | result.x = memory.readInt(roomData + Address.CutRoomXAddr); 79 | result.y = memory.readInt(roomData + Address.CutRoomYAddr); 80 | return result; 81 | } 82 | 83 | /** 84 | * 获取boss房间坐标 85 | * 86 | * @return CoordinateType 87 | */ 88 | public CoordinateType getBossRoom() { 89 | CoordinateType result = new CoordinateType(); 90 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.MxPyAddr); 91 | result.x = decode(roomData + Address.BOSSRoomXAddr); 92 | result.y = decode(roomData + Address.BOSSRoomYAddr); 93 | return result; 94 | } 95 | 96 | /** 97 | * 是否通关 98 | * 99 | * @return boolean 100 | */ 101 | public boolean isPass() { 102 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.MxPyAddr); 103 | int dataVal = memory.readInt(roomData + Address.GouHuoAddr); 104 | return dataVal == 2 || dataVal == 0; 105 | } 106 | 107 | /** 108 | * 获取疲劳值 109 | * 110 | * @return int 111 | */ 112 | public int getPl() { 113 | return decode(Address.MaxPlAddr) - decode(Address.CutPlAddr); 114 | } 115 | 116 | /** 117 | * 获取角色等级 118 | * 119 | * @return int 120 | */ 121 | public int getRoleLevel() { 122 | return memory.readInt(Address.JSDjAddr); 123 | } 124 | 125 | /** 126 | * 获取地图名称 127 | * 128 | * @return string 129 | */ 130 | public String getMapName() { 131 | long roomData = memory.readLong(memory.readLong(memory.readLong(Address.FJBHAddr) + Address.SJAddr) + Address.MxPyAddr); 132 | int[] mapByte = memory.readByte(memory.readLong(roomData + Address.DtMcAddr), 52); 133 | return Strings.unicodeToAscii(mapByte); 134 | } 135 | 136 | /** 137 | * 读坐标 138 | * 139 | * @param param int 140 | * @return CoordinateType 141 | */ 142 | public CoordinateType readCoordinate(long param) { 143 | CoordinateType coordinate = new CoordinateType(); 144 | if (memory.readInt(param + Address.LxPyAddr) == 273) { 145 | long ptr = memory.readLong(param + Address.DqZbAddr); 146 | coordinate.setX((int) memory.readFloat(ptr)); 147 | coordinate.setY((int) memory.readFloat(ptr + 4)); 148 | coordinate.setZ((int) memory.readFloat(ptr + 8)); 149 | } else { 150 | long ptr = memory.readLong(param + Address.FxPyAddr); 151 | coordinate.setX((int) memory.readFloat(ptr + 32)); 152 | coordinate.setY((int) memory.readFloat(ptr + 36)); 153 | coordinate.setZ((int) memory.readFloat(ptr + 40)); 154 | } 155 | return coordinate; 156 | } 157 | 158 | public boolean isDialogA() { 159 | return memory.readInt(Address.DHAddr) == 1; 160 | } 161 | 162 | public boolean isDialogB() { 163 | return memory.readInt(Address.DHAddrB) == 1; 164 | } 165 | 166 | public boolean isDialogEsc() { 167 | return memory.readInt(Address.EscDHAddr) == 1; 168 | } 169 | 170 | /** 171 | * 背包负重 172 | * 173 | * @return int 174 | */ 175 | public int backpackWeight() { 176 | long personPtr = gameCall.personPtr(); 177 | long backPackPtr = memory.readLong(personPtr + Address.WplAddr); 178 | int cutWeight = decode(backPackPtr + 0x58L); 179 | int maxWeight = decode(personPtr + Address.ZdFzAddr); 180 | float result = (float) cutWeight / maxWeight * 100; 181 | logger.debug("背包负重: {}", result); 182 | return (int) result; 183 | } 184 | 185 | /** 186 | * 获取名望 187 | * 188 | * @return int 189 | */ 190 | public int getFame() { 191 | long personPtr = gameCall.personPtr(); 192 | return memory.readInt(personPtr + Address.RwMwAddr); 193 | } 194 | 195 | /** 196 | * 取遍历指针 197 | * 198 | * @param ptr long 指针地址 199 | * @param offset int 漂移计次 200 | * @param t int 1 物品 2 地图 201 | * @return long 202 | */ 203 | public long getTraversalPtr(long ptr, long offset, int t) { 204 | long result = 0; 205 | 206 | if (t == 1) { 207 | long one = memory.readLong(ptr + (offset - 1) * 8L); 208 | long two = memory.readLong(one - 72); 209 | result = memory.readLong(two + 16); 210 | } 211 | if (t == 2) { 212 | long one = memory.readLong(ptr + (offset - 1) * 24L); 213 | result = memory.readLong(one + 16) - 48; 214 | } 215 | 216 | return result; 217 | } 218 | 219 | /** 220 | * 地图遍历数据 221 | * 222 | * @return MapTraversalType 223 | */ 224 | public MapTraversalType getMapData() { 225 | MapTraversalType data = new MapTraversalType(); 226 | data.rwAddr = gameCall.personPtr(); 227 | data.mapData = memory.readLong(memory.readLong(data.rwAddr + Address.DtPyAddr-8) + 16); 228 | data.start = memory.readLong(data.mapData + Address.DtKs2); 229 | data.end = memory.readLong(data.mapData + Address.DtJs2); 230 | data.objNum = (data.end - data.start) / 24; 231 | return data; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Screen.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.CoordinateType; 4 | import com.dnf.entity.GlobalData; 5 | import com.dnf.entity.MapTraversalType; 6 | import com.dnf.helper.IniUtils; 7 | import com.dnf.helper.Timer; 8 | import jakarta.annotation.Resource; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @author 情歌 13 | */ 14 | @Component 15 | public class Screen extends Base { 16 | @Resource 17 | private GameCall gameCall; 18 | 19 | @Resource 20 | private MapData mapData; 21 | 22 | @Resource 23 | private IniUtils iniUtils; 24 | 25 | 26 | public void screenSwitch() { 27 | if (!GlobalData.screenSwitch) { 28 | Thread thread = new Thread(() -> { 29 | while (GlobalData.screenSwitch) { 30 | Timer.sleep(300); 31 | fullScreen(); 32 | } 33 | }); 34 | thread.start(); 35 | GlobalData.screenSwitch = true; 36 | logger.info("技能全屏 - [ √ ]"); 37 | } else { 38 | GlobalData.screenSwitch = false; 39 | logger.info("技能全屏 - [ x ]"); 40 | } 41 | } 42 | 43 | private void fullScreen() { 44 | if (mapData.getStat() != 3) { 45 | return; 46 | } 47 | 48 | // 地图遍历数据 49 | MapTraversalType data = mapData.getMapData(); 50 | 51 | Integer screenCode = iniUtils.read("自动配置", "技能代码", Integer.class); 52 | Integer screenHarm = iniUtils.read("自动配置", "技能伤害", Integer.class); 53 | Integer screenSize = iniUtils.read("自动配置", "技能大小", Integer.class); 54 | Integer screenNumber = iniUtils.read("自动配置", "技能个数", Integer.class); 55 | 56 | int num = 0; 57 | 58 | for (data.objTmp = 1; data.objTmp < data.objNum; data.objTmp++) { 59 | data.objPtr = mapData.getTraversalPtr(data.start, data.objTmp, 2); 60 | data.objTypeA = memory.readInt(data.objPtr + Address.LxPyAddr); 61 | data.objCamp = memory.readInt(data.objPtr + Address.ZyPyAddr); 62 | data.objCode = memory.readInt(data.objPtr + Address.DmPyAddr); 63 | if (data.objTypeA == 529 || data.objTypeA == 545 || data.objTypeA == 273 || data.objTypeA == 61440) { 64 | long objBlood = memory.readLong(data.objPtr + Address.GwXlAddr); 65 | if (data.objCamp > 0 && data.objCode > 0 && objBlood > 0 && data.objPtr != data.rwAddr) { 66 | CoordinateType monster = mapData.readCoordinate(data.objPtr); 67 | gameCall.skillCall(data.rwAddr, screenCode, screenHarm, monster.x, monster.y, 0, (float) screenSize); 68 | num++; 69 | if (num >= screenNumber) { 70 | break; 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | public void screenKill() { 78 | gameCall.skillCall(0, 54141, 0, 0, 0, 0, 1.0F); 79 | logger.info("秒杀完毕 - [ √ ]"); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/SendPack.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.helper.Bytes; 4 | import jakarta.annotation.Resource; 5 | import org.springframework.stereotype.Component; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * @author 情歌 11 | */ 12 | @Component 13 | public class SendPack extends Base { 14 | @Resource 15 | private GameCall gameCall; 16 | private int[] data; 17 | 18 | /** 19 | * 缓冲call 20 | * 21 | * @param param int 22 | */ 23 | private void hcCall(int param) { 24 | data = gameCall.subRsp(256); 25 | data = Bytes.addBytes(data, new int[]{72, 185}, Bytes.intToBytes(Address.FbAddr)); 26 | data = Bytes.addBytes(data, new int[]{186}, Bytes.intToBytes(param)); 27 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.HcCallAddr)); 28 | data = Bytes.addBytes(data, new int[]{255, 208}); 29 | data = Bytes.addBytes(data, gameCall.addRsp(256)); 30 | } 31 | 32 | /** 33 | * 加密call 34 | * 35 | * @param param long 36 | * @param length int 37 | */ 38 | private void jmCall(long param, int length) { 39 | data = Bytes.addBytes(data, gameCall.subRsp(256)); 40 | data = Bytes.addBytes(data, new int[]{72, 185}, Bytes.intToBytes(Address.FbAddr)); 41 | data = Bytes.addBytes(data, new int[]{72, 186}, Bytes.intToBytes(param)); 42 | if (length == 1) { 43 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.JmB1CallAddr)); 44 | } 45 | if (length == 2) { 46 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.JmB2CallAddr)); 47 | } 48 | if (length == 4) { 49 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.JmB3CallAddr)); 50 | } 51 | if (length == 8) { 52 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.JmB4CallAddr)); 53 | } 54 | data = Bytes.addBytes(data, new int[]{255, 208}); 55 | data = Bytes.addBytes(data, gameCall.addRsp(256)); 56 | } 57 | 58 | /** 59 | * 发包call 60 | */ 61 | private void fbCall() { 62 | data = Bytes.addBytes(data, gameCall.subRsp(256)); 63 | data = Bytes.addBytes(data, new int[]{72, 184}, Bytes.intToBytes(Address.FbCallAddr)); 64 | data = Bytes.addBytes(data, new int[]{255, 208}); 65 | data = Bytes.addBytes(data, gameCall.addRsp(256)); 66 | gameCall.compileCall(data); 67 | Arrays.fill(data, 0); 68 | } 69 | 70 | /** 71 | * 组包返回角色 72 | */ 73 | public void returnRole() { 74 | hcCall(7); 75 | fbCall(); 76 | } 77 | 78 | /** 79 | * 组包选择角色 80 | * 81 | * @param index 角色索引 82 | */ 83 | public void selectRole(int index) { 84 | if (index == 0) { 85 | return; 86 | } 87 | hcCall(4); 88 | jmCall(index, 2); 89 | fbCall(); 90 | } 91 | 92 | /** 93 | * 组包选图 94 | */ 95 | public void selectMap() { 96 | hcCall(15); 97 | jmCall(0, 4); 98 | fbCall(); 99 | } 100 | 101 | /** 102 | * 组包进图 103 | * 104 | * @param bh 地图编号 105 | * @param nd 难度 106 | * @param sy 模式 107 | * @param lx 类型 108 | */ 109 | public void goMap(long bh, long nd, int sy, int lx) { 110 | hcCall(16); 111 | jmCall(bh, 4); 112 | jmCall(nd, 1); 113 | jmCall(0, 2); 114 | jmCall(sy, 1); 115 | jmCall(lx, 1); 116 | jmCall(65535, 2); 117 | jmCall(0, 4); 118 | jmCall(0, 1); 119 | jmCall(0, 4); 120 | jmCall(0, 1); 121 | jmCall(0, 4); 122 | fbCall(); 123 | } 124 | 125 | /** 126 | * 组包翻牌 127 | * 128 | * @param h 高位 129 | * @param l 低位 130 | */ 131 | public void getIncome(int h, int l) { 132 | hcCall(69); 133 | fbCall(); 134 | hcCall(70); 135 | fbCall(); 136 | hcCall(71); 137 | jmCall(h, 1); 138 | jmCall(l, 1); 139 | fbCall(); 140 | hcCall(1426); 141 | fbCall(); 142 | } 143 | 144 | /** 145 | * 组包出图 146 | */ 147 | public void leaveMap() { 148 | hcCall(42); 149 | fbCall(); 150 | } 151 | 152 | 153 | /** 154 | * 组包移动 155 | * 156 | * @param maxMap 最大地图编号 157 | * @param mixMap 最小地图编号 158 | * @param x x坐标 159 | * @param y y坐标 160 | */ 161 | public void moveMap(Long maxMap, Long mixMap, Long x, Long y) { 162 | if (maxMap < 0 || mixMap < 0 || x < 0 || y < 0) { 163 | return; 164 | } 165 | hcCall(36); 166 | jmCall(maxMap, 4); 167 | jmCall(mixMap, 4); 168 | jmCall(x, 2); 169 | jmCall(y, 2); 170 | jmCall(5, 1); 171 | jmCall(38, 4); 172 | jmCall(1, 2); 173 | jmCall(0, 4); 174 | jmCall(0, 1); 175 | jmCall(5, 1); 176 | fbCall(); 177 | } 178 | 179 | /** 180 | * 组包拾取 181 | * 182 | * @param addr 地址 183 | */ 184 | public void pickUp(long addr) { 185 | if (addr < 0) { 186 | return; 187 | } 188 | hcCall(43); 189 | jmCall(addr, 4); 190 | jmCall(0, 1); 191 | jmCall(1, 1); 192 | jmCall(420, 2); 193 | jmCall(254, 2); 194 | jmCall(4501, 2); 195 | jmCall(435, 2); 196 | jmCall(271, 2); 197 | jmCall(22624, 2); 198 | jmCall(28402, 2); 199 | jmCall(0, 1); 200 | fbCall(); 201 | } 202 | 203 | /** 204 | * 组包分解 205 | * 206 | * @param address 装备位置 207 | */ 208 | public void decomposition(int address) { 209 | if (address < 0) { 210 | return; 211 | } 212 | hcCall(26); 213 | jmCall(0, 1); 214 | jmCall(65535, 2); 215 | jmCall(317, 4); 216 | jmCall(1, 1); 217 | jmCall(address, 2); 218 | fbCall(); 219 | } 220 | 221 | 222 | /** 223 | * 组包出售 224 | * 225 | * @param index 出售位置数组 226 | */ 227 | public void sellEquip(long index) { 228 | if (index < 0) { 229 | return; 230 | } 231 | hcCall(22); 232 | jmCall(317, 4); 233 | jmCall(95, 4); 234 | jmCall(1, 1); 235 | jmCall(0, 1); 236 | jmCall(index, 2); 237 | jmCall(1, 4); 238 | jmCall(index * 2 + 2, 4); 239 | fbCall(); 240 | } 241 | 242 | /** 243 | * 整理背包 244 | * 245 | * @param packType 背包类型 246 | * @param packAddress 背包地址 247 | */ 248 | public void tidyBackpack(int packType, int packAddress) { 249 | hcCall(20); 250 | jmCall(6, 4); 251 | jmCall(16, 1); 252 | jmCall(packType, 1); // 背包类型:1 装备;2消耗品;3材料;4任务;10副职业 253 | jmCall(packAddress, 1); // 背包地址:0 背包;2个人仓库;12账号金库 254 | jmCall(packAddress, 1); // 排序方式:0 栏位排序;1品级排序;2Lv排序;3部位排序 255 | fbCall(); 256 | } 257 | 258 | /** 259 | * 接受任务 260 | * 261 | * @param taskId 任务ID 262 | */ 263 | public void acceptTask(int taskId) { 264 | hcCall(31); 265 | jmCall(31, 2); 266 | jmCall(taskId, 2); 267 | fbCall(); 268 | } 269 | 270 | /** 271 | * 放弃任务 272 | * 273 | * @param taskId 任务ID 274 | */ 275 | public void giveUpTask(int taskId) { 276 | hcCall(32); 277 | jmCall(32, 2); 278 | jmCall(taskId, 2); 279 | fbCall(); 280 | } 281 | 282 | /** 283 | * 完成任务 284 | * 285 | * @param taskId 任务ID 286 | */ 287 | public void finishTask(int taskId) { 288 | hcCall(33); 289 | jmCall(33, 2); 290 | jmCall(taskId, 2); 291 | jmCall(0, 1); 292 | jmCall(0, 1); 293 | fbCall(); 294 | } 295 | 296 | 297 | /** 298 | * 提交任务 299 | * 300 | * @param taskId 任务ID 301 | */ 302 | public void submitTask(int taskId) { 303 | hcCall(34); 304 | jmCall(34, 2); 305 | jmCall(taskId, 2); 306 | jmCall(65535, 2); 307 | jmCall(1, 2); 308 | jmCall(65535, 2); 309 | fbCall(); 310 | } 311 | } 312 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Task.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.GlobalData; 4 | import com.dnf.helper.Strings; 5 | import com.dnf.helper.Timer; 6 | import jakarta.annotation.Resource; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * @author 情歌 15 | */ 16 | @Component 17 | public class Task extends Base { 18 | public static String taskName; 19 | public static String taskCondition; 20 | public static int taskId; 21 | @Resource 22 | private MapData mapData; 23 | @Resource 24 | private GameCall gameCall; 25 | @Resource 26 | private SendPack sendPack; 27 | 28 | /** 29 | * 处理任务 30 | * 31 | * @return long 32 | */ 33 | public int handleTask() { 34 | int mapId; 35 | int nextTaskId = 0; 36 | // 无任务刷新角色 37 | boolean refreshTask = false; 38 | taskName = ""; 39 | taskCondition = ""; 40 | taskId = 0; 41 | 42 | submitTask(); 43 | 44 | while (true) { 45 | Timer.sleep(200); 46 | mainLineTask(); 47 | 48 | // 处理相同任务输出 49 | if (taskId != nextTaskId) { 50 | nextTaskId = taskId; 51 | logger.info("任务名称 [{}]", taskName); 52 | } 53 | 54 | // 无任务,刷新角色 55 | if (taskId == 0) { 56 | if (!refreshTask) { 57 | Timer.sleep(200); 58 | logger.info("暂无任务或卡任务,重新选择角色"); 59 | //返回角色 60 | sendPack.returnRole(); 61 | Timer.sleep(2000); 62 | // 选择角色 63 | sendPack.selectRole(GlobalData.roleCount - 1); 64 | Timer.sleep(500); 65 | refreshTask = true; 66 | continue; 67 | } else { 68 | mapId = highestMap(); 69 | logger.info("暂无任务,执行适应等级地图"); 70 | break; 71 | } 72 | } 73 | 74 | refreshTask = false; 75 | 76 | // 任务未接,执行接取任务 77 | if (finishStatus(taskId) == -1) { 78 | gameCall.acceptTaskCall(taskId); 79 | } 80 | 81 | // 跳过部分无法完成任务,取最高等级执行 82 | // 任务名称[返回赫顿玛尔],任务条件[[seek n meet npc]],任务ID[3509] 材料不足无法完成任务 83 | // 任务名称[黑市的商人],任务条件[[seek n meet npc]],任务ID[5943] 蛇肉任务 84 | if (taskId == 3509 || taskId == 5943) { 85 | mapId = highestMap(); 86 | logger.info("无法完成任务,执行适应等级地图"); 87 | break; 88 | } 89 | 90 | // 任务完成,执行提交任务 91 | if (finishStatus(taskId) == 0) { 92 | gameCall.submitTaskCall(taskId); 93 | continue; 94 | } 95 | 96 | // 剧情条件判断 97 | if (conditional(taskCondition) == 1) { 98 | gameCall.finishTaskCall(taskId); 99 | } 100 | // 刷图任务 101 | if (conditional(taskCondition) == 2) { 102 | mapId = taskMap(taskId); 103 | if (mapId > 0) { 104 | break; 105 | } 106 | } 107 | 108 | if (conditional(taskCondition) == 3) { 109 | logger.info("材料任务无法自动完成,执行最高等级地图"); 110 | } 111 | } 112 | 113 | return mapId; 114 | } 115 | 116 | 117 | /** 118 | * 主线任务 119 | */ 120 | public void mainLineTask() { 121 | long taskAddress = memory.readLong(Address.TaskAddr); 122 | long start = memory.readLong(taskAddress + Address.QbRwStartAddr); 123 | long end = memory.readLong(taskAddress + Address.QbRwEndAddr); 124 | long num = (end - start) / 8; 125 | 126 | for (long i = 0; i < num; i++) { 127 | long taskPtr = memory.readLong(start + i * 8); 128 | int taskType = memory.readInt(taskPtr + Address.RwLxAddr); 129 | if (taskType == 0) { 130 | int taskLength = memory.readInt(taskPtr + Address.RwDxAddr); 131 | if (taskLength > 7) { 132 | taskName = Strings.unicodeToAscii(memory.readByte(memory.readLong(taskPtr + 16), 100)); 133 | } else { 134 | taskName = Strings.unicodeToAscii(memory.readByte(taskPtr + 16, 100)); 135 | } 136 | // 任务条件 137 | taskCondition = Strings.unicodeToAscii(memory.readByte(memory.readLong(taskPtr + Address.RwTjAddr), 100)); 138 | // 任务编号 139 | taskId = memory.readInt(taskPtr); 140 | break; 141 | } 142 | } 143 | } 144 | 145 | /** 146 | * 提交任务 147 | */ 148 | public void submitTask() { 149 | long taskAddress = memory.readLong(Address.TaskAddr); 150 | long start = memory.readLong(taskAddress + Address.QbRwStartAddr); 151 | long end = memory.readLong(taskAddress + Address.QbRwEndAddr); 152 | long num = (end - start) / 8; 153 | 154 | for (long i = 0; i < num; i++) { 155 | long taskPtr = memory.readLong(start + i * 8); 156 | int taskType = memory.readInt(taskPtr + Address.RwLxAddr); 157 | if (taskType == 0) { 158 | gameCall.submitTaskCall(memory.readInt(taskPtr)); 159 | } 160 | } 161 | 162 | start = memory.readLong(taskAddress + Address.YjRwStartAddr); 163 | end = memory.readLong(taskAddress + Address.YjRwEndAddr); 164 | num = (end - start) / 16; 165 | for (long i = 0; i < num; i++) { 166 | long taskPtr = memory.readLong(start + i * 16); 167 | int taskType = memory.readInt(taskPtr + Address.RwLxAddr); 168 | if (taskType == 0) { 169 | gameCall.submitTaskCall(memory.readInt(taskPtr)); 170 | } 171 | } 172 | } 173 | 174 | public long finishStatus(long taskId) { 175 | long taskAddress = memory.readLong(Address.TaskAddr); 176 | long start = memory.readLong(taskAddress + Address.YjRwStartAddr); 177 | long end = memory.readLong(taskAddress + Address.YjRwEndAddr); 178 | long num = (end - start) / 16; 179 | long loopEnd = start + num * 16L; 180 | 181 | for (long i = start; i < loopEnd; i += 16L) { 182 | long taskPtr = memory.readLong(i); 183 | if (memory.readInt(taskPtr) == taskId) { 184 | long frequency = mapData.decode(i + 8); 185 | if (frequency < 512L) { 186 | return frequency; 187 | } 188 | long[] tmpArr = calculateFrequencyParts(frequency); 189 | Arrays.sort(tmpArr); 190 | long sortedMaxPart = tmpArr[tmpArr.length - 1]; 191 | if (sortedMaxPart == 0) { 192 | return 1L; 193 | } else { 194 | return sortedMaxPart; 195 | } 196 | } 197 | } 198 | 199 | return -1L; 200 | } 201 | 202 | private long[] calculateFrequencyParts(long frequency) { 203 | long[] parts = new long[3]; 204 | parts[0] = frequency % 512L; 205 | long theRest = frequency - parts[0]; 206 | 207 | if (theRest < 262144L) { 208 | parts[1] = theRest / 512L; 209 | parts[2] = theRest % 262144L / 512L; 210 | } else { 211 | parts[1] = theRest / 262144L; 212 | } 213 | 214 | return parts; 215 | } 216 | 217 | 218 | /** 219 | * 任务地图 220 | */ 221 | public int taskMap(long taskId) { 222 | long taskAddr = memory.readLong(Address.TaskAddr); 223 | long start = memory.readLong(taskAddr + Address.YjRwStartAddr); 224 | long end = memory.readLong(taskAddr + Address.YjRwEndAddr); 225 | long num = (end - start) / 16; 226 | 227 | for (long i = 0; i < num; i++) { 228 | long taskPtr = memory.readLong(start + i * 16L); 229 | if (memory.readInt(taskPtr) == taskId) { 230 | // 任务副本 231 | long taskData = memory.readLong(taskPtr + Address.RwFbAddr); 232 | return memory.readInt(taskData); 233 | } 234 | } 235 | return 0; 236 | } 237 | 238 | /** 239 | * conditional_judgment 条件判断 240 | * 1=城镇完成 比如:对话任务 2=刷图任务,需要进图 3=材料任务 241 | * 242 | * @return long 243 | */ 244 | private int conditional(String conditional) { 245 | List brushConditions = new ArrayList<>(); 246 | brushConditions.add("[meet npc]"); 247 | brushConditions.add("[seek n meet npc]"); 248 | brushConditions.add("[reach the range]"); 249 | brushConditions.add("[look cinematic]"); 250 | brushConditions.add("[question]"); 251 | brushConditions.add("[quest clear]"); 252 | for (String condition : brushConditions) { 253 | if (condition.equals(conditional)) { 254 | return 1; 255 | } 256 | } 257 | brushConditions = new ArrayList<>(); 258 | brushConditions.add("[hunt monster]"); 259 | brushConditions.add("[hunt enemy]"); 260 | brushConditions.add("[condition under clear]"); 261 | brushConditions.add("[clear map]"); 262 | brushConditions.add("[question]"); 263 | brushConditions.add("[seeking]"); 264 | brushConditions.add("[clear dungeon index]"); 265 | for (String condition : brushConditions) { 266 | if (condition.equals(conditional)) { 267 | return 2; 268 | } 269 | } 270 | return 0; 271 | } 272 | 273 | /** 274 | * 最高等级副本 275 | * 276 | * @return long 277 | */ 278 | public int highestMap() { 279 | long level = mapData.getRoleLevel(); 280 | if (level <= 17) { 281 | if (level <= 3) { 282 | return 3; // # 雷鸣废墟 283 | } else if (level <= 4) { 284 | return 3; // 雷鸣废墟 285 | } else if (level <= 5) { 286 | return 5; // 雷鸣废墟 287 | } else if (level <= 8) { 288 | return 6; // 猛毒雷鸣废墟 289 | } else if (level <= 11) { 290 | return 9; // 冰霜幽暗密林 291 | } else if (level <= 13) { 292 | return 7; // 格拉卡 293 | } else if (level <= 15) { 294 | return 8; // 烈焰格拉卡 295 | } else { 296 | return 1000; // 暗黑雷鸣废墟 297 | } 298 | } 299 | // 天空之城 300 | if (level <= 23) { 301 | if (level <= 18) { 302 | return 1000; // # 龙人之塔 303 | } else if (level <= 19) { 304 | return 12; // 人偶玄关 305 | } else if (level <= 20) { 306 | return 13; // 石巨人塔 307 | } else if (level <= 21) { 308 | return 14; // 黑暗玄廊 309 | } else if (level <= 22) { 310 | return 17; // 悬空城 311 | } else { 312 | return 15; // 城主宫殿 313 | } 314 | } 315 | 316 | // 神殿脊椎 317 | if (level <= 29) { 318 | if (level <= 24) { 319 | return 15; // # 神殿外围 320 | } else if (level <= 25) { 321 | return 22; // 树精丛林 322 | } else if (level <= 26) { 323 | return 23; // 炼狱 324 | } else if (level <= 27) { 325 | return 24; // 极昼 326 | } else if (level <= 28) { 327 | return 25; // 第一脊椎 328 | } else { 329 | return 26; // 第二脊椎 330 | } 331 | } 332 | 333 | // 神殿脊椎 334 | if (level <= 35) { 335 | if (level <= 30) { 336 | return 26; // # 浅栖之地 337 | } else if (level <= 31) { 338 | return 32; // 蜘蛛洞穴 339 | } else if (level <= 32) { 340 | return 150; // 蜘蛛王国 341 | } else if (level <= 33) { 342 | return 151; // 英雄冢 343 | } else if (level <= 34) { 344 | return 35; // 暗精灵墓地 345 | } else { 346 | return 36; // 熔岩穴 347 | } 348 | 349 | } 350 | 351 | // 暗精灵地区 352 | 353 | // 祭坛 354 | if (level <= 39) { 355 | if (level <= 36) { 356 | return 34; // # 暴君的祭坛 357 | } else if (level <= 37) { 358 | return 153; // 黄金矿洞 359 | } else if (level <= 38) { 360 | return 154; // 远古墓穴深处 361 | } else { 362 | return 154; // 远古墓穴深处 363 | } 364 | 365 | } 366 | 367 | 368 | // 绿都 369 | if (level <= 49) { 370 | if (level <= 46) { 371 | return 141; // # 绿都格罗兹尼 372 | } else if (level <= 47) { 373 | return 50; // 堕落的盗贼 374 | } else if (level <= 48) { 375 | return 51; // 迷乱之村哈穆林 376 | } else { 377 | return 53; // 疑惑之村 378 | } 379 | 380 | } 381 | 382 | // 绿都 383 | if (level <= 53) { 384 | if (level <= 50) { 385 | return 53; // # 炽晶森林 386 | } else if (level <= 51) { 387 | return 145; // 冰晶森林 388 | } else if (level <= 52) { 389 | return 146; // 水晶矿脉 390 | } else { 391 | return 148; // 幽冥监狱 392 | } 393 | } 394 | 395 | // 绿都 396 | if (level <= 58) { 397 | if (level <= 54) { 398 | return 148; // # 蘑菇庄园 399 | } else if (level <= 55) { 400 | return 157; // 蚁后的巢穴 401 | } else if (level <= 56) { 402 | return 158; // 腐烂之地 403 | } else if (level <= 57) { 404 | return 159; // 赫顿玛尔旧街区 405 | } else { 406 | return 160; // 鲨鱼栖息地 407 | } 408 | } 409 | 410 | if (level <= 62) { 411 | if (level <= 59) { 412 | return 160; // # 人鱼国度 413 | } else if (level <= 60) { 414 | return 163; // GBL女神殿 415 | } else if (level <= 61) { 416 | return 164; // 树精繁殖地 417 | } else { 418 | return 164; // 树精繁殖地 419 | } 420 | } 421 | 422 | if (level <= 74) { 423 | if (level <= 71) { 424 | return 85; 425 | } else if (level <= 72) { 426 | return 87; 427 | } else if (level <= 73) { 428 | return 92; 429 | } else { 430 | return 93; 431 | } 432 | } 433 | 434 | if (level <= 80) { 435 | if (level <= 75) { 436 | return 93; // # 格兰之火 437 | } else if (level <= 76) { 438 | return 71; // 瘟疫之源 439 | } else if (level <= 77) { 440 | return 72; // 卡勒特之刃 441 | } else if (level <= 78) { 442 | return 74; // 绝密区域 443 | } else if (level <= 79) { 444 | return 75; // 昔日悲鸣 445 | } else { 446 | return 76; // 凛冬 447 | } 448 | } 449 | 450 | if (level <= 85) { 451 | if (level <= 81) { 452 | return 76; // 102 普鲁兹发电站 453 | } else if (level <= 82) { 454 | return 103; // 特伦斯发电站 455 | } else { 456 | return 104; // 格蓝迪发电站 457 | } 458 | } 459 | 460 | if (level <= 90) { 461 | if (level <= 87) { 462 | return 310; // # 时间广场 463 | } else if (level <= 88) { 464 | return 312; // 恐怖的栖息地 465 | } else if (level <= 89) { 466 | return 314; // 恐怖的栖息地 467 | } else { 468 | return 314; // 红色魔女之森 469 | } 470 | } 471 | 472 | if (level <= 100) { 473 | if (level <= 95) { 474 | return 291100293; // # 全蚀市场 475 | } else if (level <= 98) { 476 | return 291100293; // 搏击俱乐部 477 | } 478 | return 0; 479 | } 480 | 481 | if (level <= 109) { 482 | if (level <= 102) { 483 | return 100002976; // 圣域中心 484 | } else if (level <= 103) { 485 | return 100002977; // 泽尔峡谷 486 | } else if (level <= 104) { 487 | return 100002978; // 洛仑山 488 | } else if (level <= 105) { 489 | return 100002979; // 白色雪原 490 | } else if (level <= 106) { 491 | return 100002980; // 贝奇的空间 492 | } else if (level <= 107) { 493 | return 100002981; // 红色魔女之森 494 | } else if (level <= 108) { 495 | return 100002982; // 红色魔女之森 496 | } else { 497 | return 100002983; // 红色魔女之森 498 | } 499 | } 500 | return 0; 501 | } 502 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/game/Traverse.java: -------------------------------------------------------------------------------- 1 | package com.dnf.game; 2 | 3 | import com.dnf.entity.CoordinateType; 4 | import com.dnf.entity.MapTraversalType; 5 | import com.dnf.helper.*; 6 | import com.sun.jna.platform.win32.Win32VK; 7 | import jakarta.annotation.Resource; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.Arrays; 11 | 12 | /** 13 | * @author 情歌 14 | */ 15 | @Component 16 | public class Traverse extends Base { 17 | @Resource 18 | private GameCall gamecall; 19 | 20 | @Resource 21 | private MapData mapData; 22 | 23 | @Resource 24 | private SendPack sendPack; 25 | 26 | @Resource 27 | private IniUtils iniUtils; 28 | 29 | protected String[] getItemConfig() { 30 | String itemStr = iniUtils.read("自动配置", "过滤物品", String.class); 31 | return itemStr.split(","); 32 | } 33 | 34 | /** 35 | * 组包拾取 36 | */ 37 | public void packPickup() { 38 | if (mapData.getStat() != 3) { 39 | return; 40 | } 41 | 42 | String[] itemArr = this.getItemConfig(); 43 | 44 | // 地图遍历数据 45 | MapTraversalType data = mapData.getMapData(); 46 | for (data.objTmp = 1; data.objTmp < data.objNum; data.objTmp++) { 47 | data.objPtr = mapData.getTraversalPtr(data.start, data.objTmp, 2); 48 | data.objTypeA = memory.readInt(data.objPtr + Address.LxPyAddr); 49 | data.objTypeB = memory.readInt(data.objPtr + Address.LxPyAddr + 4); 50 | data.objCamp = memory.readInt(data.objPtr + Address.ZyPyAddr); 51 | if ((data.objTypeA == 289 || data.objTypeB == 289) && data.objCamp == 200) { 52 | int[] goodsNameByte = memory.readByte(memory.readLong(memory.readLong(data.objPtr + Address.DmWpAddr) + Address.WpMcAddr), 100); 53 | data.objNameB = Strings.unicodeToAscii(goodsNameByte); 54 | 55 | if (Arrays.asList(itemArr).contains(data.objNameB)) { 56 | continue; 57 | } 58 | 59 | if (data.objPtr != data.rwAddr) { 60 | int resAddress = mapData.decode(data.objPtr + Address.FbSqAddr); 61 | sendPack.pickUp(resAddress); 62 | } 63 | } 64 | } 65 | } 66 | 67 | // HandleEquip 处理装备 68 | public void handleEquip() { 69 | int handleModel = iniUtils.read("自动配置", "处理装备", Integer.class); 70 | if (handleModel == 0) { 71 | return; 72 | } 73 | if (mapData.backpackWeight() < 60) { 74 | return; 75 | } 76 | 77 | int num = 0; 78 | long address = memory.readLong(memory.readLong(Address.BbJzAddr) + Address.WplPyAddr) + 0x48; 79 | 80 | for (long i = 1; i <= 56; i++) { 81 | long equip = mapData.getTraversalPtr(address, i, 1); 82 | if (equip > 0) { 83 | int equipLevel = memory.readInt(equip + Address.ZbPjAddr); 84 | int nameAddress = memory.readInt(equip + Address.WpMcAddr); 85 | String equipName = Strings.unicodeToAscii(memory.readByte(nameAddress, 100)); 86 | if (equipName.isEmpty()) { 87 | break; 88 | } 89 | 90 | // 0白 1蓝 91 | if (Arrays.asList(0, 1).contains(equipLevel)) { 92 | logger.info("分解装备 [ {} ]", equipName); 93 | sendPack.decomposition((int) i + 8); 94 | Timer.sleep(200); 95 | num++; 96 | } 97 | 98 | // 2紫 3粉 99 | if (Arrays.asList(2, 3).contains(equipLevel)) { 100 | logger.info("出售装备 [ {} ]", equipName); 101 | sendPack.sellEquip((int) i + 8); 102 | Timer.sleep(200); 103 | num++; 104 | } 105 | } 106 | } 107 | sendPack.tidyBackpack(0, 0); 108 | logger.info("处理装备 [ {} ] 件", num); 109 | } 110 | 111 | /** 112 | * 跟随怪物 113 | */ 114 | public void followMonster() { 115 | 116 | if (mapData.getStat() != 3) { 117 | return; 118 | } 119 | 120 | int followModel = iniUtils.read("自动配置", "跟随打怪", Integer.class); 121 | int code = iniUtils.read("自动配置", "技能代码", Integer.class); 122 | int harm = iniUtils.read("自动配置", "技能伤害", Integer.class); 123 | int size = iniUtils.read("自动配置", "技能大小", Integer.class); 124 | 125 | MapTraversalType data = mapData.getMapData(); 126 | for (data.objTmp = 1; data.objTmp < data.objNum; data.objTmp++) { 127 | data.objPtr = mapData.getTraversalPtr(data.start, data.objTmp, 2); 128 | if (data.objPtr > 0) { 129 | data.objTypeA = memory.readInt(data.objPtr + Address.LxPyAddr); 130 | if (data.objTypeA == 529 || data.objTypeA == 545 || data.objTypeA == 273 || data.objTypeA == 61440 || data.objTypeA == 1057) { 131 | data.objCamp = memory.readInt(data.objPtr + Address.ZyPyAddr); 132 | data.objCode = memory.readInt(data.objPtr + Address.DmPyAddr); 133 | data.objBlood = memory.readLong(data.objPtr + Address.GwXlAddr); 134 | if (data.objCamp > 0 && data.objPtr != data.rwAddr) { 135 | CoordinateType monster = mapData.readCoordinate(data.objPtr); 136 | int[] objNameByte = memory.readByte(memory.readLong(data.objPtr + Address.McPyAddr), 200); 137 | data.objNameA = Strings.unicodeToAscii(objNameByte); 138 | logger.debug("对象名称:[{}],类型:[{}],阵营:[{}],代码:[{}],血量:[{}],X:[{}],Y:[{}]", data.objNameA, data.objTypeA, data.objCamp, data.objCode, data.objBlood, monster.x, monster.y); 139 | 140 | if (data.objBlood > 0) { 141 | gamecall.driftCall(data.rwAddr, monster.x, monster.y, 0, 0); 142 | // 跟随打怪 143 | if (followModel == 2) { 144 | int[] vkCode = new int[]{Win32VK.VK_A.code, Win32VK.VK_S.code, Win32VK.VK_D.code, Win32VK.VK_F.code, Win32VK.VK_G.code, Win32VK.VK_H.code, Win32VK.VK_Q.code, Win32VK.VK_W.code, Win32VK.VK_E.code, Win32VK.VK_R.code, Win32VK.VK_T.code, Win32VK.VK_Y.code, Win32VK.VK_X.code,}; 145 | Button.DriveButton(Win32VK.VK_X.code, 1, false); 146 | Timer.sleep(800); 147 | Button.DriveButton(Win32VK.VK_X.code, 2, false); 148 | Timer.sleep(100); 149 | int vkCodeRandomIndex = NumberUtils.getRandomNumber(0, vkCode.length - 1); 150 | Button.DriveButton(vkCode[vkCodeRandomIndex], 0, false); 151 | } 152 | // 技能call 153 | if (followModel == 3) { 154 | Button.DriveButton(Win32VK.VK_X.code, 1, false); 155 | Timer.sleep(300); 156 | Button.DriveButton(Win32VK.VK_X.code, 2, false); 157 | gamecall.skillCall(data.rwAddr, code, harm, monster.x, monster.y, 0, (float) size); 158 | } 159 | } 160 | } 161 | Timer.sleep(300); 162 | } 163 | } 164 | } 165 | } 166 | 167 | /** 168 | * 是否存在物品 169 | * 170 | * @return boolean 171 | */ 172 | public boolean isExistsItem() { 173 | if (mapData.getStat() != 3) { 174 | return false; 175 | } 176 | 177 | String[] itemArr = this.getItemConfig(); 178 | MapTraversalType data = mapData.getMapData(); 179 | for (data.objTmp = 1; data.objTmp < data.objNum; data.objTmp++) { 180 | data.objPtr = mapData.getTraversalPtr(data.start, data.objTmp, 2); 181 | data.objTypeA = memory.readInt(data.objPtr + Address.LxPyAddr); 182 | data.objCamp = memory.readInt(data.objPtr + Address.ZyPyAddr); 183 | if ((data.objTypeA == 289 || data.objTypeB == 289) && data.objCamp == 200) { 184 | long goodsNamePtr = memory.readLong(memory.readLong(data.objPtr + Address.DmWpAddr) + Address.WpMcAddr); 185 | int[] goodsNameByte = memory.readByte(goodsNamePtr, 100); 186 | data.objNameB = Strings.unicodeToAscii(goodsNameByte); 187 | 188 | if (Arrays.asList(itemArr).contains(data.objNameB)) { 189 | continue; 190 | } 191 | 192 | if (data.objPtr != data.rwAddr) { 193 | return true; 194 | } 195 | } 196 | } 197 | 198 | return false; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/Button.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import com.sun.jna.platform.win32.User32; 4 | import com.sun.jna.platform.win32.WinDef; 5 | import com.sun.jna.platform.win32.WinUser; 6 | 7 | /** 8 | * @author 情歌 9 | */ 10 | public class Button { 11 | 12 | /** 13 | * 驱动按键 14 | * 15 | * @param vkCode int 按键码 16 | * @param sendType int 按键方式 0按下+抬起 1按下 2抬起 17 | * @param funcType boolean 功能键方式 true为长按 18 | */ 19 | public static void DriveButton(int vkCode, int sendType, boolean funcType) { 20 | int coverCode = User32.INSTANCE.MapVirtualKeyEx(vkCode, 0, null); 21 | 22 | WinUser.KEYBDINPUT keyboardInput = new WinUser.KEYBDINPUT(); 23 | keyboardInput.wVk = new WinDef.WORD(vkCode); 24 | keyboardInput.wScan = new WinDef.WORD(coverCode); 25 | 26 | if (sendType == 0 || sendType == 1) { 27 | keyboardInput.time = new WinDef.DWORD(System.currentTimeMillis() / 1000); 28 | keyboardInput.dwFlags = new WinDef.DWORD(funcType ? 0x1 : 0x0); 29 | sendInput(keyboardInput); 30 | Timer.sleep(10); 31 | } 32 | 33 | if (sendType == 0 || sendType == 2) { 34 | keyboardInput.time = new WinDef.DWORD(System.currentTimeMillis() / 1000); 35 | keyboardInput.dwFlags = new WinDef.DWORD(funcType ? 0x3 : 0x2); 36 | sendInput(keyboardInput); 37 | Timer.sleep(10); 38 | } 39 | } 40 | 41 | private static void sendInput(WinUser.KEYBDINPUT keyboardInput) { 42 | WinUser.INPUT inputPress = new WinUser.INPUT(); 43 | inputPress.type = new WinDef.DWORD(1); 44 | inputPress.input.setType("ki"); 45 | inputPress.input.ki = keyboardInput; 46 | User32.INSTANCE.SendInput(new WinDef.DWORD(1), new WinUser.INPUT[]{inputPress}, inputPress.size()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/Bytes.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import java.nio.ByteBuffer; 4 | import java.nio.ByteOrder; 5 | 6 | /** 7 | * @author 情歌 8 | */ 9 | public class Bytes { 10 | 11 | public static int[] intToBytes(T value) { 12 | byte[] bytes; 13 | if (value instanceof Short) { 14 | ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES); 15 | buffer.order(ByteOrder.LITTLE_ENDIAN); 16 | buffer.putInt((Short) value); 17 | bytes = buffer.array(); 18 | } else if (value instanceof Integer) { 19 | ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); 20 | buffer.order(ByteOrder.LITTLE_ENDIAN); 21 | buffer.putInt((Integer) value); 22 | bytes = buffer.array(); 23 | } else if (value instanceof Long) { 24 | ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); 25 | buffer.order(ByteOrder.LITTLE_ENDIAN); 26 | buffer.putLong((Long) value); 27 | bytes = buffer.array(); 28 | } else { 29 | throw new IllegalArgumentException("Unsupported value type"); 30 | } 31 | 32 | return byteArrToIntArr(bytes); 33 | } 34 | 35 | public static int[] floatToBytes(T value) { 36 | byte[] bytes; 37 | if (value instanceof Float) { 38 | ByteBuffer buffer = ByteBuffer.allocate(Float.BYTES); 39 | buffer.order(ByteOrder.LITTLE_ENDIAN); 40 | buffer.putFloat((Float) value); 41 | bytes = buffer.array(); 42 | } else if (value instanceof Double) { 43 | ByteBuffer buffer = ByteBuffer.allocate(Double.BYTES); 44 | buffer.order(ByteOrder.LITTLE_ENDIAN); 45 | buffer.putDouble((Double) value); 46 | bytes = buffer.array(); 47 | } else { 48 | throw new IllegalArgumentException("Unsupported value type"); 49 | } 50 | 51 | return byteArrToIntArr(bytes); 52 | } 53 | 54 | public static T bytesToInt(int[] value, Class type) { 55 | byte[] bytes = intArrToByteArr(value); 56 | 57 | // 创建一个ByteBuffer,并设置字节顺序为小端序 58 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 59 | buffer.order(ByteOrder.LITTLE_ENDIAN); 60 | 61 | if (type == Short.class) { 62 | return type.cast(buffer.getShort()); 63 | } else if (type == Integer.class) { 64 | return type.cast(buffer.getInt()); 65 | } else if (type == Long.class) { 66 | return type.cast(buffer.getLong()); 67 | } else { 68 | throw new IllegalArgumentException("Unsupported data type"); 69 | } 70 | } 71 | 72 | 73 | public static T bytesToFloat(int[] value, Class type) { 74 | byte[] bytes = intArrToByteArr(value); 75 | 76 | // 创建一个ByteBuffer,并设置字节顺序为小端序 77 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 78 | buffer.order(ByteOrder.LITTLE_ENDIAN); 79 | if (type == Float.class) { 80 | return type.cast(buffer.getFloat()); 81 | } else if (type == Double.class) { 82 | return type.cast(buffer.getDouble()); 83 | } else { 84 | throw new IllegalArgumentException("Unsupported data type"); 85 | } 86 | } 87 | 88 | private static byte[] intArrToByteArr(int[] value) { 89 | byte[] bytes = new byte[value.length]; 90 | 91 | for (int i = 0; i < value.length; i++) { 92 | bytes[i] = (byte) (value[i] & 0xFF); 93 | } 94 | return bytes; 95 | } 96 | 97 | private static int[] byteArrToIntArr(byte[] value) { 98 | int[] result = new int[value.length]; 99 | for (int i = 0; i < value.length; i++) { 100 | result[i] = value[i] & 0xFF; 101 | } 102 | 103 | return result; 104 | } 105 | 106 | /** 107 | * 将多个字节数组连接在一起 108 | * 109 | * @param oldArray 原始字节数组 110 | * @param newArrayArr 要连接的新字节数组 111 | * @return 连接后的字节数组 112 | */ 113 | public static int[] addBytes(int[] oldArray, int[]... newArrayArr) { 114 | int totalLength = oldArray.length; 115 | for (int[] array : newArrayArr) { 116 | totalLength += array.length; 117 | } 118 | int[] result = new int[totalLength]; 119 | System.arraycopy(oldArray, 0, result, 0, oldArray.length); 120 | int offset = oldArray.length; 121 | for (int[] array : newArrayArr) { 122 | System.arraycopy(array, 0, result, offset, array.length); 123 | offset += array.length; 124 | } 125 | return result; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * @author 情歌 7 | */ 8 | public class FileUtils { 9 | private final File file; 10 | 11 | public FileUtils(String filename) { 12 | file = new File(filename); 13 | } 14 | 15 | public boolean exists() { 16 | try { 17 | return file.exists(); 18 | } catch (Exception e) { 19 | return false; 20 | } 21 | } 22 | 23 | public boolean create() { 24 | try { 25 | return file.createNewFile(); 26 | } catch (Exception e) { 27 | return false; 28 | } 29 | } 30 | 31 | public void remove() { 32 | file.deleteOnExit(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/IniUtils.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.ini4j.Config; 5 | import org.ini4j.Ini; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * @author 情歌 11 | */ 12 | @Slf4j 13 | public class IniUtils { 14 | private final Ini ini; 15 | private String filename; 16 | 17 | public IniUtils() { 18 | ini = new Ini(); 19 | Config iniConfig = ini.getConfig(); 20 | iniConfig.setEscape(false); 21 | } 22 | 23 | 24 | public IniUtils setFilename(String filename) { 25 | this.filename = filename; 26 | return this; 27 | } 28 | 29 | public void write(String sectionName, String optionName, Object value) { 30 | try { 31 | File file = new File(filename); 32 | Ini.Section section = ini.get(sectionName); 33 | if (section == null) { 34 | section = ini.add(sectionName); 35 | } 36 | section.put(optionName, value); 37 | ini.store(file); 38 | } catch (Exception e) { 39 | log.error("写配置失败 section {},option {},value {}, error {}", sectionName, optionName, value, e.getMessage()); 40 | } 41 | } 42 | 43 | public T read(Object sectionName, Object optionName, Class clazz) { 44 | try { 45 | File iniFile = new File(filename); 46 | ini.load(iniFile); 47 | // 读取配置值 48 | return ini.get(sectionName, optionName, clazz); 49 | } catch (Exception e) { 50 | log.error("读配置失败 section {},option {}", sectionName, optionName); 51 | return null; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/NumberUtils.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * @author 情歌 7 | */ 8 | public class NumberUtils { 9 | public static int getRandomNumber(int min, int max) { 10 | Random random = new Random(); 11 | return random.nextInt((max - min) + 1) + min; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/Process.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import com.sun.jna.Native; 4 | import com.sun.jna.Pointer; 5 | import com.sun.jna.platform.win32.*; 6 | 7 | /** 8 | * @author 情歌 9 | */ 10 | public class Process { 11 | static final Kernel32 kernel32 = Kernel32.INSTANCE; 12 | static final User32 user32 = User32.INSTANCE; 13 | 14 | /** 15 | * 获取指定进程名的进程id 16 | * 17 | * @param processName 进程名称 18 | * @return int 19 | */ 20 | public static int getProcessId(String processName) { 21 | Tlhelp32.PROCESSENTRY32.ByReference pe = new Tlhelp32.PROCESSENTRY32.ByReference(); 22 | 23 | // 创建进程快照 24 | WinNT.HANDLE handle = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS, new WinBase.DWORD(0)); 25 | if (handle == WinBase.INVALID_HANDLE_VALUE) { 26 | return 0; 27 | } 28 | 29 | try { 30 | // 遍历进程快照 31 | if (kernel32.Process32First(handle, pe)) { 32 | do { 33 | String exeFile = Native.toString(pe.szExeFile); 34 | if (exeFile.equalsIgnoreCase(processName.toUpperCase())) { 35 | return pe.th32ProcessID.intValue(); 36 | } 37 | } while (kernel32.Process32Next(handle, pe)); 38 | } 39 | } finally { 40 | kernel32.CloseHandle(handle); 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | /** 47 | * 获取指定进程模块句柄 48 | * 49 | * @param processId 进程id 50 | * @param moduleName 模块名称 51 | * @return long 52 | */ 53 | public static long getProcessModuleHandle(int processId, String moduleName) { 54 | long result = 0; 55 | WinNT.HANDLE hModuleSnap = kernel32.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new WinBase.DWORD(processId)); 56 | Tlhelp32.MODULEENTRY32W me = new Tlhelp32.MODULEENTRY32W(); 57 | 58 | if (kernel32.Module32FirstW(hModuleSnap, me)) { 59 | do { 60 | String currentProcessModuleName = Native.toString(me.szModule).toLowerCase(); 61 | 62 | if (currentProcessModuleName.equals(moduleName.toLowerCase())) { 63 | WinDef.HMODULE hModule = me.hModule; 64 | result = Pointer.nativeValue(hModule.getPointer()); 65 | break; 66 | } 67 | } while (kernel32.Module32NextW(hModuleSnap, me)); 68 | } 69 | 70 | kernel32.CloseHandle(hModuleSnap); 71 | return result; 72 | } 73 | 74 | /** 75 | * 获取窗口句柄 76 | * 77 | * @param lpClassName 类名称 78 | * @param lpWindowName 窗口名称 79 | * @return WinDef.HWND 80 | */ 81 | public static WinDef.HWND FindWindowW(String lpClassName, String lpWindowName) { 82 | return user32.FindWindow(lpClassName, lpWindowName); 83 | } 84 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/Strings.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import java.nio.charset.StandardCharsets; 4 | import java.util.Arrays; 5 | 6 | /** 7 | * @author 情歌 8 | */ 9 | public class Strings { 10 | 11 | // 取文本左边 12 | public static String getLeftText(String text, int n) { 13 | return text.substring(0, n).trim(); 14 | } 15 | 16 | // 取文本右边 17 | public static String getRightText(String text, int n) { 18 | String[] parts = text.split(""); 19 | return String.join("", Arrays.copyOfRange(parts, parts.length - n, parts.length)); 20 | } 21 | 22 | // 到整数 23 | public static int toInteger(String str) { 24 | try { 25 | return Integer.parseInt(str); 26 | } catch (NumberFormatException e) { 27 | throw new RuntimeException("到整数失败", e); 28 | } 29 | } 30 | 31 | // 到十六进制 32 | public static String toHex(long num) { 33 | String hex = Long.toHexString(num); 34 | return hex.toUpperCase(); 35 | } 36 | 37 | /** 38 | * 将字符串转换为Unicode编码的字节数组 39 | * 40 | * @param ansi 字符串 41 | * @return Unicode编码的字节数组 42 | */ 43 | public static int[] asciiToUnicode(String ansi) { 44 | if (ansi == null || ansi.isEmpty()) { 45 | return new int[0]; 46 | } 47 | byte[] bytes = ansi.getBytes(StandardCharsets.UTF_16LE); 48 | int[] result = new int[bytes.length]; 49 | for (int i = 0; i < bytes.length; i++) { 50 | result[i] = bytes[i] & 0xFF; 51 | } 52 | return result; 53 | } 54 | 55 | /** 56 | * 将Unicode编码的字节数组转换为字符串 57 | * 58 | * @param unicodeInt Unicode编码的字节数组 59 | * @return 字符串 60 | */ 61 | public static String unicodeToAscii(int[] unicodeInt) { 62 | StringBuilder stringBuilder = new StringBuilder(); 63 | for (int i = 0; i < unicodeInt.length - 1; i += 2) { 64 | if (unicodeInt[i] == 0 && unicodeInt[i + 1] == 0) { 65 | break; 66 | } 67 | int code = unicodeInt[i + 1] << 8 | unicodeInt[i]; 68 | stringBuilder.append((char) code); 69 | } 70 | return stringBuilder.toString(); 71 | } 72 | 73 | public static int[] splitToIntArray(String input, String regex) { 74 | String[] strArray = input.split(regex); 75 | int[] intArray = new int[strArray.length]; 76 | 77 | for (int i = 0; i < strArray.length; i++) { 78 | intArray[i] = Integer.parseInt(strArray[i]); 79 | } 80 | 81 | return intArray; 82 | } 83 | } -------------------------------------------------------------------------------- /src/main/java/com/dnf/helper/Timer.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | /** 4 | * @author 情歌 5 | */ 6 | public class Timer { 7 | public static void sleep(int seconds) { 8 | try { 9 | Thread.sleep(seconds); 10 | } catch (InterruptedException ignored) { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | logging: 2 | level: 3 | com.dnf.game: info 4 | com.dnf.driver: debug 5 | spring: 6 | main: 7 | banner-mode: off 8 | -------------------------------------------------------------------------------- /src/main/resources/helper.ini: -------------------------------------------------------------------------------- 1 | [自动配置] 2 | ; ------------------技能全屏---------------------------- 3 | 技能代码 = 70231 4 | 技能伤害 = 50123641 5 | 技能大小 = 1 6 | 技能个数 = 1 7 | ; ------------------自动模式 1=剧情 2=搬砖------------------ 8 | 自动模式 = 2 9 | ; ------------------地图编号------------------ 10 | 普通地图 = 100002964,100002965,100002950,100002952,100002962,100002705,100002676,400001565 11 | ; ------------------地图难度 0=普通 1=冒险 2=勇士 3=王者 4=噩梦 5=自动取最高副本等级------------------ 12 | 地图难度 = 5 13 | ; ------------------角色数量 例如刷10个角色填写9,因为默认第一个角色不计入其中------------------ 14 | 角色数量 = 3 15 | ; ------------------跟随打怪 0=关闭 1=跟随 2=跟随打怪 3=技能call------------------ 16 | 跟随打怪 = 1 17 | ; ------------------过图方式 0=关闭 1=强制 2=漂移------------------ 18 | 过图方式 = 1 19 | ; ------------------处理装备 0=关闭 1=分解------------------ 20 | 处理装备 = 0 21 | ; ------------------开启功能 0=关闭 1=金身 2=装备buff 3=满技能------------------ 22 | 开启功能 = 0 23 | ; ------------------过滤物品------------------ 24 | 过滤物品 = 碎布片,金刚石,风化的碎骨,破旧的皮革,血滴石,金刚石,海蓝宝石,黑曜石,最下级砥石,最下级硬化剂,生锈的铁片,鸡腿,肉块,织女星的光辉,赫仑皇帝的印章,幸运兑换币,天才的地图碎片,柴火,玫,远古骑士的盔甲,使徒的气息,坚硬的龟壳,遗留的水晶碎片,[活动]闪亮的雷米援助礼袋 (10个),突变苎麻花叶,副船长的戒指,步枪零件,黑色龙舌兰酒,烤硬的黑面包,虚空魔石碎片,格林赛罗斯的果核,新手HP药剂,新手MP药剂,跃翔药剂,宠物饲料礼盒 (5个),突变草莓,暗黑城特产干酪,艾丽丝的香料,野草莓,卡勒特勋章,下级元素结晶,上级元素结晶,麻辣鲜香麻婆豆腐食盒,迷幻晶石,混沌魔石碎片,碳结晶体,数据芯片,甜蜜巧克力,沁凉雪球,神秘的胶囊碎片,阳光硬币,迷幻晶石,魔刹石,云霓碎片,克尔顿的印章,撒勒的印章,达人HP药剂,达人MP药剂,专家MP药剂,专家HP药剂,熟练MP药剂,熟练HP药剂,血滴石,黑曜石,紫玛瑙,金刚石,海蓝宝石,月饼硬币,暗黑倾向药剂,命运硬币,肉干,砂砾,天空树果实,燃烧瓶,军用回旋镖,裂空镖,甜瓜,飞镖,轰雷树果实,越桔,神圣葡萄酒,轰爆弹,爆弹,燃烧瓶,精灵香精,魔力之花,石头,苎麻花叶,怒海霸主银币,解密礼盒,无尽的永恒,风化的碎骨,破旧的皮革,最下级砥石,最下级硬化剂,生锈的铁片,碎布片,回旋镖,天界珍珠,朗姆酒,飞盘,魔力之花,卡勒特指令书,入门HP药剂,入门MP药剂,普通HP药剂,普通MP药剂,飞盘 2,邪恶药剂,圣杯,肉干,袖珍罐碎片 25 | ; ------------------出图方式 0 正常出图 1 快速出图------------------ 26 | 出图方式 = 1 -------------------------------------------------------------------------------- /src/main/resources/qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qiuapeng921/DnfHelper-Java/cea9c9242cb525fd85eee4b0b4d29c47a05765fc/src/main/resources/qq.png -------------------------------------------------------------------------------- /src/test/java/com/dnf/BufferTest.java: -------------------------------------------------------------------------------- 1 | package com.dnf; 2 | 3 | import com.dnf.driver.ltq.ReadWrite; 4 | import com.sun.jna.Memory; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | 8 | import java.nio.ByteBuffer; 9 | import java.nio.ByteOrder; 10 | import java.util.Arrays; 11 | 12 | public class BufferTest { 13 | public static int[] convertToBytes(T value) { 14 | byte[] bytes; 15 | if (value instanceof Short) { 16 | ByteBuffer buffer = ByteBuffer.allocate(Short.BYTES); 17 | buffer.order(ByteOrder.LITTLE_ENDIAN); 18 | buffer.putInt((Short) value); 19 | bytes = buffer.array(); 20 | } else if (value instanceof Integer) { 21 | ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES); 22 | buffer.order(ByteOrder.LITTLE_ENDIAN); 23 | buffer.putInt((Integer) value); 24 | bytes = buffer.array(); 25 | } else if (value instanceof Long) { 26 | ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); 27 | buffer.order(ByteOrder.LITTLE_ENDIAN); 28 | buffer.putLong((Long) value); 29 | bytes = buffer.array(); 30 | } else if (value instanceof Float) { 31 | ByteBuffer buffer = ByteBuffer.allocate(Float.BYTES); 32 | buffer.order(ByteOrder.LITTLE_ENDIAN); 33 | buffer.putFloat((Float) value); 34 | bytes = buffer.array(); 35 | } else if (value instanceof Double) { 36 | ByteBuffer buffer = ByteBuffer.allocate(Double.BYTES); 37 | buffer.order(ByteOrder.LITTLE_ENDIAN); 38 | buffer.putDouble((Double) value); 39 | bytes = buffer.array(); 40 | } else { 41 | throw new IllegalArgumentException("Unsupported value type"); 42 | } 43 | 44 | int[] result = new int[bytes.length]; 45 | for (int i = 0; i < bytes.length; i++) { 46 | result[i] = bytes[i] & 0xFF; 47 | } 48 | 49 | return result; 50 | } 51 | 52 | @Test 53 | public void test() { 54 | // 创建一个指定大小的空字节集 55 | Memory buffer = new Memory(1); 56 | 57 | // 准备输入缓冲区 58 | ReadWrite inputData = new ReadWrite(); 59 | inputData.setProcessId(1); 60 | inputData.setData(buffer); 61 | inputData.setMemoryAddress(0x100000L); 62 | inputData.setSize(1); 63 | inputData.setKey(""); 64 | 65 | System.out.println("inputData = " + inputData); 66 | } 67 | 68 | @Test 69 | public void intToByte() { 70 | int value = 123; // 要转换的int值 71 | 72 | ByteBuffer buffer = ByteBuffer.allocate(4); 73 | buffer.order(ByteOrder.LITTLE_ENDIAN); // 设置字节顺序为小端序 74 | // 将int值转换为byte数组 75 | byte[] bytes = buffer.putInt(value).array(); 76 | System.out.println(Arrays.toString(bytes)); 77 | } 78 | 79 | 80 | @Test 81 | public void byteToInt() { 82 | int[] intArr = {123, 0, 0, 0}; 83 | 84 | byte[] bytes = new byte[intArr.length]; 85 | 86 | for (int i = 0; i < intArr.length; i++) { 87 | bytes[i] = (byte) (intArr[i] & 0xFF); 88 | } 89 | 90 | // 创建一个ByteBuffer,并设置字节顺序为小端序 91 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 92 | buffer.order(ByteOrder.LITTLE_ENDIAN); 93 | 94 | // 将byte数组转换为int值 95 | int value = buffer.getInt(); 96 | System.out.println(value); // 输出: 123 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/test/java/com/dnf/ReadWriteTest.java: -------------------------------------------------------------------------------- 1 | package com.dnf; 2 | 3 | import com.dnf.driver.ReadWriteMemory; 4 | import com.dnf.helper.Process; 5 | import org.junit.jupiter.api.Test; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.boot.test.context.SpringBootTest; 8 | 9 | public class ReadWriteTest { 10 | 11 | private ReadWriteMemory apiMemory; 12 | 13 | public void test() { 14 | String modelName = "dnf.exe"; 15 | int processId = Process.getProcessId(modelName); 16 | // 设置全局进程id 17 | apiMemory.setProcessId(processId); 18 | 19 | long address = 0x00400000L; 20 | 21 | long s = apiMemory.readShort(address); 22 | System.out.println("s = " + s); 23 | long i = apiMemory.readInt(address); 24 | System.out.println("i = " + i); 25 | long l = apiMemory.readLong(address); 26 | System.out.println("l = " + l); 27 | float f = apiMemory.readFloat(address); 28 | System.out.println("f = " + f); 29 | double d = apiMemory.readDouble(address); 30 | System.out.println("d = " + d); 31 | 32 | boolean b1 = apiMemory.writeInt(address, 1000); 33 | System.out.println("b1 = " + b1); 34 | 35 | boolean b = apiMemory.writeByte(address, new int[]{77, 90, 144, 0, 3, 0, 0, 0, 4, 1}); 36 | 37 | System.out.println("b = " + b); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/test/java/com/dnf/helper/ButtonTest.java: -------------------------------------------------------------------------------- 1 | package com.dnf.helper; 2 | 3 | import com.sun.jna.platform.win32.Win32VK; 4 | import org.junit.jupiter.api.Test; 5 | 6 | class ButtonTest { 7 | 8 | void driveButton() { 9 | Timer.sleep(3000); 10 | Button.DriveButton(Win32VK.VK_LEFT.code, 0, true); 11 | Button.DriveButton(Win32VK.VK_LEFT.code, 1, true); 12 | Button.DriveButton(Win32VK.VK_UP.code, 1, true); 13 | Timer.sleep(3000); 14 | Button.DriveButton(Win32VK.VK_LEFT.code, 2, true); 15 | Button.DriveButton(Win32VK.VK_UP.code, 2, true); 16 | } 17 | } -------------------------------------------------------------------------------- /start.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | chcp 65001 3 | setlocal enabledelayedexpansion 4 | 5 | rem 设置目标目录 6 | set "javaDir=D:\Java" 7 | set "targetDirectory=%javaDir%\jdk-17.0.2" 8 | 9 | rem 检查目录是否存在 10 | if exist "%targetDirectory%\bin\java.exe" ( 11 | echo Java 已经存在,无需下载。 12 | ) else ( 13 | echo 正在下载 JDK 到 %targetDirectory%... 14 | 15 | rem 下载 JDK,可以替换为实际的下载链接 16 | powershell -Command "& { Invoke-WebRequest -Uri 'https://download.java.net/java/GA/jdk17.0.2/dfd4a8d0985749f896bed50d7138ee7f/8/GPL/openjdk-17.0.2_windows-x64_bin.zip' -OutFile 'jdk.zip' }" 17 | 18 | rem 解压缩下载的 JDK 到目标目录 19 | powershell -Command "& { Expand-Archive -Path 'jdk.zip' -DestinationPath '%javaDir%' }" 20 | 21 | rem 删除下载的 zip 文件 22 | del jdk.zip 23 | 24 | echo JDK 下载并解压完成。 25 | ) 26 | 27 | rem 设置环境变量 28 | set "JAVA_HOME=%targetDirectory%" 29 | set "PATH=%JAVA_HOME%\bin;%PATH%" 30 | 31 | echo 环境变量已设置完成。 32 | 33 | java --version 34 | 35 | java -jar Dnfhelper.jar 36 | 37 | endlocal 38 | --------------------------------------------------------------------------------