Compare commits
268 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4bea9dfb42 | ||
|
|
f039a9cff8 | ||
|
|
06d18c4500 | ||
|
|
cc6228dced | ||
|
|
0ed1c31106 | ||
|
|
8c118401f3 | ||
|
|
3e17352e99 | ||
|
|
d594b15e40 | ||
|
|
c48d47ff2c | ||
|
|
361c769650 | ||
|
|
294e847f19 | ||
|
|
c9bfcb417e | ||
|
|
1fc3155e9c | ||
|
|
a9d6aaebf3 | ||
|
|
b2ffa7de1b | ||
|
|
5ad90997ff | ||
|
|
b3afa98958 | ||
|
|
d2a5199383 | ||
|
|
d32973cae4 | ||
|
|
f5dc6a034b | ||
|
|
06590f487b | ||
|
|
3549a55f1e | ||
|
|
b8608c98e7 | ||
|
|
e15cf933aa | ||
|
|
8812f3027b | ||
|
|
69b36eea4b | ||
|
|
af320bd052 | ||
|
|
317f07283e | ||
|
|
bce0d47f95 | ||
|
|
b8d4c72a92 | ||
|
|
0f5ee85e19 | ||
|
|
e7f54ab81d | ||
|
|
6fea22dc9f | ||
|
|
2cb46e0540 | ||
|
|
79113cf9a0 | ||
|
|
fe808a5f83 | ||
|
|
200e68a1b9 | ||
|
|
59be6b4606 | ||
|
|
236d171d15 | ||
|
|
7ee6b40815 | ||
|
|
49b620a7cf | ||
|
|
52f795c3ae | ||
|
|
c177d6c708 | ||
|
|
9870cbddc3 | ||
|
|
8ee36bcd46 | ||
|
|
2af6dec3df | ||
|
|
802c81d766 | ||
|
|
3a8c6712b1 | ||
|
|
f378578ada | ||
|
|
92447c0b5c | ||
|
|
023bebee66 | ||
|
|
cadf7a06d0 | ||
|
|
19c9238ea2 | ||
|
|
19eda1bfe5 | ||
|
|
1ffadf0242 | ||
|
|
5d0904b46e | ||
|
|
3eda7a57e8 | ||
|
|
2031ed4bc3 | ||
|
|
a448d97ec9 | ||
|
|
1a59427957 | ||
|
|
217f6ecf28 | ||
|
|
0fd91edb97 | ||
|
|
35c2d8ba24 | ||
|
|
b26d10e26e | ||
|
|
9da3e09062 | ||
|
|
9fce9ef5e3 | ||
|
|
e5773da108 | ||
|
|
554c0de188 | ||
|
|
bea31fe77d | ||
|
|
bcd22dc91c | ||
|
|
33082428f3 | ||
|
|
0b4e5f753a | ||
|
|
5aa0dfc98c | ||
|
|
164d0e07cd | ||
|
|
b608a8a1e0 | ||
|
|
13d0e2a5bf | ||
|
|
c3085ae34f | ||
|
|
ce2ec8047e | ||
|
|
335cb7016e | ||
|
|
7a7b050615 | ||
|
|
6ff7ae800a | ||
|
|
2159773723 | ||
|
|
ab3a015d61 | ||
|
|
3142387e45 | ||
|
|
051325e10f | ||
|
|
199d5d57d5 | ||
|
|
b96795c517 | ||
|
|
64ba96daf2 | ||
|
|
14b17d86e5 | ||
|
|
90d5f27026 | ||
|
|
16386b8ddc | ||
|
|
cbef21b9ad | ||
|
|
cec44e3668 | ||
|
|
f09f1e7f85 | ||
|
|
95910bc6cb | ||
|
|
cc488ccff0 | ||
|
|
37e129bfb9 | ||
|
|
6adc923bcb | ||
|
|
065470cecb | ||
|
|
260989bda6 | ||
|
|
64b6e0fd56 | ||
|
|
8f45718b75 | ||
|
|
e66bb7a14b | ||
|
|
8bfe32eeef | ||
|
|
e85c1bbc0d | ||
|
|
f99bf0d000 | ||
|
|
db24a2251f | ||
|
|
fcb3747690 | ||
|
|
dcc27a7447 | ||
|
|
bea39bc613 | ||
|
|
546493ba45 | ||
|
|
dda4928693 | ||
|
|
7d144dd076 | ||
|
|
73e0700ea3 | ||
|
|
3fc36cf798 | ||
|
|
02f5d4efbe | ||
|
|
a9b7b91a3c | ||
|
|
dbc43b445b | ||
|
|
638fcd393a | ||
|
|
7b5f419713 | ||
|
|
d501359857 | ||
|
|
d9591e089d | ||
|
|
4848e901c9 | ||
|
|
65a650c1a6 | ||
|
|
ca2e7c958a | ||
|
|
c97b089c61 | ||
|
|
5170b34962 | ||
|
|
0a8b6e9427 | ||
|
|
253fbad3db | ||
|
|
efb0afcd6c | ||
|
|
5c85d0cfd1 | ||
|
|
522de3d4b7 | ||
|
|
dc9ae67657 | ||
|
|
fdf899eabf | ||
|
|
fccac8ed74 | ||
|
|
24c895084e | ||
|
|
16fbfdecb5 | ||
|
|
b79e281074 | ||
|
|
93ac380fd6 | ||
|
|
e62f480836 | ||
|
|
46066d4cac | ||
|
|
6ef6651ed6 | ||
|
|
21aaab1b9e | ||
|
|
8450675401 | ||
|
|
c13cf4f29c | ||
|
|
a4f06a9e1e | ||
|
|
cbc5fc8d08 | ||
|
|
0a27c0b6e4 | ||
|
|
f348a20035 | ||
|
|
bdb98b487f | ||
|
|
2cb6402189 | ||
|
|
861bb7a00a | ||
|
|
43d5458a25 | ||
|
|
96fd72e5b0 | ||
|
|
fe491e4a7d | ||
|
|
c1794dd0ca | ||
|
|
8f7d8b1cba | ||
|
|
8ebb8a04fa | ||
|
|
ef6435a02a | ||
|
|
5cbe0a8230 | ||
|
|
dcef6b556c | ||
|
|
c07a636db2 | ||
|
|
a6be95fe33 | ||
|
|
14149aa6b1 | ||
|
|
ae901219fb | ||
|
|
6d9600d132 | ||
|
|
ccc55562cd | ||
|
|
67d9ae881f | ||
|
|
61e0525ee8 | ||
|
|
363519db02 | ||
|
|
4bc179bd2a | ||
|
|
76debda76e | ||
|
|
b4eabe19cb | ||
|
|
971b75860f | ||
|
|
5a926bf169 | ||
|
|
d34f37c563 | ||
|
|
6035a01019 | ||
|
|
c880f71dc8 | ||
|
|
13422ef7ce | ||
|
|
84e2efd42c | ||
|
|
113d26937d | ||
|
|
34606415d7 | ||
|
|
6b9c1f0a22 | ||
|
|
132cc0e131 | ||
|
|
5926369a56 | ||
|
|
ff068d374f | ||
|
|
4132d6ff61 | ||
|
|
1f10199e64 | ||
|
|
e97704b37f | ||
|
|
4ae81a3b0b | ||
|
|
f4ec9a8ec1 | ||
|
|
711037b619 | ||
|
|
b32c9f1724 | ||
|
|
be132c531e | ||
|
|
fb8826caf4 | ||
|
|
adff60b118 | ||
|
|
8f863bf19b | ||
|
|
fd61207709 | ||
|
|
8c1a9f460d | ||
|
|
f6d4a315c7 | ||
|
|
fc1a2a6112 | ||
|
|
13547781d2 | ||
|
|
d375d1abc9 | ||
|
|
5e4f31f39e | ||
|
|
dba186347c | ||
|
|
7988274d61 | ||
|
|
b9eeab06a1 | ||
|
|
c0f7d5ab44 | ||
|
|
60f81fb89d | ||
|
|
49bc96dc7e | ||
|
|
9922382581 | ||
|
|
4aa52d2f2e | ||
|
|
9393192036 | ||
|
|
5cfefbb9ec | ||
|
|
1c8ccd4646 | ||
|
|
3cdbc4cb83 | ||
|
|
0c7741120a | ||
|
|
e529438c7e | ||
|
|
6f69316966 | ||
|
|
5e77ef97e4 | ||
|
|
aebfbba20e | ||
|
|
85cc86346c | ||
|
|
b20e095683 | ||
|
|
b4d7f05165 | ||
|
|
46a12754e9 | ||
|
|
411b2a0acc | ||
|
|
7255ccbdaa | ||
|
|
45dc8b949d | ||
|
|
10e3a5afc4 | ||
|
|
303aa79ffb | ||
|
|
35f3be9296 | ||
|
|
695954bdb0 | ||
|
|
d29b3ee893 | ||
|
|
d9cecb35cb | ||
|
|
71dd6c6333 | ||
|
|
56f13d314a | ||
|
|
b628d67961 | ||
|
|
0fed0d4371 | ||
|
|
8fe05c9119 | ||
|
|
e8e6a9bc5c | ||
|
|
1069dc9700 | ||
|
|
515a28dcfd | ||
|
|
426838f33e | ||
|
|
458cdd3ddd | ||
|
|
05d678a189 | ||
|
|
454a2b8ae7 | ||
|
|
1536209eb3 | ||
|
|
a60eac5b6c | ||
|
|
7d95d5f991 | ||
|
|
8cf1eb9230 | ||
|
|
148e9247d3 | ||
|
|
6d6b968fd6 | ||
|
|
3c75a5e39e | ||
|
|
9534c4ae06 | ||
|
|
5e23bd9c81 | ||
|
|
91b5ae633a | ||
|
|
8c98d5cc42 | ||
|
|
cd4424b94a | ||
|
|
deb397ed1e | ||
|
|
4e1fe67095 | ||
|
|
dd0fd1959c | ||
|
|
a1dba8a58c | ||
|
|
f355e1ed2b | ||
|
|
82c1fbdc7b | ||
|
|
f7f14ff852 | ||
|
|
754e11ade7 | ||
|
|
389f8ea4e2 | ||
|
|
d3e6f9ae02 |
2
.github/ISSUE_TEMPLATE/bug.md
vendored
2
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -11,7 +11,7 @@ Operating system:
|
||||
Java version:
|
||||
Minecraft version:
|
||||
Baritone version:
|
||||
Forge mods (if used):
|
||||
Other mods (if used):
|
||||
|
||||
## Exception, error or logs
|
||||
Please find your `latest.log` or `debug.log` in this folder and attach it to the issue
|
||||
|
||||
39
.github/workflows/gradle_build.yml
vendored
Normal file
39
.github/workflows/gradle_build.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# This workflow will build a Java project with Gradle
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
|
||||
|
||||
name: Java CI with Gradle
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
|
||||
- name: Archive Artifacts
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Artifacts
|
||||
path: dist/
|
||||
|
||||
- name: Archive mapping.txt
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Mappings
|
||||
path: build/tmp/proguard/mapping.txt
|
||||
26
.github/workflows/run_tests.yml
vendored
Normal file
26
.github/workflows/run_tests.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 9
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'adopt'
|
||||
|
||||
- name: Grant execute permission for gradlew
|
||||
run: chmod +x gradlew
|
||||
|
||||
- name: Executing tests
|
||||
run: ./gradlew test
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -11,8 +11,6 @@ build/
|
||||
classes/
|
||||
*.class
|
||||
|
||||
/out
|
||||
|
||||
# IntelliJ Files
|
||||
.idea/
|
||||
*.iml
|
||||
@@ -20,6 +18,12 @@ classes/
|
||||
*.iws
|
||||
/logs/
|
||||
|
||||
# Eclipse Files
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
baritone_Client.launch
|
||||
|
||||
# Copyright Files
|
||||
!/.idea/copyright/Baritone.xml
|
||||
!/.idea/copyright/profiles_settings.xml
|
||||
|
||||
28
.travis.yml
28
.travis.yml
@@ -1,28 +0,0 @@
|
||||
language: java
|
||||
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
install:
|
||||
- travis_retry docker build -t cabaletta/baritone .
|
||||
|
||||
script:
|
||||
- docker run --name baritone cabaletta/baritone ./gradlew javadoc
|
||||
- docker cp baritone:/code/dist dist
|
||||
- ls dist
|
||||
- cat dist/checksums.txt
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: YOuiXoJNpB4bW89TQoY2IGXg0tqOKls55YMXsSPU6Mx8WzRu8CjjO/A8KA9nGfNrKM+NucjiKr/h53O2Dp2uyy0i0SLvav/G0MaBMeB1NlPRwFopi6tVPNaoZsvr8NW4BIURhspckYLpOTYWnfmOkIv8q7AxrjUZWPKDlq0dte20UxEqUE6msHJ7U9XlKo/4fX40kvWMfwGI2hTyAtL0cRT1QPsd+uW3OQjAPcQj+jKaWld46V8pBK8g9Qde9mo8HC9NBv97zw1bBF1EFkynW569kElHvaS2Opl2QLGaf66guDbpnqDpGHMhQrDdxsZHJ4RksyITn+8A9UArmbkU35BxKqBeQqOWxod2+M0axdLh1pvX43Q1t9n7RiZBf7GvV8vkXL5Sjf8v6Y4LqkJGhvQkTUwpH+0knwrE761DMCtBC34AiWG70D4u7msmhurkflr9kmRHSj/3lyJ1Q2lkt8L+FOAlQBVs64vXTsfgc6Yge7N0O3UD5hCkrDNoz3BzhNBdCkbdxdKCGip71UZgUNkPy9o3ui8jATNj9ypx3+U8ovqP0XWlJqUZmyeXyNGW9NrLeCkRLTlLnZ/dv6OPONa1oAu4TwF1w5A+TGRFZcZjH/PnZKZDQ1OYQOR6drLKRYdr2unvuf5KUKUGqZ7aYtLGhP0rBvGWddRV7DSmX/s=
|
||||
all_branches: true
|
||||
file_glob: true
|
||||
file:
|
||||
- dist/*
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
repo: cabaletta/baritone
|
||||
122
README.md
122
README.md
@@ -1 +1,121 @@
|
||||
This branch of Baritone is deprecated. It will no longer recieve updates. Updates to older versions of Minecraft will not be merged into this branch, even if a newer branch is not deprecated (this branch will be skipped). Bug reports that only affect deprecated branches will not be addressed.
|
||||
# Baritone
|
||||
<p align="center">
|
||||
<a href="https://github.com/cabaletta/baritone/releases/"><img src="https://img.shields.io/github/downloads/cabaletta/baritone/total.svg" alt="GitHub All Releases"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/MC-1.12.2-brightgreen.svg" alt="Minecraft"/>
|
||||
<img src="https://img.shields.io/badge/MC-1.13.2-brightgreen.svg" alt="Minecraft"/>
|
||||
<img src="https://img.shields.io/badge/MC-1.14.4-brightgreen.svg" alt="Minecraft"/>
|
||||
<img src="https://img.shields.io/badge/MC-1.15.2-brightgreen.svg" alt="Minecraft"/>
|
||||
<img src="https://img.shields.io/badge/MC-1.16.5-brightgreen.svg" alt="Minecraft"/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/cabaletta/baritone/"><img src="https://travis-ci.com/cabaletta/baritone.svg?branch=master" alt="Build Status"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/releases/"><img src="https://img.shields.io/github/release/cabaletta/baritone.svg" alt="Release"/></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/license-LGPL--3.0%20with%20anime%20exception-green.svg" alt="License"/></a>
|
||||
<a href="https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/a73d037823b64a5faf597a18d71e3400" alt="Codacy Badge"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/blob/master/CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/%E2%9D%A4-code%20of%20conduct-blue.svg?style=flat" alt="Code of Conduct"/></a>
|
||||
<a href="https://snyk.io/test/github/cabaletta/baritone?targetFile=build.gradle"><img src="https://snyk.io/test/github/cabaletta/baritone/badge.svg?targetFile=build.gradle" alt="Known Vulnerabilities"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/issues/"><img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="Contributions welcome"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/issues/"><img src="https://img.shields.io/github/issues/cabaletta/baritone.svg" alt="Issues"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/issues?q=is%3Aissue+is%3Aclosed"><img src="https://img.shields.io/github/issues-closed/cabaletta/baritone.svg" alt="GitHub issues-closed"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/pulls/"><img src="https://img.shields.io/github/issues-pr/cabaletta/baritone.svg" alt="Pull Requests"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/graphs/contributors/"><img src="https://img.shields.io/github/contributors/cabaletta/baritone.svg" alt="GitHub contributors"/></a>
|
||||
<a href="https://github.com/cabaletta/baritone/commit/"><img src="https://img.shields.io/github/commits-since/cabaletta/baritone/v1.0.0.svg" alt="GitHub commits"/></a>
|
||||
<img src="https://img.shields.io/github/languages/code-size/cabaletta/baritone.svg" alt="Code size"/>
|
||||
<img src="https://img.shields.io/github/repo-size/cabaletta/baritone.svg" alt="GitHub repo size"/>
|
||||
<img src="https://tokei.rs/b1/github/cabaletta/baritone?category=code" alt="Lines of Code"/>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://impactclient.net/"><img src="https://img.shields.io/badge/Impact%20integration-v1.2.14%20/%20v1.3.8%20/%20v1.4.6%20/%20v1.5.3%20/%20v1.6.3-brightgreen.svg" alt="Impact integration"/></a>
|
||||
<a href="https://github.com/kami-blue/client"><img src="https://img.shields.io/badge/KAMI%20Blue%20integration-v1.2.14--master-green" alt="KAMI Blue integration"/></a>
|
||||
<a href="https://github.com/fr1kin/ForgeHax/"><img src="https://img.shields.io/badge/ForgeHax%20%22integration%22-scuffed-yellow.svg" alt="ForgeHax integration"/></a>
|
||||
<a href="https://aristois.net/"><img src="https://img.shields.io/badge/Aristois%20add--on%20integration-v1.6.3-green.svg" alt="Aristois add-on integration"/></a>
|
||||
<a href="https://rootnet.dev/"><img src="https://img.shields.io/badge/rootNET%20integration-v1.2.14-green.svg" alt="rootNET integration"/></a>
|
||||
<a href="https://futureclient.net/"><img src="https://img.shields.io/badge/Future%20integration-v1.2.12%20%2F%20v1.3.6%20%2F%20v1.4.4-red" alt="Future integration"/></a>
|
||||
<a href="https://rusherhack.org/"><img src="https://img.shields.io/badge/RusherHack%20integration-v1.2.14-green" alt="RusherHack integration"/></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="http://forthebadge.com/"><img src="https://forthebadge.com/images/badges/built-with-swag.svg" alt="forthebadge"/></a>
|
||||
<a href="http://forthebadge.com/"><img src="https://forthebadge.com/images/badges/mom-made-pizza-rolls.svg" alt="forthebadge"/></a>
|
||||
</p>
|
||||
|
||||
A Minecraft pathfinder bot.
|
||||
|
||||
[**Baritone Discord Server**](http://discord.gg/s6fRBAUpmr)
|
||||
|
||||
Baritone is the pathfinding system used in [Impact](https://impactclient.net/) since 4.4. There's a [showcase video](https://youtu.be/CZkLXWo4Fg4) made by @Adovin#0730 on Baritone which I recommend. [Here's](https://www.youtube.com/watch?v=StquF69-_wI) a (very old!) video I made showing off what it can do. [Tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa)
|
||||
|
||||
The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone. The second easiest way (for 1.12.2 only) is to install the v1.2.* `api-forge` jar from [releases](https://github.com/cabaletta/baritone/releases). **For 1.12.2 Forge, just click [here](https://github.com/cabaletta/baritone/releases/download/v1.2.15/baritone-api-forge-1.2.15.jar)**. Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it.
|
||||
|
||||
For 1.15.2, [click here](https://www.youtube.com/watch?v=j1qKtCZFURM) and see description. If you need Forge 1.15.2, look [here](https://github.com/cabaletta/baritone/releases/tag/v1.5.3), follow the instructions, and get the `api-forge` jar.
|
||||
|
||||
For 1.16.5, [click here](https://www.youtube.com/watch?v=_4eVJ9Qz2J8) and see description. If you need Forge or Fabric 1.16.5, look [here](https://github.com/cabaletta/baritone/releases/tag/v1.6.3) and get the `api-forge` or `api-fabric` jar. **For 1.16.5 Fabric, just click [here](https://github.com/cabaletta/baritone/releases/download/v1.6.3/baritone-api-fabric-1.6.3.jar)**.
|
||||
|
||||
This project is an updated version of [MineBot](https://github.com/leijurv/MineBot/),
|
||||
the original version of the bot for Minecraft 1.8.9, rebuilt for 1.12.2 through 1.16.5. Baritone focuses on reliability and particularly performance (it's over [30x faster](https://github.com/cabaletta/baritone/pull/180#issuecomment-423822928) than MineBot at calculating paths).
|
||||
|
||||
Have committed at least once a day from Aug 1, 2018, to Aug 1, 2019.
|
||||
|
||||
1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2
|
||||
|
||||
# Getting Started
|
||||
|
||||
Here are some links to help to get started:
|
||||
|
||||
- [Features](FEATURES.md)
|
||||
|
||||
- [Installation & setup](SETUP.md)
|
||||
|
||||
- [API Javadocs](https://baritone.leijurv.com/)
|
||||
|
||||
- [Settings](https://baritone.leijurv.com/baritone/api/Settings.html#field.detail)
|
||||
|
||||
- [Usage (chat control)](USAGE.md)
|
||||
|
||||
## Stars over time
|
||||
|
||||
[](https://starchart.cc/cabaletta/baritone)
|
||||
|
||||
# API
|
||||
|
||||
The API is heavily documented, you can find the Javadocs for the latest release [here](https://baritone.leijurv.com/).
|
||||
Please note that usage of anything located outside of the ``baritone.api`` package is not supported by the API release
|
||||
jar.
|
||||
|
||||
Below is an example of basic usage for changing some settings, and then pathing to an X/Z goal.
|
||||
|
||||
```
|
||||
BaritoneAPI.getSettings().allowSprint.value = true;
|
||||
BaritoneAPI.getSettings().primaryTimeoutMS.value = 2000L;
|
||||
|
||||
BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAndPath(new GoalXZ(10000, 20000));
|
||||
```
|
||||
|
||||
# FAQ
|
||||
|
||||
## Can I use Baritone as a library in my custom utility client?
|
||||
|
||||
That's what it's for, sure! (As long as usage complies with the LGPL 3.0 License)
|
||||
|
||||
## How is it so fast?
|
||||
|
||||
Magic. (Hours of [leijurv](https://github.com/leijurv/) enduring excruciating pain)
|
||||
|
||||
### Additional Special Thanks To:
|
||||
|
||||

|
||||
|
||||
YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications.
|
||||
|
||||
YourKit is the creator of the [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), [YourKit .NET Profiler](https://www.yourkit.com/.net/profiler/), and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/).
|
||||
|
||||
We thank them for granting Baritone an OSS license so that we can make our software the best it can be.
|
||||
|
||||
## Why is it called Baritone?
|
||||
|
||||
It's named for FitMC's deep sultry voice.
|
||||
|
||||
11
SETUP.md
11
SETUP.md
@@ -2,7 +2,7 @@
|
||||
|
||||
The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone.
|
||||
|
||||
You can also use a custom version json for Minecraft, with the [1.14.4](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1) version or the [1.15.2](https://www.dropbox.com/s/8rx6f0kts9hvd4f/1.15.2-Baritone.zip?dl=1) version
|
||||
You can also use a custom version json for Minecraft, with the [1.14.4](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1) version or the [1.15.2](https://www.dropbox.com/s/8rx6f0kts9hvd4f/1.15.2-Baritone.zip?dl=1) version or the [1.16.5](https://www.dropbox.com/s/i6f292o2i7o9acp/1.16.5-Baritone.zip?dl=1) version.
|
||||
|
||||
Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it.
|
||||
|
||||
@@ -11,9 +11,9 @@ These releases are not always completely up to date with latest features, and ar
|
||||
|
||||
Link to the releases page: [Releases](https://github.com/cabaletta/baritone/releases)
|
||||
|
||||
v1.2.* is for 1.12.2, v1.3.* is for 1.13.2
|
||||
v1.2.* is for 1.12.2, v1.3.* is for 1.13.2, v1.4.* is for 1.14.4, v1.5.* is for 1.15.2, v1.6.* is for 1.16.2 or 1.16.4 or 1.16.5 (LOL)
|
||||
|
||||
Any official release will be GPG signed by leijurv (44A3EA646EADAC6A) and ZeroMemes (73A788379A197567). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by those two public keys of `checksums.txt`.
|
||||
Any official release will be GPG signed by leijurv (44A3EA646EADAC6A). Please verify that the hash of the file you download is in `checksums.txt` and that `checksums_signed.asc` is a valid signature by that public keys of `checksums.txt`.
|
||||
|
||||
The build is fully deterministic and reproducible, and you can verify Travis did it properly by running `docker build --no-cache -t cabaletta/baritone .` yourself and comparing the shasum. This works identically on Travis, Mac, and Linux (if you have docker on Windows, I'd be grateful if you could let me know if it works there too).
|
||||
|
||||
@@ -32,11 +32,6 @@ If another one of your Forge mods has a Baritone integration, you want `baritone
|
||||
- **Forge Standalone**: Same as Standalone, but packaged for Forge. This should be used when Baritone is your only Forge mod, or none of your other Forge mods integrate with Baritone.
|
||||
- **Unoptimized**: Nothing is obfuscated. This shouldn't be used ever in production.
|
||||
|
||||
## More Info
|
||||
To replace out Impact 4.5's Baritone build with a customized one, build Baritone as above then copy & **rename** `dist/baritone-api-$VERSION$.jar` into `minecraft/libraries/cabaletta/baritone-api/1.2/baritone-api-1.2.jar`, replacing the jar that was previously there. You also need to edit `minecraft/versions/1.12.2-Impact_4.5/1.12.2-Impact_4.5.json`, find the line `"name": "cabaletta:baritone-api:1.2"`, remove the comma from the end, and **entirely remove the NEXT line** (starts with `"url"`). **Restart your launcher** then load as normal.
|
||||
|
||||
You can verify whether or not it worked by running `.b version` in chat (only valid in Impact). It should print out the version that you downloaded. Note: The version that comes with 4.5 is `v1.2.3`.
|
||||
|
||||
## Build it yourself
|
||||
- Clone or download Baritone
|
||||
|
||||
|
||||
2
USAGE.md
2
USAGE.md
@@ -47,7 +47,7 @@ Some common examples:
|
||||
- `build` to build a schematic. `build blah.schematic` will load `schematics/blah.schematic` and build it with the origin being your player feet. `build blah.schematic x y z` to set the origin. Any of those can be relative to your player (`~ 69 ~-420` would build at x=player x, y=69, z=player z-420).
|
||||
- `schematica` to build the schematic that is currently open in schematica
|
||||
- `tunnel` to dig and make a tunnel, 1x2. It will only deviate from the straight line if necessary such as to avoid lava. For a dumber tunnel that is really just cleararea, you can `tunnel 3 2 100`, to clear an area 3 high, 2 wide, and 100 deep.
|
||||
- `farm` to automatically harvest, replant, or bone meal crops
|
||||
- `farm` to automatically harvest, replant, or bone meal crops. Use `farm <range>` or `farm <range> <waypoint>` to limit the max distance from the starting point or a waypoint.
|
||||
- `axis` to go to an axis or diagonal axis at y=120 (`axisHeight` is a configurable setting, defaults to 120).
|
||||
- `explore x z` to explore the world from the origin of x,z. Leave out x and z to default to player feet. This will continually path towards the closest chunk to the origin that it's never seen before. `explorefilter filter.json` with optional invert can be used to load in a list of chunks to load.
|
||||
- `invert` to invert the current goal and path. This gets as far away from it as possible, instead of as close as possible. For example, do `goal` then `invert` to run as far as possible from where you're standing at the start.
|
||||
|
||||
106
build.gradle
106
build.gradle
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
group 'baritone'
|
||||
version '1.3.8'
|
||||
version '1.2.15'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
@@ -25,24 +25,25 @@ buildscript {
|
||||
url = 'http://files.minecraftforge.net/maven'
|
||||
}
|
||||
maven {
|
||||
name = 'impactdevelopment-repo'
|
||||
url = 'https://impactdevelopment.github.io/maven/'
|
||||
name = 'SpongePowered'
|
||||
url = 'https://repo.spongepowered.org/repository/maven-public/'
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath group: 'com.github.ImpactDevelopment', name: 'ForgeGradle', version: '3.0.115'
|
||||
classpath group: 'com.github.ImpactDevelopment', name: 'MixinGradle', version: '0.6.2'
|
||||
classpath 'net.minecraftforge.gradle:ForgeGradle:2.3-SNAPSHOT'
|
||||
classpath 'org.spongepowered:mixingradle:0.6-SNAPSHOT'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
import baritone.gradle.task.CreateDistTask
|
||||
import baritone.gradle.task.ProguardTask
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'net.minecraftforge.gradle'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'net.minecraftforge.gradle.tweaker-client'
|
||||
apply plugin: 'org.spongepowered.mixin'
|
||||
|
||||
sourceCompatibility = targetCompatibility = '1.8'
|
||||
@@ -52,19 +53,8 @@ compileJava {
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
api {
|
||||
compileClasspath += main.compileClasspath
|
||||
}
|
||||
main {
|
||||
compileClasspath += api.output
|
||||
}
|
||||
test {
|
||||
compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output
|
||||
runtimeClasspath += main.compileClasspath + main.runtimeClasspath + main.output
|
||||
}
|
||||
launch {
|
||||
compileClasspath += main.compileClasspath + main.runtimeClasspath + main.output
|
||||
runtimeClasspath += main.compileClasspath + main.runtimeClasspath + main.output
|
||||
}
|
||||
|
||||
schematica_api {
|
||||
@@ -76,58 +66,21 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar, dependsOn: classes) {
|
||||
classifier = 'sources'
|
||||
from sourceSets.api.allSource
|
||||
}
|
||||
|
||||
minecraft {
|
||||
mappings channel: 'snapshot', version: '20190307-1.13.1'
|
||||
reobfMappings 'notch'
|
||||
version = '1.12.2'
|
||||
mappings = 'stable_39'
|
||||
tweakClass = 'baritone.launch.BaritoneTweaker'
|
||||
runDir = 'run'
|
||||
|
||||
runs {
|
||||
client {
|
||||
workingDirectory project.file('run')
|
||||
source sourceSets.launch
|
||||
|
||||
main 'baritone.launch.LaunchTesting'
|
||||
|
||||
environment 'assetIndex', '{asset_index}'
|
||||
environment 'assetDirectory', downloadAssets.output
|
||||
environment 'nativesDirectory', extractNatives.output
|
||||
|
||||
environment 'tweakClass', 'baritone.launch.BaritoneTweaker'
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
jvmArgs "-XstartOnFirstThread"
|
||||
}
|
||||
}
|
||||
|
||||
autoTest {
|
||||
workingDirectory project.file('autotest')
|
||||
source sourceSets.launch
|
||||
|
||||
main 'baritone.launch.LaunchTesting'
|
||||
|
||||
environment 'assetIndex', '{asset_index}'
|
||||
environment 'assetDirectory', downloadAssets.output
|
||||
environment 'nativesDirectory', extractNatives.output
|
||||
|
||||
environment 'tweakClass', 'baritone.launch.BaritoneTweaker'
|
||||
environment 'BARITONE_AUTO_TEST', 'true'
|
||||
|
||||
if (Os.isFamily(Os.FAMILY_MAC)) {
|
||||
jvmArgs "-XstartOnFirstThread"
|
||||
}
|
||||
}
|
||||
}
|
||||
// The sources jar should use SRG names not MCP to ensure compatibility with all mappings
|
||||
makeObfSourceJar = true
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
||||
maven {
|
||||
name = 'SpongePowered'
|
||||
name = 'spongepowered-repo'
|
||||
url = 'https://repo.spongepowered.org/repository/maven-public/'
|
||||
}
|
||||
|
||||
@@ -138,12 +91,6 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft 'com.github.ImpactDevelopment:Vanilla:1.13.2'
|
||||
|
||||
runtime launchCompile('net.minecraft:launchwrapper:1.12') {
|
||||
exclude module: 'lwjgl'
|
||||
}
|
||||
runtime launchCompile('org.ow2.asm:asm-debug-all:5.2')
|
||||
runtime launchCompile('com.github.ImpactDevelopment:SimpleTweaker:1.2')
|
||||
runtime launchCompile('org.spongepowered:mixin:0.7.11-SNAPSHOT') {
|
||||
// Mixin includes a lot of dependencies that are too up-to-date
|
||||
@@ -153,7 +100,6 @@ dependencies {
|
||||
exclude module: 'commons-io'
|
||||
exclude module: 'log4j-core'
|
||||
}
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
@@ -199,3 +145,25 @@ task proguard(type: ProguardTask) {
|
||||
task createDist(type: CreateDistTask, dependsOn: proguard)
|
||||
|
||||
build.finalizedBy(createDist)
|
||||
|
||||
install {
|
||||
def jarApiName = String.format("%s-api-%s", rootProject.name, version.toString())
|
||||
def jarApiForgeName = String.format("%s-api-forge-%s", rootProject.name, version.toString())
|
||||
def jarSAName = String.format("%s-standalone-%s", rootProject.name, version.toString())
|
||||
def jarSAForgeName = String.format("%s-standalone-forge-%s", rootProject.name, version.toString())
|
||||
|
||||
artifacts {
|
||||
archives file("$buildDir/libs/"+jarApiName+".jar")
|
||||
archives file("$buildDir/libs/"+jarApiForgeName+".jar")
|
||||
archives file("$buildDir/libs/"+jarSAName+".jar")
|
||||
archives file("$buildDir/libs/"+jarSAForgeName+".jar")
|
||||
}
|
||||
repositories.mavenInstaller {
|
||||
addFilter('api') { artifact, file -> artifact.name == "baritone-api" }
|
||||
addFilter('api-forge') { artifact, file -> artifact.name == "baritone-api-forge" }
|
||||
addFilter('standalone') { artifact, file -> artifact.name == "baritone-standalone" }
|
||||
addFilter('standalone-forge') { artifact, file -> artifact.name == "baritone-standalone-forge" }
|
||||
}
|
||||
}
|
||||
|
||||
install.dependsOn(build)
|
||||
|
||||
@@ -40,13 +40,17 @@ class BaritoneGradleTask extends DefaultTask {
|
||||
PROGUARD_STANDALONE_CONFIG = "standalone.pro",
|
||||
PROGUARD_EXPORT_PATH = "proguard_out.jar",
|
||||
|
||||
TEMP_LIBRARY_DIR = "tempLibraries/",
|
||||
|
||||
ARTIFACT_STANDARD = "%s-%s.jar",
|
||||
ARTIFACT_UNOPTIMIZED = "%s-unoptimized-%s.jar",
|
||||
ARTIFACT_API = "%s-api-%s.jar",
|
||||
ARTIFACT_STANDALONE = "%s-standalone-%s.jar";
|
||||
ARTIFACT_STANDALONE = "%s-standalone-%s.jar",
|
||||
ARTIFACT_FORGE_API = "%s-api-forge-%s.jar",
|
||||
ARTIFACT_FORGE_STANDALONE = "%s-standalone-forge-%s.jar";
|
||||
|
||||
protected String artifactName, artifactVersion;
|
||||
protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, proguardOut;
|
||||
protected Path artifactPath, artifactUnoptimizedPath, artifactApiPath, artifactStandalonePath, artifactForgeApiPath, artifactForgeStandalonePath, proguardOut;
|
||||
|
||||
protected void verifyArtifacts() throws IllegalStateException {
|
||||
this.artifactName = getProject().getName();
|
||||
@@ -56,6 +60,8 @@ class BaritoneGradleTask extends DefaultTask {
|
||||
this.artifactUnoptimizedPath = this.getBuildFile(formatVersion(ARTIFACT_UNOPTIMIZED));
|
||||
this.artifactApiPath = this.getBuildFile(formatVersion(ARTIFACT_API));
|
||||
this.artifactStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_STANDALONE));
|
||||
this.artifactForgeApiPath = this.getBuildFile(formatVersion(ARTIFACT_FORGE_API));
|
||||
this.artifactForgeStandalonePath = this.getBuildFile(formatVersion(ARTIFACT_FORGE_STANDALONE));
|
||||
|
||||
this.proguardOut = this.getTemporaryFile(PROGUARD_EXPORT_PATH);
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ public class CreateDistTask extends BaritoneGradleTask {
|
||||
Path api = getRelativeFile("dist/" + formatVersion(ARTIFACT_API));
|
||||
Path standalone = getRelativeFile("dist/" + formatVersion(ARTIFACT_STANDALONE));
|
||||
Path unoptimized = getRelativeFile("dist/" + formatVersion(ARTIFACT_UNOPTIMIZED));
|
||||
Path forgeApi = getRelativeFile("dist/" + formatVersion(ARTIFACT_FORGE_API));
|
||||
Path forgeStandalone = getRelativeFile("dist/" + formatVersion(ARTIFACT_FORGE_STANDALONE));
|
||||
|
||||
// NIO will not automatically create directories
|
||||
Path dir = getRelativeFile("dist/");
|
||||
@@ -56,9 +58,11 @@ public class CreateDistTask extends BaritoneGradleTask {
|
||||
Files.copy(this.artifactApiPath, api, REPLACE_EXISTING);
|
||||
Files.copy(this.artifactStandalonePath, standalone, REPLACE_EXISTING);
|
||||
Files.copy(this.artifactUnoptimizedPath, unoptimized, REPLACE_EXISTING);
|
||||
Files.copy(this.artifactForgeApiPath, forgeApi, REPLACE_EXISTING);
|
||||
Files.copy(this.artifactForgeStandalonePath, forgeStandalone, REPLACE_EXISTING);
|
||||
|
||||
// Calculate all checksums and format them like "shasum"
|
||||
List<String> shasum = Stream.of(api, standalone, unoptimized)
|
||||
List<String> shasum = Stream.of(api, forgeApi, standalone, forgeStandalone, unoptimized)
|
||||
.map(path -> sha1(path) + " " + path.getFileName().toString())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
||||
@@ -18,10 +18,9 @@
|
||||
package baritone.gradle.task;
|
||||
|
||||
import baritone.gradle.util.Determinizer;
|
||||
import baritone.gradle.util.MappingType;
|
||||
import baritone.gradle.util.ReobfWrapper;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.gradle.api.plugins.JavaPluginConvention;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.JavaVersion;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
@@ -39,12 +38,14 @@ import org.gradle.internal.jvm.inspection.DefaultJvmVersionDetector;
|
||||
import org.gradle.process.internal.DefaultExecActionFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@@ -56,12 +57,18 @@ import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
|
||||
*/
|
||||
public class ProguardTask extends BaritoneGradleTask {
|
||||
|
||||
private static final Pattern TEMP_LIBRARY_PATTERN = Pattern.compile("-libraryjars 'tempLibraries\\/([a-zA-Z0-9/_\\-\\.]+)\\.jar'");
|
||||
|
||||
@Input
|
||||
private String url;
|
||||
|
||||
@Input
|
||||
private String extract;
|
||||
|
||||
private List<String> requiredLibraries;
|
||||
|
||||
private File mixin;
|
||||
|
||||
@TaskAction
|
||||
protected void exec() throws Exception {
|
||||
super.verifyArtifacts();
|
||||
@@ -71,6 +78,7 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
downloadProguard();
|
||||
extractProguard();
|
||||
generateConfigs();
|
||||
acquireDependencies();
|
||||
proguardApi();
|
||||
proguardStandalone();
|
||||
cleanup();
|
||||
@@ -81,7 +89,7 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
Files.delete(this.artifactUnoptimizedPath);
|
||||
}
|
||||
|
||||
Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString());
|
||||
Determinizer.determinize(this.artifactPath.toString(), this.artifactUnoptimizedPath.toString(), Optional.empty());
|
||||
}
|
||||
|
||||
private void downloadProguard() throws Exception {
|
||||
@@ -209,19 +217,6 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
String out = IOUtils.toString(p.getInputStream(), "UTF-8").split("\n")[0].split("Opened ")[1].replace("]", "");
|
||||
template.add(2, "-libraryjars '" + out + "'");
|
||||
|
||||
// Discover all of the libraries that we will need to acquire from gradle
|
||||
acquireDependencies().forEach(f -> {
|
||||
if (f.toString().endsWith("-recomp.jar")) {
|
||||
// remove MCP mapped jar
|
||||
return;
|
||||
}
|
||||
if (f.toString().endsWith("client-extra.jar")) {
|
||||
// go from the extra to the original downloaded client
|
||||
f = new File(f.getParentFile(), "client.jar");
|
||||
}
|
||||
template.add(2, "-libraryjars '" + f + "'");
|
||||
});
|
||||
|
||||
// API config doesn't require any changes from the changes that we made to the template
|
||||
Files.write(getTemporaryFile(PROGUARD_API_CONFIG), template);
|
||||
|
||||
@@ -229,20 +224,165 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
List<String> standalone = new ArrayList<>(template);
|
||||
standalone.removeIf(s -> s.contains("# this is the keep api"));
|
||||
Files.write(getTemporaryFile(PROGUARD_STANDALONE_CONFIG), standalone);
|
||||
|
||||
// Discover all of the libraries that we will need to acquire from gradle
|
||||
this.requiredLibraries = new ArrayList<>();
|
||||
template.forEach(line -> {
|
||||
if (!line.startsWith("#")) {
|
||||
Matcher m = TEMP_LIBRARY_PATTERN.matcher(line);
|
||||
if (m.find()) {
|
||||
this.requiredLibraries.add(m.group(1));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Stream<File> acquireDependencies() {
|
||||
return getProject().getConvention().getPlugin(JavaPluginConvention.class).getSourceSets().findByName("launch").getRuntimeClasspath().getFiles().stream().filter(File::isFile);
|
||||
private void acquireDependencies() throws Exception {
|
||||
|
||||
// Create a map of all of the dependencies that we are able to access in this project
|
||||
// Likely a better way to do this, I just pair the dependency with the first valid configuration
|
||||
Map<String, Pair<Configuration, Dependency>> dependencyLookupMap = new HashMap<>();
|
||||
getProject().getConfigurations().stream().filter(Configuration::isCanBeResolved).forEach(config ->
|
||||
config.getAllDependencies().forEach(dependency ->
|
||||
dependencyLookupMap.putIfAbsent(dependency.getName() + "-" + dependency.getVersion(), Pair.of(config, dependency))));
|
||||
|
||||
// Create the directory if it doesn't already exist
|
||||
Path tempLibraries = getTemporaryFile(TEMP_LIBRARY_DIR);
|
||||
if (!Files.exists(tempLibraries)) {
|
||||
Files.createDirectory(tempLibraries);
|
||||
}
|
||||
|
||||
// Iterate the required libraries to copy them to tempLibraries
|
||||
for (String lib : this.requiredLibraries) {
|
||||
// copy from the forgegradle cache
|
||||
if (lib.equals("minecraft")) {
|
||||
Path cachedJar = getMinecraftJar();
|
||||
Path inTempDir = getTemporaryFile("tempLibraries/minecraft.jar");
|
||||
// TODO: maybe try not to copy every time
|
||||
Files.copy(cachedJar, inTempDir, REPLACE_EXISTING);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find a configuration/dependency pair that matches the desired library
|
||||
Pair<Configuration, Dependency> pair = null;
|
||||
for (Map.Entry<String, Pair<Configuration, Dependency>> entry : dependencyLookupMap.entrySet()) {
|
||||
if (entry.getKey().startsWith(lib)) {
|
||||
pair = entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
// The pair must be non-null
|
||||
Objects.requireNonNull(pair);
|
||||
|
||||
// Find the library jar file, and copy it to tempLibraries
|
||||
for (File file : pair.getLeft().files(pair.getRight())) {
|
||||
if (file.getName().startsWith(lib)) {
|
||||
if (lib.contains("mixin")) {
|
||||
mixin = file;
|
||||
}
|
||||
Files.copy(file.toPath(), getTemporaryFile("tempLibraries/" + lib + ".jar"), REPLACE_EXISTING);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mixin == null) {
|
||||
throw new IllegalStateException("Unable to find mixin jar");
|
||||
}
|
||||
}
|
||||
|
||||
// a bunch of epic stuff to get the path to the cached jar
|
||||
private Path getMinecraftJar() throws Exception {
|
||||
MappingType mappingType;
|
||||
try {
|
||||
mappingType = getMappingType();
|
||||
} catch (Exception e) {
|
||||
System.err.println("Failed to get mapping type, assuming NOTCH.");
|
||||
mappingType = MappingType.NOTCH;
|
||||
}
|
||||
|
||||
String suffix;
|
||||
switch (mappingType) {
|
||||
case NOTCH:
|
||||
suffix = "";
|
||||
break;
|
||||
case SEARGE:
|
||||
suffix = "-srgBin";
|
||||
break;
|
||||
case CUSTOM:
|
||||
throw new IllegalStateException("Custom mappings not supported!");
|
||||
default:
|
||||
throw new IllegalStateException("Unknown mapping type: " + mappingType);
|
||||
}
|
||||
|
||||
DefaultConvention convention = (DefaultConvention) this.getProject().getConvention();
|
||||
Object extension = convention.getAsMap().get("minecraft");
|
||||
Objects.requireNonNull(extension);
|
||||
|
||||
// for some reason cant use Class.forName
|
||||
Class<?> class_baseExtension = extension.getClass().getSuperclass().getSuperclass().getSuperclass(); // <-- cursed
|
||||
Field f_replacer = class_baseExtension.getDeclaredField("replacer");
|
||||
f_replacer.setAccessible(true);
|
||||
Object replacer = f_replacer.get(extension);
|
||||
Class<?> class_replacementProvider = replacer.getClass();
|
||||
Field replacement_replaceMap = class_replacementProvider.getDeclaredField("replaceMap");
|
||||
replacement_replaceMap.setAccessible(true);
|
||||
|
||||
Map<String, Object> replacements = (Map) replacement_replaceMap.get(replacer);
|
||||
String cacheDir = replacements.get("CACHE_DIR").toString() + "/net/minecraft";
|
||||
String mcVersion = replacements.get("MC_VERSION").toString();
|
||||
String mcpInsert = replacements.get("MAPPING_CHANNEL").toString() + "/" + replacements.get("MAPPING_VERSION").toString();
|
||||
String fullJarName = "minecraft-" + mcVersion + suffix + ".jar";
|
||||
|
||||
String baseDir = String.format("%s/minecraft/%s/", cacheDir, mcVersion);
|
||||
|
||||
String jarPath;
|
||||
if (mappingType == MappingType.SEARGE) {
|
||||
jarPath = String.format("%s/%s/%s", baseDir, mcpInsert, fullJarName);
|
||||
} else {
|
||||
jarPath = baseDir + fullJarName;
|
||||
}
|
||||
jarPath = jarPath
|
||||
.replace("/", File.separator)
|
||||
.replace("\\", File.separator); // hecking regex
|
||||
|
||||
return new File(jarPath).toPath();
|
||||
}
|
||||
|
||||
// throws IllegalStateException if mapping type is ambiguous or it fails to find it
|
||||
private MappingType getMappingType() {
|
||||
// if it fails to find this then its probably a forgegradle version problem
|
||||
Set<Object> reobf = (NamedDomainObjectContainer<Object>) this.getProject().getExtensions().getByName("reobf");
|
||||
|
||||
List<MappingType> mappingTypes = getUsedMappingTypes(reobf);
|
||||
long mappingTypesUsed = mappingTypes.size();
|
||||
if (mappingTypesUsed == 0) {
|
||||
throw new IllegalStateException("Failed to find mapping type (no jar task?)");
|
||||
}
|
||||
if (mappingTypesUsed > 1) {
|
||||
throw new IllegalStateException("Ambiguous mapping type (multiple jars with different mapping types?)");
|
||||
}
|
||||
|
||||
return mappingTypes.get(0);
|
||||
}
|
||||
|
||||
private List<MappingType> getUsedMappingTypes(Set<Object> reobf) {
|
||||
return reobf.stream()
|
||||
.map(ReobfWrapper::new)
|
||||
.map(ReobfWrapper::getMappingType)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private void proguardApi() throws Exception {
|
||||
runProguard(getTemporaryFile(PROGUARD_API_CONFIG));
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString());
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactApiPath.toString(), Optional.empty());
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactForgeApiPath.toString(), Optional.of(mixin));
|
||||
}
|
||||
|
||||
private void proguardStandalone() throws Exception {
|
||||
runProguard(getTemporaryFile(PROGUARD_STANDALONE_CONFIG));
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString());
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactStandalonePath.toString(), Optional.empty());
|
||||
Determinizer.determinize(this.proguardOut.toString(), this.artifactForgeStandalonePath.toString(), Optional.of(mixin));
|
||||
}
|
||||
|
||||
private void cleanup() {
|
||||
|
||||
@@ -22,10 +22,7 @@ import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
@@ -39,7 +36,7 @@ import java.util.stream.Collectors;
|
||||
*/
|
||||
public class Determinizer {
|
||||
|
||||
public static void determinize(String inputPath, String outputPath) throws IOException {
|
||||
public static void determinize(String inputPath, String outputPath, Optional<File> toInclude) throws IOException {
|
||||
System.out.println("Running Determinizer");
|
||||
System.out.println(" Input path: " + inputPath);
|
||||
System.out.println(" Output path: " + outputPath);
|
||||
@@ -66,10 +63,30 @@ public class Determinizer {
|
||||
if (entry.getName().endsWith(".refmap.json")) {
|
||||
JsonObject object = new JsonParser().parse(new InputStreamReader(jarFile.getInputStream(entry))).getAsJsonObject();
|
||||
jos.write(writeSorted(object).getBytes());
|
||||
} else if (entry.getName().equals("META-INF/MANIFEST.MF") && toInclude.isPresent()) { // only replace for forge jar
|
||||
ByteArrayOutputStream cancer = new ByteArrayOutputStream();
|
||||
copy(jarFile.getInputStream(entry), cancer);
|
||||
String manifest = new String(cancer.toByteArray());
|
||||
if (!manifest.contains("baritone.launch.BaritoneTweaker")) {
|
||||
throw new IllegalStateException("unable to replace");
|
||||
}
|
||||
manifest = manifest.replace("baritone.launch.BaritoneTweaker", "org.spongepowered.asm.launch.MixinTweaker");
|
||||
jos.write(manifest.getBytes());
|
||||
} else {
|
||||
copy(jarFile.getInputStream(entry), jos);
|
||||
}
|
||||
}
|
||||
if (toInclude.isPresent()) {
|
||||
try (JarFile mixin = new JarFile(toInclude.get())) {
|
||||
for (JarEntry entry : mixin.stream().sorted(Comparator.comparing(JarEntry::getName)).collect(Collectors.toList())) {
|
||||
if (entry.getName().startsWith("META-INF") && !entry.getName().startsWith("META-INF/services")) {
|
||||
continue;
|
||||
}
|
||||
jos.putNextEntry(entry);
|
||||
copy(mixin.getInputStream(entry), jos);
|
||||
}
|
||||
}
|
||||
}
|
||||
jos.finish();
|
||||
}
|
||||
}
|
||||
|
||||
29
buildSrc/src/main/java/baritone/gradle/util/MappingType.java
Normal file
29
buildSrc/src/main/java/baritone/gradle/util/MappingType.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.gradle.util;
|
||||
|
||||
/**
|
||||
* All credits go to AsmLibGradle and its contributors.
|
||||
*
|
||||
* @see <a href="https://github.com/pozzed/AsmLibGradle/blob/8f917dbc3939eab7a3d9daf54d9d285fdf34f4b2/src/main/java/net/futureclient/asmlib/forgegradle/MappingType.java">Original Source</a>
|
||||
*/
|
||||
public enum MappingType {
|
||||
SEARGE,
|
||||
NOTCH,
|
||||
CUSTOM // forgegradle
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.gradle.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* All credits go to AsmLibGradle and its contributors.
|
||||
*
|
||||
* @see <a href="https://github.com/pozzed/AsmLibGradle/blob/8f917dbc3939eab7a3d9daf54d9d285fdf34f4b2/src/main/java/net/futureclient/asmlib/forgegradle/ReobfWrapper.java">Original Source</a>
|
||||
*/
|
||||
public class ReobfWrapper {
|
||||
|
||||
private final Object instance;
|
||||
private final Class<?> type;
|
||||
|
||||
public ReobfWrapper(Object instance) {
|
||||
this.instance = instance;
|
||||
Objects.requireNonNull(instance);
|
||||
this.type = instance.getClass();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
try {
|
||||
Field nameField = type.getDeclaredField("name");
|
||||
nameField.setAccessible(true);
|
||||
return (String) nameField.get(this.instance);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public MappingType getMappingType() {
|
||||
try {
|
||||
Field enumField = type.getDeclaredField("mappingType");
|
||||
enumField.setAccessible(true);
|
||||
Enum<?> aEnum = (Enum<?>) enumField.get(this.instance);
|
||||
MappingType mappingType = MappingType.values()[aEnum.ordinal()];
|
||||
if (!aEnum.name().equals(mappingType.name())) {
|
||||
throw new IllegalStateException("ForgeGradle ReobfMappingType is not equivalent to MappingType (version error?)");
|
||||
}
|
||||
return mappingType;
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
scripts/proguard.pro
vendored
53
scripts/proguard.pro
vendored
@@ -14,8 +14,6 @@
|
||||
|
||||
# lwjgl is weird
|
||||
-dontwarn org.lwjgl.**
|
||||
# also lwjgl lol
|
||||
-dontwarn module-info
|
||||
|
||||
-keep class baritone.api.** { *; } # this is the keep api
|
||||
|
||||
@@ -45,6 +43,55 @@
|
||||
#proguard doesnt like it when it cant find our fake schematica classes
|
||||
-dontwarn baritone.utils.schematic.schematica.**
|
||||
|
||||
# copy all necessary libraries into tempLibraries to build
|
||||
|
||||
# The correct jar will be copied from the forgegradle cache based on the mapping type being compiled with
|
||||
-libraryjars 'tempLibraries/minecraft.jar'
|
||||
|
||||
-libraryjars 'tempLibraries/SimpleTweaker-1.2.jar'
|
||||
|
||||
-libraryjars 'tempLibraries/authlib-1.5.25.jar'
|
||||
-libraryjars 'tempLibraries/codecjorbis-20101023.jar'
|
||||
-libraryjars 'tempLibraries/codecwav-20101023.jar'
|
||||
-libraryjars 'tempLibraries/commons-codec-1.10.jar'
|
||||
-libraryjars 'tempLibraries/commons-compress-1.8.1.jar'
|
||||
-libraryjars 'tempLibraries/commons-io-2.5.jar'
|
||||
-libraryjars 'tempLibraries/commons-lang3-3.5.jar'
|
||||
-libraryjars 'tempLibraries/commons-logging-1.1.3.jar'
|
||||
-libraryjars 'tempLibraries/fastutil-7.1.0.jar'
|
||||
-libraryjars 'tempLibraries/gson-2.8.0.jar'
|
||||
-libraryjars 'tempLibraries/guava-21.0.jar'
|
||||
-libraryjars 'tempLibraries/httpclient-4.3.3.jar'
|
||||
-libraryjars 'tempLibraries/httpcore-4.3.2.jar'
|
||||
-libraryjars 'tempLibraries/icu4j-core-mojang-51.2.jar'
|
||||
-libraryjars 'tempLibraries/jinput-2.0.5.jar'
|
||||
-libraryjars 'tempLibraries/jna-4.4.0.jar'
|
||||
-libraryjars 'tempLibraries/jopt-simple-5.0.3.jar'
|
||||
-libraryjars 'tempLibraries/jsr305-3.0.1.jar'
|
||||
-libraryjars 'tempLibraries/jutils-1.0.0.jar'
|
||||
-libraryjars 'tempLibraries/libraryjavasound-20101123.jar'
|
||||
-libraryjars 'tempLibraries/librarylwjglopenal-20100824.jar'
|
||||
-libraryjars 'tempLibraries/log4j-api-2.8.1.jar'
|
||||
-libraryjars 'tempLibraries/log4j-core-2.8.1.jar'
|
||||
|
||||
# startsWith is used to check the library, and mac/linux differ in which version they use
|
||||
# this is FINE
|
||||
-libraryjars 'tempLibraries/lwjgl-.jar'
|
||||
-libraryjars 'tempLibraries/lwjgl_util-.jar'
|
||||
|
||||
-libraryjars 'tempLibraries/netty-all-4.1.9.Final.jar'
|
||||
-libraryjars 'tempLibraries/oshi-core-1.1.jar'
|
||||
-libraryjars 'tempLibraries/patchy-1.2.jar'
|
||||
-libraryjars 'tempLibraries/platform-3.4.0.jar'
|
||||
-libraryjars 'tempLibraries/realms-1.10.22.jar'
|
||||
-libraryjars 'tempLibraries/soundsystem-20120107.jar'
|
||||
-libraryjars 'tempLibraries/text2speech-1.10.3.jar'
|
||||
|
||||
-libraryjars 'tempLibraries/mixin-0.7.11-SNAPSHOT.jar'
|
||||
-libraryjars 'tempLibraries/launchwrapper-1.11.jar' # TODO why does only 1.11.jar exist?
|
||||
|
||||
|
||||
|
||||
|
||||
# Keep - Applications. Keep all application classes, along with their 'main'
|
||||
# methods.
|
||||
@@ -334,3 +381,5 @@
|
||||
public java.lang.String substring(int);
|
||||
public java.lang.String substring(int,int);
|
||||
}
|
||||
|
||||
-printmapping mapping.txt
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
package baritone.api;
|
||||
|
||||
import baritone.api.utils.NotificationHelper;
|
||||
import baritone.api.utils.SettingsUtil;
|
||||
import baritone.api.utils.TypeUtils;
|
||||
import baritone.api.utils.gui.BaritoneToast;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -33,6 +35,7 @@ import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
* Baritone's settings. Settings apply to all Baritone instances.
|
||||
@@ -70,9 +73,9 @@ public final class Settings {
|
||||
public final Setting<Boolean> assumeExternalAutoTool = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* If this setting is on, no auto tool will occur at all, not at calculation time nor execution time
|
||||
* Automatically select the best available tool
|
||||
*/
|
||||
public final Setting<Boolean> disableAutoTool = new Setting<>(false);
|
||||
public final Setting<Boolean> autoTool = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* It doesn't actually take twenty ticks to place a block, this cost is so high
|
||||
@@ -171,10 +174,10 @@ public final class Settings {
|
||||
* Blocks that Baritone is allowed to place (as throwaway, for sneak bridging, pillaring, etc.)
|
||||
*/
|
||||
public final Setting<List<Item>> acceptableThrowawayItems = new Setting<>(new ArrayList<>(Arrays.asList(
|
||||
Blocks.DIRT.asItem(),
|
||||
Blocks.COBBLESTONE.asItem(),
|
||||
Blocks.NETHERRACK.asItem(),
|
||||
Blocks.STONE.asItem()
|
||||
Item.getItemFromBlock(Blocks.DIRT),
|
||||
Item.getItemFromBlock(Blocks.COBBLESTONE),
|
||||
Item.getItemFromBlock(Blocks.NETHERRACK),
|
||||
Item.getItemFromBlock(Blocks.STONE)
|
||||
)));
|
||||
|
||||
/**
|
||||
@@ -190,9 +193,10 @@ public final class Settings {
|
||||
public final Setting<List<Block>> blocksToAvoidBreaking = new Setting<>(new ArrayList<>(Arrays.asList( // TODO can this be a HashSet or ImmutableSet?
|
||||
Blocks.CRAFTING_TABLE,
|
||||
Blocks.FURNACE,
|
||||
Blocks.LIT_FURNACE,
|
||||
Blocks.CHEST,
|
||||
Blocks.TRAPPED_CHEST,
|
||||
Blocks.SIGN,
|
||||
Blocks.STANDING_SIGN,
|
||||
Blocks.WALL_SIGN
|
||||
)));
|
||||
|
||||
@@ -205,6 +209,29 @@ public final class Settings {
|
||||
|
||||
)));
|
||||
|
||||
/**
|
||||
* A list of blocks to be treated as correct.
|
||||
* <p>
|
||||
* If a schematic asks for any block on this list at a certain position, it will be treated as correct, regardless of what it currently is.
|
||||
*/
|
||||
public final Setting<List<Block>> buildSkipBlocks = new Setting<>(new ArrayList<>(Arrays.asList(
|
||||
|
||||
)));
|
||||
|
||||
/**
|
||||
* A mapping of blocks to blocks treated as correct in their position
|
||||
* <p>
|
||||
* If a schematic asks for a block on this mapping, all blocks on the mapped list will be accepted at that location as well
|
||||
*/
|
||||
public final Setting<Map<Block, List<Block>>> buildValidSubstitutes = new Setting<>(new HashMap<>());
|
||||
|
||||
/**
|
||||
* A mapping of blocks to blocks to be built instead
|
||||
* <p>
|
||||
* If a schematic asks for a block on this mapping, Baritone will place the first placeable block in the mapped list
|
||||
*/
|
||||
public final Setting<Map<Block, List<Block>>> buildSubstitutes = new Setting<>(new HashMap<>());
|
||||
|
||||
/**
|
||||
* A list of blocks to become air
|
||||
* <p>
|
||||
@@ -283,11 +310,6 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Integer> rightClickSpeed = new Setting<>(4);
|
||||
|
||||
/**
|
||||
* How many degrees to randomize the yaw every tick. Set to 0 to disable
|
||||
*/
|
||||
public final Setting<Double> randomLooking113 = new Setting<>(2d);
|
||||
|
||||
/**
|
||||
* Block reach distance
|
||||
*/
|
||||
@@ -576,6 +598,12 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> renderGoal = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Render the goal as a sick animated thingy instead of just a box
|
||||
* (also controls animation of GoalXZ if {@link #renderGoalXZBeacon} is enabled)
|
||||
*/
|
||||
public final Setting<Boolean> renderGoalAnimated = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Render selection boxes
|
||||
*/
|
||||
@@ -645,7 +673,7 @@ public final class Settings {
|
||||
|
||||
/**
|
||||
* When GetToBlockProcess or MineProcess fails to calculate a path, instead of just giving up, mark the closest instance
|
||||
* of that block as "unreachable" and go towards the next closest. GetToBlock expands this seaarch to the whole "vein"; MineProcess does not.
|
||||
* of that block as "unreachable" and go towards the next closest. GetToBlock expands this search to the whole "vein"; MineProcess does not.
|
||||
* This is because MineProcess finds individual impossible blocks (like one block in a vein that has gravel on top then lava, so it can't break)
|
||||
* Whereas GetToBlock should blacklist the whole "vein" if it can't get to any of them.
|
||||
*/
|
||||
@@ -700,6 +728,16 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> censorRanCommands = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Stop using tools just before they are going to break.
|
||||
*/
|
||||
public final Setting<Boolean> itemSaver = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Durability to leave on the tool when using itemSaver
|
||||
*/
|
||||
public final Setting<Integer> itemSaverThreshold = new Setting<>(10);
|
||||
|
||||
/**
|
||||
* Always prefer silk touch tools over regular tools. This will not sacrifice speed, but it will always prefer silk
|
||||
* touch tools over other tools of the same speed. This includes always choosing ANY silk touch tool over your hand.
|
||||
@@ -760,7 +798,7 @@ public final class Settings {
|
||||
public final Setting<Integer> allowOnlyExposedOresDistance = new Setting<>(1);
|
||||
|
||||
/**
|
||||
* When GetToBlock doesn't know any locations for the desired block, explore randomly instead of giving up.
|
||||
* When GetToBlock or non-legit Mine doesn't know any locations for the desired block, explore randomly instead of giving up.
|
||||
*/
|
||||
public final Setting<Boolean> exploreForBlocks = new Setting<>(true);
|
||||
|
||||
@@ -812,6 +850,11 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> layerOrder = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* How high should the individual layers be?
|
||||
*/
|
||||
public final Setting<Integer> layerHeight = new Setting<>(1);
|
||||
|
||||
/**
|
||||
* Start building the schematic at a specific layer.
|
||||
* Can help on larger builds when schematic wants to break things its already built
|
||||
@@ -823,6 +866,11 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> skipFailedLayers = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Only build the selected part of schematics
|
||||
*/
|
||||
public final Setting<Boolean> buildOnlySelection = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* How far to move before repeating the build. 0 to disable repeating on a certain axis, 0,0,0 to disable entirely
|
||||
*/
|
||||
@@ -833,6 +881,13 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Integer> buildRepeatCount = new Setting<>(-1);
|
||||
|
||||
/**
|
||||
* Don't notify schematics that they are moved.
|
||||
* e.g. replacing will replace the same spots for every repetition
|
||||
* Mainly for backward compatibility.
|
||||
*/
|
||||
public final Setting<Boolean> buildRepeatSneaky = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Allow standing above a block while mining it, in BuilderProcess
|
||||
* <p>
|
||||
@@ -941,6 +996,7 @@ public final class Settings {
|
||||
* Disallow MineBehavior from using X-Ray to see where the ores are. Turn this option on to force it to mine "legit"
|
||||
* where it will only mine an ore once it can actually see it, so it won't do or know anything that a normal player
|
||||
* couldn't. If you don't want it to look like you're X-Raying, turn this on
|
||||
* This will always explore, regardless of exploreForBlocks
|
||||
*/
|
||||
public final Setting<Boolean> legitMine = new Setting<>(false);
|
||||
|
||||
@@ -1025,7 +1081,21 @@ public final class Settings {
|
||||
* via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting
|
||||
* {@link Setting#value};
|
||||
*/
|
||||
public final Setting<Consumer<ITextComponent>> logger = new Setting<>(Minecraft.getInstance().ingameGUI.getChatGUI()::printChatMessage);
|
||||
public final Setting<Consumer<ITextComponent>> logger = new Setting<>(Minecraft.getMinecraft().ingameGUI.getChatGUI()::printChatMessage);
|
||||
|
||||
/**
|
||||
* The function that is called when Baritone will send a desktop notification. This function can be added to
|
||||
* via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting
|
||||
* {@link Setting#value};
|
||||
*/
|
||||
public final Setting<BiConsumer<String, Boolean>> notifier = new Setting<>(NotificationHelper::notify);
|
||||
|
||||
/**
|
||||
* The function that is called when Baritone will show a toast. This function can be added to
|
||||
* via {@link Consumer#andThen(Consumer)} or it can completely be overriden via setting
|
||||
* {@link Setting#value};
|
||||
*/
|
||||
public final Setting<BiConsumer<ITextComponent, ITextComponent>> toaster = new Setting<>(BaritoneToast::addOrUpdate);
|
||||
|
||||
/**
|
||||
* The size of the box that is rendered when the current goal is a GoalYLevel
|
||||
@@ -1117,6 +1187,11 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> renderSelectionCorners = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Use sword to mine.
|
||||
*/
|
||||
public final Setting<Boolean> useSwordToMine = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notifications
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,15 @@ public interface IPathingBehavior extends IBehavior {
|
||||
return Optional.of(current.getPath().ticksRemainingFrom(start));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the estimated remaining ticks to the current goal.
|
||||
* Given that the return type is an optional, {@link Optional#empty()}
|
||||
* will be returned in the case that there is no current goal.
|
||||
*
|
||||
* @return The estimated remaining ticks to the current goal.
|
||||
*/
|
||||
Optional<Double> estimatedTicksToGoal();
|
||||
|
||||
/**
|
||||
* @return The current pathing goal
|
||||
*/
|
||||
|
||||
@@ -22,7 +22,6 @@ import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -33,7 +32,7 @@ public enum BlockById implements IDatatypeFor<Block> {
|
||||
public Block get(IDatatypeContext ctx) throws CommandException {
|
||||
ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
|
||||
Block block;
|
||||
if ((block = IRegistry.BLOCK.get(id)) == Blocks.AIR) {
|
||||
if ((block = Block.REGISTRY.getObject(id)) == Blocks.AIR) {
|
||||
throw new IllegalArgumentException("no block found by that id");
|
||||
}
|
||||
return block;
|
||||
@@ -43,7 +42,7 @@ public enum BlockById implements IDatatypeFor<Block> {
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException {
|
||||
return new TabCompleteHelper()
|
||||
.append(
|
||||
IRegistry.BLOCK.keySet()
|
||||
Block.REGISTRY.getKeys()
|
||||
.stream()
|
||||
.map(Object::toString)
|
||||
)
|
||||
|
||||
@@ -19,20 +19,32 @@ package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum EntityClassById implements IDatatypeFor<EntityType> {
|
||||
public enum EntityClassById implements IDatatypeFor<Class<? extends Entity>> {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public EntityType get(IDatatypeContext ctx) throws CommandException {
|
||||
public Class<? extends Entity> get(IDatatypeContext ctx) throws CommandException {
|
||||
ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
|
||||
EntityType entity;
|
||||
if ((entity = IRegistry.ENTITY_TYPE.get(id)) == null) {
|
||||
Class<? extends Entity> entity;
|
||||
try {
|
||||
entity = EntityList.REGISTRY.getObject(id);
|
||||
} catch (NoSuchFieldError e) {
|
||||
// Forge removes EntityList.REGISTRY field and provides the getClass method as a replacement
|
||||
// See https://github.com/MinecraftForge/MinecraftForge/blob/1.12.x/patches/minecraft/net/minecraft/entity/EntityList.java.patch
|
||||
try {
|
||||
entity = (Class<? extends Entity>) EntityList.class.getMethod("getClass", ResourceLocation.class).invoke(null, id);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("EntityList.REGISTRY does not exist and failed to call the Forge-replacement method", ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("no entity found by that id");
|
||||
}
|
||||
return entity;
|
||||
@@ -41,7 +53,7 @@ public enum EntityClassById implements IDatatypeFor<EntityType> {
|
||||
@Override
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException {
|
||||
return new TabCompleteHelper()
|
||||
.append(IRegistry.ENTITY_TYPE.stream().map(Object::toString))
|
||||
.append(EntityList.getEntityNameList().stream().map(Object::toString))
|
||||
.filterPrefixNamespaced(ctx.getConsumer().getString())
|
||||
.sortAlphabetically()
|
||||
.stream();
|
||||
|
||||
@@ -21,7 +21,6 @@ import baritone.api.IBaritone;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
@@ -37,14 +36,14 @@ public enum NearbyPlayer implements IDatatypeFor<EntityPlayer> {
|
||||
public EntityPlayer get(IDatatypeContext ctx) throws CommandException {
|
||||
final String username = ctx.getConsumer().getString();
|
||||
return getPlayers(ctx).stream()
|
||||
.filter(s -> s.getName().getString().equalsIgnoreCase(username))
|
||||
.filter(s -> s.getName().equalsIgnoreCase(username))
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<String> tabComplete(IDatatypeContext ctx) throws CommandException {
|
||||
return new TabCompleteHelper()
|
||||
.append(getPlayers(ctx).stream().map(EntityPlayer::getName).map(ITextComponent::getString))
|
||||
.append(getPlayers(ctx).stream().map(EntityPlayer::getName))
|
||||
.filterPrefix(ctx.getConsumer().getString())
|
||||
.sortAlphabetically()
|
||||
.stream();
|
||||
|
||||
@@ -253,8 +253,8 @@ public class TabCompleteHelper {
|
||||
public TabCompleteHelper addSettings() {
|
||||
return append(
|
||||
BaritoneAPI.getSettings().allSettings.stream()
|
||||
.filter(s -> !SettingsUtil.javaOnlySetting(s))
|
||||
.map(Settings.Setting::getName)
|
||||
.filter(s -> !s.equalsIgnoreCase("logger"))
|
||||
.sorted(String.CASE_INSENSITIVE_ORDER)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import baritone.api.event.events.type.Cancellable;
|
||||
/**
|
||||
* @author LoganDark
|
||||
*/
|
||||
public final class TabCompleteEvent extends Cancellable {
|
||||
public class TabCompleteEvent extends Cancellable {
|
||||
|
||||
public final String prefix;
|
||||
public String[] completions;
|
||||
|
||||
@@ -18,11 +18,12 @@
|
||||
package baritone.api.event.listener;
|
||||
|
||||
import baritone.api.event.events.*;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.GuiGameOver;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.client.settings.GameSettings;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.Packet;
|
||||
|
||||
@@ -44,7 +45,7 @@ public interface IGameEventListener {
|
||||
* Run once per game tick from before and after the player rotation is sent to the server.
|
||||
*
|
||||
* @param event The event
|
||||
* @see EntityPlayerSP#tick()
|
||||
* @see EntityPlayerSP#onUpdate()
|
||||
*/
|
||||
void onPlayerUpdate(PlayerUpdateEvent event);
|
||||
|
||||
@@ -67,11 +68,14 @@ public interface IGameEventListener {
|
||||
* Runs before and after whenever a chunk is either loaded, unloaded, or populated.
|
||||
*
|
||||
* @param event The event
|
||||
* @see WorldClient#doPreChunk(int, int, boolean)
|
||||
*/
|
||||
void onChunkEvent(ChunkEvent event);
|
||||
|
||||
/**
|
||||
* Runs once per world render pass.
|
||||
* Runs once per world render pass. Two passes are made when {@link GameSettings#anaglyph} is on.
|
||||
* <p>
|
||||
* <b>Note:</b> {@link GameSettings#anaglyph} has been removed in Minecraft 1.13
|
||||
*
|
||||
* @param event The event
|
||||
*/
|
||||
@@ -81,7 +85,7 @@ public interface IGameEventListener {
|
||||
* Runs before and after whenever a new world is loaded
|
||||
*
|
||||
* @param event The event
|
||||
* @see Minecraft#loadWorld(WorldClient, GuiScreen)
|
||||
* @see Minecraft#loadWorld(WorldClient, String)
|
||||
*/
|
||||
void onWorldEvent(WorldEvent event);
|
||||
|
||||
@@ -90,6 +94,7 @@ public interface IGameEventListener {
|
||||
*
|
||||
* @param event The event
|
||||
* @see Packet
|
||||
* @see GenericFutureListener
|
||||
*/
|
||||
void onSendPacket(PacketEvent event);
|
||||
|
||||
@@ -98,6 +103,7 @@ public interface IGameEventListener {
|
||||
*
|
||||
* @param event The event
|
||||
* @see Packet
|
||||
* @see GenericFutureListener
|
||||
*/
|
||||
void onReceivePacket(PacketEvent event);
|
||||
|
||||
@@ -111,10 +117,10 @@ public interface IGameEventListener {
|
||||
void onPlayerRotationMove(RotationMoveEvent event);
|
||||
|
||||
/**
|
||||
* Called whenever the sprint keybind state is checked in {@link EntityPlayerSP#livingTick}
|
||||
* Called whenever the sprint keybind state is checked in {@link EntityPlayerSP#onLivingUpdate}
|
||||
*
|
||||
* @param event The event
|
||||
* @see EntityPlayerSP#livingTick()
|
||||
* @see EntityPlayerSP#onLivingUpdate()
|
||||
*/
|
||||
void onPlayerSprintState(SprintStateEvent event);
|
||||
|
||||
|
||||
@@ -54,4 +54,18 @@ public interface Goal {
|
||||
default double heuristic(BlockPos pos) {
|
||||
return heuristic(pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the heuristic at the goal.
|
||||
* i.e. {@code heuristic() == heuristic(x,y,z)}
|
||||
* when {@code isInGoal(x,y,z) == true}
|
||||
* This is needed by {@code PathingBehavior#estimatedTicksToGoal} because
|
||||
* some Goals actually do not have a heuristic of 0 when that condition is met
|
||||
*
|
||||
* @return The estimate number of ticks to satisfy the goal when the goal
|
||||
* is already satisfied
|
||||
*/
|
||||
default double heuristic() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,16 @@ public class GoalComposite implements Goal {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic() {
|
||||
double min = Double.MAX_VALUE;
|
||||
for (Goal g : goals) {
|
||||
// just take the highest value that is guaranteed to be inside the goal
|
||||
min = Math.min(min, g.heuristic());
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GoalComposite" + Arrays.toString(goals);
|
||||
|
||||
@@ -45,6 +45,11 @@ public class GoalInverted implements Goal {
|
||||
return -origin.heuristic(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic() {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("GoalInverted{%s}", origin.toString());
|
||||
|
||||
@@ -19,6 +19,8 @@ package baritone.api.pathing.goals;
|
||||
|
||||
import baritone.api.utils.SettingsUtil;
|
||||
import baritone.api.utils.interfaces.IGoalRenderPos;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleIterator;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public class GoalNear implements Goal, IGoalRenderPos {
|
||||
@@ -51,6 +53,34 @@ public class GoalNear implements Goal, IGoalRenderPos {
|
||||
return GoalBlock.calculate(xDiff, yDiff, zDiff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic() {// TODO less hacky solution
|
||||
int range = (int) Math.ceil(Math.sqrt(rangeSq));
|
||||
DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978
|
||||
double minOutside = Double.POSITIVE_INFINITY;
|
||||
for (int dx = -range; dx <= range; dx++) {
|
||||
for (int dy = -range; dy <= range; dy++) {
|
||||
for (int dz = -range; dz <= range; dz++) {
|
||||
double h = heuristic(x + dx, y + dy, z + dz);
|
||||
if (h < minOutside && isInGoal(x + dx, y + dy, z + dz)) {
|
||||
maybeAlwaysInside.add(h);
|
||||
} else {
|
||||
minOutside = Math.min(minOutside, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
double maxInside = Double.NEGATIVE_INFINITY;
|
||||
DoubleIterator it = maybeAlwaysInside.iterator();
|
||||
while (it.hasNext()) {
|
||||
double inside = it.nextDouble();
|
||||
if (inside < minOutside) {
|
||||
maxInside = Math.max(maxInside, inside);
|
||||
}
|
||||
}
|
||||
return maxInside;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getGoalPos() {
|
||||
return new BlockPos(x, y, z);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
package baritone.api.pathing.goals;
|
||||
|
||||
import baritone.api.utils.SettingsUtil;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleIterator;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -65,7 +67,7 @@ public class GoalRunAway implements Goal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic(int x, int y, int z) {//mostly copied from GoalBlock
|
||||
public double heuristic(int x, int y, int z) {// mostly copied from GoalBlock
|
||||
double min = Double.MAX_VALUE;
|
||||
for (BlockPos p : from) {
|
||||
double h = GoalXZ.calculate(p.getX() - x, p.getZ() - z);
|
||||
@@ -80,6 +82,48 @@ public class GoalRunAway implements Goal {
|
||||
return min;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic() {// TODO less hacky solution
|
||||
int distance = (int) Math.ceil(Math.sqrt(distanceSq));
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int minZ = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE;
|
||||
int maxY = Integer.MIN_VALUE;
|
||||
int maxZ = Integer.MIN_VALUE;
|
||||
for (BlockPos p : from) {
|
||||
minX = Math.min(minX, p.getX() - distance);
|
||||
minY = Math.min(minY, p.getY() - distance);
|
||||
minZ = Math.min(minZ, p.getZ() - distance);
|
||||
maxX = Math.max(minX, p.getX() + distance);
|
||||
maxY = Math.max(minY, p.getY() + distance);
|
||||
maxZ = Math.max(minZ, p.getZ() + distance);
|
||||
}
|
||||
DoubleOpenHashSet maybeAlwaysInside = new DoubleOpenHashSet(); // see pull request #1978
|
||||
double minOutside = Double.POSITIVE_INFINITY;
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
double h = heuristic(x, y, z);
|
||||
if (h < minOutside && isInGoal(x, y, z)) {
|
||||
maybeAlwaysInside.add(h);
|
||||
} else {
|
||||
minOutside = Math.min(minOutside, h);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
double maxInside = Double.NEGATIVE_INFINITY;
|
||||
DoubleIterator it = maybeAlwaysInside.iterator();
|
||||
while (it.hasNext()) {
|
||||
double inside = it.nextDouble();
|
||||
if (inside < minOutside) {
|
||||
maxInside = Math.max(maxInside, inside);
|
||||
}
|
||||
}
|
||||
return maxInside;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (maintainY != null) {
|
||||
|
||||
@@ -64,6 +64,11 @@ public class GoalStrictDirection implements Goal {
|
||||
return heuristic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double heuristic() {
|
||||
return Double.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
|
||||
@@ -52,7 +52,7 @@ public interface IBuilderProcess extends IBaritoneProcess {
|
||||
boolean build(String name, File schematic, Vec3i origin);
|
||||
|
||||
default boolean build(String schematicFile, BlockPos origin) {
|
||||
File file = new File(new File(Minecraft.getInstance().gameDir, "schematics"), schematicFile);
|
||||
File file = new File(new File(Minecraft.getMinecraft().gameDir, "schematics"), schematicFile);
|
||||
return build(schematicFile, file, origin);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,4 +71,11 @@ public class CompositeSchematic extends AbstractSchematic {
|
||||
}
|
||||
return entry.schematic.desiredState(x - entry.x, y - entry.y, z - entry.z, current, approxPlaceable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for (CompositeSchematicEntry entry : schematicArr) {
|
||||
entry.schematic.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package baritone.api.schematic;
|
||||
|
||||
import baritone.api.utils.BlockOptionalMeta;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Blocks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,7 +32,7 @@ public class FillSchematic extends AbstractSchematic {
|
||||
}
|
||||
|
||||
public FillSchematic(int x, int y, int z, IBlockState state) {
|
||||
this(x, y, z, new BlockOptionalMeta(state.getBlock()));
|
||||
this(x, y, z, new BlockOptionalMeta(state.getBlock(), state.getBlock().getMetaFromState(state)));
|
||||
}
|
||||
|
||||
public BlockOptionalMeta getBom() {
|
||||
@@ -44,8 +43,6 @@ public class FillSchematic extends AbstractSchematic {
|
||||
public IBlockState desiredState(int x, int y, int z, IBlockState current, List<IBlockState> approxPlaceable) {
|
||||
if (bom.matches(current)) {
|
||||
return current;
|
||||
} else if (current.getBlock() != Blocks.AIR) {
|
||||
return Blocks.AIR.getDefaultState();
|
||||
}
|
||||
for (IBlockState placeable : approxPlaceable) {
|
||||
if (bom.matches(placeable)) {
|
||||
|
||||
@@ -73,6 +73,11 @@ public interface ISchematic {
|
||||
*/
|
||||
IBlockState desiredState(int x, int y, int z, IBlockState current, List<IBlockState> approxPlaceable);
|
||||
|
||||
/**
|
||||
* Resets possible caches to avoid wrong behavior when moving the schematic around
|
||||
*/
|
||||
default void reset() {}
|
||||
|
||||
/**
|
||||
* @return The width (X axis length) of this schematic
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,18 @@ public class ReplaceSchematic extends MaskSchematic {
|
||||
this.cache = new Boolean[widthX()][heightY()][lengthZ()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// it's final, can't use this.cache = new Boolean[widthX()][heightY()][lengthZ()]
|
||||
for (int x = 0; x < cache.length; x++) {
|
||||
for (int y = 0; y < cache[0].length; y++) {
|
||||
for (int z = 0; z < cache[0][0].length; z++) {
|
||||
cache[x][y][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean partOfMask(int x, int y, int z, IBlockState currentState) {
|
||||
if (cache[x][y][z] == null) {
|
||||
|
||||
89
src/api/java/baritone/api/schematic/SubstituteSchematic.java
Normal file
89
src/api/java/baritone/api/schematic/SubstituteSchematic.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.api.schematic;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockAir;
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.init.Blocks;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SubstituteSchematic extends AbstractSchematic {
|
||||
|
||||
private final ISchematic schematic;
|
||||
private final Map<Block, List<Block>> substitutions;
|
||||
private final Map<IBlockState, Map<Block, IBlockState>> blockStateCache = new HashMap<>();
|
||||
|
||||
public SubstituteSchematic(ISchematic schematic, Map<Block,List<Block>> substitutions) {
|
||||
super(schematic.widthX(), schematic.heightY(), schematic.lengthZ());
|
||||
this.schematic = schematic;
|
||||
this.substitutions = substitutions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inSchematic(int x, int y, int z, IBlockState currentState) {
|
||||
return schematic.inSchematic(x, y, z, currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockState desiredState(int x, int y, int z, IBlockState current, List<IBlockState> approxPlaceable) {
|
||||
IBlockState desired = schematic.desiredState(x, y, z, current, approxPlaceable);
|
||||
Block desiredBlock = desired.getBlock();
|
||||
if (!substitutions.containsKey(desiredBlock)) {
|
||||
return desired;
|
||||
}
|
||||
List<Block> substitutes = substitutions.get(desiredBlock);
|
||||
if (substitutes.contains(current.getBlock()) && !(current.getBlock() instanceof BlockAir)) {// don't preserve air, it's almost always there and almost never wanted
|
||||
return withBlock(desired, current.getBlock());
|
||||
}
|
||||
for (Block substitute : substitutes) {
|
||||
if (substitute instanceof BlockAir) {
|
||||
return current.getBlock() instanceof BlockAir ? current : Blocks.AIR.getDefaultState(); // can always "place" air
|
||||
}
|
||||
for (IBlockState placeable : approxPlaceable) {
|
||||
if (substitute.equals(placeable.getBlock())) {
|
||||
return withBlock(desired, placeable.getBlock());
|
||||
}
|
||||
}
|
||||
}
|
||||
return substitutes.get(0).getDefaultState();
|
||||
}
|
||||
|
||||
private IBlockState withBlock(IBlockState state, Block block) {
|
||||
if (blockStateCache.containsKey(state) && blockStateCache.get(state).containsKey(block)) {
|
||||
return blockStateCache.get(state).get(block);
|
||||
}
|
||||
Collection<IProperty<?>> properties = state.getPropertyKeys();
|
||||
IBlockState newState = block.getDefaultState();
|
||||
for (IProperty<?> property : properties) {
|
||||
try {
|
||||
newState = copySingleProp(state, newState, property);
|
||||
} catch (IllegalArgumentException e) { //property does not exist for target block
|
||||
}
|
||||
}
|
||||
blockStateCache.computeIfAbsent(state, s -> new HashMap<Block,IBlockState>()).put(block, newState);
|
||||
return newState;
|
||||
}
|
||||
private <T extends Comparable<T>> IBlockState copySingleProp(IBlockState fromState, IBlockState toState, IProperty<T> prop) {
|
||||
return toState.withProperty(prop, fromState.getValue(prop));
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
package baritone.api.utils;
|
||||
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
@@ -79,25 +80,98 @@ public final class BetterBlockPos extends BlockPos {
|
||||
return longHash(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
public static final int NUM_X_BITS = 26;
|
||||
public static final int NUM_Z_BITS = NUM_X_BITS;
|
||||
public static final int NUM_Y_BITS = 9; // note: even though Y goes from 0 to 255, that doesn't mean 8 bits will "just work" because the deserializer assumes signed. i could change it for just Y to assume unsigned and leave X and Z as signed, however, we know that in 1.17 they plan to add negative Y. for that reason, the better approach is to give the extra bits to Y and leave it as signed.
|
||||
// also, if 1.17 sticks with the current plan which is -64 to +320, we could have 9 bits for Y and a constant offset of -64 to change it to -128 to +256.
|
||||
// that would result in the packed long representation of any valid coordinate still being a positive integer
|
||||
// i like that property, so i will keep num_y_bits at 9 and plan for an offset in 1.17
|
||||
// it also gives 1 bit of wiggle room in case anything else happens in the future, so we are only using 63 out of 64 bits at the moment
|
||||
public static final int Z_SHIFT = 0;
|
||||
public static final int Y_SHIFT = Z_SHIFT + NUM_Z_BITS + 1; // 1 padding bit to make twos complement not overflow
|
||||
public static final int X_SHIFT = Y_SHIFT + NUM_Y_BITS + 1; // and also here too
|
||||
public static final long X_MASK = (1L << NUM_X_BITS) - 1L; // X doesn't need padding as the overflow carry bit is just discarded, like a normal long (-1) + (1) = 0
|
||||
public static final long Y_MASK = (1L << NUM_Y_BITS) - 1L;
|
||||
public static final long Z_MASK = (1L << NUM_Z_BITS) - 1L;
|
||||
|
||||
public static final long POST_ADDITION_MASK = X_MASK << X_SHIFT | Y_MASK << Y_SHIFT | Z_MASK << Z_SHIFT; // required to "manually inline" toLong(-1, -1, -1) here so that javac inserts proper ldc2_w instructions at usage points instead of getstatic
|
||||
// what's this ^ mask for?
|
||||
// it allows for efficient offsetting and manipulation of a long packed coordinate
|
||||
// if we had three ints, x y z, it would be easy to do "y += 1" or "x -= 1"
|
||||
// but how do you do those things if you have a long with x y and z all stuffed into one primitive?
|
||||
// adding together two long coordinates actually works perfectly if both sides have X, Y, and Z as all positive, no issues at all
|
||||
// but when Y or Z is negative, we run into an issue. consider 8 bits: negative one is 11111111 and one is 00000001
|
||||
// adding them together gives 00000000, zero, **but only because there isn't a 9th bit to carry into**
|
||||
// if we had, instead, 00000000 11111111 + 00000000 00000001 we would rightly get 00000001 00000000 with the 1 being carried into the 9th position there
|
||||
// this is exactly what happens. "toLong(0, 1, 0) + toLong(0, -1, 0)" ends up equaling toLong(1, 0, 0) while we'd rather it equal toLong(0, 0, 0)
|
||||
// so, we simply mask out the unwanted result of the carry by inserting 1 bit of padding space (as added above) between each
|
||||
// it used to be 000XXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZ
|
||||
// and now it is 0XXXXXXXXXXXXXXXXXXXXXXXXXX0YYYYYYYYY0ZZZZZZZZZZZZZZZZZZZZZZZZZZ
|
||||
// we simply place the X Y and Z in slightly different sections of the long, putting a bit of space between each
|
||||
// the mask ^ is 0111111111111111111111111110111111111011111111111111111111111111
|
||||
// using that example of (0,1,0) + (0,-1,0), here's what happens
|
||||
// 0000000000000000000000000000000000001000000000000000000000000000 (this is X=0 Y=1 Z=0)
|
||||
// + 0000000000000000000000000000111111111000000000000000000000000000 (this is X=0 Y=-1 Z=0)
|
||||
// = 0000000000000000000000000001000000000000000000000000000000000000
|
||||
// the unwanted carry bit here ^ is no longer corrupting the least significant bit of X and making it 1!
|
||||
// now it's just turning on the unused padding bit that we don't care about
|
||||
// using the mask and bitwise and, we can easily and branchlessly turn off the padding bits just in case something overflow carried into them!
|
||||
// 0000000000000000000000000001000000000000000000000000000000000000 (the result of the addition from earlier)
|
||||
// & 0111111111111111111111111110111111111011111111111111111111111111 (this is POST_ADDITION_MASK)
|
||||
// = 0000000000000000000000000000000000000000000000000000000000000000
|
||||
// POST_ADDITION_MASK retains the bits that actually form X, Y, and Z, but intentionally turns off the padding bits
|
||||
// so, we can simply do "(toLong(0, 1, 0) + toLong(0, -1, 0)) & POST_ADDITION_MASK" and correctly get toLong(0, 0, 0)
|
||||
// which is incredibly fast and efficient, an add then a bitwise AND against a constant
|
||||
// and it doesn't require us to pull out X, Y, and Z, modify one of them, and put them all back into the long
|
||||
// that's what the point of the mask is
|
||||
|
||||
static {
|
||||
if (POST_ADDITION_MASK != toLong(-1, -1, -1)) {
|
||||
throw new IllegalStateException(POST_ADDITION_MASK + " " + toLong(-1, -1, -1)); // sanity check
|
||||
}
|
||||
}
|
||||
|
||||
public long toLong() {
|
||||
return toLong(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
public static BetterBlockPos fromLong(long serialized) {
|
||||
return new BetterBlockPos(XfromLong(serialized), YfromLong(serialized), ZfromLong(serialized));
|
||||
}
|
||||
|
||||
public static int XfromLong(long serialized) {
|
||||
return (int) (serialized << (64 - X_SHIFT - NUM_X_BITS) >> (64 - NUM_X_BITS));
|
||||
}
|
||||
|
||||
public static int YfromLong(long serialized) {
|
||||
return (int) (serialized << (64 - Y_SHIFT - NUM_Y_BITS) >> (64 - NUM_Y_BITS));
|
||||
}
|
||||
|
||||
public static int ZfromLong(long serialized) {
|
||||
return (int) (serialized << (64 - Z_SHIFT - NUM_Z_BITS) >> (64 - NUM_Z_BITS));
|
||||
}
|
||||
|
||||
public static long toLong(final int x, final int y, final int z) {
|
||||
return ((long) x & X_MASK) << X_SHIFT | ((long) y & Y_MASK) << Y_SHIFT | ((long) z & Z_MASK) << Z_SHIFT;
|
||||
}
|
||||
|
||||
public static long offsetBy(long pos, int x, int y, int z) {
|
||||
return (pos + toLong(x, y, z)) & BetterBlockPos.POST_ADDITION_MASK;
|
||||
}
|
||||
|
||||
public static final long HASHCODE_MURMUR_MASK = murmur64(-1);
|
||||
public static final long ZOBRIST_MURMUR_MASK = murmur64(-2);
|
||||
|
||||
public static long longHash(int x, int y, int z) {
|
||||
// TODO use the same thing as BlockPos.fromLong();
|
||||
// invertibility would be incredibly useful
|
||||
/*
|
||||
* This is the hashcode implementation of Vec3i (the superclass of the class which I shall not name)
|
||||
*
|
||||
* public int hashCode() {
|
||||
* return (this.getY() + this.getZ() * 31) * 31 + this.getX();
|
||||
* }
|
||||
*
|
||||
* That is terrible and has tons of collisions and makes the HashMap terribly inefficient.
|
||||
*
|
||||
* That's why we grab out the X, Y, Z and calculate our own hashcode
|
||||
*/
|
||||
long hash = 3241;
|
||||
hash = 3457689L * hash + x;
|
||||
hash = 8734625L * hash + y;
|
||||
hash = 2873465L * hash + z;
|
||||
return hash;
|
||||
return longHash(toLong(x, y, z));
|
||||
}
|
||||
|
||||
public static long longHash(long packed) {
|
||||
return murmur64(HASHCODE_MURMUR_MASK ^ packed);
|
||||
}
|
||||
|
||||
public static long murmur64(long h) {
|
||||
return HashCommon.murmurHash3(h);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -206,10 +280,10 @@ public final class BetterBlockPos extends BlockPos {
|
||||
@Nonnull
|
||||
public String toString() {
|
||||
return String.format(
|
||||
"BetterBlockPos{x=%s,y=%s,z=%s}",
|
||||
SettingsUtil.maybeCensor(x),
|
||||
SettingsUtil.maybeCensor(y),
|
||||
SettingsUtil.maybeCensor(z)
|
||||
"BetterBlockPos{x=%d,y=%d,z=%d}",
|
||||
x,
|
||||
y,
|
||||
z
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,34 +19,46 @@ package baritone.api.utils;
|
||||
|
||||
import baritone.api.utils.accessor.IItemStack;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class BlockOptionalMeta {
|
||||
|
||||
private final Block block;
|
||||
private final int meta;
|
||||
private final boolean noMeta;
|
||||
private final Set<IBlockState> blockstates;
|
||||
private final ImmutableSet<Integer> stateHashes;
|
||||
private final ImmutableSet<Integer> stackHashes;
|
||||
private static final Pattern pattern = Pattern.compile("^(.+?)(?::(\\d+))?$");
|
||||
private static final Map<Object, Object> normalizations;
|
||||
|
||||
public BlockOptionalMeta(@Nonnull Block block) {
|
||||
public BlockOptionalMeta(@Nonnull Block block, @Nullable Integer meta) {
|
||||
this.block = block;
|
||||
this.blockstates = getStates(block);
|
||||
this.noMeta = meta == null;
|
||||
this.meta = noMeta ? 0 : meta;
|
||||
this.blockstates = getStates(block, meta);
|
||||
this.stateHashes = getStateHashes(blockstates);
|
||||
this.stackHashes = getStackHashes(blockstates);
|
||||
}
|
||||
|
||||
public BlockOptionalMeta(@Nonnull Block block) {
|
||||
this(block, null);
|
||||
}
|
||||
|
||||
public BlockOptionalMeta(@Nonnull String selector) {
|
||||
Matcher matcher = pattern.matcher(selector);
|
||||
|
||||
@@ -55,21 +67,186 @@ public final class BlockOptionalMeta {
|
||||
}
|
||||
|
||||
MatchResult matchResult = matcher.toMatchResult();
|
||||
noMeta = matchResult.group(2) == null;
|
||||
|
||||
ResourceLocation id = new ResourceLocation(matchResult.group(1));
|
||||
|
||||
if (!IRegistry.BLOCK.containsKey(id)) {
|
||||
if (!Block.REGISTRY.containsKey(id)) {
|
||||
throw new IllegalArgumentException("Invalid block ID");
|
||||
}
|
||||
|
||||
block = IRegistry.BLOCK.get(id);
|
||||
blockstates = getStates(block);
|
||||
block = Block.REGISTRY.getObject(id);
|
||||
meta = noMeta ? 0 : Integer.parseInt(matchResult.group(2));
|
||||
blockstates = getStates(block, getMeta());
|
||||
stateHashes = getStateHashes(blockstates);
|
||||
stackHashes = getStackHashes(blockstates);
|
||||
}
|
||||
|
||||
private static Set<IBlockState> getStates(@Nonnull Block block) {
|
||||
return new HashSet<>(block.getStateContainer().getValidStates());
|
||||
static {
|
||||
Map<Object, Object> _normalizations = new HashMap<>();
|
||||
Consumer<Enum> put = instance -> _normalizations.put(instance.getClass(), instance);
|
||||
put.accept(EnumFacing.NORTH);
|
||||
put.accept(EnumFacing.Axis.Y);
|
||||
put.accept(BlockLog.EnumAxis.Y);
|
||||
put.accept(BlockStairs.EnumHalf.BOTTOM);
|
||||
put.accept(BlockStairs.EnumShape.STRAIGHT);
|
||||
put.accept(BlockLever.EnumOrientation.DOWN_X);
|
||||
put.accept(BlockDoublePlant.EnumBlockHalf.LOWER);
|
||||
put.accept(BlockSlab.EnumBlockHalf.BOTTOM);
|
||||
put.accept(BlockDoor.EnumDoorHalf.LOWER);
|
||||
put.accept(BlockDoor.EnumHingePosition.LEFT);
|
||||
put.accept(BlockBed.EnumPartType.HEAD);
|
||||
put.accept(BlockRailBase.EnumRailDirection.NORTH_SOUTH);
|
||||
put.accept(BlockTrapDoor.DoorHalf.BOTTOM);
|
||||
_normalizations.put(BlockBanner.ROTATION, 0);
|
||||
_normalizations.put(BlockBed.OCCUPIED, false);
|
||||
_normalizations.put(BlockBrewingStand.HAS_BOTTLE[0], false);
|
||||
_normalizations.put(BlockBrewingStand.HAS_BOTTLE[1], false);
|
||||
_normalizations.put(BlockBrewingStand.HAS_BOTTLE[2], false);
|
||||
_normalizations.put(BlockButton.POWERED, false);
|
||||
// _normalizations.put(BlockCactus.AGE, 0);
|
||||
// _normalizations.put(BlockCauldron.LEVEL, 0);
|
||||
// _normalizations.put(BlockChorusFlower.AGE, 0);
|
||||
_normalizations.put(BlockChorusPlant.NORTH, false);
|
||||
_normalizations.put(BlockChorusPlant.EAST, false);
|
||||
_normalizations.put(BlockChorusPlant.SOUTH, false);
|
||||
_normalizations.put(BlockChorusPlant.WEST, false);
|
||||
_normalizations.put(BlockChorusPlant.UP, false);
|
||||
_normalizations.put(BlockChorusPlant.DOWN, false);
|
||||
// _normalizations.put(BlockCocoa.AGE, 0);
|
||||
// _normalizations.put(BlockCrops.AGE, 0);
|
||||
_normalizations.put(BlockDirt.SNOWY, false);
|
||||
_normalizations.put(BlockDoor.OPEN, false);
|
||||
_normalizations.put(BlockDoor.POWERED, false);
|
||||
// _normalizations.put(BlockFarmland.MOISTURE, 0);
|
||||
_normalizations.put(BlockFence.NORTH, false);
|
||||
_normalizations.put(BlockFence.EAST, false);
|
||||
_normalizations.put(BlockFence.WEST, false);
|
||||
_normalizations.put(BlockFence.SOUTH, false);
|
||||
// _normalizations.put(BlockFenceGate.POWERED, false);
|
||||
// _normalizations.put(BlockFenceGate.IN_WALL, false);
|
||||
_normalizations.put(BlockFire.AGE, 0);
|
||||
_normalizations.put(BlockFire.NORTH, false);
|
||||
_normalizations.put(BlockFire.EAST, false);
|
||||
_normalizations.put(BlockFire.SOUTH, false);
|
||||
_normalizations.put(BlockFire.WEST, false);
|
||||
_normalizations.put(BlockFire.UPPER, false);
|
||||
// _normalizations.put(BlockFrostedIce.AGE, 0);
|
||||
_normalizations.put(BlockGrass.SNOWY, false);
|
||||
// _normalizations.put(BlockHopper.ENABLED, true);
|
||||
// _normalizations.put(BlockLever.POWERED, false);
|
||||
// _normalizations.put(BlockLiquid.LEVEL, 0);
|
||||
// _normalizations.put(BlockMycelium.SNOWY, false);
|
||||
// _normalizations.put(BlockNetherWart.AGE, false);
|
||||
_normalizations.put(BlockLeaves.CHECK_DECAY, false);
|
||||
// _normalizations.put(BlockLeaves.DECAYABLE, false);
|
||||
// _normalizations.put(BlockObserver.POWERED, false);
|
||||
_normalizations.put(BlockPane.NORTH, false);
|
||||
_normalizations.put(BlockPane.EAST, false);
|
||||
_normalizations.put(BlockPane.WEST, false);
|
||||
_normalizations.put(BlockPane.SOUTH, false);
|
||||
// _normalizations.put(BlockPistonBase.EXTENDED, false);
|
||||
// _normalizations.put(BlockPressurePlate.POWERED, false);
|
||||
// _normalizations.put(BlockPressurePlateWeighted.POWER, false);
|
||||
_normalizations.put(BlockQuartz.EnumType.LINES_X, BlockQuartz.EnumType.LINES_Y);
|
||||
_normalizations.put(BlockQuartz.EnumType.LINES_Z, BlockQuartz.EnumType.LINES_Y);
|
||||
// _normalizations.put(BlockRailDetector.POWERED, false);
|
||||
// _normalizations.put(BlockRailPowered.POWERED, false);
|
||||
_normalizations.put(BlockRedstoneWire.NORTH, false);
|
||||
_normalizations.put(BlockRedstoneWire.EAST, false);
|
||||
_normalizations.put(BlockRedstoneWire.SOUTH, false);
|
||||
_normalizations.put(BlockRedstoneWire.WEST, false);
|
||||
// _normalizations.put(BlockReed.AGE, false);
|
||||
_normalizations.put(BlockSapling.STAGE, 0);
|
||||
_normalizations.put(BlockSkull.NODROP, false);
|
||||
_normalizations.put(BlockStandingSign.ROTATION, 0);
|
||||
_normalizations.put(BlockStem.AGE, 0);
|
||||
_normalizations.put(BlockTripWire.NORTH, false);
|
||||
_normalizations.put(BlockTripWire.EAST, false);
|
||||
_normalizations.put(BlockTripWire.WEST, false);
|
||||
_normalizations.put(BlockTripWire.SOUTH, false);
|
||||
_normalizations.put(BlockVine.NORTH, false);
|
||||
_normalizations.put(BlockVine.EAST, false);
|
||||
_normalizations.put(BlockVine.SOUTH, false);
|
||||
_normalizations.put(BlockVine.WEST, false);
|
||||
_normalizations.put(BlockVine.UP, false);
|
||||
_normalizations.put(BlockWall.UP, false);
|
||||
_normalizations.put(BlockWall.NORTH, false);
|
||||
_normalizations.put(BlockWall.EAST, false);
|
||||
_normalizations.put(BlockWall.WEST, false);
|
||||
_normalizations.put(BlockWall.SOUTH, false);
|
||||
normalizations = Collections.unmodifiableMap(_normalizations);
|
||||
}
|
||||
|
||||
public static <C extends Comparable<C>, P extends IProperty<C>> P castToIProperty(Object value) {
|
||||
//noinspection unchecked
|
||||
return (P) value;
|
||||
}
|
||||
|
||||
public static <C extends Comparable<C>, P extends IProperty<C>> C castToIPropertyValue(P iproperty, Object value) {
|
||||
//noinspection unchecked
|
||||
return (C) value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes the specified blockstate by setting meta-affecting properties which
|
||||
* are not being targeted by the meta parameter to their default values.
|
||||
* <p>
|
||||
* For example, block variant/color is the primary target for the meta value, so properties
|
||||
* such as rotation/facing direction will be set to default values in order to nullify
|
||||
* the effect that they have on the state's meta value.
|
||||
*
|
||||
* @param state The state to normalize
|
||||
* @return The normalized block state
|
||||
*/
|
||||
public static IBlockState normalize(IBlockState state) {
|
||||
IBlockState newState = state;
|
||||
|
||||
for (IProperty<?> property : state.getProperties().keySet()) {
|
||||
Class<?> valueClass = property.getValueClass();
|
||||
if (normalizations.containsKey(property)) {
|
||||
try {
|
||||
newState = newState.withProperty(
|
||||
castToIProperty(property),
|
||||
castToIPropertyValue(property, normalizations.get(property))
|
||||
);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
} else if (normalizations.containsKey(state.getValue(property))) {
|
||||
try {
|
||||
newState = newState.withProperty(
|
||||
castToIProperty(property),
|
||||
castToIPropertyValue(property, normalizations.get(state.getValue(property)))
|
||||
);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
} else if (normalizations.containsKey(valueClass)) {
|
||||
try {
|
||||
newState = newState.withProperty(
|
||||
castToIProperty(property),
|
||||
castToIPropertyValue(property, normalizations.get(valueClass))
|
||||
);
|
||||
} catch (IllegalArgumentException ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
return newState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the target meta value for the specified state. The target meta value is
|
||||
* most often that which is influenced by the variant/color property of the block state.
|
||||
*
|
||||
* @param state The state to check
|
||||
* @return The target meta of the state
|
||||
* @see #normalize(IBlockState)
|
||||
*/
|
||||
public static int stateMeta(IBlockState state) {
|
||||
return state.getBlock().getMetaFromState(normalize(state));
|
||||
}
|
||||
|
||||
private static Set<IBlockState> getStates(@Nonnull Block block, @Nullable Integer meta) {
|
||||
return block.getBlockState().getValidStates().stream()
|
||||
.filter(blockstate -> meta == null || stateMeta(blockstate) == meta)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private static ImmutableSet<Integer> getStateHashes(Set<IBlockState> blockstates) {
|
||||
@@ -85,8 +262,8 @@ public final class BlockOptionalMeta {
|
||||
return ImmutableSet.copyOf(
|
||||
blockstates.stream()
|
||||
.map(state -> new ItemStack(
|
||||
state.getBlock().getItemDropped(state, null, null, 0).asItem(),
|
||||
1
|
||||
state.getBlock().getItemDropped(state, new Random(), 0),
|
||||
state.getBlock().damageDropped(state)
|
||||
))
|
||||
.map(stack -> ((IItemStack) (Object) stack).getBaritoneHash())
|
||||
.toArray(Integer[]::new)
|
||||
@@ -97,6 +274,10 @@ public final class BlockOptionalMeta {
|
||||
return block;
|
||||
}
|
||||
|
||||
public Integer getMeta() {
|
||||
return noMeta ? null : meta;
|
||||
}
|
||||
|
||||
public boolean matches(@Nonnull Block block) {
|
||||
return block == this.block;
|
||||
}
|
||||
@@ -110,14 +291,21 @@ public final class BlockOptionalMeta {
|
||||
//noinspection ConstantConditions
|
||||
int hash = ((IItemStack) (Object) stack).getBaritoneHash();
|
||||
|
||||
hash -= stack.getDamage();
|
||||
if (noMeta) {
|
||||
hash -= stack.getItemDamage();
|
||||
}
|
||||
|
||||
return stackHashes.contains(hash);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("BlockOptionalMeta{block=%s}", block);
|
||||
return String.format("BlockOptionalMeta{block=%s,meta=%s}", block, getMeta());
|
||||
}
|
||||
|
||||
public static IBlockState blockStateFromStack(ItemStack stack) {
|
||||
//noinspection deprecation
|
||||
return Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata());
|
||||
}
|
||||
|
||||
public IBlockState getAnyBlockState() {
|
||||
|
||||
@@ -19,7 +19,6 @@ package baritone.api.utils;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -29,7 +28,7 @@ public class BlockUtils {
|
||||
private static transient Map<String, Block> resourceCache = new HashMap<>();
|
||||
|
||||
public static String blockToString(Block block) {
|
||||
ResourceLocation loc = IRegistry.BLOCK.getKey(block);
|
||||
ResourceLocation loc = Block.REGISTRY.getNameForObject(block);
|
||||
String name = loc.getPath(); // normally, only write the part after the minecraft:
|
||||
if (!loc.getNamespace().equals("minecraft")) {
|
||||
// Baritone is running on top of forge with mods installed, perhaps?
|
||||
@@ -57,7 +56,7 @@ public class BlockUtils {
|
||||
if (resourceCache.containsKey(name)) {
|
||||
return null; // cached as null
|
||||
}
|
||||
block = IRegistry.BLOCK.get(ResourceLocation.tryCreate(name.contains(":") ? name : "minecraft:" + name));
|
||||
block = Block.getBlockFromName(name.contains(":") ? name : "minecraft:" + name);
|
||||
Map<String, Block> copy = new HashMap<>(resourceCache); // read only copy is safe, wont throw concurrentmodification
|
||||
copy.put(name, block);
|
||||
resourceCache = copy;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package baritone.api.utils;
|
||||
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.utils.gui.BaritoneToast;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
@@ -45,7 +44,7 @@ public interface Helper {
|
||||
/**
|
||||
* Instance of the game
|
||||
*/
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
Minecraft mc = Minecraft.getMinecraft();
|
||||
|
||||
static ITextComponent getPrefix() {
|
||||
// Inner text component
|
||||
@@ -71,7 +70,7 @@ public interface Helper {
|
||||
* @param message The message to display in the popup
|
||||
*/
|
||||
default void logToast(ITextComponent title, ITextComponent message) {
|
||||
mc.addScheduledTask(() -> BaritoneToast.addOrUpdate(mc.getToastGui(), title, message, BaritoneAPI.getSettings().toastTimer.value));
|
||||
mc.addScheduledTask(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,6 +92,48 @@ public interface Helper {
|
||||
logToast(Helper.getPrefix(), new TextComponentString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as a desktop notification
|
||||
*
|
||||
* @param message The message to display in the notification
|
||||
*/
|
||||
default void logNotification(String message) {
|
||||
logNotification(message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as a desktop notification
|
||||
*
|
||||
* @param message The message to display in the notification
|
||||
* @param error Whether to log as an error
|
||||
*/
|
||||
default void logNotification(String message, boolean error) {
|
||||
if (BaritoneAPI.getSettings().desktopNotifications.value) {
|
||||
logNotificationDirect(message, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as a desktop notification regardless of desktopNotifications
|
||||
* (should only be used for critically important messages)
|
||||
*
|
||||
* @param message The message to display in the notification
|
||||
*/
|
||||
default void logNotificationDirect(String message) {
|
||||
logNotificationDirect(message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message as a desktop notification regardless of desktopNotifications
|
||||
* (should only be used for critically important messages)
|
||||
*
|
||||
* @param message The message to display in the notification
|
||||
* @param error Whether to log as an error
|
||||
*/
|
||||
default void logNotificationDirect(String message, boolean error) {
|
||||
mc.addScheduledTask(() -> BaritoneAPI.getSettings().notifier.value.accept(message, error));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to chat only if chatDebug is on
|
||||
*
|
||||
|
||||
@@ -87,7 +87,7 @@ public interface IPlayerContext {
|
||||
*/
|
||||
default Optional<BlockPos> getSelectedBlock() {
|
||||
RayTraceResult result = objectMouseOver();
|
||||
if (result != null && result.type == RayTraceResult.Type.BLOCK) {
|
||||
if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) {
|
||||
return Optional.of(result.getBlockPos());
|
||||
}
|
||||
return Optional.empty();
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.utils;
|
||||
package baritone.api.utils;
|
||||
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package baritone.api.utils;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.RayTraceFluidMode;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
@@ -49,7 +48,7 @@ public final class RayTraceUtils {
|
||||
if (wouldSneak) {
|
||||
start = inferSneakingEyePosition(entity);
|
||||
} else {
|
||||
start = entity.getEyePosition(1.0F); // do whatever is correct
|
||||
start = entity.getPositionEyes(1.0F); // do whatever is correct
|
||||
}
|
||||
Vec3d direction = RotationUtils.calcVec3dFromRotation(rotation);
|
||||
Vec3d end = start.add(
|
||||
@@ -57,7 +56,7 @@ public final class RayTraceUtils {
|
||||
direction.y * blockReachDistance,
|
||||
direction.z * blockReachDistance
|
||||
);
|
||||
return entity.world.rayTraceBlocks(start, end, RayTraceFluidMode.NEVER, false, true);
|
||||
return entity.world.rayTraceBlocks(start, end, false, false, true);
|
||||
}
|
||||
|
||||
public static Vec3d inferSneakingEyePosition(Entity entity) {
|
||||
|
||||
@@ -36,9 +36,6 @@ public class Rotation {
|
||||
public Rotation(float yaw, float pitch) {
|
||||
this.yaw = yaw;
|
||||
this.pitch = pitch;
|
||||
if (Float.isInfinite(yaw) || Float.isNaN(yaw) || Float.isInfinite(pitch) || Float.isNaN(pitch)) {
|
||||
throw new IllegalStateException(yaw + " " + pitch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,13 +23,7 @@ import net.minecraft.block.BlockFire;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.util.math.shapes.VoxelShapes;
|
||||
import net.minecraft.util.math.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -183,7 +177,7 @@ public final class RotationUtils {
|
||||
if (wouldSneak) {
|
||||
// the concern here is: what if we're looking at it now, but as soon as we start sneaking we no longer are
|
||||
RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, hypothetical, blockReachDistance, true);
|
||||
if (result != null && result.type == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(pos)) {
|
||||
if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK && result.getBlockPos().equals(pos)) {
|
||||
return Optional.of(hypothetical); // yes, if we sneaked we would still be looking at the block
|
||||
}
|
||||
} else {
|
||||
@@ -197,14 +191,11 @@ public final class RotationUtils {
|
||||
}
|
||||
|
||||
IBlockState state = entity.world.getBlockState(pos);
|
||||
VoxelShape shape = state.getShape(entity.world, pos);
|
||||
if (shape.isEmpty()) {
|
||||
shape = VoxelShapes.fullCube();
|
||||
}
|
||||
AxisAlignedBB aabb = state.getBoundingBox(entity.world, pos);
|
||||
for (Vec3d sideOffset : BLOCK_SIDE_MULTIPLIERS) {
|
||||
double xDiff = shape.getStart(EnumFacing.Axis.X) * sideOffset.x + shape.getEnd(EnumFacing.Axis.X) * (1 - sideOffset.x);
|
||||
double yDiff = shape.getStart(EnumFacing.Axis.Y) * sideOffset.y + shape.getEnd(EnumFacing.Axis.Y) * (1 - sideOffset.y);
|
||||
double zDiff = shape.getStart(EnumFacing.Axis.Z) * sideOffset.z + shape.getEnd(EnumFacing.Axis.Z) * (1 - sideOffset.z);
|
||||
double xDiff = aabb.minX * sideOffset.x + aabb.maxX * (1 - sideOffset.x);
|
||||
double yDiff = aabb.minY * sideOffset.y + aabb.maxY * (1 - sideOffset.y);
|
||||
double zDiff = aabb.minZ * sideOffset.z + aabb.maxZ * (1 - sideOffset.z);
|
||||
possibleRotation = reachableOffset(entity, pos, new Vec3d(pos).add(xDiff, yDiff, zDiff), blockReachDistance, wouldSneak);
|
||||
if (possibleRotation.isPresent()) {
|
||||
return possibleRotation;
|
||||
@@ -225,11 +216,11 @@ public final class RotationUtils {
|
||||
* @return The optional rotation
|
||||
*/
|
||||
public static Optional<Rotation> reachableOffset(Entity entity, BlockPos pos, Vec3d offsetPos, double blockReachDistance, boolean wouldSneak) {
|
||||
Vec3d eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getEyePosition(1.0F);
|
||||
Vec3d eyes = wouldSneak ? RayTraceUtils.inferSneakingEyePosition(entity) : entity.getPositionEyes(1.0F);
|
||||
Rotation rotation = calcRotationFromVec3d(eyes, offsetPos, new Rotation(entity.rotationYaw, entity.rotationPitch));
|
||||
RayTraceResult result = RayTraceUtils.rayTraceTowards(entity, rotation, blockReachDistance, wouldSneak);
|
||||
//System.out.println(result);
|
||||
if (result != null && result.type == RayTraceResult.Type.BLOCK) {
|
||||
if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) {
|
||||
if (result.getBlockPos().equals(pos)) {
|
||||
return Optional.of(rotation);
|
||||
}
|
||||
|
||||
@@ -20,12 +20,9 @@ package baritone.api.utils;
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.Settings;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraft.util.registry.IRegistry;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.BufferedReader;
|
||||
@@ -38,6 +35,7 @@ import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -46,12 +44,13 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.minecraft.client.Minecraft.getMinecraft;
|
||||
|
||||
public class SettingsUtil {
|
||||
|
||||
private static final Path SETTINGS_PATH = Minecraft.getInstance().gameDir.toPath().resolve("baritone").resolve("settings.txt");
|
||||
private static final Path SETTINGS_PATH = getMinecraft().gameDir.toPath().resolve("baritone").resolve("settings.txt");
|
||||
private static final Pattern SETTING_PATTERN = Pattern.compile("^(?<setting>[^ ]+) +(?<value>.+)"); // key and value split by the first space
|
||||
|
||||
private static final String[] JAVA_ONLY_SETTINGS = {"logger", "notifier", "toaster"};
|
||||
|
||||
private static boolean isComment(String line) {
|
||||
return line.startsWith("#") || line.startsWith("//");
|
||||
@@ -113,7 +112,7 @@ public class SettingsUtil {
|
||||
System.out.println("NULL SETTING?" + setting.getName());
|
||||
continue;
|
||||
}
|
||||
if (setting.getName().equals("logger")) {
|
||||
if (javaOnlySetting(setting)) {
|
||||
continue; // NO
|
||||
}
|
||||
if (setting.value == setting.defaultValue) {
|
||||
@@ -167,13 +166,28 @@ public class SettingsUtil {
|
||||
}
|
||||
|
||||
public static String settingToString(Settings.Setting setting) throws IllegalStateException {
|
||||
if (setting.getName().equals("logger")) {
|
||||
return "logger";
|
||||
if (javaOnlySetting(setting)) {
|
||||
return setting.getName();
|
||||
}
|
||||
|
||||
return setting.getName() + " " + settingValueToString(setting);
|
||||
}
|
||||
|
||||
/**
|
||||
* This should always be the same as whether the setting can be parsed from or serialized to a string
|
||||
*
|
||||
* @param the setting
|
||||
* @return true if the setting can not be set or read by the user
|
||||
*/
|
||||
public static boolean javaOnlySetting(Settings.Setting setting) {
|
||||
for (String name : JAVA_ONLY_SETTINGS) { // no JAVA_ONLY_SETTINGS.contains(...) because that would be case sensitive
|
||||
if (setting.getName().equalsIgnoreCase(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void parseAndApply(Settings settings, String settingName, String settingValue) throws IllegalStateException, NumberFormatException {
|
||||
Settings.Setting setting = settings.byLowerName.get(settingName);
|
||||
if (setting == null) {
|
||||
@@ -236,14 +250,15 @@ public class SettingsUtil {
|
||||
),
|
||||
ITEM(
|
||||
Item.class,
|
||||
str -> IRegistry.ITEM.get(new ResourceLocation(str.trim())),
|
||||
item -> IRegistry.ITEM.getKey(item).toString()
|
||||
str -> Item.getByNameOrId(str.trim()),
|
||||
item -> Item.REGISTRY.getNameForObject(item).toString()
|
||||
),
|
||||
LIST() {
|
||||
@Override
|
||||
public Object parse(ParserContext context, String raw) {
|
||||
Type type = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0];
|
||||
Parser parser = Parser.getParser(type);
|
||||
|
||||
return Stream.of(raw.split(","))
|
||||
.map(s -> parser.parse(context, s))
|
||||
.collect(Collectors.toList());
|
||||
@@ -263,6 +278,36 @@ public class SettingsUtil {
|
||||
public boolean accepts(Type type) {
|
||||
return List.class.isAssignableFrom(TypeUtils.resolveBaseClass(type));
|
||||
}
|
||||
},
|
||||
MAPPING() {
|
||||
@Override
|
||||
public Object parse(ParserContext context, String raw) {
|
||||
Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0];
|
||||
Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1];
|
||||
Parser keyParser = Parser.getParser(keyType);
|
||||
Parser valueParser = Parser.getParser(valueType);
|
||||
|
||||
return Stream.of(raw.split(",(?=[^,]*->)"))
|
||||
.map(s -> s.split("->"))
|
||||
.collect(Collectors.toMap(s -> keyParser.parse(context, s[0]), s -> valueParser.parse(context, s[1])));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(ParserContext context, Object value) {
|
||||
Type keyType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[0];
|
||||
Type valueType = ((ParameterizedType) context.getSetting().getType()).getActualTypeArguments()[1];
|
||||
Parser keyParser = Parser.getParser(keyType);
|
||||
Parser valueParser = Parser.getParser(valueType);
|
||||
|
||||
return ((Map<?,?>) value).entrySet().stream()
|
||||
.map(o -> keyParser.toString(context, o.getKey()) + "->" + valueParser.toString(context, o.getValue()))
|
||||
.collect(Collectors.joining(","));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Type type) {
|
||||
return Map.class.isAssignableFrom(TypeUtils.resolveBaseClass(type));
|
||||
}
|
||||
};
|
||||
|
||||
private final Class<?> cla$$;
|
||||
|
||||
@@ -20,10 +20,9 @@ package baritone.api.utils;
|
||||
import net.minecraft.block.BlockFire;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.shapes.VoxelShape;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
@@ -44,16 +43,10 @@ public final class VecUtils {
|
||||
*/
|
||||
public static Vec3d calculateBlockCenter(World world, BlockPos pos) {
|
||||
IBlockState b = world.getBlockState(pos);
|
||||
VoxelShape shape = b.getCollisionShape(world, pos);
|
||||
if (shape.isEmpty()) {
|
||||
return getBlockPosCenter(pos);
|
||||
}
|
||||
double xDiff = (shape.getStart(EnumFacing.Axis.X) + shape.getEnd(EnumFacing.Axis.X)) / 2;
|
||||
double yDiff = (shape.getStart(EnumFacing.Axis.Y) + shape.getEnd(EnumFacing.Axis.Y)) / 2;
|
||||
double zDiff = (shape.getStart(EnumFacing.Axis.Z) + shape.getEnd(EnumFacing.Axis.Z)) / 2;
|
||||
if (Double.isNaN(xDiff) || Double.isNaN(yDiff) || Double.isNaN(zDiff)) {
|
||||
throw new IllegalStateException(b + " " + pos + " " + shape);
|
||||
}
|
||||
AxisAlignedBB bbox = b.getBoundingBox(world, pos);
|
||||
double xDiff = (bbox.minX + bbox.maxX) / 2;
|
||||
double yDiff = (bbox.minY + bbox.maxY) / 2;
|
||||
double zDiff = (bbox.minZ + bbox.maxZ) / 2;
|
||||
if (b.getBlock() instanceof BlockFire) {//look at bottom of fire when putting it out
|
||||
yDiff = 0;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class BaritoneToast implements IToast {
|
||||
}
|
||||
|
||||
toastGui.getMinecraft().getTextureManager().bindTexture(new ResourceLocation("textures/gui/toasts.png"));
|
||||
GlStateManager.color4f(1.0F, 1.0F, 1.0F, 255.0f);
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 255.0f);
|
||||
toastGui.drawTexturedModalRect(0, 0, 0, 32, 160, 32);
|
||||
|
||||
if (this.subtitle == null) {
|
||||
@@ -71,4 +71,8 @@ public class BaritoneToast implements IToast {
|
||||
baritonetoast.setDisplayedText(title, subtitle);
|
||||
}
|
||||
}
|
||||
|
||||
public static void addOrUpdate(ITextComponent title, ITextComponent subtitle) {
|
||||
addOrUpdate(net.minecraft.client.Minecraft.getMinecraft().getToastGui(), title, subtitle, baritone.api.BaritoneAPI.getSettings().toastTimer.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.launch;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.mojang.authlib.Agent;
|
||||
import com.mojang.authlib.exceptions.AuthenticationException;
|
||||
import com.mojang.authlib.properties.PropertyMap;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
|
||||
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
|
||||
import net.minecraft.launchwrapper.Launch;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Based on GradleStart from ForgeGradle 2.3
|
||||
*
|
||||
* @author Brady
|
||||
* @since 3/11/2019
|
||||
*/
|
||||
public class LaunchTesting {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Map<String, String> arguments = new HashMap<>();
|
||||
|
||||
hackNatives();
|
||||
arguments.put("version", "BaritownedDeveloperEnvironment");
|
||||
arguments.put("assetIndex", System.getenv("assetIndex"));
|
||||
arguments.put("assetsDir", System.getenv().getOrDefault("assetDirectory", "assets"));
|
||||
arguments.put("accessToken", "FML");
|
||||
arguments.put("userProperties", "{}");
|
||||
arguments.put("tweakClass", System.getenv("tweakClass"));
|
||||
String password = System.getenv("password");
|
||||
if (password != null && !password.isEmpty()) {
|
||||
attemptLogin(arguments, System.getenv("username"), System.getenv("password"));
|
||||
}
|
||||
|
||||
List<String> argsArray = new ArrayList<>();
|
||||
arguments.forEach((k, v) -> {
|
||||
argsArray.add("--" + k);
|
||||
argsArray.add(v);
|
||||
});
|
||||
|
||||
Launch.main(argsArray.toArray(new String[0]));
|
||||
}
|
||||
|
||||
private static void hackNatives() {
|
||||
String paths = System.getProperty("java.library.path");
|
||||
String nativesDir = System.getenv().get("nativesDirectory");
|
||||
|
||||
if (Strings.isNullOrEmpty(paths))
|
||||
paths = nativesDir;
|
||||
else
|
||||
paths += File.pathSeparator + nativesDir;
|
||||
|
||||
System.setProperty("java.library.path", paths);
|
||||
|
||||
// hack the classloader now.
|
||||
try {
|
||||
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
|
||||
sysPathsField.setAccessible(true);
|
||||
sysPathsField.set(null, null);
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
private static void attemptLogin(Map<String, String> argMap, String username, String password) {
|
||||
YggdrasilUserAuthentication auth = (YggdrasilUserAuthentication) (new YggdrasilAuthenticationService(Proxy.NO_PROXY, "1")).createUserAuthentication(Agent.MINECRAFT);
|
||||
auth.setUsername(username);
|
||||
auth.setPassword(password);
|
||||
|
||||
try {
|
||||
auth.logIn();
|
||||
} catch (AuthenticationException var4) {
|
||||
throw new RuntimeException(var4);
|
||||
}
|
||||
|
||||
argMap.put("accessToken", auth.getAuthenticatedToken());
|
||||
argMap.put("uuid", auth.getSelectedProfile().getId().toString().replace("-", ""));
|
||||
argMap.put("username", auth.getSelectedProfile().getName());
|
||||
argMap.put("userType", auth.getUserType().getName());
|
||||
argMap.put("userProperties", (new GsonBuilder()).registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create().toJson(auth.getUserProperties()));
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,11 @@ public abstract class MixinBlockStateContainer implements IBlockStateContainer {
|
||||
protected BitArray storage;
|
||||
|
||||
@Shadow
|
||||
protected IBlockStatePalette<IBlockState> palette;
|
||||
protected IBlockStatePalette palette;
|
||||
|
||||
@Override
|
||||
public IBlockState getAtPalette(int index) {
|
||||
return palette.get(index);
|
||||
return palette.getBlockState(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,31 +17,23 @@
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import baritone.Baritone;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.chunk.RenderChunkCache;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RenderChunkCache.class)
|
||||
public class MixinRenderChunkCache {
|
||||
@Mixin(GuiChat.ChatTabCompleter.class)
|
||||
public abstract class MixinChatTabCompleter extends MixinTabCompleter {
|
||||
|
||||
@Redirect(
|
||||
method = "generateCache",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/world/chunk/Chunk.isEmptyBetween(II)Z"
|
||||
)
|
||||
@Inject(
|
||||
method = "complete",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private static boolean isEmpty(Chunk chunk, int yStart, int yEnd) {
|
||||
if (!chunk.isEmptyBetween(yStart, yEnd)) {
|
||||
return false;
|
||||
private void onComplete(CallbackInfo ci) {
|
||||
if (dontComplete) {
|
||||
ci.cancel();
|
||||
}
|
||||
if (chunk.isEmpty() && Baritone.settings().renderCachedChunks.value && Minecraft.getInstance().getIntegratedServer() == null) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,7 @@ import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_ONE;
|
||||
import static org.lwjgl.opengl.GL11.GL_ZERO;
|
||||
import static org.lwjgl.opengl.GL14.GL_CONSTANT_ALPHA;
|
||||
import static org.lwjgl.opengl.GL14.GL_ONE_MINUS_CONSTANT_ALPHA;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
@Mixin(ChunkRenderContainer.class)
|
||||
public class MixinChunkRenderContainer {
|
||||
@@ -44,11 +41,11 @@ public class MixinChunkRenderContainer {
|
||||
)
|
||||
)
|
||||
private BlockPos getPosition(RenderChunk renderChunkIn) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getInstance().isSingleplayer() && Minecraft.getInstance().world.getChunk(renderChunkIn.getPosition()).isEmpty()) {
|
||||
GlStateManager.enableAlphaTest();
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer() && Minecraft.getMinecraft().world.getChunk(renderChunkIn.getPosition()).isEmpty()) {
|
||||
GlStateManager.enableAlpha();
|
||||
GlStateManager.enableBlend();
|
||||
GL14.glBlendColor(0, 0, 0, Baritone.settings().cachedChunksOpacity.value);
|
||||
GlStateManager.blendFuncSeparate(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ONE, GL_ZERO);
|
||||
GlStateManager.tryBlendFuncSeparate(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
return renderChunkIn.getPosition();
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class MixinChunkRenderWorker {
|
||||
)
|
||||
)
|
||||
private boolean isChunkExisting(ChunkRenderWorker worker, BlockPos pos, World world) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getInstance().isSingleplayer()) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) {
|
||||
Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone();
|
||||
IPlayerContext ctx = baritone.getPlayerContext();
|
||||
if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) {
|
||||
|
||||
@@ -23,7 +23,6 @@ import baritone.api.event.events.RotationMoveEvent;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -45,8 +44,9 @@ public abstract class MixinEntityLivingBase extends Entity {
|
||||
*/
|
||||
private RotationMoveEvent jumpRotationEvent;
|
||||
|
||||
public MixinEntityLivingBase(EntityType<?> entityTypeIn, World worldIn) {
|
||||
super(entityTypeIn, worldIn);
|
||||
public MixinEntityLivingBase(World worldIn, RotationMoveEvent jumpRotationEvent) {
|
||||
super(worldIn);
|
||||
this.jumpRotationEvent = jumpRotationEvent;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
|
||||
@@ -58,10 +58,10 @@ public class MixinEntityPlayerSP {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "tick",
|
||||
method = "onUpdate",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/entity/EntityPlayerSP.isPassenger()Z",
|
||||
target = "net/minecraft/client/entity/EntityPlayerSP.isRiding()Z",
|
||||
shift = At.Shift.BY,
|
||||
by = -3
|
||||
)
|
||||
@@ -74,7 +74,7 @@ public class MixinEntityPlayerSP {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "tick",
|
||||
method = "onUpdate",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/entity/EntityPlayerSP.onUpdateWalkingPlayer()V",
|
||||
@@ -90,7 +90,7 @@ public class MixinEntityPlayerSP {
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "livingTick",
|
||||
method = "onLivingUpdate",
|
||||
at = @At(
|
||||
value = "FIELD",
|
||||
target = "net/minecraft/entity/player/PlayerCapabilities.allowFlying:Z"
|
||||
@@ -105,7 +105,7 @@ public class MixinEntityPlayerSP {
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "livingTick",
|
||||
method = "onLivingUpdate",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/settings/KeyBinding.isKeyDown()Z"
|
||||
|
||||
@@ -20,24 +20,24 @@ package baritone.launch.mixins;
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.event.events.RenderEvent;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.EntityRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(GameRenderer.class)
|
||||
public class MixinGameRenderer {
|
||||
@Mixin(EntityRenderer.class)
|
||||
public class MixinEntityRenderer {
|
||||
|
||||
@Inject(
|
||||
method = "updateCameraAndRender(FJ)V",
|
||||
method = "renderWorldPass",
|
||||
at = @At(
|
||||
value = "INVOKE_STRING",
|
||||
target = "Lnet/minecraft/profiler/Profiler;endStartSection(Ljava/lang/String;)V",
|
||||
args = {"ldc=hand"}
|
||||
)
|
||||
)
|
||||
private void renderWorldPass(float partialTicks, long finishTimeNano, CallbackInfo ci) {
|
||||
private void renderWorldPass(int pass, float partialTicks, long finishTimeNano, CallbackInfo ci) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
ibaritone.getGameEventHandler().onRenderPass(new RenderEvent(partialTicks));
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.event.events.TabCompleteEvent;
|
||||
import com.mojang.brigadier.context.StringRange;
|
||||
import com.mojang.brigadier.suggestion.Suggestion;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author Brady
|
||||
* @since 10/9/2019
|
||||
*/
|
||||
@Mixin(GuiChat.class)
|
||||
public class MixinGuiChat {
|
||||
|
||||
@Shadow
|
||||
protected GuiTextField inputField;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
protected List<String> commandUsage;
|
||||
|
||||
@Shadow
|
||||
private CompletableFuture<Suggestions> pendingSuggestions;
|
||||
|
||||
@Inject(
|
||||
method = "updateSuggestion",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void preUpdateSuggestion(CallbackInfo ci) {
|
||||
// Anything that is present in the input text before the cursor position
|
||||
String prefix = this.inputField.getText().substring(0, Math.min(this.inputField.getText().length(), this.inputField.getCursorPosition()));
|
||||
|
||||
TabCompleteEvent event = new TabCompleteEvent(prefix);
|
||||
BaritoneAPI.getProvider().getPrimaryBaritone().getGameEventHandler().onPreTabComplete(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.completions != null) {
|
||||
ci.cancel();
|
||||
|
||||
// TODO: Support populating the command usage
|
||||
this.commandUsage.clear();
|
||||
|
||||
if (event.completions.length == 0) {
|
||||
this.pendingSuggestions = Suggestions.empty();
|
||||
} else {
|
||||
int offset = this.inputField.getText().endsWith(" ")
|
||||
? this.inputField.getCursorPosition()
|
||||
: this.inputField.getText().lastIndexOf(" ") + 1; // If there is no space this is still 0 haha yes
|
||||
|
||||
List<Suggestion> suggestionList = Stream.of(event.completions)
|
||||
.map(s -> new Suggestion(StringRange.between(offset, offset + s.length()), s))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Suggestions suggestions = new Suggestions(
|
||||
StringRange.between(offset, offset + suggestionList.stream().mapToInt(s -> s.getText().length()).max().orElse(0)),
|
||||
suggestionList);
|
||||
|
||||
this.pendingSuggestions = new CompletableFuture<>();
|
||||
this.pendingSuggestions.complete(suggestions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,14 +35,14 @@ public abstract class MixinItemStack implements IItemStack {
|
||||
@Final
|
||||
private Item item;
|
||||
|
||||
@Shadow
|
||||
private int itemDamage;
|
||||
|
||||
@Unique
|
||||
private int baritoneHash;
|
||||
|
||||
@Shadow
|
||||
protected abstract int getDamage();
|
||||
|
||||
private void recalculateHash() {
|
||||
baritoneHash = item == null ? -1 : item.hashCode() + getDamage();
|
||||
baritoneHash = item == null ? -1 : item.hashCode() + itemDamage;
|
||||
}
|
||||
|
||||
@Inject(
|
||||
@@ -54,7 +54,7 @@ public abstract class MixinItemStack implements IItemStack {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "setDamage",
|
||||
method = "setItemDamage",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private void onItemDamageSet(CallbackInfo ci) {
|
||||
|
||||
@@ -88,10 +88,10 @@ public class MixinMinecraft {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/gui/GuiScreen;)V",
|
||||
method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Ljava/lang/String;)V",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
private void preLoadWorld(WorldClient world, GuiScreen loadingScreen, CallbackInfo ci) {
|
||||
private void preLoadWorld(WorldClient world, String loadingMessage, CallbackInfo ci) {
|
||||
// If we're unloading the world but one doesn't exist, ignore it
|
||||
if (this.world == null && world == null) {
|
||||
return;
|
||||
@@ -108,10 +108,10 @@ public class MixinMinecraft {
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Lnet/minecraft/client/gui/GuiScreen;)V",
|
||||
method = "loadWorld(Lnet/minecraft/client/multiplayer/WorldClient;Ljava/lang/String;)V",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postLoadWorld(WorldClient world, GuiScreen loadingScreen, CallbackInfo ci) {
|
||||
private void postLoadWorld(WorldClient world, String loadingMessage, CallbackInfo ci) {
|
||||
// still fire event for both null, as that means we've just finished exiting a world
|
||||
|
||||
// mc.world changing is only the primary baritone
|
||||
|
||||
@@ -25,7 +25,10 @@ import baritone.api.event.events.type.EventState;
|
||||
import baritone.cache.CachedChunk;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.network.NetHandlerPlayClient;
|
||||
import net.minecraft.network.play.server.*;
|
||||
import net.minecraft.network.play.server.SPacketBlockChange;
|
||||
import net.minecraft.network.play.server.SPacketChunkData;
|
||||
import net.minecraft.network.play.server.SPacketCombatEvent;
|
||||
import net.minecraft.network.play.server.SPacketMultiBlockChange;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
@@ -43,7 +46,7 @@ public class MixinNetHandlerPlayClient {
|
||||
method = "handleChunkData",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/multiplayer/ChunkProviderClient.func_212474_a(IILnet/minecraft/network/PacketBuffer;IZ)Lnet/minecraft/world/chunk/Chunk;"
|
||||
target = "net/minecraft/world/chunk/Chunk.read(Lnet/minecraft/network/PacketBuffer;IZ)V"
|
||||
)
|
||||
)
|
||||
private void preRead(SPacketChunkData packetIn, CallbackInfo ci) {
|
||||
@@ -82,36 +85,6 @@ public class MixinNetHandlerPlayClient {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "processChunkUnload",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
private void preChunkUnload(SPacketUnloadChunk packet, CallbackInfo ci) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
EntityPlayerSP player = ibaritone.getPlayerContext().player();
|
||||
if (player != null && player.connection == (NetHandlerPlayClient) (Object) this) {
|
||||
ibaritone.getGameEventHandler().onChunkEvent(
|
||||
new ChunkEvent(EventState.PRE, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "processChunkUnload",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postChunkUnload(SPacketUnloadChunk packet, CallbackInfo ci) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
EntityPlayerSP player = ibaritone.getPlayerContext().player();
|
||||
if (player != null && player.connection == (NetHandlerPlayClient) (Object) this) {
|
||||
ibaritone.getGameEventHandler().onChunkEvent(
|
||||
new ChunkEvent(EventState.POST, ChunkEvent.Type.UNLOAD, packet.getX(), packet.getZ())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "handleBlockChange",
|
||||
at = @At("RETURN")
|
||||
@@ -120,7 +93,7 @@ public class MixinNetHandlerPlayClient {
|
||||
if (!Baritone.settings().repackOnAnyBlockChange.value) {
|
||||
return;
|
||||
}
|
||||
if (!CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(packetIn.getState().getBlock())) {
|
||||
if (!CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(packetIn.getBlockState().getBlock())) {
|
||||
return;
|
||||
}
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
@@ -130,8 +103,8 @@ public class MixinNetHandlerPlayClient {
|
||||
new ChunkEvent(
|
||||
EventState.POST,
|
||||
ChunkEvent.Type.POPULATE_FULL,
|
||||
packetIn.getPos().getX() >> 4,
|
||||
packetIn.getPos().getZ() >> 4
|
||||
packetIn.getBlockPosition().getX() >> 4,
|
||||
packetIn.getBlockPosition().getZ() >> 4
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public class MixinNetworkManager {
|
||||
method = "dispatchPacket",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
private void preDispatchPacket(Packet<?> inPacket, final GenericFutureListener<? extends Future<? super Void>> futureListeners, CallbackInfo ci) {
|
||||
private void preDispatchPacket(Packet<?> inPacket, final GenericFutureListener<? extends Future<? super Void>>[] futureListeners, CallbackInfo ci) {
|
||||
if (this.direction != EnumPacketDirection.CLIENTBOUND) {
|
||||
return;
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class MixinNetworkManager {
|
||||
method = "dispatchPacket",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postDispatchPacket(Packet<?> inPacket, final GenericFutureListener<? extends Future<? super Void>> futureListeners, CallbackInfo ci) {
|
||||
private void postDispatchPacket(Packet<?> inPacket, final GenericFutureListener<? extends Future<? super Void>>[] futureListeners, CallbackInfo ci) {
|
||||
if (this.direction != EnumPacketDirection.CLIENTBOUND) {
|
||||
return;
|
||||
}
|
||||
@@ -85,7 +85,7 @@ public class MixinNetworkManager {
|
||||
method = "channelRead0",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/network/NetworkManager.processPacket(Lnet/minecraft/network/Packet;Lnet/minecraft/network/INetHandler;)V"
|
||||
target = "net/minecraft/network/Packet.processPacket(Lnet/minecraft/network/INetHandler;)V"
|
||||
)
|
||||
)
|
||||
private void preProcessPacket(ChannelHandlerContext context, Packet<?> packet, CallbackInfo ci) {
|
||||
|
||||
@@ -23,8 +23,8 @@ import baritone.api.utils.IPlayerContext;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.chunk.RenderChunk;
|
||||
import net.minecraft.client.renderer.chunk.RenderChunkCache;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.ChunkCache;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
@@ -40,11 +40,43 @@ public class MixinRenderChunk {
|
||||
method = "rebuildChunk",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/renderer/chunk/RenderChunkCache.getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/state/IBlockState;"
|
||||
target = "net/minecraft/world/ChunkCache.isEmpty()Z"
|
||||
)
|
||||
)
|
||||
private IBlockState getBlockState(RenderChunkCache chunkCache, BlockPos pos) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getInstance().isSingleplayer()) {
|
||||
private boolean isEmpty(ChunkCache chunkCache) {
|
||||
if (!chunkCache.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) {
|
||||
Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone();
|
||||
IPlayerContext ctx = baritone.getPlayerContext();
|
||||
if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) {
|
||||
BlockPos position = ((RenderChunk) (Object) this).getPosition();
|
||||
// RenderChunk extends from -1,-1,-1 to +16,+16,+16
|
||||
// then the constructor of ChunkCache extends it one more (presumably to get things like the connected status of fences? idk)
|
||||
// so if ANY of the adjacent chunks are loaded, we are unempty
|
||||
for (int dx = -1; dx <= 1; dx++) {
|
||||
for (int dz = -1; dz <= 1; dz++) {
|
||||
if (baritone.bsi.isLoaded(16 * dx + position.getX(), 16 * dz + position.getZ())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Redirect(
|
||||
method = "rebuildChunk",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/world/ChunkCache.getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/state/IBlockState;"
|
||||
)
|
||||
)
|
||||
private IBlockState getBlockState(ChunkCache chunkCache, BlockPos pos) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) {
|
||||
Baritone baritone = (Baritone) BaritoneAPI.getProvider().getPrimaryBaritone();
|
||||
IPlayerContext ctx = baritone.getPlayerContext();
|
||||
if (ctx.player() != null && ctx.world() != null && baritone.bsi != null) {
|
||||
|
||||
@@ -38,9 +38,9 @@ public class MixinRenderList {
|
||||
)
|
||||
)
|
||||
private void popMatrix() {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getInstance().isSingleplayer()) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) {
|
||||
// reset the blend func to normal (not dependent on constant alpha)
|
||||
GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.block.properties.IProperty;
|
||||
import org.spongepowered.asm.mixin.*;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(targets = "net.minecraft.block.state.BlockStateContainer$StateImplementation")
|
||||
public abstract class MixinStateImplementation {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private ImmutableMap<IProperty<?>, Comparable<?>> properties;
|
||||
|
||||
/**
|
||||
* Block states are fucking immutable
|
||||
*/
|
||||
@Unique
|
||||
private int hashCode;
|
||||
|
||||
@Inject(
|
||||
method = "<init>*",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void onInit(CallbackInfo ci) {
|
||||
hashCode = properties.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache this instead of using the fucking map every time
|
||||
*
|
||||
* @author LoganDark
|
||||
* @reason Regular IBlockState generates a new hash every fucking time. This is not needed when scanning millions
|
||||
* per second
|
||||
*/
|
||||
@Override
|
||||
@Overwrite
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.event.events.TabCompleteEvent;
|
||||
import net.minecraft.client.gui.GuiChat;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
import net.minecraft.util.TabCompleter;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(TabCompleter.class)
|
||||
public abstract class MixinTabCompleter {
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
protected GuiTextField textField;
|
||||
|
||||
@Shadow
|
||||
protected boolean requestedCompletions;
|
||||
|
||||
@Shadow
|
||||
public abstract void setCompletions(String... newCompl);
|
||||
|
||||
@Unique
|
||||
protected boolean dontComplete = false;
|
||||
|
||||
@Inject(
|
||||
method = "requestCompletions",
|
||||
at = @At("HEAD"),
|
||||
cancellable = true
|
||||
)
|
||||
private void onRequestCompletions(String prefix, CallbackInfo ci) {
|
||||
if (!((Object) this instanceof GuiChat.ChatTabCompleter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IBaritone baritone = BaritoneAPI.getProvider().getPrimaryBaritone();
|
||||
|
||||
TabCompleteEvent event = new TabCompleteEvent(prefix);
|
||||
baritone.getGameEventHandler().onPreTabComplete(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
ci.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.completions != null) {
|
||||
ci.cancel();
|
||||
|
||||
this.dontComplete = true;
|
||||
|
||||
try {
|
||||
this.requestedCompletions = true;
|
||||
setCompletions(event.completions);
|
||||
} finally {
|
||||
this.dontComplete = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,9 +38,9 @@ public class MixinVboRenderList {
|
||||
)
|
||||
)
|
||||
private void popMatrix() {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getInstance().isSingleplayer()) {
|
||||
if (Baritone.settings().renderCachedChunks.value && !Minecraft.getMinecraft().isSingleplayer()) {
|
||||
// reset the blend func to normal (not dependent on constant alpha)
|
||||
GlStateManager.blendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
GlStateManager.tryBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
|
||||
}
|
||||
GlStateManager.popMatrix();
|
||||
}
|
||||
|
||||
75
src/launch/java/baritone/launch/mixins/MixinWorldClient.java
Normal file
75
src/launch/java/baritone/launch/mixins/MixinWorldClient.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.event.events.ChunkEvent;
|
||||
import baritone.api.event.events.type.EventState;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
/**
|
||||
* @author Brady
|
||||
* @since 8/2/2018
|
||||
*/
|
||||
@Mixin(WorldClient.class)
|
||||
public class MixinWorldClient {
|
||||
|
||||
@Inject(
|
||||
method = "doPreChunk",
|
||||
at = @At("HEAD")
|
||||
)
|
||||
private void preDoPreChunk(int chunkX, int chunkZ, boolean loadChunk, CallbackInfo ci) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
if (ibaritone.getPlayerContext().world() == (WorldClient) (Object) this) {
|
||||
ibaritone.getGameEventHandler().onChunkEvent(
|
||||
new ChunkEvent(
|
||||
EventState.PRE,
|
||||
loadChunk ? ChunkEvent.Type.LOAD : ChunkEvent.Type.UNLOAD,
|
||||
chunkX,
|
||||
chunkZ
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "doPreChunk",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postDoPreChunk(int chunkX, int chunkZ, boolean loadChunk, CallbackInfo ci) {
|
||||
for (IBaritone ibaritone : BaritoneAPI.getProvider().getAllBaritones()) {
|
||||
if (ibaritone.getPlayerContext().world() == (WorldClient) (Object) this) {
|
||||
ibaritone.getGameEventHandler().onChunkEvent(
|
||||
new ChunkEvent(
|
||||
EventState.POST,
|
||||
loadChunk ? ChunkEvent.Type.LOAD : ChunkEvent.Type.UNLOAD,
|
||||
chunkX,
|
||||
chunkZ
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,14 @@
|
||||
"MixinAnvilChunkLoader",
|
||||
"MixinBitArray",
|
||||
"MixinBlockStateContainer",
|
||||
"MixinChatTabCompleter",
|
||||
"MixinChunkProviderClient",
|
||||
"MixinChunkProviderServer",
|
||||
"MixinChunkRenderContainer",
|
||||
"MixinChunkRenderWorker",
|
||||
"MixinEntityLivingBase",
|
||||
"MixinEntityPlayerSP",
|
||||
"MixinGameRenderer",
|
||||
"MixinGuiChat",
|
||||
"MixinEntityRenderer",
|
||||
"MixinGuiScreen",
|
||||
"MixinItemStack",
|
||||
"MixinMinecraft",
|
||||
@@ -26,8 +26,10 @@
|
||||
"MixinNetworkManager",
|
||||
"MixinPlayerControllerMP",
|
||||
"MixinRenderChunk",
|
||||
"MixinRenderChunkCache",
|
||||
"MixinRenderList",
|
||||
"MixinVboRenderList"
|
||||
"MixinStateImplementation",
|
||||
"MixinTabCompleter",
|
||||
"MixinVboRenderList",
|
||||
"MixinWorldClient"
|
||||
]
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import baritone.api.event.listener.IEventBus;
|
||||
import baritone.api.utils.Helper;
|
||||
import baritone.api.utils.IPlayerContext;
|
||||
import baritone.behavior.*;
|
||||
import baritone.builder.Main;
|
||||
import baritone.cache.WorldProvider;
|
||||
import baritone.command.manager.CommandManager;
|
||||
import baritone.event.GameEventHandler;
|
||||
@@ -56,7 +57,7 @@ public class Baritone implements IBaritone {
|
||||
static {
|
||||
threadPool = new ThreadPoolExecutor(4, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
|
||||
|
||||
dir = new File(Minecraft.getInstance().gameDir, "baritone");
|
||||
dir = new File(Minecraft.getMinecraft().gameDir, "baritone");
|
||||
if (!Files.exists(dir.toPath())) {
|
||||
try {
|
||||
Files.createDirectories(dir.toPath());
|
||||
@@ -120,6 +121,15 @@ public class Baritone implements IBaritone {
|
||||
this.worldProvider = new WorldProvider();
|
||||
this.selectionManager = new SelectionManager(this);
|
||||
this.commandManager = new CommandManager(this);
|
||||
|
||||
try {
|
||||
Main.main();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package baritone.behavior;
|
||||
|
||||
import baritone.Baritone;
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.event.events.TickEvent;
|
||||
import baritone.utils.ToolSet;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -112,6 +113,9 @@ public final class InventoryBehavior extends Behavior {
|
||||
if (stack.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (Baritone.settings().itemSaver.value && (stack.getItemDamage() + Baritone.settings().itemSaverThreshold.value) >= stack.getMaxDamage() && stack.getMaxDamage() > 1) {
|
||||
continue;
|
||||
}
|
||||
if (cla$$.isInstance(stack.getItem())) {
|
||||
double speed = ToolSet.calculateSpeedVsBlock(stack, against.getDefaultState()); // takes into account enchants
|
||||
if (speed > bestSpeed) {
|
||||
@@ -134,7 +138,7 @@ public final class InventoryBehavior extends Behavior {
|
||||
|
||||
public boolean selectThrowawayForLocation(boolean select, int x, int y, int z) {
|
||||
IBlockState maybe = baritone.getBuilderProcess().placeAt(x, y, z, baritone.bsi.get0(x, y, z));
|
||||
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && maybe.equals(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(new BlockItemUseContext(new ItemUseContext(ctx.player(), stack, ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ)))))) {
|
||||
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && maybe.equals(((ItemBlock) stack.getItem()).getBlock().getStateForPlacement(ctx.world(), ctx.playerFeet(), EnumFacing.UP, (float) ctx.player().posX, (float) ctx.player().posY, (float) ctx.player().posZ, stack.getItem().getMetadata(stack.getMetadata()), ctx.player())))) {
|
||||
return true; // gotem
|
||||
}
|
||||
if (maybe != null && throwaway(select, stack -> stack.getItem() instanceof ItemBlock && ((ItemBlock) stack.getItem()).getBlock().equals(maybe.getBlock()))) {
|
||||
@@ -149,6 +153,10 @@ public final class InventoryBehavior extends Behavior {
|
||||
}
|
||||
|
||||
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired) {
|
||||
return throwaway(select, desired, Baritone.settings().allowInventory.value);
|
||||
}
|
||||
|
||||
public boolean throwaway(boolean select, Predicate<? super ItemStack> desired, boolean allowInventory) {
|
||||
EntityPlayerSP p = ctx.player();
|
||||
NonNullList<ItemStack> inv = p.inventory.mainInventory;
|
||||
for (int i = 0; i < 9; i++) {
|
||||
@@ -181,6 +189,19 @@ public final class InventoryBehavior extends Behavior {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowInventory) {
|
||||
for (int i = 9; i < 36; i++) {
|
||||
if (desired.test(inv.get(i))) {
|
||||
swapWithHotBar(i, 7);
|
||||
if (select) {
|
||||
p.inventory.currentItem = 7;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
|
||||
|
||||
/**
|
||||
* Target's values are as follows:
|
||||
* <p>
|
||||
* getFirst() -> yaw
|
||||
* getSecond() -> pitch
|
||||
*/
|
||||
private Rotation target;
|
||||
|
||||
@@ -50,13 +53,6 @@ public final class LookBehavior extends Behavior implements ILookBehavior {
|
||||
@Override
|
||||
public void updateTarget(Rotation target, boolean force) {
|
||||
this.target = target;
|
||||
if (!force) {
|
||||
double rand = Math.random() - 0.5;
|
||||
if (Math.abs(rand) < 0.1) {
|
||||
rand *= 4;
|
||||
}
|
||||
this.target = new Rotation(this.target.getYaw() + (float) (rand * Baritone.settings().randomLooking113.value), this.target.getPitch());
|
||||
}
|
||||
this.force = force || !Baritone.settings().freeLook.value;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import baritone.api.event.events.PlayerUpdateEvent;
|
||||
import baritone.api.event.events.TickEvent;
|
||||
import baritone.api.event.events.type.EventState;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.utils.Helper;
|
||||
import baritone.cache.ContainerMemory;
|
||||
import baritone.utils.BlockStateInterface;
|
||||
import net.minecraft.block.Block;
|
||||
@@ -40,13 +41,20 @@ import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityLockable;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
import net.minecraft.util.text.event.ClickEvent;
|
||||
import net.minecraft.util.text.event.HoverEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
import static baritone.api.command.IBaritoneChatControl.FORCE_COMMAND_PREFIX;
|
||||
|
||||
/**
|
||||
* doesn't work for horse inventories :^)
|
||||
*
|
||||
@@ -166,7 +174,26 @@ public final class MemoryBehavior extends Behavior {
|
||||
|
||||
@Override
|
||||
public void onPlayerDeath() {
|
||||
baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet()));
|
||||
Waypoint deathWaypoint = new Waypoint("death", Waypoint.Tag.DEATH, ctx.playerFeet());
|
||||
baritone.getWorldProvider().getCurrentWorld().getWaypoints().addWaypoint(deathWaypoint);
|
||||
ITextComponent component = new TextComponentString("Death position saved.");
|
||||
component.getStyle()
|
||||
.setColor(TextFormatting.WHITE)
|
||||
.setHoverEvent(new HoverEvent(
|
||||
HoverEvent.Action.SHOW_TEXT,
|
||||
new TextComponentString("Click to goto death")
|
||||
))
|
||||
.setClickEvent(new ClickEvent(
|
||||
ClickEvent.Action.RUN_COMMAND,
|
||||
String.format(
|
||||
"%s%s goto %s @ %d",
|
||||
FORCE_COMMAND_PREFIX,
|
||||
"wp",
|
||||
deathWaypoint.getTag().getName(),
|
||||
deathWaypoint.getCreationTimestamp()
|
||||
)
|
||||
));
|
||||
Helper.HELPER.logDirect(component);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -52,6 +52,10 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
||||
private Goal goal;
|
||||
private CalculationContext context;
|
||||
|
||||
/*eta*/
|
||||
private int ticksElapsedSoFar;
|
||||
private BetterBlockPos startPosition;
|
||||
|
||||
private boolean safeToCancel;
|
||||
private boolean pauseRequestedLastTick;
|
||||
private boolean unpausedLastTick;
|
||||
@@ -98,6 +102,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
||||
expectedSegmentStart = pathStart();
|
||||
baritone.getPathingControlManager().preTick();
|
||||
tickPath();
|
||||
ticksElapsedSoFar++;
|
||||
dispatchEvents();
|
||||
}
|
||||
|
||||
@@ -372,6 +377,40 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
||||
return context;
|
||||
}
|
||||
|
||||
public Optional<Double> estimatedTicksToGoal() {
|
||||
BetterBlockPos currentPos = ctx.playerFeet();
|
||||
if (goal == null || currentPos == null || startPosition == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
if (goal.isInGoal(ctx.playerFeet())) {
|
||||
resetEstimatedTicksToGoal();
|
||||
return Optional.of(0.0);
|
||||
}
|
||||
if (ticksElapsedSoFar == 0) {
|
||||
return Optional.empty();
|
||||
}
|
||||
double current = goal.heuristic(currentPos.x, currentPos.y, currentPos.z);
|
||||
double start = goal.heuristic(startPosition.x, startPosition.y, startPosition.z);
|
||||
if (current == start) {// can't check above because current and start can be equal even if currentPos and startPosition are not
|
||||
return Optional.empty();
|
||||
}
|
||||
double eta = Math.abs(current - goal.heuristic()) * ticksElapsedSoFar / Math.abs(start - current);
|
||||
return Optional.of(eta);
|
||||
}
|
||||
|
||||
private void resetEstimatedTicksToGoal() {
|
||||
resetEstimatedTicksToGoal(expectedSegmentStart);
|
||||
}
|
||||
|
||||
private void resetEstimatedTicksToGoal(BlockPos start) {
|
||||
resetEstimatedTicksToGoal(new BetterBlockPos(start));
|
||||
}
|
||||
|
||||
private void resetEstimatedTicksToGoal(BetterBlockPos start) {
|
||||
ticksElapsedSoFar = 0;
|
||||
startPosition = start;
|
||||
}
|
||||
|
||||
/**
|
||||
* See issue #209
|
||||
*
|
||||
@@ -468,6 +507,7 @@ public final class PathingBehavior extends Behavior implements IPathingBehavior,
|
||||
if (executor.get().getPath().positions().contains(expectedSegmentStart)) {
|
||||
queuePathEvent(PathEvent.CALC_FINISHED_NOW_EXECUTING);
|
||||
current = executor.get();
|
||||
resetEstimatedTicksToGoal(start);
|
||||
} else {
|
||||
logDebug("Warning: discarding orphan path segment with incorrect start");
|
||||
}
|
||||
|
||||
56
src/main/java/baritone/builder/Blip.java
Normal file
56
src/main/java/baritone/builder/Blip.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.IPlayerContext;
|
||||
|
||||
/**
|
||||
* 1/16th of a block
|
||||
* <p>
|
||||
* Why do this? Several reasons:
|
||||
* <p>
|
||||
* • No floating point inaccuracy. I got incredibly annoyed with simple stuff like >1 and <1 being randomly wrong. It helps with stuff like slab calculations, 0.5 block steps, etc
|
||||
* <p>
|
||||
* • It's obscenely fast
|
||||
*/
|
||||
public class Blip {
|
||||
|
||||
public static final int FULL_BLOCK = 16;
|
||||
public static final double RATIO = 0.0625;
|
||||
public static final int HALF_BLOCK = 8;
|
||||
public static final int PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE = 28;
|
||||
public static final int PLAYER_HEIGHT_SLIGHT_OVERESTIMATE = 29;
|
||||
public static final int TWO_BLOCKS = 2 * FULL_BLOCK;
|
||||
public static final int FEET_TO_EYE_APPROX = (int) (IPlayerContext.eyeHeight(false) / RATIO);
|
||||
public static final int JUMP = 20; // 1.25
|
||||
public static final int TALLEST_BLOCK = FULL_BLOCK + HALF_BLOCK; // 24, 1.5 blocks tall, the fence / wall has the highest collision box
|
||||
|
||||
public static double playerEyeFromFeetBlips(int feetBlips, boolean sneaking) {
|
||||
return feetBlips * RATIO + IPlayerContext.eyeHeight(sneaking);
|
||||
}
|
||||
|
||||
static {
|
||||
double realPlayerHeight = 1.8;
|
||||
if (PLAYER_HEIGHT_SLIGHT_OVERESTIMATE * RATIO <= realPlayerHeight || PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE * RATIO >= realPlayerHeight || PLAYER_HEIGHT_SLIGHT_OVERESTIMATE != PLAYER_HEIGHT_SLIGHT_UNDERESTIMATE + 1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (FULL_BLOCK * RATIO != 1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/main/java/baritone/builder/BlockData.java
Normal file
38
src/main/java/baritone/builder/BlockData.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockData {
|
||||
private final BlockStateCachedData[] PER_STATE;
|
||||
|
||||
public BlockData(IBlockStateDataProvider provider) {
|
||||
PER_STATE = provider.allNullable();
|
||||
}
|
||||
|
||||
public BlockStateCachedData get(int state) {
|
||||
return PER_STATE[state];
|
||||
}
|
||||
|
||||
public List<BlockStateCachedData> getAllStates() {
|
||||
return Collections.unmodifiableList(Arrays.asList(PER_STATE));
|
||||
}
|
||||
}
|
||||
85
src/main/java/baritone/builder/BlockStateCachedData.java
Normal file
85
src/main/java/baritone/builder/BlockStateCachedData.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Information about an IBlockState
|
||||
* <p>
|
||||
* There will be exactly one of these per valid IBlockState in the game
|
||||
*/
|
||||
public final class BlockStateCachedData {
|
||||
public final boolean fullyWalkableTop;
|
||||
private final int collisionHeightBlips;
|
||||
public final boolean isAir;
|
||||
|
||||
public final boolean collidesWithPlayer;
|
||||
|
||||
public final boolean mustSneakWhenPlacingAgainstMe;
|
||||
|
||||
public final List<BlockStatePlacementOption> placeMe; // list because of unknown size with no obvious indexing
|
||||
|
||||
public final PlaceAgainstData[] placeAgainstMe; // array because of fixed size with obvious indexing (no more than one per face, so, index per face)
|
||||
|
||||
public BlockStateCachedData(BlockStateCachedDataBuilder builder) {
|
||||
builder.sanityCheck();
|
||||
this.isAir = builder.isAir();
|
||||
this.fullyWalkableTop = builder.isFullyWalkableTop();
|
||||
this.collidesWithPlayer = builder.isCollidesWithPlayer();
|
||||
if (collidesWithPlayer) {
|
||||
this.collisionHeightBlips = builder.collisionHeightBlips();
|
||||
} else {
|
||||
this.collisionHeightBlips = -1;
|
||||
}
|
||||
|
||||
this.mustSneakWhenPlacingAgainstMe = builder.isMustSneakWhenPlacingAgainstMe();
|
||||
this.placeMe = Collections.unmodifiableList(builder.howCanIBePlaced());
|
||||
|
||||
this.placeAgainstMe = builder.placeAgainstMe();
|
||||
}
|
||||
|
||||
public int collisionHeightBlips() {
|
||||
if (Main.DEBUG && !collidesWithPlayer) { // confirmed and tested: when DEBUG is false, proguard removes this if in the first pass, then inlines the calls in the second pass, making this just as good as a field access in release builds
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return collisionHeightBlips;
|
||||
}
|
||||
|
||||
public boolean possibleAgainstMe(BlockStatePlacementOption placement) {
|
||||
PlaceAgainstData against = againstMe(placement);
|
||||
return against != null && possible(placement, against);
|
||||
}
|
||||
|
||||
public PlaceAgainstData againstMe(BlockStatePlacementOption placement) {
|
||||
return placeAgainstMe[placement.against.oppositeIndex];
|
||||
}
|
||||
|
||||
public static boolean possible(BlockStatePlacementOption placement, PlaceAgainstData against) {
|
||||
if (placement.against != against.against.opposite()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (placement.against.vertical) {
|
||||
return true;
|
||||
}
|
||||
return
|
||||
(against.presentsAnOptionStrictlyInTheBottomHalfOfTheStandardVoxelPlane() && placement.half != Half.TOP) ||
|
||||
(against.presentsAnOptionStrictlyInTheTopHalfOfTheStandardVoxelPlane() && placement.half != Half.BOTTOM);
|
||||
}
|
||||
}
|
||||
326
src/main/java/baritone/builder/BlockStateCachedDataBuilder.java
Normal file
326
src/main/java/baritone/builder/BlockStateCachedDataBuilder.java
Normal file
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class BlockStateCachedDataBuilder {
|
||||
|
||||
// should all these be optionals? like maybe Boolean? that start as null? and each has to be set explicitly?
|
||||
private boolean isAir;
|
||||
private boolean canPlaceAgainstMe;
|
||||
private boolean fullyWalkableTop;
|
||||
private boolean collidesWithPlayer;
|
||||
private boolean mustSneakWhenPlacingAgainstMe;
|
||||
private boolean mustBePlacedBottomToTop;
|
||||
/**
|
||||
* Examples:
|
||||
* <p>
|
||||
* Upside down stairs must be placed against TOP
|
||||
* <p>
|
||||
* Bottom slabs must be placed against BOTTOM
|
||||
* <p>
|
||||
* Normal blocks must be placed against EITHER
|
||||
*/
|
||||
private Half mustBePlacedAgainst = Half.EITHER;
|
||||
private Face playerMustBeHorizontalFacingInOrderToPlaceMe;
|
||||
private Integer collisionHeightBlips;
|
||||
private Face canOnlyPlaceAgainst;
|
||||
/**
|
||||
* Blocks that have a collision height lower than their placement bounding box height.
|
||||
* aka snow layers and soul sand.
|
||||
* e.g. soul sand only collides with the player 0.875 high, but the BLOCK is 1.000 high, i.e. you can place against it at height 0.9 even though the player stands at height 0.875
|
||||
*/
|
||||
private boolean fakeLessThanFullHeight;
|
||||
private boolean placementLogicNotImplementedYet;
|
||||
private Face playerMustBeEntityFacingInOrderToPlaceMe;
|
||||
|
||||
public BlockStateCachedDataBuilder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Really just air. This is a fully open block that won't collide with object mouse over raytrace.
|
||||
*/
|
||||
public BlockStateCachedDataBuilder setAir() {
|
||||
isAir = true;
|
||||
mustBePlacedAgainst = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isAir() {
|
||||
return isAir;
|
||||
}
|
||||
|
||||
/**
|
||||
* does the top face of this block fully support the player from 0.0,0.0 to 1.0,1.0? true for most normal blocks. false for, for example, fences
|
||||
*/
|
||||
public BlockStateCachedDataBuilder fullyWalkableTop() {
|
||||
fullyWalkableTop = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isFullyWalkableTop() {
|
||||
return fullyWalkableTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* The highest collision extension of this block possible
|
||||
* <p>
|
||||
* For example, should be 1 for stairs, even though part of the top face is really 0.5
|
||||
* <p>
|
||||
* Should be 1 for top slabs
|
||||
* <p>
|
||||
* Should be 1 for trapdoors because when they're open, they touch the top face of the voxel
|
||||
*/
|
||||
public BlockStateCachedDataBuilder collisionHeight(double y) {
|
||||
for (int h = 0; h <= Blip.TALLEST_BLOCK; h++) { // max height of
|
||||
if (y == h * Blip.RATIO) {
|
||||
collisionHeightBlips = h;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
public Integer collisionHeightBlips() { // e.g. slabs are 0.5, soul sand is 0.875, normal blocks are 1, fences are 1.5
|
||||
return collisionHeightBlips;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder mustSneakWhenPlacingAgainstMe() {
|
||||
mustSneakWhenPlacingAgainstMe = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isMustSneakWhenPlacingAgainstMe() {
|
||||
return mustSneakWhenPlacingAgainstMe;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder canPlaceAgainstMe() {
|
||||
canPlaceAgainstMe = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isCollidesWithPlayer() {
|
||||
return collidesWithPlayer;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder collidesWithPlayer(boolean val) {
|
||||
collidesWithPlayer = val;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder playerMustBeHorizontalFacingInOrderToPlaceMe(Face face) {
|
||||
playerMustBeHorizontalFacingInOrderToPlaceMe = face;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder playerMustBeEntityFacingInOrderToPlaceMe(Face face) {
|
||||
playerMustBeEntityFacingInOrderToPlaceMe = face;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder mustBePlacedAgainst(Half half) {
|
||||
if (half == null) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
mustBePlacedAgainst = half;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder mustBePlacedBottomToTop() {
|
||||
mustBePlacedBottomToTop = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder canOnlyPlaceAgainst(Face face) {
|
||||
canOnlyPlaceAgainst = face;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder placementLogicNotImplementedYet() {
|
||||
placementLogicNotImplementedYet = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockStateCachedDataBuilder fakeLessThanFullHeight() {
|
||||
fakeLessThanFullHeight = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<BlockStatePlacementOption> howCanIBePlaced() {
|
||||
if (mustBePlacedAgainst == null || placementLogicNotImplementedYet) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<BlockStatePlacementOption> ret = new ArrayList<>();
|
||||
for (Face face : Face.VALUES) {
|
||||
if (Main.STRICT_Y && face == Face.UP) {
|
||||
continue; // TODO don't do this...
|
||||
}
|
||||
if (playerMustBeHorizontalFacingInOrderToPlaceMe == face.opposite()) { // obv, this won't happen if playerMustBeHorizontalFacing is null
|
||||
continue;
|
||||
}
|
||||
if (playerMustBeEntityFacingInOrderToPlaceMe == face) {
|
||||
continue;
|
||||
}
|
||||
if (mustBePlacedBottomToTop && face != Face.DOWN) {
|
||||
continue;
|
||||
}
|
||||
if (canOnlyPlaceAgainst != null && face != canOnlyPlaceAgainst) {
|
||||
continue;
|
||||
}
|
||||
Half overrideHalf = mustBePlacedAgainst;
|
||||
if (face == Face.DOWN) {
|
||||
if (mustBePlacedAgainst == Half.TOP) {
|
||||
continue;
|
||||
} else {
|
||||
overrideHalf = Half.EITHER;
|
||||
}
|
||||
}
|
||||
if (face == Face.UP) {
|
||||
if (mustBePlacedAgainst == Half.BOTTOM) {
|
||||
continue;
|
||||
} else {
|
||||
overrideHalf = Half.EITHER;
|
||||
}
|
||||
}
|
||||
ret.add(BlockStatePlacementOption.get(face, overrideHalf, Optional.ofNullable(playerMustBeHorizontalFacingInOrderToPlaceMe), Optional.ofNullable(playerMustBeEntityFacingInOrderToPlaceMe)));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PlaceAgainstData[] placeAgainstMe() {
|
||||
PlaceAgainstData[] data = new PlaceAgainstData[Face.NUM_FACES];
|
||||
if (!canPlaceAgainstMe) {
|
||||
return data;
|
||||
}
|
||||
for (int i = 0; i < Face.NUM_FACES; i++) {
|
||||
data[i] = placeAgainstFace(Face.VALUES[i]);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
protected PlaceAgainstData placeAgainstFace(Face face) {
|
||||
// TODO this makes the stair/slab assumption that the same half is the mustBePlacedAgainst as the faces offered for placement... counterexample is daylight sensor
|
||||
// note that this is actually correct behavior for stairs - you can place against the top half of a upside down stair and against the bottom half of a normal stair
|
||||
if (mustBePlacedAgainst == Half.TOP && face == Face.DOWN) {
|
||||
return null;
|
||||
}
|
||||
if (mustBePlacedAgainst == Half.BOTTOM && face == Face.UP) {
|
||||
return null;
|
||||
}
|
||||
return new PlaceAgainstData(face, face.vertical ? Half.EITHER : mustBePlacedAgainst, mustSneakWhenPlacingAgainstMe);
|
||||
}
|
||||
|
||||
/**
|
||||
* The idea here is that I codify all my assumptions in one place instead of having ad hoc checks absolutely everywhere
|
||||
* <p>
|
||||
* Example: in PlayerPhysics, I made an assumption that a block will never have a collision block taller than 1.5 blocks (e.g. like a fence)
|
||||
* When I wrote the code that assumed that, I also added a check here to make sure every block is like that.
|
||||
* If, in some future update to Minecraft, mojang adds a block that's even taller than a fence, it will be caught here immediately, with a comment saying "playerphysics assumes this is never true"
|
||||
* This way, I'll know immediately, instead of pathing randomly trying to do something impossible with that new block and it being really confusing and annoying.
|
||||
*/
|
||||
public void sanityCheck() {
|
||||
if (isAir()) {
|
||||
if (!howCanIBePlaced().isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (isFullyWalkableTop()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (collidesWithPlayer) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (mustBePlacedAgainst == null ^ isAir()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (howCanIBePlaced().isEmpty()) {
|
||||
if (mustBePlacedAgainst != null && !placementLogicNotImplementedYet) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (playerMustBeHorizontalFacingInOrderToPlaceMe != null || playerMustBeEntityFacingInOrderToPlaceMe != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (canOnlyPlaceAgainst != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (isMustSneakWhenPlacingAgainstMe() && mustBePlacedAgainst != Half.EITHER) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if ((playerMustBeHorizontalFacingInOrderToPlaceMe != null || playerMustBeEntityFacingInOrderToPlaceMe != null) && mustBePlacedAgainst == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (collisionHeightBlips != null && (collisionHeightBlips > Blip.TALLEST_BLOCK || collisionHeightBlips < 0)) { // playerphysics assumes this is never true
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (collidesWithPlayer ^ collisionHeightBlips != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (fullyWalkableTop && !collidesWithPlayer) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (canPlaceAgainstMe && !collidesWithPlayer) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (playerMustBeHorizontalFacingInOrderToPlaceMe != null && playerMustBeHorizontalFacingInOrderToPlaceMe.vertical) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.STRICT_Y && howCanIBePlaced().stream().anyMatch(opt -> opt.against == Face.UP)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
PlaceAgainstData[] data = placeAgainstMe();
|
||||
if (data.length != Face.NUM_FACES) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
boolean any = false;
|
||||
for (int i = 0; i < Face.NUM_FACES; i++) {
|
||||
if (data[i] != null) {
|
||||
if (data[i].against != Face.VALUES[i]) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!canPlaceAgainstMe) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
if (canPlaceAgainstMe && !any) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (collisionHeightBlips != null && !fakeLessThanFullHeight) {
|
||||
for (PlaceAgainstData d : data) {
|
||||
if (d == null) {
|
||||
continue;
|
||||
}
|
||||
d.streamRelativeToMyself().forEach(hit -> {
|
||||
if (hit.y > collisionHeightBlips * Blip.RATIO) {
|
||||
throw new IllegalStateException(d.against + " " + hit.y + " " + collisionHeightBlips * Blip.RATIO);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
new BlockStateCachedDataBuilder().sanityCheck();
|
||||
}
|
||||
}
|
||||
236
src/main/java/baritone/builder/BlockStatePlacementOption.java
Normal file
236
src/main/java/baritone/builder/BlockStatePlacementOption.java
Normal file
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* A plane against which this block state can be placed
|
||||
* <p>
|
||||
* For a normal block, this will be a full face of a block. In that case, this class is no more than an EnumFacing
|
||||
* <p>
|
||||
* For a block like a slab or a stair, this will contain the information that the placement must be against the top or bottom half of the face
|
||||
* <p>
|
||||
* For a block like a furnace, this will contain the information that the player must be facing a specific horizontal direction in order to get the desired orientation
|
||||
* <p>
|
||||
* For a block like a piston, dispenser, or observer, this will contain the information that be player must pass a combination of: specific relative eye coordinate, specific relative X Z, and specific horizontal facing
|
||||
*/
|
||||
public class BlockStatePlacementOption {
|
||||
|
||||
/**
|
||||
* e.g. a torch placed down on the ground is placed against the bottom of "the torch bounding box", so this would be DOWN for the torch
|
||||
*/
|
||||
public final Face against;
|
||||
public final Half half;
|
||||
public final Optional<Face> playerMustBeHorizontalFacing; // getHorizontalFacing
|
||||
/**
|
||||
* IMPORTANT this is the RAW getDirectionFromEntityLiving meaning that it is the OPPOSITE of getHorizontalFacing (when in the horizontal plane)
|
||||
*/
|
||||
public final Optional<Face> playerMustBeEntityFacing; // EnumFacing.getDirectionFromEntityLiving, used by piston, dispenser, observer
|
||||
|
||||
private BlockStatePlacementOption(Face against, Half half, Optional<Face> playerMustBeHorizontalFacing, Optional<Face> playerMustBeEntityFacing) {
|
||||
Objects.requireNonNull(against);
|
||||
Objects.requireNonNull(half);
|
||||
this.against = against;
|
||||
this.half = half;
|
||||
this.playerMustBeHorizontalFacing = playerMustBeHorizontalFacing;
|
||||
this.playerMustBeEntityFacing = playerMustBeEntityFacing;
|
||||
validate(against, half, playerMustBeHorizontalFacing, playerMustBeEntityFacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* This value must be greater than the face projections.
|
||||
* <p>
|
||||
* Otherwise certain stair placements would not work. This is verified in the test
|
||||
*/
|
||||
public static final double LOOSE_CENTER_DISTANCE = 0.15;
|
||||
|
||||
public List<Raytracer.Raytrace> computeTraceOptions(PlaceAgainstData placingAgainst, int playerSupportingX, int playerFeetBlips, int playerSupportingZ, PlayerVantage vantage, double blockReachDistance) {
|
||||
if (!BlockStateCachedData.possible(this, placingAgainst)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.DEBUG && placingAgainst.streamRelativeToPlace().noneMatch(hit -> hitOk(half, hit))) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
List<Vec2d> acceptableVantages = new ArrayList<>();
|
||||
Vec2d center = Vec2d.HALVED_CENTER.plus(playerSupportingX, playerSupportingZ);
|
||||
switch (vantage) {
|
||||
case LOOSE_CENTER: {
|
||||
acceptableVantages.add(center.plus(LOOSE_CENTER_DISTANCE, 0));
|
||||
acceptableVantages.add(center.plus(-LOOSE_CENTER_DISTANCE, 0));
|
||||
acceptableVantages.add(center.plus(0, LOOSE_CENTER_DISTANCE));
|
||||
acceptableVantages.add(center.plus(0, -LOOSE_CENTER_DISTANCE));
|
||||
// no break!
|
||||
} // FALLTHROUGH!
|
||||
case STRICT_CENTER: {
|
||||
acceptableVantages.add(center);
|
||||
break;
|
||||
}
|
||||
case SNEAK_BACKPLACE: {
|
||||
if (playerSupportingX != against.x || playerSupportingZ != against.z) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// in a sneak backplace, there is exactly one location where the player will be
|
||||
acceptableVantages.add(Vec2d.HALVED_CENTER.plus(0.25 * against.x, 0.25 * against.z));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// direction from placed block to place-against block = this.against
|
||||
long blockPlacedAt = 0;
|
||||
long placeAgainstPos = against.offset(blockPlacedAt);
|
||||
|
||||
return sanityCheckTraces(acceptableVantages
|
||||
.stream()
|
||||
.map(playerEyeXZ -> new Vec3d(playerEyeXZ.x, Blip.playerEyeFromFeetBlips(playerFeetBlips, placingAgainst.mustSneak), playerEyeXZ.z))
|
||||
.flatMap(eye ->
|
||||
placingAgainst.streamRelativeToPlace()
|
||||
.filter(hit -> hitOk(half, hit))
|
||||
.filter(hit -> eye.distSq(hit) < blockReachDistance * blockReachDistance)
|
||||
.filter(hit -> directionOk(eye, hit))
|
||||
.<Supplier<Optional<Raytracer.Raytrace>>>map(hit -> () -> Raytracer.runTrace(eye, placeAgainstPos, against.opposite(), hit))
|
||||
)
|
||||
.collect(Collectors.toList())
|
||||
.parallelStream() // wrap it like this because flatMap forces .sequential() on the interior child stream, defeating the point
|
||||
.map(Supplier::get)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.sorted()
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static boolean hitOk(Half half, Vec3d hit) {
|
||||
if (half == Half.EITHER) {
|
||||
return true;
|
||||
} else if (hit.y == 0.1) {
|
||||
return half == Half.BOTTOM;
|
||||
} else if (hit.y == 0.5) {
|
||||
return false; // ambiguous, so force it to pick either down or up
|
||||
} else if (hit.y == 0.9) {
|
||||
return half == Half.TOP;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In EnumFacing.getDirectionFromEntityLiving, it checks if the player feet is within 2 blocks of the center of the block to be placed.
|
||||
* Normally, this is a nonissue, but a problem arises because we are considering hypothetical placements where the player stands at the exact +0.5,+0.5 center of a block.
|
||||
* In that case, it's possible for our hypothetical to have the player at precisely 2 blocks away, i.e. precisely on the edge of this condition being true or false.
|
||||
* For that reason, we treat those exact cases as "ambiguous". So, if the distance is within this tolerance of 2 (so, 1.99 to 2.01), we treat it as a "could go either way",
|
||||
* because when we really get there in-game, floating point inaccuracy could indeed actually make it go either way.
|
||||
*/
|
||||
private static final double ENTITY_FACING_TOLERANCE = 0.01;
|
||||
|
||||
private boolean directionOk(Vec3d eye, Vec3d hit) {
|
||||
if (playerMustBeHorizontalFacing.isPresent()) {
|
||||
return eye.flatDirectionTo(hit) == playerMustBeHorizontalFacing.get();
|
||||
}
|
||||
if (playerMustBeEntityFacing.isPresent()) { // handle piston, dispenser, dropper, observer
|
||||
if (!hit.inOriginUnitVoxel()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Face entFace = playerMustBeEntityFacing.get();
|
||||
// see EnumFacing.getDirectionFromEntityLiving
|
||||
double dx = Math.abs(eye.x - 0.5); // TODO this is changed between 1.12 and 1.19, in 1.19 this should be eye.x-hit.x
|
||||
double dz = Math.abs(eye.z - 0.5);
|
||||
if (dx < 2 - ENTITY_FACING_TOLERANCE && dz < 2 - ENTITY_FACING_TOLERANCE) { // < 1.99
|
||||
if (eye.y < 0) { // eye below placement level = it will be facing down, so this is only okay if we want that
|
||||
return entFace == Face.DOWN;
|
||||
}
|
||||
if (eye.y > 2) { // same for up, if y>2 then it will be facing up
|
||||
return entFace == Face.UP;
|
||||
}
|
||||
} else if (!(dx > 2 + ENTITY_FACING_TOLERANCE || dz > 2 + ENTITY_FACING_TOLERANCE)) { // > 2.01
|
||||
// this is the ambiguous case, because we are neither unambiguously both-within-2 (previous case), nor unambiguously either-above-two (this elseif condition).
|
||||
// UP/DOWN are impossible, but that's caught by flat check
|
||||
if (eye.y < 0 || eye.y > 2) { // this check is okay because player eye height is not an even multiple of blips, therefore there's no way for it to == 0 or == 2, so using > and < is safe
|
||||
return false; // anything that could cause up/down instead of horizontal is also not allowed sadly
|
||||
}
|
||||
} // else we are in unambiguous either-above-two, putting us in simple horizontal mode, so fallthrough to flat condition is correct, yay
|
||||
return eye.flatDirectionTo(hit) == entFace.opposite();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static BlockStatePlacementOption get(Face against, Half half, Optional<Face> playerMustBeHorizontalFacing, Optional<Face> playerMustBeEntityFacing) {
|
||||
BlockStatePlacementOption ret = PLACEMENT_OPTION_SINGLETON_CACHE[against.index][half.ordinal()][Face.OPTS.indexOf(playerMustBeHorizontalFacing)][Face.OPTS.indexOf(playerMustBeEntityFacing)];
|
||||
if (ret == null) {
|
||||
throw new IllegalStateException(against + " " + half + " " + playerMustBeHorizontalFacing + " " + playerMustBeEntityFacing);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final BlockStatePlacementOption[][][][] PLACEMENT_OPTION_SINGLETON_CACHE;
|
||||
|
||||
static {
|
||||
PLACEMENT_OPTION_SINGLETON_CACHE = new BlockStatePlacementOption[Face.NUM_FACES][Half.values().length][Face.OPTS.size()][Face.OPTS.size()];
|
||||
for (Face against : Face.VALUES) {
|
||||
for (Half half : Half.values()) {
|
||||
for (Optional<Face> horizontalFacing : Face.OPTS) {
|
||||
for (Optional<Face> entityFacing : Face.OPTS) {
|
||||
try {
|
||||
PLACEMENT_OPTION_SINGLETON_CACHE[against.index][half.ordinal()][Face.OPTS.indexOf(horizontalFacing)][Face.OPTS.indexOf(entityFacing)] = new BlockStatePlacementOption(against, half, horizontalFacing, entityFacing);
|
||||
} catch (RuntimeException ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validate(Face against, Half half, Optional<Face> playerMustBeHorizontalFacing, Optional<Face> playerMustBeEntityFacing) {
|
||||
if (playerMustBeEntityFacing.isPresent() && playerMustBeHorizontalFacing.isPresent()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (against.vertical && half != Half.EITHER) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (Main.STRICT_Y && against == Face.UP) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
playerMustBeHorizontalFacing.ifPresent(face -> {
|
||||
if (face.vertical) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (face == against.opposite()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
});
|
||||
playerMustBeEntityFacing.ifPresent(face -> {
|
||||
if (half != Half.EITHER) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (against == face) { // impossible because EnumFacing inverts the horizontal facing AND because the down and up require the eye to be <0 and >2 respectively
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static List<Raytracer.Raytrace> sanityCheckTraces(List<Raytracer.Raytrace> traces) {
|
||||
if (Main.DEBUG && traces.stream().mapToDouble(Raytracer.Raytrace::centerDistApprox).distinct().count() > 2) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return traces;
|
||||
}
|
||||
}
|
||||
98
src/main/java/baritone/builder/Bounds.java
Normal file
98
src/main/java/baritone/builder/Bounds.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
/**
|
||||
* An area.
|
||||
* <p>
|
||||
* More likely than not a cuboid, but who knows :)
|
||||
*/
|
||||
public interface Bounds {
|
||||
|
||||
@FunctionalInterface
|
||||
interface BoundsIntsConsumer {
|
||||
|
||||
void consume(int x, int y, int z);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface BoundsLongConsumer {
|
||||
|
||||
void consume(long pos);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface BoundsIntAndLongConsumer {
|
||||
|
||||
void consume(int x, int y, int z, long pos);
|
||||
}
|
||||
|
||||
void forEach(BoundsIntsConsumer consumer);
|
||||
|
||||
void forEach(BoundsLongConsumer consumer);
|
||||
|
||||
void forEach(BoundsIntAndLongConsumer consumer);
|
||||
|
||||
// there is no "forEach" for the "index" lookup, because it is always going to just be a loop from 0 to bounds.volume()-1
|
||||
|
||||
boolean inRange(int x, int y, int z);
|
||||
|
||||
default boolean inRangePos(long pos) {
|
||||
return inRange(BetterBlockPos.XfromLong(pos), BetterBlockPos.YfromLong(pos), BetterBlockPos.ZfromLong(pos));
|
||||
}
|
||||
|
||||
int volume();
|
||||
|
||||
// this must be implemented EXTREMELY efficiently. no integer division allowed! even a hashmap lookup is borderline.
|
||||
int toIndex(int x, int y, int z); // easy to implement for cuboid, harder for more complicated shapes
|
||||
|
||||
default int toIndex(long pos) {
|
||||
return toIndex(BetterBlockPos.XfromLong(pos), BetterBlockPos.YfromLong(pos), BetterBlockPos.ZfromLong(pos));
|
||||
}
|
||||
|
||||
static void sanityCheckConnectedness(Bounds bounds) {
|
||||
LongOpenHashSet all = new LongOpenHashSet();
|
||||
bounds.forEach(all::add);
|
||||
if (all.size() != bounds.volume()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
long any = all.iterator().nextLong();
|
||||
LongOpenHashSet reachable = new LongOpenHashSet();
|
||||
LongArrayFIFOQueue queue = new LongArrayFIFOQueue();
|
||||
queue.enqueue(any);
|
||||
while (!queue.isEmpty()) {
|
||||
long pos = queue.dequeueLong();
|
||||
if (bounds.inRangePos(pos) && reachable.add(pos)) {
|
||||
for (Face face : Face.VALUES) {
|
||||
queue.enqueueFirst(face.offset(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
LongIterator it = all.iterator();
|
||||
while (it.hasNext()) {
|
||||
if (!reachable.contains(it.nextLong())) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
120
src/main/java/baritone/builder/Column.java
Normal file
120
src/main/java/baritone/builder/Column.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static baritone.api.utils.BetterBlockPos.Y_MASK;
|
||||
import static baritone.api.utils.BetterBlockPos.Y_SHIFT;
|
||||
|
||||
/**
|
||||
* A mutable class representing a 1x6x1 column of blocks
|
||||
* <p>
|
||||
* Mutable because allocations are not on the table for the core solver loop
|
||||
*/
|
||||
public class Column {
|
||||
|
||||
public long pos; // TODO this isn't set?
|
||||
//public BlockStateCachedData underUnderneath;
|
||||
public BlockStateCachedData underneath;
|
||||
public BlockStateCachedData feet;
|
||||
public BlockStateCachedData head;
|
||||
public BlockStateCachedData above;
|
||||
public BlockStateCachedData aboveAbove;
|
||||
public PlayerPhysics.VoxelResidency voxelResidency;
|
||||
public Integer feetBlips;
|
||||
|
||||
public void initFrom(long pos, WorldState worldState, SolverEngineInput engineInput) {
|
||||
this.pos = pos;
|
||||
//this.underUnderneath = engineInput.at((pos + DOWN_2) & BetterBlockPos.POST_ADDITION_MASK, worldState);
|
||||
this.underneath = engineInput.at((pos + DOWN_1) & BetterBlockPos.POST_ADDITION_MASK, worldState);
|
||||
this.feet = engineInput.at(pos, worldState);
|
||||
this.head = engineInput.at((pos + UP_1) & BetterBlockPos.POST_ADDITION_MASK, worldState);
|
||||
this.above = engineInput.at((pos + UP_2) & BetterBlockPos.POST_ADDITION_MASK, worldState);
|
||||
this.aboveAbove = engineInput.at((pos + UP_3) & BetterBlockPos.POST_ADDITION_MASK, worldState);
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
this.voxelResidency = PlayerPhysics.canPlayerStand(underneath, feet);
|
||||
this.feetBlips = boxNullable(PlayerPhysics.determinePlayerRealSupportLevel(underneath, feet, voxelResidency));
|
||||
if (feetBlips != null && !playerCanExistAtFootBlip(feetBlips)) { // TODO is this the correct way to handle head collision?
|
||||
voxelResidency = PlayerPhysics.VoxelResidency.IMPOSSIBLE_WITHOUT_SUFFOCATING; // TODO this is a misuse of this enum value i think
|
||||
feetBlips = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean playerCanExistAtFootBlip(int blipWithinFeet) {
|
||||
if (head.collidesWithPlayer) {
|
||||
return false;
|
||||
}
|
||||
if (PlayerPhysics.protrudesIntoThirdBlock(blipWithinFeet) && above.collidesWithPlayer) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean okToSneakIntoHereAtHeight(int blips) {
|
||||
return playerCanExistAtFootBlip(blips) // no collision at head level
|
||||
&& PlayerPhysics.highestCollision(underneath, feet) < blips; // and at foot level, we only collide strictly below where the feet will be
|
||||
}
|
||||
|
||||
public boolean standing() {
|
||||
return feetBlips != null;
|
||||
}
|
||||
|
||||
public static final long DOWN_3 = (Y_MASK - 2) << Y_SHIFT;
|
||||
public static final long DOWN_2 = (Y_MASK - 1) << Y_SHIFT;
|
||||
public static final long DOWN_1 = Y_MASK << Y_SHIFT;
|
||||
public static final long UP_1 = 1L << Y_SHIFT;
|
||||
public static final long UP_2 = 2L << Y_SHIFT;
|
||||
public static final long UP_3 = 3L << Y_SHIFT;
|
||||
|
||||
static {
|
||||
if (DOWN_3 != BetterBlockPos.toLong(0, -3, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (DOWN_2 != BetterBlockPos.toLong(0, -2, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (DOWN_1 != BetterBlockPos.toLong(0, -1, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (UP_1 != BetterBlockPos.toLong(0, 1, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (UP_2 != BetterBlockPos.toLong(0, 2, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (UP_3 != BetterBlockPos.toLong(0, 3, 0)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Integer[] BLIPS = IntStream.range(-1, Blip.FULL_BLOCK).boxed().toArray(Integer[]::new);
|
||||
|
||||
static {
|
||||
BLIPS[0] = null;
|
||||
}
|
||||
|
||||
private static Integer boxNullable(int blips) {
|
||||
return BLIPS[blips + 1]; // map -1 to [0] which is null
|
||||
}
|
||||
}
|
||||
94
src/main/java/baritone/builder/CountingSurface.java
Normal file
94
src/main/java/baritone/builder/CountingSurface.java
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public class CountingSurface extends NavigableSurface {
|
||||
public CountingSurface(int x, int y, int z) {
|
||||
super(x, y, z, Attachment::new, $ -> new Attachment());
|
||||
}
|
||||
|
||||
private static class Attachment {
|
||||
public final int surfaceSize;
|
||||
|
||||
public Attachment(Object a, Object b) {
|
||||
this((Attachment) a, (Attachment) b);
|
||||
}
|
||||
|
||||
public Attachment(Attachment a, Attachment b) {
|
||||
this.surfaceSize = a.surfaceSize + b.surfaceSize;
|
||||
}
|
||||
|
||||
public Attachment() {
|
||||
this.surfaceSize = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) { // used as performance optimization in RedBlackNode to avoid augmenting unchanged attachments
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Attachment)) {
|
||||
return false;
|
||||
}
|
||||
Attachment that = (Attachment) o;
|
||||
return surfaceSize == that.surfaceSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return surfaceSize;
|
||||
}
|
||||
}
|
||||
|
||||
public OptionalInt surfaceSize(BetterBlockPos pos) { // how big is the navigable surface from here? how many distinct coordinates can i walk to (in the future, the augmentation will probably have a list of those coordinates or something?)
|
||||
Object data = getComponentAugmentation(pos);
|
||||
if (data != null) { // i disagree with the intellij suggestion here i think it makes it worse
|
||||
return OptionalInt.of(((Attachment) data).surfaceSize);
|
||||
} else {
|
||||
return OptionalInt.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public int requireSurfaceSize(int x, int y, int z) {
|
||||
return surfaceSize(new BetterBlockPos(x, y, z)).getAsInt();
|
||||
}
|
||||
|
||||
private void placeOrRemoveBlock(BetterBlockPos where, boolean place) {
|
||||
setBlock(where.toLong(), place ? FakeStates.SOLID : FakeStates.AIR);
|
||||
}
|
||||
|
||||
public void placeBlock(BetterBlockPos where) {
|
||||
placeOrRemoveBlock(where, true);
|
||||
}
|
||||
|
||||
public void placeBlock(int x, int y, int z) {
|
||||
placeBlock(new BetterBlockPos(x, y, z));
|
||||
}
|
||||
|
||||
public void removeBlock(BetterBlockPos where) {
|
||||
placeOrRemoveBlock(where, false);
|
||||
}
|
||||
|
||||
public void removeBlock(int x, int y, int z) {
|
||||
removeBlock(new BetterBlockPos(x, y, z));
|
||||
}
|
||||
}
|
||||
165
src/main/java/baritone/builder/CuboidBounds.java
Normal file
165
src/main/java/baritone/builder/CuboidBounds.java
Normal file
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
|
||||
/**
|
||||
* Bounding box of a cuboid
|
||||
* <p>
|
||||
* Basically just a lot of helper util methods lol
|
||||
*/
|
||||
public class CuboidBounds implements Bounds {
|
||||
|
||||
public final int sizeX;
|
||||
public final int sizeY;
|
||||
public final int sizeZ;
|
||||
private final int sizeXMinusOne;
|
||||
private final int sizeYMinusOne;
|
||||
private final int sizeZMinusOne;
|
||||
public final int size;
|
||||
private final int sizeMinusOne;
|
||||
|
||||
public CuboidBounds(int sizeX, int sizeY, int sizeZ) {
|
||||
this.sizeX = sizeX;
|
||||
this.sizeY = sizeY;
|
||||
this.sizeZ = sizeZ;
|
||||
this.sizeXMinusOne = sizeX - 1;
|
||||
this.sizeYMinusOne = sizeY - 1;
|
||||
this.sizeZMinusOne = sizeZ - 1;
|
||||
this.size = sizeX * sizeY * sizeZ;
|
||||
this.sizeMinusOne = size - 1;
|
||||
if (Main.SLOW_DEBUG) {
|
||||
sanityCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int toIndex(int x, int y, int z) {
|
||||
if (Main.DEBUG && !inRange(x, y, z)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return (x * sizeY + y) * sizeZ + z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inRange(int x, int y, int z) {
|
||||
return inRangeBranchless(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int volume() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean inRangeBranchy(int x, int y, int z) { // benchmarked: approx 4x slower than branchless
|
||||
return (x >= 0) && (x < sizeX) && (y >= 0) && (y < sizeY) && (z >= 0) && (z < sizeZ);
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless(int x, int y, int z) {
|
||||
return (x | y | z | (sizeXMinusOne - x) | (sizeYMinusOne - y) | (sizeZMinusOne - z)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless2(int x, int y, int z) {
|
||||
return (x | y | z | ((sizeX - 1) - x) | ((sizeY - 1) - y) | ((sizeZ - 1) - z)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless3(int x, int y, int z) {
|
||||
return (x | y | z | (sizeX - (x + 1)) | (sizeY - (y + 1)) | (sizeZ - (z + 1))) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeBranchless4(int x, int y, int z) {
|
||||
return (x | y | z | ((sizeX - x) - 1) | ((sizeY - y) - 1) | ((sizeZ - z) - 1)) >= 0;
|
||||
}
|
||||
|
||||
public boolean inRangeIndex(int index) {
|
||||
return (index | (sizeMinusOne - index)) >= 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void forEach(BoundsIntsConsumer consumer) {
|
||||
int sizeX = this.sizeX, sizeY = this.sizeY, sizeZ = this.sizeZ;
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
consumer.consume(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BoundsLongConsumer consumer) {
|
||||
int sizeX = this.sizeX, sizeY = this.sizeY, sizeZ = this.sizeZ;
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
consumer.consume(BetterBlockPos.toLong(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(BoundsIntAndLongConsumer consumer) {
|
||||
int sizeX = this.sizeX, sizeY = this.sizeY, sizeZ = this.sizeZ;
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
consumer.consume(x, y, z, BetterBlockPos.toLong(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void sanityCheck() {
|
||||
if (sizeY > 256) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
long chk = ((long) sizeX) * ((long) sizeY) * ((long) sizeZ);
|
||||
if (chk != (long) size) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int index = 0;
|
||||
for (int x = 0; x < sizeX; x++) {
|
||||
for (int y = 0; y < sizeY; y++) {
|
||||
for (int z = 0; z < sizeZ; z++) {
|
||||
if (!inRange(x, y, z)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (toIndex(x, y, z) != index) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index != size) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (inRange(-1, 0, 0) || inRange(0, -1, 0) || inRange(0, 0, -1)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (inRange(sizeX, 0, 0) || inRange(0, sizeY, 0) || inRange(0, 0, sizeZ)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Bounds.sanityCheckConnectedness(this);
|
||||
}
|
||||
}
|
||||
126
src/main/java/baritone/builder/DependencyGraphAnalyzer.java
Normal file
126
src/main/java/baritone/builder/DependencyGraphAnalyzer.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Some initial checks on the schematic
|
||||
* <p>
|
||||
* The intent is to provide reasonable error messages, which we can do by catching common cases as early as possible
|
||||
* <p>
|
||||
* So that it's an actual comprehensible error **that tells you where the problem is** instead of just "pathing failed"
|
||||
*/
|
||||
public class DependencyGraphAnalyzer {
|
||||
|
||||
/**
|
||||
* Just a simple check to make sure that everything is placeable.
|
||||
* <p>
|
||||
* Mostly for my own testing because every Minecraft block is placeable, and if the schematic has something weird
|
||||
* and funky, it should be caught earlier anyway.
|
||||
*/
|
||||
public static void prevalidate(PlaceOrderDependencyGraph graph) {
|
||||
List<String> locs = new ArrayList<>();
|
||||
graph.bounds().forEach(pos -> {
|
||||
if (graph.airTreatedAsScaffolding(pos)) {
|
||||
// completely fine to, for example, have an air pocket with non-place-against-able stuff all around it
|
||||
return;
|
||||
}
|
||||
for (Face face : Face.VALUES) {
|
||||
if (graph.incomingEdge(pos, face)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
locs.add(BetterBlockPos.fromLong(pos).toString());
|
||||
});
|
||||
if (!locs.isEmpty()) {
|
||||
throw new IllegalStateException("Unplaceable from any side: " + cuteTrim(locs));
|
||||
}
|
||||
// TODO instead of cuteTrim have a like SpecificBlockPositionsImpossibleException that this throws, and then later, an enclosing function can give the option to reset those locations to air
|
||||
}
|
||||
|
||||
/**
|
||||
* Search from all exterior nodes breadth-first to ensure that, theoretically, everything is reachable.
|
||||
* <p>
|
||||
* This is NOT a sufficient test, because later we are going to ensure that everything is scaffold-placeable which
|
||||
* requires a single root node at the bottom.
|
||||
*/
|
||||
public static void prevalidateExternalToInteriorSearch(PlaceOrderDependencyGraph graph) {
|
||||
LongList edgeBegins = new LongArrayList();
|
||||
graph.bounds().forEach(pos -> {
|
||||
for (Face face : Face.VALUES) {
|
||||
if (graph.incomingEdgePermitExterior(pos, face) && !graph.incomingEdge(pos, face)) {
|
||||
// this block is placeable from the exterior of the schematic!
|
||||
edgeBegins.add(pos); // this will intentionally put the top of the schematic at the front
|
||||
}
|
||||
}
|
||||
});
|
||||
LongSet reachable = searchGraph(edgeBegins, graph::outgoingEdge);
|
||||
List<String> locs = new ArrayList<>();
|
||||
graph.bounds().forEach(pos -> {
|
||||
if (graph.airTreatedAsScaffolding(pos)) {
|
||||
// same as previous validation
|
||||
return;
|
||||
}
|
||||
if (!reachable.contains(pos)) {
|
||||
locs.add(BetterBlockPos.fromLong(pos).toString());
|
||||
}
|
||||
});
|
||||
if (!locs.isEmpty()) {
|
||||
throw new IllegalStateException("Placeable, in theory, but in practice there is no valid path from the exterior to it: " + cuteTrim(locs));
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface EdgeTester {
|
||||
boolean hasEdge(long from, Face dir);
|
||||
}
|
||||
|
||||
public static LongSet searchGraph(LongCollection origin, EdgeTester edgeTester) {
|
||||
LongOpenHashSet reachable = new LongOpenHashSet();
|
||||
LongArrayFIFOQueue queue = new LongArrayFIFOQueue(origin.size());
|
||||
LongIterator it = origin.iterator();
|
||||
while (it.hasNext()) {
|
||||
queue.enqueue(it.nextLong());
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
long pos = queue.dequeueLong();
|
||||
if (reachable.add(pos)) {
|
||||
for (Face face : Face.VALUES) {
|
||||
if (edgeTester.hasEdge(pos, face)) {
|
||||
queue.enqueueFirst(face.offset(pos));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reachable;
|
||||
}
|
||||
|
||||
private static List<String> cuteTrim(List<String> pos) {
|
||||
if (pos.size() <= 20) {
|
||||
return pos;
|
||||
}
|
||||
pos = pos.subList(0, 20);
|
||||
pos.set(pos.size() - 1, "...");
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import it.unimi.dsi.fastutil.HashCommon;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.*;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A mutable addition of scaffolding blocks to a schematic
|
||||
* <p>
|
||||
* Contains a set of coordinates that are "air" in the schematic, but we are going to put scaffolding throwaway blocks there
|
||||
* <p>
|
||||
* Maintains and incrementally updates a collapsed dependency graph, which is the block placement dependency graph reduced to directed acyclic graph form by way of collapsing all strongly connected components into single nodes
|
||||
* <p>
|
||||
* Helper class, only intended to be used within Scaffolder
|
||||
*/
|
||||
public class DependencyGraphScaffoldingOverlay {
|
||||
|
||||
private final PlaceOrderDependencyGraph delegate;
|
||||
private final LongOpenHashSet scaffoldingAdded;
|
||||
private final CollapsedDependencyGraph collapsedGraph;
|
||||
|
||||
public DependencyGraphScaffoldingOverlay(PlaceOrderDependencyGraph delegate) {
|
||||
this.delegate = delegate;
|
||||
this.scaffoldingAdded = new LongOpenHashSet();
|
||||
this.collapsedGraph = new CollapsedDependencyGraph(TarjansAlgorithm.run(this));
|
||||
//System.out.println("Num components: " + collapsedGraph.components.size());
|
||||
}
|
||||
|
||||
public boolean outgoingEdge(long pos, Face face) {
|
||||
if (overrideOff(pos)) {
|
||||
return false;
|
||||
}
|
||||
if (overrideOff(face.offset(pos))) {
|
||||
return false;
|
||||
}
|
||||
return delegate.outgoingEdge(pos, face);
|
||||
}
|
||||
|
||||
public boolean incomingEdge(long pos, Face face) {
|
||||
if (overrideOff(pos)) {
|
||||
return false;
|
||||
}
|
||||
if (overrideOff(face.offset(pos))) {
|
||||
return false;
|
||||
}
|
||||
return delegate.incomingEdge(pos, face);
|
||||
}
|
||||
|
||||
public boolean hypotheticalScaffoldingIncomingEdge(long pos, Face face) {
|
||||
return delegate.incomingEdge(pos, face);
|
||||
}
|
||||
|
||||
public Bounds bounds() {
|
||||
return delegate.bounds();
|
||||
}
|
||||
|
||||
private boolean overrideOff(long pos) {
|
||||
return bounds().inRangePos(pos) && air(pos);
|
||||
}
|
||||
|
||||
public boolean real(long pos) {
|
||||
return !air(pos);
|
||||
}
|
||||
|
||||
public void forEachReal(Bounds.BoundsLongConsumer consumer) {
|
||||
bounds().forEach(pos -> {
|
||||
if (real(pos)) {
|
||||
consumer.consume(pos);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean air(long pos) {
|
||||
return delegate.airTreatedAsScaffolding(pos) && !scaffoldingAdded.contains(pos);
|
||||
}
|
||||
|
||||
public void enable(long pos) {
|
||||
if (!delegate.airTreatedAsScaffolding(pos)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (!scaffoldingAdded.add(pos)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
collapsedGraph.incrementalUpdate(pos);
|
||||
if (Main.SLOW_DEBUG) {
|
||||
//System.out.println(collapsedGraph.posToComponent.containsKey(pos) + " " + scaffoldingAdded.contains(pos) + " " + real(pos));
|
||||
recheckEntireCollapsedGraph();
|
||||
}
|
||||
}
|
||||
|
||||
public void recheckEntireCollapsedGraph() {
|
||||
checkEquality(collapsedGraph, new CollapsedDependencyGraph(TarjansAlgorithm.run(this)));
|
||||
//System.out.println("Checked equality");
|
||||
//System.out.println("Num connected components: " + collapsedGraph.components.size());
|
||||
}
|
||||
|
||||
public LongSets.UnmodifiableSet scaffolding() {
|
||||
return (LongSets.UnmodifiableSet) LongSets.unmodifiable(scaffoldingAdded);
|
||||
}
|
||||
|
||||
public BlockStateCachedData data(long pos) {
|
||||
if (Main.DEBUG && !real(pos)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return delegate.data(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remember that this returns a collapsed graph that will be updated in-place as positions are enabled. It does not return a copy.
|
||||
*/
|
||||
public CollapsedDependencyGraph getCollapsedGraph() {
|
||||
return collapsedGraph;
|
||||
}
|
||||
|
||||
public class CollapsedDependencyGraph {
|
||||
|
||||
private int nextComponentID;
|
||||
private final Int2ObjectOpenHashMap<CollapsedDependencyGraphComponent> components;
|
||||
private final Long2ObjectOpenHashMap<CollapsedDependencyGraphComponent> posToComponent;
|
||||
|
||||
private CollapsedDependencyGraph(TarjansAlgorithm.TarjansResult partition) {
|
||||
components = new Int2ObjectOpenHashMap<>();
|
||||
for (int i = 0; i < partition.numComponents(); i++) {
|
||||
addComponent();
|
||||
}
|
||||
posToComponent = new Long2ObjectOpenHashMap<>();
|
||||
forEachReal(pos -> {
|
||||
CollapsedDependencyGraphComponent component = components.get(partition.getComponent(pos));
|
||||
component.positions.add(pos);
|
||||
posToComponent.put(pos, component);
|
||||
});
|
||||
forEachReal(pos -> {
|
||||
for (Face face : Face.VALUES) {
|
||||
if (outgoingEdge(pos, face)) {
|
||||
CollapsedDependencyGraphComponent src = posToComponent.get(pos);
|
||||
CollapsedDependencyGraphComponent dst = posToComponent.get(face.offset(pos));
|
||||
if (src != dst) {
|
||||
src.outgoingEdges.add(dst);
|
||||
dst.incomingEdges.add(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (Main.SLOW_DEBUG) {
|
||||
sanityCheck();
|
||||
}
|
||||
}
|
||||
|
||||
public Int2ObjectMap<CollapsedDependencyGraphComponent> getComponents() {
|
||||
return Int2ObjectMaps.unmodifiable(components);
|
||||
}
|
||||
|
||||
public Long2ObjectMap<CollapsedDependencyGraphComponent> getComponentLocations() {
|
||||
return Long2ObjectMaps.unmodifiable(posToComponent);
|
||||
}
|
||||
|
||||
public OptionalInt lastComponentID() {
|
||||
return nextComponentID == 0 ? OptionalInt.empty() : OptionalInt.of(nextComponentID - 1);
|
||||
}
|
||||
|
||||
private CollapsedDependencyGraphComponent addComponent() {
|
||||
CollapsedDependencyGraphComponent component = new CollapsedDependencyGraphComponent(nextComponentID);
|
||||
components.put(component.id, component);
|
||||
nextComponentID++;
|
||||
return component;
|
||||
}
|
||||
|
||||
private CollapsedDependencyGraphComponent mergeInto(CollapsedDependencyGraphComponent child, CollapsedDependencyGraphComponent parent) {
|
||||
if (child.deleted() || parent.deleted()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (child == parent) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (child.positions.size() > parent.positions.size() || (child.positions.size() == parent.positions.size() && child.id < parent.id)) {
|
||||
return mergeInto(parent, child);
|
||||
}
|
||||
if (Main.DEBUG) {
|
||||
//System.out.println("Merging " + child.index + " into " + parent.index);
|
||||
}
|
||||
child.incomingEdges.forEach(intoChild -> {
|
||||
intoChild.outgoingEdges.remove(child);
|
||||
if (intoChild == parent) {
|
||||
return;
|
||||
}
|
||||
intoChild.outgoingEdges.add(parent);
|
||||
parent.incomingEdges.add(intoChild);
|
||||
});
|
||||
child.outgoingEdges.forEach(outOfChild -> {
|
||||
outOfChild.incomingEdges.remove(child);
|
||||
if (outOfChild == parent) {
|
||||
return;
|
||||
}
|
||||
outOfChild.incomingEdges.add(parent);
|
||||
parent.outgoingEdges.add(outOfChild);
|
||||
});
|
||||
parent.positions.addAll(child.positions);
|
||||
LongIterator it = child.positions.iterator();
|
||||
while (it.hasNext()) {
|
||||
long pos = it.nextLong();
|
||||
posToComponent.put(pos, parent);
|
||||
}
|
||||
components.remove(child.id);
|
||||
child.deletedInto = parent;
|
||||
// TODO clear and trim child.positions? maybe unnecessary because nothing should retain a reference to child for longer than a moment
|
||||
return parent;
|
||||
}
|
||||
|
||||
private void incrementalEdgeAddition(long src, long dst) { // TODO put in a param here like "bias" that determines which of the two components gets to eat the other if they are of the same size? could help with the scaffolder if we could guarantee that no new components would be added without cause
|
||||
CollapsedDependencyGraphComponent srcComponent = posToComponent.get(src);
|
||||
CollapsedDependencyGraphComponent dstComponent = posToComponent.get(dst);
|
||||
if (srcComponent == dstComponent) { // already strongly connected
|
||||
return;
|
||||
}
|
||||
if (srcComponent.outgoingEdges.contains(dstComponent)) { // we already know about this edge
|
||||
return;
|
||||
}
|
||||
List<List<CollapsedDependencyGraphComponent>> paths = new ArrayList<>();
|
||||
if (!srcComponent.incomingEdges.isEmpty() && pathExists(dstComponent, srcComponent, paths) > 0) {
|
||||
CollapsedDependencyGraphComponent survivor = srcComponent;
|
||||
for (List<CollapsedDependencyGraphComponent> path : paths) {
|
||||
if (path.get(0) != srcComponent || path.get(path.size() - 1) != dstComponent) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
for (int i = 1; i < path.size(); i++) {
|
||||
if (path.get(i).deleted() || path.get(i) == survivor) { // two different paths to the same goal, only merge the components once, so skip is already survivor or deleted
|
||||
continue;
|
||||
}
|
||||
survivor = mergeInto(survivor, path.get(i));
|
||||
}
|
||||
}
|
||||
// can't run sanityCheck after each mergeInto because it could leave a 2-way connection between components as an intermediary state while collapsing
|
||||
if (Main.SLOW_DEBUG) {
|
||||
sanityCheck();
|
||||
}
|
||||
return;
|
||||
}
|
||||
srcComponent.outgoingEdges.add(dstComponent);
|
||||
dstComponent.incomingEdges.add(srcComponent);
|
||||
}
|
||||
|
||||
private int pathExists(CollapsedDependencyGraphComponent src, CollapsedDependencyGraphComponent dst, List<List<CollapsedDependencyGraphComponent>> paths) {
|
||||
if (src == dst) {
|
||||
paths.add(new ArrayList<>(Collections.singletonList(src)));
|
||||
return 1;
|
||||
}
|
||||
if (Main.DEBUG && dst.incomingEdges.isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.STRICT_Y && src.y() > dst.y()) {
|
||||
return 0; // no downward edges in strict_y mode
|
||||
}
|
||||
int numAdded = 0;
|
||||
for (CollapsedDependencyGraphComponent nxt : src.outgoingEdges) {
|
||||
int cnt = pathExists(nxt, dst, paths);
|
||||
if (cnt > 0) {
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
paths.get(paths.size() - 1 - i).add(src);
|
||||
}
|
||||
numAdded += cnt;
|
||||
}
|
||||
}
|
||||
return numAdded;
|
||||
}
|
||||
|
||||
private void incrementalUpdate(long pos) {
|
||||
if (posToComponent.containsKey(pos)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
CollapsedDependencyGraphComponent component = addComponent();
|
||||
component.positions.add(pos);
|
||||
posToComponent.put(pos, component);
|
||||
if (Main.SLOW_DEBUG) {
|
||||
sanityCheck();
|
||||
}
|
||||
//System.out.println("Incremental " + pos);
|
||||
//System.out.println("Pos core " + posToComponent.get(pos).index);
|
||||
for (Face face : Face.VALUES) {
|
||||
if (outgoingEdge(pos, face)) {
|
||||
//System.out.println("Pos outgoing edge " + face + " goes to " + posToComponent.get(face.offset(pos)).index);
|
||||
incrementalEdgeAddition(pos, face.offset(pos));
|
||||
}
|
||||
if (incomingEdge(pos, face)) {
|
||||
//System.out.println("Pos incoming edge " + face + " comes from " + posToComponent.get(face.offset(pos)).index);
|
||||
incrementalEdgeAddition(face.offset(pos), pos);
|
||||
}
|
||||
}
|
||||
if (Main.SLOW_DEBUG) {
|
||||
sanityCheck();
|
||||
}
|
||||
}
|
||||
|
||||
public class CollapsedDependencyGraphComponent {
|
||||
|
||||
private final int id;
|
||||
private final int hash;
|
||||
private final LongOpenHashSet positions = new LongOpenHashSet();
|
||||
private final Set<CollapsedDependencyGraphComponent> outgoingEdges = new ObjectOpenHashSet<>();
|
||||
private final Set<CollapsedDependencyGraphComponent> incomingEdges = new ObjectOpenHashSet<>();
|
||||
// if i change ^^ that "Set" to "ObjectOpenHashSet" it actually makes the bench about 15% SLOWER?!?!?
|
||||
private int y = -1;
|
||||
private CollapsedDependencyGraphComponent deletedInto;
|
||||
private final Set<CollapsedDependencyGraphComponent> unmodifiableOutgoing = Collections.unmodifiableSet(outgoingEdges);
|
||||
private final Set<CollapsedDependencyGraphComponent> unmodifiableIncoming = Collections.unmodifiableSet(incomingEdges);
|
||||
|
||||
private CollapsedDependencyGraphComponent(int id) {
|
||||
this.id = id;
|
||||
this.hash = HashCommon.murmurHash3(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash; // no need to enter native code to get a hashCode, that saves a few nanoseconds
|
||||
}
|
||||
|
||||
private int y() {
|
||||
if (!Main.STRICT_Y || positions.isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (y == -1) { // TODO won't work in 1.17+ lol
|
||||
y = BetterBlockPos.YfromLong(positions.iterator().nextLong());
|
||||
if (y == -1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
public boolean deleted() {
|
||||
return deletedInto != null;
|
||||
}
|
||||
|
||||
public CollapsedDependencyGraphComponent deletedIntoRecursive() { // what cid was this merged into that caused it to be deleted
|
||||
if (!deleted()) {
|
||||
return this;
|
||||
}
|
||||
return deletedInto = deletedInto.deletedIntoRecursive();
|
||||
}
|
||||
|
||||
public LongSet getPositions() {
|
||||
if (deleted()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return LongSets.unmodifiable(positions);
|
||||
}
|
||||
|
||||
public Set<CollapsedDependencyGraphComponent> getIncoming() {
|
||||
if (deleted()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return unmodifiableIncoming;
|
||||
}
|
||||
|
||||
public Set<CollapsedDependencyGraphComponent> getOutgoing() {
|
||||
if (deleted()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return unmodifiableOutgoing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (!Main.DEBUG) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return "cid" + id;
|
||||
}
|
||||
}
|
||||
|
||||
private void sanityCheck() {
|
||||
LongOpenHashSet inComponents = new LongOpenHashSet();
|
||||
for (int componentID : components.keySet()) {
|
||||
CollapsedDependencyGraphComponent component = components.get(componentID);
|
||||
if (component.id != componentID) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (component.incomingEdges.contains(component) || component.outgoingEdges.contains(component)) {
|
||||
throw new IllegalStateException(component.id + "");
|
||||
}
|
||||
if (component.positions.isEmpty()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Integer y = Main.STRICT_Y ? component.y() : null;
|
||||
for (CollapsedDependencyGraphComponent out : component.outgoingEdges) {
|
||||
if (Main.STRICT_Y && out.y() < y) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!out.incomingEdges.contains(component)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (component.incomingEdges.contains(out)) {
|
||||
throw new IllegalStateException(out.id + " is both an incoming AND and outgoing of " + component.id);
|
||||
}
|
||||
}
|
||||
for (CollapsedDependencyGraphComponent in : component.incomingEdges) {
|
||||
if (Main.STRICT_Y && in.y() > y) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!in.outgoingEdges.contains(component)) {
|
||||
throw new IllegalStateException(in.id + " is an incoming edge of " + component.id + " but it doesn't have that as an outgoing edge");
|
||||
}
|
||||
if (component.outgoingEdges.contains(in)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
LongIterator it = component.positions.iterator();
|
||||
while (it.hasNext()) {
|
||||
long l = it.nextLong();
|
||||
if (posToComponent.get(l) != component) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.STRICT_Y && BetterBlockPos.YfromLong(l) != y) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!real(l)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
inComponents.addAll(component.positions);
|
||||
}
|
||||
if (!inComponents.equals(posToComponent.keySet())) {
|
||||
for (long l : posToComponent.keySet()) {
|
||||
if (!inComponents.contains(l)) {
|
||||
System.out.println(l);
|
||||
System.out.println(posToComponent.get(l).id);
|
||||
System.out.println(posToComponent.get(l).positions.contains(l));
|
||||
System.out.println(posToComponent.get(l).deleted());
|
||||
System.out.println(components.containsValue(posToComponent.get(l)));
|
||||
throw new IllegalStateException(l + " is in posToComponent but not actually in any component");
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkEquality(CollapsedDependencyGraph a, CollapsedDependencyGraph b) {
|
||||
if (a.components.size() != b.components.size()) {
|
||||
throw new IllegalStateException(a.components.size() + " " + b.components.size());
|
||||
}
|
||||
if (a.posToComponent.size() != b.posToComponent.size()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!a.posToComponent.keySet().equals(b.posToComponent.keySet())) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
a.sanityCheck();
|
||||
b.sanityCheck();
|
||||
Int2IntOpenHashMap aToB = new Int2IntOpenHashMap();
|
||||
for (int key : a.components.keySet()) {
|
||||
aToB.put(key, b.posToComponent.get(a.components.get(key).positions.iterator().nextLong()).id);
|
||||
}
|
||||
for (int i : a.components.keySet()) {
|
||||
int bInd = aToB.get(i);
|
||||
if (!a.components.get(i).positions.equals(b.components.get(bInd).positions)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
for (List<Set<CollapsedDependencyGraph.CollapsedDependencyGraphComponent>> toCompare : Arrays.asList(
|
||||
Arrays.asList(a.components.get(i).incomingEdges, b.components.get(bInd).incomingEdges),
|
||||
Arrays.asList(a.components.get(i).outgoingEdges, b.components.get(bInd).outgoingEdges)
|
||||
)) {
|
||||
Set<CollapsedDependencyGraph.CollapsedDependencyGraphComponent> aEdges = toCompare.get(0);
|
||||
Set<CollapsedDependencyGraph.CollapsedDependencyGraphComponent> bEdges = toCompare.get(1);
|
||||
if (aEdges.size() != bEdges.size()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
for (CollapsedDependencyGraph.CollapsedDependencyGraphComponent dst : aEdges) {
|
||||
if (!bEdges.contains(b.components.get(aToB.get(dst.id)))) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
138
src/main/java/baritone/builder/DijkstraScaffolder.java
Normal file
138
src/main/java/baritone/builder/DijkstraScaffolder.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.builder.DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph.CollapsedDependencyGraphComponent;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum DijkstraScaffolder implements IScaffolderStrategy {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LongList scaffoldTo(CollapsedDependencyGraphComponent root, DependencyGraphScaffoldingOverlay overlayGraph) {
|
||||
// TODO what if this root is unreachable, e.g. it's lower in STRICT_Y mode?
|
||||
Set<CollapsedDependencyGraphComponent> exclusiveDescendents = new ObjectOpenHashSet<>();
|
||||
walkAllDescendents(root, exclusiveDescendents);
|
||||
exclusiveDescendents.remove(root);
|
||||
PriorityQueue<ScaffoldingSearchNode> openSet = new PriorityQueue<>(Comparator.comparingInt(node -> node.costSoFar));
|
||||
Long2ObjectOpenHashMap<ScaffoldingSearchNode> nodeMap = new Long2ObjectOpenHashMap<>();
|
||||
LongIterator it = root.getPositions().iterator();
|
||||
while (it.hasNext()) {
|
||||
long l = it.nextLong();
|
||||
nodeMap.put(l, new ScaffoldingSearchNode(l));
|
||||
}
|
||||
System.out.println(root.getPositions().stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
|
||||
openSet.addAll(nodeMap.values());
|
||||
while (!openSet.isEmpty()) {
|
||||
ScaffoldingSearchNode node = openSet.poll();
|
||||
CollapsedDependencyGraphComponent tentativeComponent = overlayGraph.getCollapsedGraph().getComponentLocations().get(node.pos);
|
||||
if (tentativeComponent != null) {
|
||||
if (exclusiveDescendents.contains(tentativeComponent)) { // TODO is exclusiveDescendants even valid? returning a route into one of the descendants, if it's on the top of the heap, is valid because it closes a loop and the next dijkstra can start from there? perhaps there's no need to treat descendant interactions differently from any other non-root component? EDIT: maybe it's good to prevent adding useless scaffolding that closes loops for no good reason?
|
||||
// have gone back onto a descendent of this node
|
||||
// sadly this can happen even at the same Y level even in Y_STRICT mode due to orientable blocks forming a loop
|
||||
continue; // TODO does this need to be here? can I expand THROUGH an unrelated component? probably requires testing, this is quite a mind bending possibility. cost should be zero i think?
|
||||
} else {
|
||||
// found a path to a component that isn't a descendent of the root
|
||||
if (tentativeComponent != root) { // but if it IS the root, then we're just on our first loop iteration, we are far from done
|
||||
return reconstructPathTo(node); // all done! found a path to a component unrelated to this one, meaning we have successfully connected this part of the build with scaffolding back to the rest of it
|
||||
// TODO scaffolder strategy should be reworked into a coroutine-like format to decomposes a persistent dijkstra that retains the openset and nodemap between scaffolder component connections. each scaffoldersearchnode would need a persistent progeny (source component) and new combined components would need to be introduced as they're created. then the search can be simultaneous. this would solve the problem of potential incorrect selection of root node, as all possible root nodes are expanded at once
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Face face : Face.VALUES) {
|
||||
if (overlayGraph.hypotheticalScaffoldingIncomingEdge(node.pos, face)) { // we don't have to worry about an incoming edge going into the frontier set because the root component is strongly connected and has no incoming edges from other SCCs, therefore any and all incoming edges will come from hypothetical scaffolding air locations
|
||||
long neighborPos = face.offset(node.pos);
|
||||
int newCost = node.costSoFar + edgeCost(face); // TODO future edge cost should include an added modifier for if neighborPos is in a favorable or unfavorable position e.g. above / under a diagonal depending on if map art or not
|
||||
ScaffoldingSearchNode existingNode = nodeMap.get(neighborPos);
|
||||
if (existingNode != null) {
|
||||
// it's okay if neighbor isn't marked as "air" in the overlay - that's what we want to find - a path to another component
|
||||
// however, we can't consider neighbors within the same component as a solution, clearly
|
||||
// we can accomplish this and kill two birds with one stone by skipping all nodes already in the node map
|
||||
// any position in the initial frontier is clearly in the node map, but also any node that has already been considered
|
||||
// this prevents useless cycling of equivalent paths
|
||||
// this is okay because all paths are equivalent, so there is no possible way to find a better path (because currently it's a fixed value for horizontal / vertical movements)
|
||||
if (existingNode.costSoFar > newCost) { // initialization nodes will have costSoFar = 0 as a base case
|
||||
// note that obviously there is a loopback possibility: search one block north then one block south, you'll run into the same node again. that's fine - "costSoFar < newCost" doesn't mean anything
|
||||
// same for diagonals: one block north then one block down, versus one block down then one block north. that's also fine - "costSoFar == newCost" doesn't mean anything
|
||||
System.out.println(BetterBlockPos.fromLong(node.pos) + " to " + BetterBlockPos.fromLong(neighborPos) + " " + existingNode.costSoFar + " " + newCost + " " + root.getPositions().contains(node.pos) + " " + root.getPositions().contains(neighborPos) + " " + reconstructPathTo(node).stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()) + " " + reconstructPathTo(existingNode).stream().map(BetterBlockPos::fromLong).collect(Collectors.toList()));
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// TODO if root spans more than 1 y level, then this assumption is not correct because edgeCost is different for a horizontal vs vertical face, meaning that a neighbor can have different cost routes if both sideways and up are part of the root component
|
||||
continue; // nothing to do - we already have an equal-or-better path to this location
|
||||
}
|
||||
ScaffoldingSearchNode newNode = new ScaffoldingSearchNode(neighborPos);
|
||||
newNode.costSoFar = newCost;
|
||||
newNode.prev = node;
|
||||
nodeMap.put(newNode.pos, newNode);
|
||||
openSet.add(newNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void walkAllDescendents(CollapsedDependencyGraphComponent root, Set<CollapsedDependencyGraphComponent> set) {
|
||||
set.add(root);
|
||||
for (CollapsedDependencyGraphComponent component : root.getOutgoing()) {
|
||||
walkAllDescendents(component, set);
|
||||
}
|
||||
}
|
||||
|
||||
private static LongList reconstructPathTo(ScaffoldingSearchNode end) {
|
||||
LongList path = new LongArrayList();
|
||||
while (end != null) {
|
||||
path.add(end.pos);
|
||||
end = end.prev;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static int edgeCost(Face face) {
|
||||
if (Main.STRICT_Y && face == Face.UP) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// gut feeling: give slight bias to moving horizontally
|
||||
// that will influence it to create horizontal bridges more often than vertical pillars
|
||||
// horizontal bridges are easier to maneuver around and over
|
||||
if (face.y == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static class ScaffoldingSearchNode {
|
||||
|
||||
private final long pos;
|
||||
private int costSoFar;
|
||||
private ScaffoldingSearchNode prev;
|
||||
|
||||
private ScaffoldingSearchNode(long pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
940
src/main/java/baritone/builder/EulerTourForest.java
Normal file
940
src/main/java/baritone/builder/EulerTourForest.java
Normal file
@@ -0,0 +1,940 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class EulerTourForest {
|
||||
|
||||
static long parentWalks;
|
||||
static long parentCalls;
|
||||
// https://web.stanford.edu/class/archive/cs/cs166/cs166.1166/lectures/17/Small17.pdf
|
||||
// https://u.cs.biu.ac.il/~rodittl/p723-holm.pdf
|
||||
// https://web.archive.org/web/20180725100607/https://infoscience.epfl.ch/record/99353/files/HenzingerK99.pdf
|
||||
// https://en.wikipedia.org/wiki/Dynamic_connectivity#The_Level_structure
|
||||
|
||||
public final BSTNode[] loopbacks; // a (v,v) fake edge is created per vertex and maintained at the appropriate location in the tree, to allow fast lookups of where "v" is, without having to rely on the presence or absence of tree edges connected to v
|
||||
|
||||
public EulerTourForest(int n) {
|
||||
this.loopbacks = IntStream.range(0, n).mapToObj(SplayNode::new).toArray(BSTNode[]::new);
|
||||
}
|
||||
|
||||
public TreeEdge link(int vertA, int vertB) {
|
||||
if (connected(vertA, vertB)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
BSTNode outgoing = new SplayNode(vertA, vertB);
|
||||
BSTNode incoming = new SplayNode(vertB, vertA);
|
||||
BSTNode.barrelRollToLowest(loopbacks[vertA]);
|
||||
BSTNode.barrelRollToLowest(loopbacks[vertB]);
|
||||
BSTNode.concatenate(loopbacks[vertA], outgoing); // (a,a) ... (a,b)
|
||||
BSTNode.concatenate(outgoing, loopbacks[vertB]); // (a,a) ... (a,b) (b,b) ...
|
||||
BSTNode.concatenate(loopbacks[vertB], incoming); // (a,a) ... (a,b) (b,b) ... (b,a)
|
||||
return new TreeEdge(incoming, outgoing);
|
||||
}
|
||||
|
||||
public void cut(TreeEdge edge) {
|
||||
if (edge.owner() != this) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (edge.cut) {
|
||||
return;
|
||||
}
|
||||
edge.cut = true;
|
||||
BSTNode outgoing = edge.left;
|
||||
BSTNode incoming = edge.right;
|
||||
if (incoming.src != outgoing.dst || incoming.dst != outgoing.src || outgoing == incoming || incoming.src == incoming.dst) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!connected(incoming.src, incoming.dst)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
BSTNode.barrelRollToLowest(outgoing);
|
||||
BSTNodePair disconnected = incoming.disconnect(Direction.RIGHT);
|
||||
if (disconnected.left.walkDescendant(Direction.LEFT) != outgoing || disconnected.right.walkDescendant(Direction.LEFT) != incoming) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (loopbacks[incoming.src].walkAncestor() != disconnected.left || loopbacks[outgoing.src].walkAncestor() != disconnected.right) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
outgoing.remove();
|
||||
incoming.remove();
|
||||
}
|
||||
|
||||
public boolean connected(int vertA, int vertB) {
|
||||
return loopbacks[vertA].walkAncestor() == loopbacks[vertB].walkAncestor();
|
||||
}
|
||||
|
||||
public int size(int vert) {
|
||||
return loopbacks[vert].walkAncestor().loopbackSize;
|
||||
}
|
||||
|
||||
public int[] walk(int vert) {
|
||||
BSTNode root = loopbacks[vert].walkAncestor();
|
||||
int[] ret = new int[root.loopbackSize];
|
||||
int[] idx = {0};
|
||||
root.walk(node -> {
|
||||
if (node.isLoopback()) {
|
||||
ret[idx[0]++] = node.src;
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
// redblacknode impl deleted here
|
||||
|
||||
public static class SplayNode extends BSTNode {
|
||||
|
||||
private SplayNode() {
|
||||
super();
|
||||
}
|
||||
|
||||
private SplayNode(int same) {
|
||||
super(same);
|
||||
}
|
||||
|
||||
private SplayNode(int src, int dst) {
|
||||
super(src, dst);
|
||||
}
|
||||
|
||||
public void splay() {
|
||||
while (parent != null) {
|
||||
BSTNode grandparent = parent.parent;
|
||||
Direction myDir = whichChildAmI();
|
||||
if (grandparent != null) {
|
||||
Direction parentDir = parent.whichChildAmI();
|
||||
if (myDir == parentDir) {
|
||||
// see "Zig-zig step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
grandparent.splayRotate(parentDir);
|
||||
parent.splayRotate(myDir);
|
||||
} else {
|
||||
// see "Zig-zag step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
parent.splayRotate(myDir);
|
||||
grandparent.splayRotate(parentDir);
|
||||
}
|
||||
} else {
|
||||
parent.splayRotate(myDir);
|
||||
if (parent != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BSTNode concatenateRoots(BSTNode right) {
|
||||
if (this.parent != null || right.parent != null || this == right) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
SplayNode newRoot = (SplayNode) right.walkDescendant(Direction.LEFT);
|
||||
newRoot.splay();
|
||||
// newRoot is now the root of a splay tree containing all of right
|
||||
// and, it is the LOWEST value of right, and left is assumed to be less than it, so now we can just attach left as its left child
|
||||
// (since it is the lowest value of right, it cannot possibly have any left child already)
|
||||
if (newRoot.getChild(Direction.LEFT) != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
newRoot.setChild(Direction.LEFT, this);
|
||||
newRoot.childUpdated();
|
||||
return newRoot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BSTNodePair disconnect(Direction remainOnSide) {
|
||||
splay(); // SIGNIFICANTLY easier to split the tree in half, if we are the root node
|
||||
BSTNode left = this;
|
||||
BSTNode right = this;
|
||||
// simple detach of one side
|
||||
if (remainOnSide == Direction.LEFT) {
|
||||
right = rightChild;
|
||||
rightChild = null;
|
||||
childUpdated();
|
||||
if (right != null) {
|
||||
right.sizeMustBeAccurate();
|
||||
right.parent = null;
|
||||
}
|
||||
} else {
|
||||
left = leftChild;
|
||||
leftChild = null;
|
||||
childUpdated();
|
||||
if (left != null) {
|
||||
left.sizeMustBeAccurate();
|
||||
left.parent = null;
|
||||
}
|
||||
}
|
||||
return new BSTNodePair(left, right);
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
splay();
|
||||
if (leftChild != null) {
|
||||
leftChild.parent = null;
|
||||
}
|
||||
if (rightChild != null) {
|
||||
rightChild.parent = null;
|
||||
}
|
||||
if (leftChild != null && rightChild != null) {
|
||||
leftChild.concatenateRoots(rightChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class BSTNode {
|
||||
|
||||
protected final int src;
|
||||
protected final int dst;
|
||||
|
||||
protected BSTNode leftChild;
|
||||
protected BSTNode rightChild;
|
||||
protected BSTNode parent;
|
||||
|
||||
protected int loopbackSize;
|
||||
|
||||
private BSTNode() {
|
||||
this(-1);
|
||||
}
|
||||
|
||||
private BSTNode(int same) {
|
||||
this.src = same;
|
||||
this.dst = same;
|
||||
this.loopbackSize = 1;
|
||||
}
|
||||
|
||||
private BSTNode(int src, int dst) {
|
||||
if (src == dst) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.src = src;
|
||||
this.dst = dst;
|
||||
this.loopbackSize = 0;
|
||||
}
|
||||
|
||||
protected void splayRotate(Direction dir) {
|
||||
rotateSecret(dir);
|
||||
}
|
||||
|
||||
protected void redBlackRotate(Direction dir) {
|
||||
rotateSecret(dir.opposite()); // i guess they just use different conventions?
|
||||
}
|
||||
|
||||
private void rotateSecret(Direction dir) {
|
||||
// promote my "dir" child to my level, swap myself down to that level
|
||||
|
||||
// see "Zig step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
BSTNode child = this.getChild(dir);
|
||||
BSTNode replacementChild = child.getChild(dir.opposite()); // stays at the same level, is just rotated to the other side of the tree
|
||||
this.setChild(dir, replacementChild);
|
||||
if (parent == null) {
|
||||
child.parent = null;
|
||||
} else {
|
||||
parent.setChild(whichChildAmI(), child);
|
||||
}
|
||||
child.setChild(dir.opposite(), this); // e.g. my left child now has me as their right child
|
||||
childUpdated();
|
||||
parent.childUpdated();
|
||||
}
|
||||
|
||||
public static BSTNode concatenate(BSTNode left, BSTNode right) {
|
||||
if (left == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (right == null) {
|
||||
return left;
|
||||
}
|
||||
return left.walkAncestor().concatenateRoots(right.walkAncestor());
|
||||
}
|
||||
|
||||
protected void afterSwap(BSTNode other) {
|
||||
|
||||
}
|
||||
|
||||
protected void swapLocationWith(BSTNode other) {
|
||||
if (other == this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (other.parent == this) {
|
||||
other.swapLocationWith(this);
|
||||
return;
|
||||
}
|
||||
if (parent == other) {
|
||||
// grandpa
|
||||
// other
|
||||
// this otherChild
|
||||
// left right
|
||||
// and we want that to become
|
||||
// grandpa
|
||||
// this
|
||||
// other otherChild
|
||||
// left right
|
||||
Direction dir = whichChildAmI(); // LEFT, in the above example
|
||||
BSTNode otherChild = other.getChild(dir.opposite());
|
||||
BSTNode left = leftChild;
|
||||
BSTNode right = rightChild;
|
||||
if (other.parent == null) { // grandpa
|
||||
parent = null;
|
||||
} else {
|
||||
other.parent.setChild(other.whichChildAmI(), this);
|
||||
}
|
||||
setChild(dir, other);
|
||||
setChild(dir.opposite(), otherChild);
|
||||
other.setChild(Direction.LEFT, left);
|
||||
other.setChild(Direction.RIGHT, right);
|
||||
other.childUpdated();
|
||||
childUpdated();
|
||||
} else {
|
||||
Direction myDir = parent == null ? null : whichChildAmI();
|
||||
Direction otherDir = other.parent == null ? null : other.whichChildAmI();
|
||||
BSTNode tmpLeft = leftChild;
|
||||
BSTNode tmpRight = rightChild;
|
||||
|
||||
BSTNode tmpParent = parent;
|
||||
if (other.parent == null) { // grandpa
|
||||
parent = null;
|
||||
} else {
|
||||
other.parent.setChild(otherDir, this);
|
||||
}
|
||||
if (tmpParent == null) {
|
||||
other.parent = null;
|
||||
} else {
|
||||
tmpParent.setChild(myDir, other);
|
||||
}
|
||||
|
||||
setChild(Direction.LEFT, other.leftChild);
|
||||
setChild(Direction.RIGHT, other.rightChild);
|
||||
other.setChild(Direction.LEFT, tmpLeft);
|
||||
other.setChild(Direction.RIGHT, tmpRight);
|
||||
calcSize();
|
||||
if (parent != null) {
|
||||
parent.bubbleUpSize();
|
||||
}
|
||||
other.calcSize();
|
||||
if (other.parent != null) {
|
||||
other.parent.bubbleUpSize();
|
||||
}
|
||||
}
|
||||
afterSwap(other);
|
||||
}
|
||||
|
||||
protected abstract BSTNode concatenateRoots(BSTNode right);
|
||||
|
||||
public static BSTNode barrelRollToLowest(BSTNode target) {
|
||||
// 1. chop the tree in half, centered at target
|
||||
// 2. reattach them in the opposite order
|
||||
// in other words, "cut the deck but don't riffle" - leave "target" as the first node (NOT necessarily the root node)
|
||||
BSTNodePair pair = target.disconnect(Direction.RIGHT);
|
||||
if ((target instanceof SplayNode && pair.right != target) || pair.right.parent != null) { // splay to root only happens with a splay tree, obviously
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
// target is now the lowest (leftmost) element of pair.right
|
||||
BSTNode ret = BSTNode.concatenate(pair.right, pair.left); // target is now first, and everything else is still in order :D
|
||||
// use concatenate and not concatenateRoots because pair.left could be null
|
||||
if (ret == target && pair.left != null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected abstract BSTNodePair disconnect(Direction remainOnSide); // chops the tree in half, with "this" remaining on the side "remainOnSide"
|
||||
|
||||
public abstract void remove();
|
||||
|
||||
protected void bubbleUpSize() {
|
||||
int ns = calcSize();
|
||||
if (loopbackSize != ns) {
|
||||
loopbackSize = ns;
|
||||
if (parent != null) {
|
||||
parent.bubbleUpSize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void childUpdated() {
|
||||
loopbackSize = calcSize();
|
||||
}
|
||||
|
||||
protected void sizeMustBeAccurate() {
|
||||
if (loopbackSize != calcSize()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
protected int calcSize() {
|
||||
int size = 0;
|
||||
if (isLoopback()) {
|
||||
size++;
|
||||
}
|
||||
if (rightChild != null) {
|
||||
size += rightChild.loopbackSize;
|
||||
}
|
||||
if (leftChild != null) {
|
||||
size += leftChild.loopbackSize;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
protected BSTNode getChild(Direction dir) {
|
||||
return dir == Direction.LEFT ? leftChild : rightChild;
|
||||
}
|
||||
|
||||
protected void setChild(Direction dir, BSTNode newChild) {
|
||||
if (newChild == this) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (dir == Direction.LEFT) {
|
||||
leftChild = newChild;
|
||||
} else {
|
||||
rightChild = newChild;
|
||||
}
|
||||
if (newChild != null) {
|
||||
newChild.parent = this;
|
||||
}
|
||||
}
|
||||
|
||||
protected Direction whichChildAmI() {
|
||||
if (parent.leftChild == this) {
|
||||
return Direction.LEFT;
|
||||
}
|
||||
if (parent.rightChild == this) {
|
||||
return Direction.RIGHT;
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
protected BSTNode walkDescendant(Direction side) {
|
||||
BSTNode child = getChild(side);
|
||||
if (child == null) {
|
||||
return this;
|
||||
} else {
|
||||
return child.walkDescendant(side);
|
||||
}
|
||||
}
|
||||
|
||||
protected BSTNode walkAncestor() {
|
||||
BSTNode walk = this;
|
||||
while (walk.parent != null) {
|
||||
parentWalks++;
|
||||
walk = walk.parent;
|
||||
}
|
||||
parentCalls++;
|
||||
return walk;
|
||||
/*if (parent == null) {
|
||||
return this;
|
||||
} else {
|
||||
return parent.walkAncestor();
|
||||
}*/
|
||||
}
|
||||
|
||||
protected void walk(Consumer<BSTNode> consumer) {
|
||||
if (leftChild != null) {
|
||||
leftChild.walk(consumer);
|
||||
}
|
||||
consumer.accept(this);
|
||||
if (rightChild != null) {
|
||||
rightChild.walk(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
protected BSTNode walkNext() {
|
||||
if (rightChild != null) {
|
||||
return rightChild.walkDescendant(Direction.LEFT);
|
||||
}
|
||||
BSTNode itr = this;
|
||||
while (itr.parent != null && itr.whichChildAmI() == Direction.RIGHT) {
|
||||
itr = itr.parent;
|
||||
}
|
||||
return itr.parent;
|
||||
}
|
||||
|
||||
protected boolean isAlone() {
|
||||
return parent == null && leftChild == null && rightChild == null;
|
||||
}
|
||||
|
||||
public boolean isLoopback() {
|
||||
return src == dst;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Direction { // TODO check if proguard converts this to an int
|
||||
LEFT, RIGHT;
|
||||
|
||||
public Direction opposite() {
|
||||
return this == LEFT ? RIGHT : LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
private static class BSTNodePair {
|
||||
|
||||
final BSTNode left;
|
||||
final BSTNode right;
|
||||
|
||||
private BSTNodePair(BSTNode left, BSTNode right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
if ((left != null && left.parent != null) || (right != null && right.parent != null)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class TreeEdge {
|
||||
|
||||
private boolean cut;
|
||||
final BSTNode left;
|
||||
final BSTNode right;
|
||||
|
||||
private TreeEdge(BSTNode left, BSTNode right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
private EulerTourForest owner() {
|
||||
return EulerTourForest.this;
|
||||
}
|
||||
}
|
||||
|
||||
private static void mustEq(BSTNode a, BSTNode b) {
|
||||
if (a != b) {
|
||||
throw new IllegalStateException(a + " " + b);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sanityCheck2() {
|
||||
for (int i = 0; i < 9; i++) {
|
||||
int mode = i % 3;
|
||||
int SZ = 700;
|
||||
TreeEdge[] up = new TreeEdge[SZ * SZ];
|
||||
TreeEdge[] right = new TreeEdge[SZ * SZ];
|
||||
EulerTourForest forest = new EulerTourForest(SZ * SZ);
|
||||
for (int y = 0; y < SZ; y++) {
|
||||
for (int x = 0; x < SZ; x++) {
|
||||
if (y != SZ - 1) {
|
||||
try {
|
||||
up[x * SZ + y] = forest.link(x * SZ + y, x * SZ + (y + 1));
|
||||
} catch (IllegalStateException ex) {} // ignore if already linked
|
||||
}
|
||||
if (x != SZ - 1) {
|
||||
try {
|
||||
right[x * SZ + y] = forest.link(x * SZ + y, (x + 1) * SZ + y);
|
||||
} catch (IllegalStateException ex) {} // ignore if already linked
|
||||
}
|
||||
}
|
||||
}
|
||||
Random rand = new Random(5021);
|
||||
for (int x = 0; x < SZ; x++) {
|
||||
int y = SZ / 2;
|
||||
forest.cut(up[x * SZ + y]);
|
||||
//System.out.println("Sz " + forest.size(x * SZ + y));
|
||||
}
|
||||
if (mode == 1) {
|
||||
for (int j = 0; j < SZ * SZ * 2; j++) { // *2 for a fair comparison to during connection, since that one splays both sides of each test
|
||||
((EulerTourForest.SplayNode) forest.loopbacks[rand.nextInt(SZ * SZ)]).splay();
|
||||
}
|
||||
}
|
||||
long a = System.currentTimeMillis();
|
||||
parentCalls = 0; // reset metrics
|
||||
parentWalks = 0;
|
||||
for (int checks = 0; checks < SZ * SZ; checks++) {
|
||||
int v1 = rand.nextInt(SZ * SZ);
|
||||
int v2 = rand.nextInt(SZ * SZ);
|
||||
forest.connected(v1, v2);
|
||||
if (mode == 2) {
|
||||
((SplayNode) forest.loopbacks[v1]).splay();
|
||||
((SplayNode) forest.loopbacks[v2]).splay();
|
||||
}
|
||||
}
|
||||
forest.checkForest(false);
|
||||
if (mode == 0) {
|
||||
System.out.println("WITHOUT random accesses");
|
||||
} else if (mode == 1) {
|
||||
System.out.println("WITH pre-connection random accesses");
|
||||
} else {
|
||||
System.out.println("WITH random accesses during connection");
|
||||
}
|
||||
System.out.println("Walk ancestor was called " + parentCalls + " times, and it traversed " + parentWalks + " in total, implying an average height of " + (parentWalks / (float) parentCalls));
|
||||
System.out.println("Time: " + (System.currentTimeMillis() - a));
|
||||
}
|
||||
}
|
||||
|
||||
public static void sanityCheck() {
|
||||
for (Direction dir : Direction.values()) {
|
||||
System.out.println("Testing zig " + dir);
|
||||
// see "Zig step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
SplayNode p = new SplayNode();
|
||||
SplayNode x = new SplayNode();
|
||||
SplayNode A = new SplayNode();
|
||||
SplayNode B = new SplayNode();
|
||||
SplayNode C = new SplayNode();
|
||||
p.setChild(dir, x);
|
||||
p.setChild(dir.opposite(), C);
|
||||
x.setChild(dir, A);
|
||||
x.setChild(dir.opposite(), B);
|
||||
|
||||
x.splay();
|
||||
|
||||
mustEq(p.parent, x);
|
||||
mustEq(p.getChild(dir), B);
|
||||
mustEq(p.getChild(dir.opposite()), C);
|
||||
mustEq(x.parent, null);
|
||||
mustEq(x.getChild(dir), A);
|
||||
mustEq(x.getChild(dir.opposite()), p);
|
||||
mustEq(A.parent, x);
|
||||
mustEq(A.getChild(dir), null);
|
||||
mustEq(A.getChild(dir.opposite()), null);
|
||||
mustEq(B.parent, p);
|
||||
mustEq(B.getChild(dir), null);
|
||||
mustEq(B.getChild(dir.opposite()), null);
|
||||
mustEq(C.parent, p);
|
||||
mustEq(C.getChild(dir), null);
|
||||
mustEq(C.getChild(dir.opposite()), null);
|
||||
}
|
||||
for (Direction dir : Direction.values()) {
|
||||
System.out.println("Testing zig-zig " + dir);
|
||||
// see "Zig-zig step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
SplayNode g = new SplayNode();
|
||||
SplayNode p = new SplayNode();
|
||||
SplayNode x = new SplayNode();
|
||||
SplayNode A = new SplayNode();
|
||||
SplayNode B = new SplayNode();
|
||||
SplayNode C = new SplayNode();
|
||||
SplayNode D = new SplayNode();
|
||||
g.setChild(dir, p);
|
||||
g.setChild(dir.opposite(), D);
|
||||
p.setChild(dir, x);
|
||||
p.setChild(dir.opposite(), C);
|
||||
x.setChild(dir, A);
|
||||
x.setChild(dir.opposite(), B);
|
||||
|
||||
x.splay();
|
||||
|
||||
mustEq(g.parent, p);
|
||||
mustEq(g.getChild(dir), C);
|
||||
mustEq(g.getChild(dir.opposite()), D);
|
||||
mustEq(p.parent, x);
|
||||
mustEq(p.getChild(dir), B);
|
||||
mustEq(p.getChild(dir.opposite()), g);
|
||||
mustEq(x.parent, null);
|
||||
mustEq(x.getChild(dir), A);
|
||||
mustEq(x.getChild(dir.opposite()), p);
|
||||
mustEq(A.parent, x);
|
||||
mustEq(A.getChild(dir), null);
|
||||
mustEq(A.getChild(dir.opposite()), null);
|
||||
mustEq(B.parent, p);
|
||||
mustEq(B.getChild(dir), null);
|
||||
mustEq(B.getChild(dir.opposite()), null);
|
||||
mustEq(C.parent, g);
|
||||
mustEq(C.getChild(dir), null);
|
||||
mustEq(C.getChild(dir.opposite()), null);
|
||||
mustEq(D.parent, g);
|
||||
mustEq(D.getChild(dir), null);
|
||||
mustEq(D.getChild(dir.opposite()), null);
|
||||
}
|
||||
for (Direction dir : Direction.values()) {
|
||||
System.out.println("Testing zig-zag " + dir);
|
||||
// see "Zig-zag step" of https://en.wikipedia.org/wiki/Splay_tree
|
||||
SplayNode g = new SplayNode();
|
||||
SplayNode p = new SplayNode();
|
||||
SplayNode x = new SplayNode();
|
||||
SplayNode A = new SplayNode();
|
||||
SplayNode B = new SplayNode();
|
||||
SplayNode C = new SplayNode();
|
||||
SplayNode D = new SplayNode();
|
||||
g.setChild(dir, p);
|
||||
g.setChild(dir.opposite(), D);
|
||||
p.setChild(dir, A);
|
||||
p.setChild(dir.opposite(), x);
|
||||
x.setChild(dir, B);
|
||||
x.setChild(dir.opposite(), C);
|
||||
|
||||
x.splay();
|
||||
|
||||
mustEq(g.parent, x);
|
||||
mustEq(g.getChild(dir), C);
|
||||
mustEq(g.getChild(dir.opposite()), D);
|
||||
mustEq(p.parent, x);
|
||||
mustEq(p.getChild(dir), A);
|
||||
mustEq(p.getChild(dir.opposite()), B);
|
||||
mustEq(x.parent, null);
|
||||
mustEq(x.getChild(dir), p);
|
||||
mustEq(x.getChild(dir.opposite()), g);
|
||||
mustEq(A.parent, p);
|
||||
mustEq(A.getChild(dir), null);
|
||||
mustEq(A.getChild(dir.opposite()), null);
|
||||
mustEq(B.parent, p);
|
||||
mustEq(B.getChild(dir), null);
|
||||
mustEq(B.getChild(dir.opposite()), null);
|
||||
mustEq(C.parent, g);
|
||||
mustEq(C.getChild(dir), null);
|
||||
mustEq(C.getChild(dir.opposite()), null);
|
||||
mustEq(D.parent, g);
|
||||
mustEq(D.getChild(dir), null);
|
||||
mustEq(D.getChild(dir.opposite()), null);
|
||||
}
|
||||
for (Direction GtoP : Direction.values()) {
|
||||
for (Direction PtoX : Direction.values()) {
|
||||
System.out.println("Testing connected swap " + GtoP + " " + PtoX);
|
||||
SplayNode g = new SplayNode();
|
||||
SplayNode p = new SplayNode();
|
||||
SplayNode x = new SplayNode();
|
||||
SplayNode A = new SplayNode();
|
||||
SplayNode B = new SplayNode();
|
||||
SplayNode C = new SplayNode();
|
||||
SplayNode D = new SplayNode();
|
||||
g.setChild(GtoP, p);
|
||||
g.setChild(GtoP.opposite(), D);
|
||||
p.setChild(PtoX, x);
|
||||
p.setChild(PtoX.opposite(), A);
|
||||
x.setChild(Direction.LEFT, B);
|
||||
x.setChild(Direction.RIGHT, C);
|
||||
|
||||
/*x.black = true;
|
||||
p.black = false;*/
|
||||
p.swapLocationWith(x);
|
||||
|
||||
/*if (x.black || !p.black) {
|
||||
throw new IllegalStateException();
|
||||
}*/
|
||||
mustEq(g.parent, null);
|
||||
mustEq(g.getChild(GtoP), x);
|
||||
mustEq(g.getChild(GtoP.opposite()), D);
|
||||
mustEq(p.parent, x);
|
||||
mustEq(p.getChild(Direction.LEFT), B);
|
||||
mustEq(p.getChild(Direction.RIGHT), C);
|
||||
mustEq(x.parent, g);
|
||||
mustEq(x.getChild(PtoX), p);
|
||||
mustEq(x.getChild(PtoX.opposite()), A);
|
||||
mustEq(A.parent, x);
|
||||
mustEq(A.getChild(Direction.LEFT), null);
|
||||
mustEq(A.getChild(Direction.RIGHT), null);
|
||||
mustEq(B.parent, p);
|
||||
mustEq(B.getChild(Direction.LEFT), null);
|
||||
mustEq(B.getChild(Direction.RIGHT), null);
|
||||
mustEq(C.parent, p);
|
||||
mustEq(C.getChild(Direction.LEFT), null);
|
||||
mustEq(C.getChild(Direction.RIGHT), null);
|
||||
mustEq(D.parent, g);
|
||||
mustEq(D.getChild(Direction.LEFT), null);
|
||||
mustEq(D.getChild(Direction.RIGHT), null);
|
||||
}
|
||||
}
|
||||
for (Direction APtoA : Direction.values()) {
|
||||
for (Direction BPtoB : Direction.values()) {
|
||||
System.out.println("Testing disconnected swap " + APtoA + " " + BPtoB);
|
||||
SplayNode ap = new SplayNode();
|
||||
SplayNode apoc = new SplayNode();
|
||||
SplayNode a = new SplayNode();
|
||||
SplayNode alc = new SplayNode();
|
||||
SplayNode arc = new SplayNode();
|
||||
SplayNode bp = new SplayNode();
|
||||
SplayNode bpoc = new SplayNode();
|
||||
SplayNode b = new SplayNode();
|
||||
SplayNode blc = new SplayNode();
|
||||
SplayNode brc = new SplayNode();
|
||||
ap.setChild(APtoA, a);
|
||||
ap.setChild(APtoA.opposite(), apoc);
|
||||
a.setChild(Direction.LEFT, alc);
|
||||
a.setChild(Direction.RIGHT, arc);
|
||||
bp.setChild(BPtoB, b);
|
||||
bp.setChild(BPtoB.opposite(), bpoc);
|
||||
b.setChild(Direction.LEFT, blc);
|
||||
b.setChild(Direction.RIGHT, brc);
|
||||
|
||||
/*a.black = true;
|
||||
b.black = false;*/
|
||||
a.swapLocationWith(b);
|
||||
|
||||
/*if (a.black || !b.black) {
|
||||
throw new IllegalStateException();
|
||||
}*/
|
||||
mustEq(ap.parent, null);
|
||||
mustEq(ap.getChild(APtoA), b);
|
||||
mustEq(ap.getChild(APtoA.opposite()), apoc);
|
||||
mustEq(apoc.parent, ap);
|
||||
mustEq(apoc.getChild(Direction.LEFT), null);
|
||||
mustEq(apoc.getChild(Direction.RIGHT), null);
|
||||
mustEq(a.parent, bp);
|
||||
mustEq(a.getChild(Direction.LEFT), blc);
|
||||
mustEq(a.getChild(Direction.RIGHT), brc);
|
||||
mustEq(alc.parent, b);
|
||||
mustEq(alc.getChild(Direction.LEFT), null);
|
||||
mustEq(alc.getChild(Direction.RIGHT), null);
|
||||
mustEq(arc.parent, b);
|
||||
mustEq(arc.getChild(Direction.LEFT), null);
|
||||
mustEq(arc.getChild(Direction.RIGHT), null);
|
||||
mustEq(bp.parent, null);
|
||||
mustEq(bp.getChild(BPtoB), a);
|
||||
mustEq(bp.getChild(BPtoB.opposite()), bpoc);
|
||||
mustEq(bpoc.parent, bp);
|
||||
mustEq(bpoc.getChild(Direction.LEFT), null);
|
||||
mustEq(bpoc.getChild(Direction.RIGHT), null);
|
||||
mustEq(b.parent, ap);
|
||||
mustEq(b.getChild(Direction.LEFT), alc);
|
||||
mustEq(b.getChild(Direction.RIGHT), arc);
|
||||
mustEq(blc.parent, a);
|
||||
mustEq(blc.getChild(Direction.LEFT), null);
|
||||
mustEq(blc.getChild(Direction.RIGHT), null);
|
||||
mustEq(brc.parent, a);
|
||||
mustEq(brc.getChild(Direction.LEFT), null);
|
||||
mustEq(brc.getChild(Direction.RIGHT), null);
|
||||
}
|
||||
}
|
||||
{
|
||||
Random rand = new Random(5021);
|
||||
List<Supplier<BSTNode>> constructors = Arrays.asList(SplayNode::new/*, SplayNode::new*/);
|
||||
for (int run = 0; run < 10; run++) {
|
||||
int NODES = 10000;
|
||||
Supplier<BSTNode> toUse = constructors.get(run % constructors.size());
|
||||
List<BSTNode> nodes = new ArrayList<>();
|
||||
{
|
||||
BSTNode root = toUse.get();
|
||||
nodes.add(root);
|
||||
for (int i = 1; i < NODES; i++) {
|
||||
nodes.add(toUse.get());
|
||||
root = BSTNode.concatenate(root, nodes.get(i));
|
||||
}
|
||||
}
|
||||
int shuffledBy = 0;
|
||||
for (int ii = 0; ii < 10000; ii++) {
|
||||
if (rand.nextBoolean()) {
|
||||
BSTNode root = nodes.get(rand.nextInt(NODES));
|
||||
if (root instanceof SplayNode) {
|
||||
((SplayNode) root).splay();
|
||||
if (root != nodes.get(rand.nextInt(NODES)).walkAncestor() || root.loopbackSize != NODES) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (rand.nextBoolean()) {
|
||||
shuffledBy = rand.nextInt(NODES);
|
||||
BSTNode root = BSTNode.barrelRollToLowest(nodes.get(shuffledBy));
|
||||
if (root != nodes.get(rand.nextInt(NODES)).walkAncestor() || root.loopbackSize != NODES) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (rand.nextBoolean()) {
|
||||
int pos = rand.nextBoolean() ? (shuffledBy + NODES + rand.nextInt(10) - 5) % NODES : rand.nextInt(NODES);
|
||||
BSTNode remove = nodes.remove(pos);
|
||||
NODES--;
|
||||
remove.remove();
|
||||
if (shuffledBy > pos) {
|
||||
shuffledBy--;
|
||||
}
|
||||
}
|
||||
List<BSTNode> order = new ArrayList<>(NODES);
|
||||
nodes.get(rand.nextInt(NODES)).walkAncestor().walk(order::add);
|
||||
for (int n = 0; n < NODES; n++) {
|
||||
if (order.get(n) != nodes.get((n + shuffledBy) % NODES)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
order.get(n).sizeMustBeAccurate();
|
||||
if (order.get(n).walkNext() != (n < NODES - 1 ? order.get(n + 1) : null)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// slide 22 of https://web.stanford.edu/class/archive/cs/cs166/cs166.1166/lectures/17/Small17.pdf
|
||||
EulerTourForest forest = new EulerTourForest(11);
|
||||
forest.link(0, 1);
|
||||
forest.link(1, 3);
|
||||
forest.link(2, 4);
|
||||
forest.link(1, 2);
|
||||
TreeEdge toCut = forest.link(0, 5);
|
||||
forest.link(5, 6);
|
||||
forest.link(6, 9);
|
||||
forest.link(9, 10);
|
||||
forest.link(9, 8);
|
||||
forest.link(6, 7);
|
||||
BSTNode.barrelRollToLowest(forest.loopbacks[0]);
|
||||
if (!forest.checkForest(true).equals("abdbcecbafgjkjijghgf")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
forest.cut(toCut);
|
||||
if (!forest.checkForest(true).equals("abdbcecb fgjkjijghg")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
{
|
||||
// slide 26 of https://web.stanford.edu/class/archive/cs/cs166/cs166.1166/lectures/17/Small17.pdf
|
||||
EulerTourForest forest = new EulerTourForest(11);
|
||||
forest.link(2, 4);
|
||||
TreeEdge toCut = forest.link(2, 1);
|
||||
forest.link(1, 0);
|
||||
forest.link(1, 3);
|
||||
forest.link(2, 6);
|
||||
forest.link(6, 9);
|
||||
forest.link(9, 10);
|
||||
forest.link(9, 8);
|
||||
forest.link(6, 7);
|
||||
forest.link(5, 6);
|
||||
BSTNode.barrelRollToLowest(forest.loopbacks[2]);
|
||||
if (!forest.checkForest(true).equals("cecbabdbcgjkjijghgfg")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
forest.cut(toCut);
|
||||
if (!forest.checkForest(true).equals("babd cgjkjijghgfgce")) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String checkForest(boolean verbose) {
|
||||
boolean[] seen = new boolean[loopbacks.length];
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for (int vert = 0; vert < loopbacks.length; vert++) {
|
||||
if (seen[vert]) {
|
||||
continue;
|
||||
}
|
||||
List<BSTNode> order = new ArrayList<>();
|
||||
loopbacks[vert].walkAncestor().walk(order::add);
|
||||
for (int i = 0; i < order.size(); i++) {
|
||||
if (verbose) {
|
||||
System.out.print("(" + (char) ('a' + order.get(i).src) + "," + (char) ('a' + order.get(i).dst) + ") ");
|
||||
}
|
||||
if (order.get(i).dst != order.get((i + 1) % order.size()).src) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (order.get(i).isLoopback()) {
|
||||
seen[order.get(i).src] = true;
|
||||
} else {
|
||||
ret.append((char) ('a' + order.get(i).src));
|
||||
}
|
||||
}
|
||||
if (verbose) {
|
||||
System.out.println();
|
||||
}
|
||||
ret.append(" ");
|
||||
if (!seen[vert]) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
if (verbose) {
|
||||
System.out.println(ret);
|
||||
}
|
||||
return ret.toString().trim();
|
||||
}
|
||||
}
|
||||
94
src/main/java/baritone/builder/Face.java
Normal file
94
src/main/java/baritone/builder/Face.java
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* I hate porting things to new versions of Minecraft
|
||||
* <p>
|
||||
* So just like BetterBlockPos, we now have Face
|
||||
*/
|
||||
public enum Face {
|
||||
DOWN, UP, NORTH, SOUTH, WEST, EAST;
|
||||
public final int index = ordinal();
|
||||
public final int oppositeIndex = opposite(index);
|
||||
public final int x = toMC().getXOffset();
|
||||
public final int y = toMC().getYOffset();
|
||||
public final int z = toMC().getZOffset();
|
||||
public final long offset = BetterBlockPos.toLong(x, y, z); // both the previous three lines (avoidably) and this one (unavoidably due to BlockPos superclass of BetterBlockPos) mess up test timing / profiling because it calls the <clinit> of both EnumFacing and BlockPos which does some Log4j bs lol
|
||||
public final int[] vec = new int[]{x, y, z};
|
||||
public final boolean vertical = y != 0;
|
||||
public final int horizontalIndex = x & 1 | (x | z) & 2;
|
||||
public static final int NUM_FACES = 6;
|
||||
public static final Face[] VALUES = new Face[NUM_FACES];
|
||||
public static final Face[] HORIZONTALS = new Face[4];
|
||||
public static final List<Optional<Face>> OPTS;
|
||||
public static final long[] OFFSETS = new long[NUM_FACES];
|
||||
|
||||
static {
|
||||
List<Optional<Face>> lst = new ArrayList<>();
|
||||
for (Face face : values()) {
|
||||
VALUES[face.index] = face;
|
||||
OFFSETS[face.index] = face.offset;
|
||||
lst.add(Optional.of(face));
|
||||
HORIZONTALS[face.horizontalIndex] = face;
|
||||
}
|
||||
lst.add(Optional.empty());
|
||||
OPTS = Collections.unmodifiableList(lst);
|
||||
}
|
||||
|
||||
public final EnumFacing toMC() {
|
||||
return EnumFacing.byIndex(index);
|
||||
}
|
||||
|
||||
public final Face opposite() {
|
||||
return VALUES[oppositeIndex];
|
||||
}
|
||||
|
||||
public final long offset(long pos) {
|
||||
return (pos + offset) & BetterBlockPos.POST_ADDITION_MASK;
|
||||
}
|
||||
|
||||
public static long offset(long pos, int face) {
|
||||
return (pos + OFFSETS[face]) & BetterBlockPos.POST_ADDITION_MASK;
|
||||
}
|
||||
|
||||
public static int opposite(int face) {
|
||||
return face ^ 1;
|
||||
}
|
||||
|
||||
public static int oppositeHorizontal(int horizontalIndex) {
|
||||
return horizontalIndex ^ 2;
|
||||
}
|
||||
|
||||
public static Face between(long from, long to) {
|
||||
for (int i = 0; i < NUM_FACES; i++) {
|
||||
if (offset(from, i) == to) {
|
||||
return VALUES[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
88
src/main/java/baritone/builder/FakeStates.java
Normal file
88
src/main/java/baritone/builder/FakeStates.java
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* There will be a BlockStateCachedData for every IBlockState in the game, yes.
|
||||
* <p>
|
||||
* But, we need some additional BlockStateCachedDatas. For example, we need one that represents hypothetical scaffolding, and another for air, for properly computing place order dependency graphs. Some other places need a placeholder for out of bounds.
|
||||
* <p>
|
||||
* We could just say that scaffolding is always, like, dirt, or something. But that would be inelegant.
|
||||
* <p>
|
||||
* And beyond the needs at runtime, unit tests shouldn't need to bootstrap and boot up the entire Minecraft game. So, let's have some fake BlockStateCachedData for testing purposes too!
|
||||
*/
|
||||
public class FakeStates {
|
||||
// the three aformentioned placeholders for runtime
|
||||
public static final BlockStateCachedData SCAFFOLDING = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe());
|
||||
// need a second solid block so that "== FakeStates.SCAFFOLDING" doesn't get tricked
|
||||
public static final BlockStateCachedData SOLID = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(1).canPlaceAgainstMe());
|
||||
public static final BlockStateCachedData AIR = new BlockStateCachedData(new BlockStateCachedDataBuilder().setAir());
|
||||
public static final BlockStateCachedData OUT_OF_BOUNDS = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).collisionHeight(1));
|
||||
|
||||
// and some for testing
|
||||
public static final BlockStateCachedData[] BY_HEIGHT;
|
||||
|
||||
static {
|
||||
BY_HEIGHT = new BlockStateCachedData[Blip.FULL_BLOCK + 1];
|
||||
for (int height = 1; height <= Blip.FULL_BLOCK; height++) {
|
||||
BY_HEIGHT[height] = new BlockStateCachedData(new BlockStateCachedDataBuilder().collidesWithPlayer(true).fullyWalkableTop().collisionHeight(height * Blip.RATIO));
|
||||
}
|
||||
BY_HEIGHT[0] = AIR;
|
||||
}
|
||||
|
||||
private static List<BlockStateCachedData> PROBABLE_BLOCKS = null;
|
||||
|
||||
private static List<BlockStateCachedData> getProbableBlocks() {
|
||||
if (PROBABLE_BLOCKS == null) {
|
||||
//long a = System.currentTimeMillis();
|
||||
Random rand = new Random(5021);
|
||||
PROBABLE_BLOCKS = IntStream.range(0, 10000).mapToObj($ -> {
|
||||
List<BlockStatePlacementOption> ret = new ArrayList<>(SCAFFOLDING.placeMe);
|
||||
ret.removeIf($$ -> rand.nextInt(10) < 2);
|
||||
BlockStateCachedDataBuilder builder = new BlockStateCachedDataBuilder() {
|
||||
@Override
|
||||
public List<BlockStatePlacementOption> howCanIBePlaced() {
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
if (ret.isEmpty()) {
|
||||
builder.placementLogicNotImplementedYet();
|
||||
}
|
||||
return new BlockStateCachedData(builder
|
||||
.fullyWalkableTop()
|
||||
.collisionHeight(1)
|
||||
.canPlaceAgainstMe()
|
||||
.collidesWithPlayer(true));
|
||||
}).collect(Collectors.toList());
|
||||
//System.out.println("Took " + (System.currentTimeMillis() - a));
|
||||
}
|
||||
return PROBABLE_BLOCKS;
|
||||
}
|
||||
|
||||
public static BlockStateCachedData probablyCanBePlaced(Random rand) {
|
||||
return getProbableBlocks().get(rand.nextInt(getProbableBlocks().size()));
|
||||
}
|
||||
|
||||
// probably more will go here such as for making sure that slabs and stairs work right (like there'll be a fake slab and a fake stair i guess?)
|
||||
}
|
||||
298
src/main/java/baritone/builder/GreedySolver.java
Normal file
298
src/main/java/baritone/builder/GreedySolver.java
Normal file
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GreedySolver {
|
||||
|
||||
private final SolverEngineInput engineInput;
|
||||
private final NodeBinaryHeap heap = new NodeBinaryHeap();
|
||||
private final Long2ObjectOpenHashMap<Node> nodes = new Long2ObjectOpenHashMap<>();
|
||||
private final ZobristWorldStateCache zobristCache;
|
||||
private final long allCompleted;
|
||||
private final Bounds bounds;
|
||||
private Column scratchpadExpandNode1 = new Column();
|
||||
private Column scratchpadExpandNode2 = new Column();
|
||||
private Column scratchpadExpandNode3 = new Column();
|
||||
|
||||
public GreedySolver(SolverEngineInput input) {
|
||||
this.engineInput = input;
|
||||
this.bounds = engineInput.graph.bounds();
|
||||
this.zobristCache = new ZobristWorldStateCache(new WorldState.WorldStateWrappedSubstrate(engineInput));
|
||||
Node root = new Node(engineInput.player, null, 0L, -1L, 0);
|
||||
nodes.put(root.nodeMapKey(), root);
|
||||
heap.insert(root);
|
||||
this.allCompleted = WorldState.predetermineGoalZobrist(engineInput.allToPlaceNow);
|
||||
}
|
||||
|
||||
synchronized SolverEngineOutput search() {
|
||||
while (!heap.isEmpty()) {
|
||||
Node node = heap.removeLowest();
|
||||
if (!node.sneaking() && node.worldStateZobristHash == allCompleted) {
|
||||
return backwards(node);
|
||||
}
|
||||
expandNode(node);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private SolverEngineOutput backwards(Node node) {
|
||||
ArrayDeque<SolvedActionStep> steps = new ArrayDeque<>();
|
||||
while (node.previous != null) {
|
||||
steps.addFirst(step(node, node.previous));
|
||||
node = node.previous;
|
||||
}
|
||||
return new SolverEngineOutput(new ArrayList<>(steps));
|
||||
}
|
||||
|
||||
private SolvedActionStep step(Node next, Node prev) {
|
||||
if (next.worldStateZobristHash == prev.worldStateZobristHash) {
|
||||
return new SolvedActionStep(next.pos());
|
||||
} else {
|
||||
return new SolvedActionStep(next.pos(), WorldState.unzobrist(prev.worldStateZobristHash ^ next.worldStateZobristHash));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean wantToPlaceAt(long blockGoesAt, Node vantage, int blipsWithinVoxel, WorldState worldState) {
|
||||
if (worldState.blockExists(blockGoesAt)) {
|
||||
return false;
|
||||
}
|
||||
long vpos = vantage.pos();
|
||||
int relativeX = BetterBlockPos.XfromLong(vpos) - BetterBlockPos.XfromLong(blockGoesAt);
|
||||
int relativeY = blipsWithinVoxel + Blip.FULL_BLOCK * (BetterBlockPos.YfromLong(vpos) - BetterBlockPos.YfromLong(blockGoesAt));
|
||||
int relativeZ = BetterBlockPos.ZfromLong(vpos) - BetterBlockPos.ZfromLong(blockGoesAt);
|
||||
BlockStateCachedData blockBeingPlaced = engineInput.graph.data(blockGoesAt);
|
||||
for (BlockStatePlacementOption option : blockBeingPlaced.placeMe) {
|
||||
long maybePlaceAgainst = option.against.offset(blockGoesAt);
|
||||
if (!bounds.inRangePos(maybePlaceAgainst)) {
|
||||
continue;
|
||||
}
|
||||
if (!worldState.blockExists(maybePlaceAgainst)) {
|
||||
continue;
|
||||
}
|
||||
BlockStateCachedData placingAgainst = engineInput.graph.data(maybePlaceAgainst);
|
||||
PlaceAgainstData againstData = placingAgainst.againstMe(option);
|
||||
traces:
|
||||
for (Raytracer.Raytrace trace : option.computeTraceOptions(againstData, relativeX, relativeY, relativeZ, PlayerVantage.LOOSE_CENTER, blockReachDistance())) { // TODO or only take the best one
|
||||
for (long l : trace.passedThrough) {
|
||||
if (worldState.blockExists(l)) {
|
||||
continue traces;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void expandNode(Node node) {
|
||||
WorldState worldState = zobristCache.coalesceState(node);
|
||||
long pos = node.pos();
|
||||
Column within = scratchpadExpandNode1;
|
||||
within.initFrom(pos, worldState, engineInput);
|
||||
|
||||
Column supportedBy;
|
||||
boolean sneaking = node.sneaking();
|
||||
if (sneaking) {
|
||||
supportedBy = scratchpadExpandNode3;
|
||||
long supportedFeetVoxel = SneakPosition.sneakDirectionFromPlayerToSupportingBlock(node.sneakingPosition()).offset(pos);
|
||||
supportedBy.initFrom(supportedFeetVoxel, worldState, engineInput);
|
||||
if (Main.DEBUG && !within.okToSneakIntoHereAtHeight(supportedBy.feetBlips)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
} else {
|
||||
supportedBy = within;
|
||||
}
|
||||
|
||||
int playerFeet = supportedBy.feetBlips;
|
||||
if (Main.DEBUG && !supportedBy.playerCanExistAtFootBlip(playerFeet)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// place block beneath or within feet voxel
|
||||
for (int dy = -1; dy <= 0; dy++) {
|
||||
// this is the common case for sneak bridging with full blocks
|
||||
long maybePlaceAt = BetterBlockPos.offsetBy(pos, 0, dy, 0);
|
||||
BlockStateCachedData wouldBePlaced = engineInput.graph.data(maybePlaceAt);
|
||||
int cost = blockPlaceCost();
|
||||
int playerFeetWouldBeAt = playerFeet;
|
||||
/*if (wouldBePlaced.collidesWithPlayer) {
|
||||
int heightRelativeToCurrentVoxel = wouldBePlaced.collisionHeightBlips() + dy * Blip.FULL_BLOCK;
|
||||
if (heightRelativeToCurrentVoxel > playerFeet) {
|
||||
// we would need to jump in order to do this
|
||||
cost += jumpCost();
|
||||
playerFeetWouldBeAt = heightRelativeToCurrentVoxel; // because we'd have to jump, and could only place the block once we had cleared the collision box for it
|
||||
if (!within.playerCanExistAtFootBlip(heightRelativeToCurrentVoxel) || !supportedBy.playerCanExistAtFootBlip(heightRelativeToCurrentVoxel)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wantToPlaceAt(maybePlaceAt, node, playerFeetWouldBeAt, worldState)) {
|
||||
upsertEdge(node, worldState, pos, null, maybePlaceAt, cost);
|
||||
}*/
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
if (sneaking) {
|
||||
// we can walk back to where we were
|
||||
upsertEdge(node, worldState, supportedBy.pos, null, -1, flatCost());
|
||||
// this will probably rarely be used. i can only imagine rare scenarios such as needing the extra perspective in order to place a block a bit more efficiently. like, this could avoid unnecessary ancillary scaffolding i suppose.
|
||||
// ----
|
||||
// also let's try just letting ourselves fall off the edge of the block
|
||||
int descendBy = PlayerPhysics.playerFalls(pos, worldState, engineInput);
|
||||
if (descendBy != -1) {
|
||||
upsertEdge(node, worldState, BetterBlockPos.offsetBy(pos, 0, -descendBy, 0), null, -1, fallCost(descendBy));
|
||||
}
|
||||
return;
|
||||
}
|
||||
// not sneaking! sneaking returned ^^
|
||||
// -------------------------------------------------------------------------------------------------------------
|
||||
// walk sideways and either stay level, ascend, or descend
|
||||
Column into = scratchpadExpandNode2;
|
||||
for (Face travel : Face.HORIZONTALS) {
|
||||
long newPos = travel.offset(pos);
|
||||
into.initFrom(newPos, worldState, engineInput);
|
||||
PlayerPhysics.Collision collision = PlayerPhysics.playerTravelCollides(within, into);
|
||||
switch (collision) {
|
||||
case BLOCKED: {
|
||||
continue;
|
||||
}
|
||||
case FALL: {
|
||||
upsertEdge(node, worldState, newPos, travel, -1, flatCost()); // sneak off edge of block
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
long realNewPos = BetterBlockPos.offsetBy(newPos, 0, collision.voxelVerticalOffset(), 0);
|
||||
upsertEdge(node, worldState, realNewPos, null, -1, collision.requiresJump() ? jumpCost() : flatCost());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int fallCost(int blocks) {
|
||||
if (blocks < 1) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private int flatCost() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private int jumpCost() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private double blockReachDistance() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private void upsertEdge(Node node, WorldState worldState, long newPlayerPosition, Face sneakingTowards, long blockPlacement, int edgeCost) {
|
||||
Node neighbor = getNode(newPlayerPosition, sneakingTowards, node, worldState, blockPlacement);
|
||||
if (Main.SLOW_DEBUG && blockPlacement != -1 && !zobristCache.coalesceState(neighbor).blockExists(blockPlacement)) { // only in slow_debug because this force-allocates a WorldState for every neighbor of every node!
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (Main.DEBUG && node == neighbor) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
updateNeighbor(node, neighbor, edgeCost);
|
||||
}
|
||||
|
||||
private void updateNeighbor(Node node, Node neighbor, int edgeCost) {
|
||||
int currentCost = neighbor.cost;
|
||||
int offeredCost = node.cost + edgeCost;
|
||||
if (currentCost < offeredCost) {
|
||||
return;
|
||||
}
|
||||
neighbor.previous = node;
|
||||
neighbor.cost = offeredCost;
|
||||
neighbor.combinedCost = offeredCost + neighbor.heuristic;
|
||||
if (Main.DEBUG && neighbor.combinedCost < Integer.MIN_VALUE / 2) { // simple attempt to catch obvious overflow
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (neighbor.inHeap()) {
|
||||
heap.update(neighbor);
|
||||
} else {
|
||||
heap.insert(neighbor);
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateHeuristicModifier(WorldState previous, long blockPlacedAt) {
|
||||
if (Main.DEBUG && previous.blockExists(blockPlacedAt)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (true) {
|
||||
throw new UnsupportedOperationException("tune the values first lol");
|
||||
}
|
||||
switch (engineInput.desiredToBePlaced(blockPlacedAt)) {
|
||||
case PART_OF_CURRENT_GOAL:
|
||||
case SCAFFOLDING_OF_CURRENT_GOAL:
|
||||
return -100; // keep kitten on task
|
||||
case PART_OF_FUTURE_GOAL:
|
||||
return -10; // smaller kitten treat for working ahead
|
||||
case SCAFFOLDING_OF_FUTURE_GOAL:
|
||||
return -5; // smallest kitten treat for working ahead on scaffolding
|
||||
case ANCILLARY:
|
||||
return 0; // no kitten treat for placing a random extra block
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private int blockPlaceCost() {
|
||||
// maybe like... ten?
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private Node getNode(long playerPosition, Face sneakingTowards, Node prev, WorldState prevWorld, long blockPlacement) {
|
||||
if (Main.DEBUG && blockPlacement != -1 && prevWorld.blockExists(blockPlacement)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
long worldStateZobristHash = prev.worldStateZobristHash;
|
||||
if (blockPlacement != -1) {
|
||||
worldStateZobristHash = WorldState.updateZobrist(worldStateZobristHash, blockPlacement);
|
||||
}
|
||||
long code = SneakPosition.encode(playerPosition, sneakingTowards) ^ worldStateZobristHash;
|
||||
Node existing = nodes.get(code);
|
||||
if (existing != null) {
|
||||
return existing;
|
||||
}
|
||||
int newHeuristic = prev.heuristic;
|
||||
if (blockPlacement != -1) {
|
||||
newHeuristic += calculateHeuristicModifier(prevWorld, blockPlacement);
|
||||
}
|
||||
Node node = new Node(playerPosition, null, worldStateZobristHash, blockPlacement, newHeuristic);
|
||||
if (Main.DEBUG && node.nodeMapKey() != code) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
nodes.put(code, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public BlockStateCachedData at(long pos, WorldState inWorldState) {
|
||||
return engineInput.at(pos, inWorldState);
|
||||
}
|
||||
}
|
||||
27
src/main/java/baritone/builder/GreedySolverEngine.java
Normal file
27
src/main/java/baritone/builder/GreedySolverEngine.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
public enum GreedySolverEngine implements ISolverEngine {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public SolverEngineOutput solve(SolverEngineInput in) {
|
||||
return new GreedySolver(in).search();
|
||||
}
|
||||
}
|
||||
22
src/main/java/baritone/builder/Half.java
Normal file
22
src/main/java/baritone/builder/Half.java
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
public enum Half {
|
||||
TOP, BOTTOM, EITHER
|
||||
}
|
||||
44
src/main/java/baritone/builder/IBlockStateDataProvider.java
Normal file
44
src/main/java/baritone/builder/IBlockStateDataProvider.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
public interface IBlockStateDataProvider {
|
||||
|
||||
int numStates();
|
||||
|
||||
BlockStateCachedData getNullable(int i);
|
||||
|
||||
default BlockStateCachedData[] allNullable() {
|
||||
BlockStateCachedData[] ret = new BlockStateCachedData[numStates()];
|
||||
RuntimeException ex = null;
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
try {
|
||||
ret[i] = getNullable(i);
|
||||
} catch (RuntimeException e) {
|
||||
if (ex != null) {
|
||||
ex.printStackTrace(); // printstacktrace all but the one that we throw
|
||||
}
|
||||
ex = e;
|
||||
}
|
||||
}
|
||||
if (ex != null) {
|
||||
throw ex; // throw the last one
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
21
src/main/java/baritone/builder/INavigableSurface.java
Normal file
21
src/main/java/baritone/builder/INavigableSurface.java
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
public interface INavigableSurface {
|
||||
}
|
||||
33
src/main/java/baritone/builder/IReachabilityProvider.java
Normal file
33
src/main/java/baritone/builder/IReachabilityProvider.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
|
||||
public interface IReachabilityProvider {
|
||||
|
||||
LongList candidates(long playerEyeVoxel);
|
||||
|
||||
static IReachabilityProvider get(DependencyGraphScaffoldingOverlay overlay, PlayerReachSphere sphere) {
|
||||
try {
|
||||
return new ReachabilityCache(overlay, sphere);
|
||||
} catch (ReachabilityCache.SchematicIsTooDenseForThisToMakeSenseException ex) {
|
||||
return new ReachabilityLive(overlay, sphere);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
src/main/java/baritone/builder/IScaffolderStrategy.java
Normal file
27
src/main/java/baritone/builder/IScaffolderStrategy.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.LongList;
|
||||
|
||||
public interface IScaffolderStrategy {
|
||||
// TODO passing in DGSO is not ideal because it's mutable
|
||||
// TODO should it instead take in a list of (all) possible roots?
|
||||
LongList scaffoldTo(DependencyGraphScaffoldingOverlay.CollapsedDependencyGraph.CollapsedDependencyGraphComponent root, DependencyGraphScaffoldingOverlay overlayGraph);
|
||||
}
|
||||
23
src/main/java/baritone/builder/ISolverEngine.java
Normal file
23
src/main/java/baritone/builder/ISolverEngine.java
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
public interface ISolverEngine {
|
||||
|
||||
SolverEngineOutput solve(SolverEngineInput in);
|
||||
}
|
||||
346
src/main/java/baritone/builder/Main.java
Normal file
346
src/main/java/baritone/builder/Main.java
Normal file
@@ -0,0 +1,346 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.pathing.movement.ActionCosts;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.builder.mc.DebugStates;
|
||||
import baritone.builder.mc.VanillaBlockStateDataProvider;
|
||||
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class Main {
|
||||
|
||||
public static final boolean DEBUG = true;
|
||||
public static final boolean SLOW_DEBUG = false;
|
||||
public static final boolean VERY_SLOW_DEBUG = false;
|
||||
|
||||
/**
|
||||
* If true, many different parts of the builder switch to a more efficient mode where blocks can only be placed adjacent or upwards, never downwards
|
||||
*/
|
||||
public static final boolean STRICT_Y = true;
|
||||
|
||||
public static final BlockData DATA = new BlockData(new VanillaBlockStateDataProvider());
|
||||
|
||||
public static final Random RAND = new Random(5021);
|
||||
|
||||
public static void main() throws InterruptedException {
|
||||
System.out.println("Those costs are " + (ActionCosts.FALL_N_BLOCKS_COST[2] / 2) + " and " + ActionCosts.JUMP_ONE_BLOCK_COST + " and " + ActionCosts.FALL_N_BLOCKS_COST[1]);
|
||||
for (Face face : Face.VALUES) {
|
||||
System.out.println(face);
|
||||
System.out.println(face.x);
|
||||
System.out.println(face.y);
|
||||
System.out.println(face.z);
|
||||
System.out.println(face.index);
|
||||
System.out.println(face.offset);
|
||||
System.out.println(face.oppositeIndex);
|
||||
System.out.println("Horizontal " + face.horizontalIndex);
|
||||
}
|
||||
{
|
||||
System.out.println("Without");
|
||||
long start = BetterBlockPos.toLong(5021, 69, 420);
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.UP.offset;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.DOWN.offset;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.UP.offset;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.DOWN.offset;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
}
|
||||
{
|
||||
System.out.println("With");
|
||||
long start = BetterBlockPos.toLong(5021, 69, 420);
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.UP.offset;
|
||||
start &= BetterBlockPos.POST_ADDITION_MASK;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.DOWN.offset;
|
||||
start &= BetterBlockPos.POST_ADDITION_MASK;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.UP.offset;
|
||||
start &= BetterBlockPos.POST_ADDITION_MASK;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
start += Face.DOWN.offset;
|
||||
start &= BetterBlockPos.POST_ADDITION_MASK;
|
||||
System.out.println(BetterBlockPos.fromLong(start));
|
||||
}
|
||||
{
|
||||
System.out.println(BetterBlockPos.fromLong(BetterBlockPos.toLong(150, 150, 150)));
|
||||
}
|
||||
for (int i = 0; i < 0; i++) {
|
||||
Stream.of(new Object())
|
||||
.flatMap(ignored -> IntStream.range(0, 100).boxed())
|
||||
.parallel()
|
||||
.forEach(x -> System.out.println(x + ""));
|
||||
IntStream.range(100, 200).boxed()
|
||||
.parallel()
|
||||
.forEach(x -> System.out.println(x + ""));
|
||||
Stream.of(new Object())
|
||||
.flatMap(ignored -> IntStream.range(200, 300).boxed())
|
||||
.collect(Collectors.toList()).parallelStream()
|
||||
.forEach(x -> System.out.println(x + ""));
|
||||
}
|
||||
for (int aaaa = 0; aaaa < 0; aaaa++) {
|
||||
/*Raytracer.raytraceMode++;
|
||||
Raytracer.raytraceMode %= 3;*/
|
||||
Random rand = new Random(5021);
|
||||
DoubleArrayList A = new DoubleArrayList();
|
||||
DoubleArrayList B = new DoubleArrayList();
|
||||
DoubleArrayList C = new DoubleArrayList();
|
||||
DoubleArrayList D = new DoubleArrayList();
|
||||
DoubleArrayList E = new DoubleArrayList();
|
||||
DoubleArrayList F = new DoubleArrayList();
|
||||
LongArrayList G = new LongArrayList();
|
||||
long a = System.currentTimeMillis();
|
||||
for (int trial = 0; trial < 10_000_000; ) {
|
||||
Vec3d playerEye = new Vec3d(rand.nextDouble() * 5 - 2.5, rand.nextDouble() * 5, rand.nextDouble() * 5 - 2.5);
|
||||
long eyeBlock = playerEye.getRoundedToZeroPositionUnsafeDontUse();
|
||||
if (eyeBlock == 0) {
|
||||
// origin, unlucky
|
||||
continue;
|
||||
}
|
||||
Face placeToAgainst = Face.VALUES[rand.nextInt(Face.NUM_FACES)];
|
||||
Face againstToPlace = placeToAgainst.opposite();
|
||||
long placeAgainst = placeToAgainst.offset(0);
|
||||
if (eyeBlock == placeAgainst) {
|
||||
continue;
|
||||
}
|
||||
double[] hitVec = new double[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
switch (placeToAgainst.vec[i]) {
|
||||
case -1: {
|
||||
hitVec[i] = 0;
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
hitVec[i] = rand.nextDouble();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
hitVec[i] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Vec3d hit = new Vec3d(hitVec);
|
||||
Raytracer.runTrace(playerEye, placeAgainst, againstToPlace, hit);
|
||||
A.add(playerEye.x);
|
||||
B.add(playerEye.y);
|
||||
C.add(playerEye.z);
|
||||
D.add(hit.x);
|
||||
E.add(hit.y);
|
||||
F.add(hit.z);
|
||||
G.add(placeAgainst);
|
||||
trial++;
|
||||
}
|
||||
long b = System.currentTimeMillis();
|
||||
System.out.println("Nominal first run with overhead: " + (b - a) + "ms");
|
||||
boolean checkAgainst = true;
|
||||
for (int i = 0; i < 10_000_000; i++) {
|
||||
if (i % 1000 == 0 && checkAgainst) {
|
||||
System.out.println(i);
|
||||
}
|
||||
LongArrayList normal = Raytracer.rayTraceZoomy(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
|
||||
LongArrayList alternate = Raytracer.rayTraceZoomyAlternate(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
|
||||
if (!normal.equals(alternate)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (checkAgainst) {
|
||||
LongArrayList superSlow = Raytracer.rayTraceFast(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i));
|
||||
if (!normal.equals(superSlow)) {
|
||||
Raytracer.print(normal);
|
||||
Raytracer.print(superSlow);
|
||||
checkAgainst = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int it = 0; it < 20; it++) {
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 10_000_000; i++) {
|
||||
Raytracer.rayTraceZoomy(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Normal took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < 10_000_000; i++) {
|
||||
Raytracer.rayTraceZoomyAlternate(A.getDouble(i), B.getDouble(i), C.getDouble(i), D.getDouble(i), E.getDouble(i), F.getDouble(i), G.getLong(i));
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Alternate took " + (end - start) + "ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
DebugStates.debug();
|
||||
}
|
||||
for (int aaaa = 0; aaaa < 0; aaaa++) {
|
||||
Random rand = new Random(5021);
|
||||
int trials = 10_000_000;
|
||||
int[] X = new int[trials];
|
||||
int[] Y = new int[trials];
|
||||
int[] Z = new int[trials];
|
||||
int sz = 10;
|
||||
CuboidBounds bounds = new CuboidBounds(sz, sz, sz);
|
||||
for (int i = 0; i < trials; i++) {
|
||||
for (int[] toAdd : new int[][]{X, Y, Z}) {
|
||||
toAdd[i] = rand.nextBoolean() ? rand.nextInt(sz) : rand.nextBoolean() ? -1 : sz;
|
||||
}
|
||||
}
|
||||
boolean[] a = new boolean[trials];
|
||||
boolean[] b = new boolean[trials];
|
||||
boolean[] c = new boolean[trials];
|
||||
boolean[] d = new boolean[trials];
|
||||
boolean[] e = new boolean[trials];
|
||||
for (int it = 0; it < 20; it++) {
|
||||
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
a[i] = bounds.inRangeBranchy(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchy took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
b[i] = bounds.inRangeBranchless(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
c[i] = bounds.inRangeBranchless2(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless2 took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
d[i] = bounds.inRangeBranchless3(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless3 took " + (end - start) + "ms");
|
||||
}
|
||||
{
|
||||
Thread.sleep(1000);
|
||||
System.gc();
|
||||
Thread.sleep(1000);
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < trials; i++) {
|
||||
e[i] = bounds.inRangeBranchless4(X[i], Y[i], Z[i]);
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("Branchless4 took " + (end - start) + "ms");
|
||||
}
|
||||
/*
|
||||
Branchless2 took 55ms
|
||||
Branchless3 took 53ms
|
||||
Branchless4 took 47ms
|
||||
Branchy took 137ms
|
||||
Branchless took 35ms
|
||||
Branchless2 took 36ms
|
||||
Branchless3 took 35ms
|
||||
Branchless4 took 41ms
|
||||
Branchy took 118ms
|
||||
Branchless took 33ms
|
||||
Branchless2 took 39ms
|
||||
Branchless3 took 36ms
|
||||
Branchless4 took 42ms
|
||||
Branchy took 125ms
|
||||
Branchless took 41ms
|
||||
Branchless2 took 45ms
|
||||
Branchless3 took 41ms
|
||||
Branchless4 took 45ms
|
||||
Branchy took 123ms
|
||||
Branchless took 38ms
|
||||
Branchless2 took 43ms
|
||||
Branchless3 took 35ms
|
||||
Branchless4 took 43ms
|
||||
Branchy took 117ms
|
||||
Branchless took 37ms
|
||||
Branchless2 took 42ms
|
||||
Branchless3 took 41ms
|
||||
Branchless4 took 45ms
|
||||
Branchy took 123ms
|
||||
Branchless took 35ms
|
||||
Branchless2 took 42ms
|
||||
Branchless3 took 38ms
|
||||
Branchless4 took 46ms
|
||||
Branchy took 126ms
|
||||
Branchless took 34ms
|
||||
Branchless2 took 47ms
|
||||
Branchless3 took 40ms
|
||||
Branchless4 took 47ms
|
||||
Branchy took 124ms
|
||||
*/
|
||||
|
||||
// 3 is better than 2 and 4 because of data dependency
|
||||
// the L1 cache fetch for this.sizeX can happen at the same time as "x+1" (which is an increment of an argument)
|
||||
// in other words: in options 2 and 4, the "+1" or "-1" has a data dependency on the RAM fetch for this.sizeX, but in option 3 alone, the +1 happens upon the argument x, which is likely in a register, meaning it can be pipelined in parallel with the L1 cache fetch for this.sizeX
|
||||
|
||||
}
|
||||
}
|
||||
/*{ // proguard test
|
||||
PlayerPhysics.determinePlayerRealSupport(BlockStateCachedData.get(69), BlockStateCachedData.get(420));
|
||||
PlayerPhysics.determinePlayerRealSupport(BlockStateCachedData.get(420), BlockStateCachedData.get(69));
|
||||
}*/
|
||||
{
|
||||
for (int sneak = 0; sneak < 4; sneak++) {
|
||||
System.out.println("meow");
|
||||
System.out.println(sneak);
|
||||
System.out.println(SneakPosition.encode(0, sneak));
|
||||
System.out.println(SneakPosition.encode(BetterBlockPos.POST_ADDITION_MASK, sneak));
|
||||
System.out.println(SneakPosition.decode(SneakPosition.encode(0, sneak)));
|
||||
System.out.println(SneakPosition.decode(SneakPosition.encode(BetterBlockPos.POST_ADDITION_MASK, sneak)));
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
126
src/main/java/baritone/builder/NavigableSurface.java
Normal file
126
src/main/java/baritone/builder/NavigableSurface.java
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.builder.utils.com.github.btrekkie.connectivity.Augmentation;
|
||||
import baritone.builder.utils.com.github.btrekkie.connectivity.ConnGraph;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class NavigableSurface {
|
||||
|
||||
private final CuboidBounds bounds;
|
||||
|
||||
private final BlockStateCachedData[] blocks; // TODO switch to xzy ordering so columnFrom is faster
|
||||
|
||||
private final ConnGraph connGraph;
|
||||
|
||||
private final Function<BetterBlockPos, Object> genVertexAugmentation;
|
||||
|
||||
private final Column col1 = new Column();
|
||||
private final Column col2 = new Column();
|
||||
|
||||
public NavigableSurface(int x, int y, int z, Augmentation augmentation, Function<BetterBlockPos, Object> genVertexAugmentation) {
|
||||
this.bounds = new CuboidBounds(x, y, z);
|
||||
this.blocks = new BlockStateCachedData[bounds.volume()];
|
||||
Arrays.fill(blocks, FakeStates.AIR);
|
||||
this.genVertexAugmentation = genVertexAugmentation;
|
||||
this.connGraph = new ConnGraph(augmentation);
|
||||
|
||||
if (!genVertexAugmentation.apply(new BetterBlockPos(0, 0, 0)).equals(genVertexAugmentation.apply(new BetterBlockPos(0, 0, 0)))) {
|
||||
throw new IllegalStateException("RedBlackNode optimization requires correct impl of .equals on the attachment, to avoid percolating up spurious augmentation non-updates");
|
||||
}
|
||||
}
|
||||
|
||||
private void columnFrom(Column column, long pos) {
|
||||
column.underneath = getBlockOrAir((pos + Column.DOWN_1) & BetterBlockPos.POST_ADDITION_MASK);
|
||||
column.feet = getBlockOrAir(pos);
|
||||
column.head = getBlockOrAir((pos + Column.UP_1) & BetterBlockPos.POST_ADDITION_MASK);
|
||||
column.above = getBlockOrAir((pos + Column.UP_2) & BetterBlockPos.POST_ADDITION_MASK);
|
||||
column.aboveAbove = getBlockOrAir((pos + Column.UP_3) & BetterBlockPos.POST_ADDITION_MASK);
|
||||
column.init();
|
||||
}
|
||||
|
||||
protected void setBlock(long pos, BlockStateCachedData data) {
|
||||
blocks[bounds.toIndex(pos)] = data;
|
||||
for (int dy = -2; dy <= 1; dy++) {
|
||||
long couldHaveChanged = BetterBlockPos.offsetBy(pos, 0, dy, 0);
|
||||
columnFrom(col1, couldHaveChanged);
|
||||
boolean currentlyAllowed = col1.standing();
|
||||
if (currentlyAllowed) {
|
||||
// TODO skip the next line if it already has an augmentation?
|
||||
connGraph.setVertexAugmentation(couldHaveChanged, genVertexAugmentation.apply(BetterBlockPos.fromLong(couldHaveChanged)));
|
||||
|
||||
for (Face dir : Face.HORIZONTALS) {
|
||||
long adj = dir.offset(couldHaveChanged);
|
||||
columnFrom(col2, adj);
|
||||
Integer connDy = PlayerPhysics.bidirectionalPlayerTravel(col1, col2, getBlockOrAir((adj + Column.DOWN_2) & BetterBlockPos.POST_ADDITION_MASK), getBlockOrAir((adj + Column.DOWN_3) & BetterBlockPos.POST_ADDITION_MASK));
|
||||
for (int fakeDy = -2; fakeDy <= 2; fakeDy++) {
|
||||
long neighbor = BetterBlockPos.offsetBy(adj, 0, fakeDy, 0);
|
||||
if (((Integer) fakeDy).equals(connDy)) {
|
||||
connGraph.addEdge(couldHaveChanged, neighbor);
|
||||
} else {
|
||||
connGraph.removeEdge(couldHaveChanged, neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
connGraph.removeVertexAugmentation(couldHaveChanged);
|
||||
for (Face dir : Face.HORIZONTALS) {
|
||||
long adj = dir.offset(couldHaveChanged);
|
||||
for (int fakeDy = -2; fakeDy <= 2; fakeDy++) {
|
||||
connGraph.removeEdge(couldHaveChanged, BetterBlockPos.offsetBy(adj, 0, fakeDy, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setBlock(int x, int y, int z, BlockStateCachedData data) {
|
||||
setBlock(BetterBlockPos.toLong(x, y, z), data);
|
||||
}
|
||||
|
||||
public CuboidBounds bounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public BlockStateCachedData getBlock(long pos) {
|
||||
return blocks[bounds.toIndex(pos)];
|
||||
}
|
||||
|
||||
public BlockStateCachedData getBlockOrAir(long pos) {
|
||||
if (!bounds.inRangePos(pos)) {
|
||||
return FakeStates.AIR;
|
||||
}
|
||||
return getBlock(pos);
|
||||
}
|
||||
|
||||
public boolean hasBlock(BetterBlockPos pos) {
|
||||
return getBlockOrAir(pos.toLong()).collidesWithPlayer;
|
||||
}
|
||||
|
||||
public boolean connected(BetterBlockPos a, BetterBlockPos b) {
|
||||
return connGraph.connected(a.toLong(), b.toLong());
|
||||
}
|
||||
|
||||
public Object getComponentAugmentation(BetterBlockPos pos) { // maybe should be protected? subclass defines it anyway
|
||||
return connGraph.getComponentAugmentation(pos.toLong());
|
||||
}
|
||||
}
|
||||
66
src/main/java/baritone/builder/Node.java
Normal file
66
src/main/java/baritone/builder/Node.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
|
||||
public class Node {
|
||||
|
||||
private final long posAndSneak;
|
||||
public final long worldStateZobristHash;
|
||||
|
||||
public final int heuristic;
|
||||
public int cost;
|
||||
public int combinedCost;
|
||||
public Node previous;
|
||||
int heapPosition;
|
||||
|
||||
// boolean unrealizedZobristBlockChange; // no longer needed since presence in the overall GreedySolver zobristWorldStateCache indicates if this is a yet-unrealized branch of the zobrist space
|
||||
long packedUnrealizedCoordinate;
|
||||
// int unrealizedState; // no longer needed now that world state is binarized with scaffolding/build versus air
|
||||
// long unrealizedZobristParentHash; // no longer needed since we can compute it backwards with XOR
|
||||
|
||||
public Node(long pos, Face sneakingTowards, long zobristState, long unrealizedBlockPlacement, int heuristic) {
|
||||
this.posAndSneak = SneakPosition.encode(pos, sneakingTowards);
|
||||
this.heapPosition = -1;
|
||||
this.cost = Integer.MAX_VALUE;
|
||||
this.heuristic = heuristic;
|
||||
this.worldStateZobristHash = zobristState;
|
||||
this.packedUnrealizedCoordinate = unrealizedBlockPlacement;
|
||||
}
|
||||
|
||||
public boolean sneaking() {
|
||||
return SneakPosition.hasSneak(posAndSneak);
|
||||
}
|
||||
|
||||
public long pos() {
|
||||
return posAndSneak & BetterBlockPos.POST_ADDITION_MASK;
|
||||
}
|
||||
|
||||
public long sneakingPosition() {
|
||||
return posAndSneak;
|
||||
}
|
||||
|
||||
public long nodeMapKey() {
|
||||
return posAndSneak ^ worldStateZobristHash;
|
||||
}
|
||||
|
||||
public boolean inHeap() {
|
||||
return heapPosition >= 0;
|
||||
}
|
||||
}
|
||||
110
src/main/java/baritone/builder/NodeBinaryHeap.java
Normal file
110
src/main/java/baritone/builder/NodeBinaryHeap.java
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is part of Baritone.
|
||||
*
|
||||
* Baritone is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Baritone is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Baritone. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package baritone.builder;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NodeBinaryHeap {
|
||||
|
||||
private static final int INITIAL_CAPACITY = 1024;
|
||||
|
||||
private Node[] array;
|
||||
|
||||
private int size;
|
||||
|
||||
public NodeBinaryHeap() {
|
||||
this.size = INITIAL_CAPACITY;
|
||||
this.array = new Node[this.size];
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void insert(Node value) {
|
||||
if (size >= array.length - 1) {
|
||||
array = Arrays.copyOf(array, array.length << 1);
|
||||
}
|
||||
size++;
|
||||
value.heapPosition = size;
|
||||
array[size] = value;
|
||||
update(value);
|
||||
}
|
||||
|
||||
public void update(Node val) {
|
||||
int index = val.heapPosition;
|
||||
int parentInd = index >>> 1;
|
||||
int cost = val.combinedCost;
|
||||
Node parentNode = array[parentInd];
|
||||
while (index > 1 && parentNode.combinedCost > cost) {
|
||||
array[index] = parentNode;
|
||||
array[parentInd] = val;
|
||||
val.heapPosition = parentInd;
|
||||
parentNode.heapPosition = index;
|
||||
index = parentInd;
|
||||
parentInd = index >>> 1;
|
||||
parentNode = array[parentInd];
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
public Node removeLowest() {
|
||||
if (size == 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
Node result = array[1];
|
||||
Node val = array[size];
|
||||
array[1] = val;
|
||||
val.heapPosition = 1;
|
||||
array[size] = null;
|
||||
size--;
|
||||
result.heapPosition = -1;
|
||||
if (size < 2) {
|
||||
return result;
|
||||
}
|
||||
int index = 1;
|
||||
int smallerChild = 2;
|
||||
int cost = val.combinedCost;
|
||||
do {
|
||||
Node smallerChildNode = array[smallerChild];
|
||||
int smallerChildCost = smallerChildNode.combinedCost;
|
||||
if (smallerChild < size) {
|
||||
Node rightChildNode = array[smallerChild + 1];
|
||||
int rightChildCost = rightChildNode.combinedCost;
|
||||
if (smallerChildCost > rightChildCost) {
|
||||
smallerChild++;
|
||||
smallerChildCost = rightChildCost;
|
||||
smallerChildNode = rightChildNode;
|
||||
}
|
||||
}
|
||||
if (cost <= smallerChildCost) {
|
||||
break;
|
||||
}
|
||||
array[index] = smallerChildNode;
|
||||
array[smallerChild] = val;
|
||||
val.heapPosition = smallerChild;
|
||||
smallerChildNode.heapPosition = index;
|
||||
index = smallerChild;
|
||||
} while ((smallerChild <<= 1) <= size);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user