├── .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 | [](https://starchart.cc/SunnyEmbrace/DnfHelper-Java)
20 |
21 |
22 | ## 免责声明
23 |
24 | 程序是免费开源的产品,仅用于学习交流使用!
25 | 不可用于任何违反`中华人民共和国(含台湾省)`或`使用者所在地区`法律法规的用途。
26 | 因为作者即本人仅完成代码的开发和开源活动`(开源即任何人都可以下载使用)`,从未参与用户的任何运营和盈利活动。
27 | 且不知晓用户后续将`程序源代码`用于何种用途,故用户使用过程中所带来的任何法律责任即由用户自己承担。
28 |
29 | ## JetBrains 支持的项目
30 |
31 | 非常感谢 Jetbrains 友好地为我提供了一个许可,让我可以从事这个项目和其他开源项目。
32 |
33 | [](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 |
--------------------------------------------------------------------------------