Compare commits
412 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 | ||
|
|
331a7f3842 | ||
|
|
25de16e051 | ||
|
|
a732e766f4 | ||
|
|
91854f97ad | ||
|
|
835fb3471f | ||
|
|
a9f497981f | ||
|
|
74dd88d123 | ||
|
|
fd61207709 | ||
|
|
b94494d386 | ||
|
|
974b19fcc7 | ||
|
|
364b7b5438 | ||
|
|
8c1a9f460d | ||
|
|
f6d4a315c7 | ||
|
|
0222c47113 | ||
|
|
fc1a2a6112 | ||
|
|
13547781d2 | ||
|
|
d375d1abc9 | ||
|
|
33d007eea5 | ||
|
|
5e4f31f39e | ||
|
|
79e0f4ba23 | ||
|
|
dba186347c | ||
|
|
7988274d61 | ||
|
|
72cf9392b1 | ||
|
|
b6265036d4 | ||
|
|
dc70d810c9 | ||
|
|
16cfcb36a3 | ||
|
|
6bec994a57 | ||
|
|
a99206b6c2 | ||
|
|
c491d67925 | ||
|
|
b9eeab06a1 | ||
|
|
853b5ca771 | ||
|
|
f4706fff18 | ||
|
|
7218a46211 | ||
|
|
c0f7d5ab44 | ||
|
|
60f81fb89d | ||
|
|
49bc96dc7e | ||
|
|
320a5d64a3 | ||
|
|
b16a8ecf21 | ||
|
|
73265ef944 | ||
|
|
3bbc141e9a | ||
|
|
9ca95e8e14 | ||
|
|
9922382581 | ||
|
|
4aa52d2f2e | ||
|
|
6d51e10090 | ||
|
|
14b75973b0 | ||
|
|
0bfd0bca0c | ||
|
|
cafb810a1f | ||
|
|
07704b3ec7 | ||
|
|
7032efcc05 | ||
|
|
65a5677b33 | ||
|
|
5d3522ca0a | ||
|
|
bfb530e02b | ||
|
|
9393192036 | ||
|
|
5cfefbb9ec | ||
|
|
1c8ccd4646 | ||
|
|
3cdbc4cb83 | ||
|
|
0c7741120a | ||
|
|
e529438c7e | ||
|
|
5a5d11922f | ||
|
|
39cfebeb34 | ||
|
|
950f47ccae | ||
|
|
d2c625e1c9 | ||
|
|
3f207b7041 | ||
|
|
8deb9e5a35 | ||
|
|
5eeb33a6f5 | ||
|
|
6f69316966 | ||
|
|
5e77ef97e4 | ||
|
|
918f009e8a | ||
|
|
d6665f1cd5 | ||
|
|
af29ed7146 | ||
|
|
8ddc795158 | ||
|
|
8a572516fc | ||
|
|
aebfbba20e | ||
|
|
85cc86346c | ||
|
|
b20e095683 | ||
|
|
b4d7f05165 | ||
|
|
46a12754e9 | ||
|
|
411b2a0acc | ||
|
|
508dacac63 | ||
|
|
13ca691f5d | ||
|
|
7e4d2c9b28 | ||
|
|
500c34c330 | ||
|
|
234fd3f75e | ||
|
|
32ef30686b | ||
|
|
175df6ce7a | ||
|
|
96b874c099 | ||
|
|
612510550f | ||
|
|
80e4852f90 | ||
|
|
071243b99a | ||
|
|
547db3a6b4 | ||
|
|
063f698bd2 | ||
|
|
bcbfca100f | ||
|
|
8206b1673c | ||
|
|
7255ccbdaa | ||
|
|
45dc8b949d | ||
|
|
10e3a5afc4 | ||
|
|
303aa79ffb | ||
|
|
35f3be9296 | ||
|
|
d7088c8eea | ||
|
|
695954bdb0 | ||
|
|
d29b3ee893 | ||
|
|
d9cecb35cb | ||
|
|
71dd6c6333 | ||
|
|
56f13d314a | ||
|
|
b628d67961 | ||
|
|
bf723deced | ||
|
|
9636e69a6b | ||
|
|
ed91e2aa7b | ||
|
|
3c691bb498 | ||
|
|
ea35a3478f | ||
|
|
b7d1ba1fa5 | ||
|
|
5f686c1c12 | ||
|
|
4eea8308d7 | ||
|
|
7f9704592e | ||
|
|
523f13c430 | ||
|
|
1c8079c051 | ||
|
|
2abd024b3c | ||
|
|
7992b63aae | ||
|
|
836ef3328c | ||
|
|
c470dc1a17 | ||
|
|
97a1029a72 | ||
|
|
5f9602796a | ||
|
|
02e7886e77 | ||
|
|
9851623557 | ||
|
|
f0d2bae3d1 | ||
|
|
f3561cab47 | ||
|
|
38d047dbd1 | ||
|
|
e6ba4ef309 | ||
|
|
460833f7d6 | ||
|
|
2eba22031a | ||
|
|
ac55de63e9 | ||
|
|
88e2fba447 | ||
|
|
94ac15a8fc | ||
|
|
19355cb4f2 | ||
|
|
e0ff16e2dd | ||
|
|
49db52673a | ||
|
|
f8872e1cd4 | ||
|
|
2ed3e10833 | ||
|
|
2b6fe188ed | ||
|
|
2ddc2c3660 | ||
|
|
b187d86074 | ||
|
|
727694fdd5 | ||
|
|
7081cf7172 | ||
|
|
a25afd5c13 | ||
|
|
c180eb9975 | ||
|
|
9e45998a65 | ||
|
|
2acf64a0d0 | ||
|
|
9d41367eff | ||
|
|
b64dd8f25f | ||
|
|
cbb3483456 | ||
|
|
6603e8b11e | ||
|
|
d522f9dfa3 | ||
|
|
f02476ff3a | ||
|
|
eafca1e1fe | ||
|
|
0055a059f7 | ||
|
|
8262909063 | ||
|
|
e42c19bfec | ||
|
|
0fed0d4371 | ||
|
|
8fe05c9119 | ||
|
|
e8e6a9bc5c | ||
|
|
c41eb4afa0 | ||
|
|
80c8294f5f | ||
|
|
fc58673abd | ||
|
|
1069dc9700 | ||
|
|
515a28dcfd | ||
|
|
ca831bc734 | ||
|
|
a76cf0fe9a | ||
|
|
688853c523 | ||
|
|
806dc8f017 | ||
|
|
89ab78a329 | ||
|
|
3a7cb5e020 | ||
|
|
d0f594065d | ||
|
|
10677040e0 | ||
|
|
c2624bbaaf | ||
|
|
33d4dd47bb | ||
|
|
ec92ab8b39 | ||
|
|
ff3234fcdd | ||
|
|
c32652394e | ||
|
|
580ba60762 | ||
|
|
0e7bf25de7 | ||
|
|
e218ced2c7 | ||
|
|
3cc4c0ea98 | ||
|
|
ac2a88d186 | ||
|
|
d6106a44f7 | ||
|
|
0434e1c5d3 | ||
|
|
4c79701d22 | ||
|
|
5f49bedc42 | ||
|
|
0fba32853b | ||
|
|
9f5d9bceaa | ||
|
|
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
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -18,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
|
||||
|
||||
31
.gitmessage
Normal file
31
.gitmessage
Normal file
@@ -0,0 +1,31 @@
|
||||
<emoji> <title> (<ticket>)
|
||||
|
||||
# 📝 Update README.md (WD-1234)
|
||||
# ✅ Add unit test for inputs (WD-1234)
|
||||
|
||||
# <emoji> can be:
|
||||
# 🎨 :art: when improving structure of the code
|
||||
# ⚡️ :zap: when improving performance
|
||||
# 🔥 :fire: when removing code or files
|
||||
# ✨ :sparkles: when introducing new features
|
||||
# 🚧 :construction: when work in progress
|
||||
# 🔨 :hammer: when refactoring code
|
||||
# 📝 :memo: when writing docs
|
||||
# 💄 :lipstick: when updating the UI and style files
|
||||
# 📈 :chart_with_upwards_trend: when adding analytics or tracking code
|
||||
# 🌐 :globe_with_meridians: when adding internationalization and localization
|
||||
# ✏️ :pencil2: when fixing typos
|
||||
# 🚚 :truck: when moving or renaming files
|
||||
# ✅ :white_check_mark: when adding tests
|
||||
|
||||
# 👌 :ok_hand: when updating code due to code review changes
|
||||
# 🐛 :bug: when fixing a bug
|
||||
# 🚑 :ambulance: when doing a critical hotfix
|
||||
# 🚨 :rotating_light: when removing linter warnings
|
||||
|
||||
# 🔀 :twisted_rightwards_arrows: when merging branches
|
||||
# ⬆️ :arrow_up: when upgrading dependencies
|
||||
# ⬇️ :arrow_down: when downgrading dependencies
|
||||
# 🔧 :wrench: when changing configuration files
|
||||
# 🔖 :bookmark: when releasing / version tagging
|
||||
# 💚 :green_heart: when fixing the CI build
|
||||
29
.travis.yml
29
.travis.yml
@@ -1,29 +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 run --name baritone cabaletta/baritone /bin/sh -c "set -e; /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -screen 0 128x128x24 -ac +extension GLX +render; DISPLAY=:99 BARITONE_AUTO_TEST=true ./gradlew runClient"
|
||||
- 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
|
||||
@@ -1,7 +1,5 @@
|
||||
FROM debian:stretch
|
||||
|
||||
RUN echo 'deb http://deb.debian.org/debian stretch-backports main' > /etc/apt/sources.list.d/stretch-backports.list
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
RUN apt update -y
|
||||
@@ -10,14 +8,8 @@ RUN apt install \
|
||||
openjdk-8-jdk \
|
||||
--assume-yes
|
||||
|
||||
RUN apt install -qq --assume-yes mesa-utils libgl1-mesa-glx libxcursor1 libxrandr2 libxxf86vm1 x11-xserver-utils xfonts-base xserver-common
|
||||
|
||||
COPY . /code
|
||||
|
||||
WORKDIR /code
|
||||
|
||||
# this .deb is specially patched to support lwjgl
|
||||
# source: https://github.com/tectonicus/tectonicus/issues/60#issuecomment-154239173
|
||||
RUN dpkg -i scripts/xvfb_1.16.4-1_amd64.deb
|
||||
|
||||
RUN ./gradlew build
|
||||
|
||||
105
README.md
105
README.md
@@ -1,46 +1,65 @@
|
||||
# Baritone
|
||||
[](https://travis-ci.com/cabaletta/baritone/)
|
||||
[](https://github.com/cabaletta/baritone/releases/)
|
||||
[](LICENSE)
|
||||
[](https://www.codacy.com/app/leijurv/baritone?utm_source=github.com&utm_medium=referral&utm_content=cabaletta/baritone&utm_campaign=Badge_Grade)
|
||||
[](http://hits.dwyl.com/cabaletta/baritone/)
|
||||
[](https://github.com/cabaletta/baritone/releases/)
|
||||
[](https://github.com/cabaletta/baritone/tree/master/)
|
||||
[](https://github.com/cabaletta/baritone/tree/1.13.2/)
|
||||
[](https://github.com/cabaletta/baritone/tree/1.14.4/)
|
||||
[](https://github.com/cabaletta/baritone/tree/1.15.2/)
|
||||
[](https://github.com/cabaletta/baritone/blob/master/CODE_OF_CONDUCT.md)
|
||||
[](https://snyk.io/test/github/cabaletta/baritone?targetFile=build.gradle)
|
||||
[](https://github.com/cabaletta/baritone/issues/)
|
||||
[](https://github.com/cabaletta/baritone/issues/)
|
||||
[](https://github.com/cabaletta/baritone/issues?q=is%3Aissue+is%3Aclosed)
|
||||
[](https://github.com/cabaletta/baritone/pulls/)
|
||||

|
||||

|
||||

|
||||
[](https://github.com/cabaletta/baritone/graphs/contributors/)
|
||||
[](https://github.com/cabaletta/baritone/commit/)
|
||||
[](https://impactclient.net/)
|
||||
[](https://github.com/fr1kin/ForgeHax/)
|
||||
[](https://gitlab.com/emc-mods-indrit/baritone_api)
|
||||
[](https://rootnet.dev/)
|
||||
[](https://wweclient.com/)
|
||||
[](https://futureclient.net/)
|
||||
[](http://forthebadge.com/)
|
||||
[](http://forthebadge.com/)
|
||||
<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>
|
||||
|
||||
A Minecraft pathfinder bot.
|
||||
<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>
|
||||
|
||||
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.
|
||||
<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>
|
||||
|
||||
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.* forge api jar from [releases](https://github.com/cabaletta/baritone/releases). Otherwise, see [Installation & setup](SETUP.md). Once Baritone is installed, look [here](USAGE.md) for instructions on how to use it.
|
||||
<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>
|
||||
|
||||
For 1.15.2, [click here](https://www.youtube.com/watch?v=j1qKtCZFURM) and see description.
|
||||
<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.15.2. 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).
|
||||
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.
|
||||
Have committed at least once a day from Aug 1, 2018, to Aug 1, 2019.
|
||||
|
||||
1Leijurv3DWTrGAfmmiTphjhXLvQiHg7K2
|
||||
|
||||
@@ -68,7 +87,7 @@ The API is heavily documented, you can find the Javadocs for the latest release
|
||||
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 a X/Z goal.
|
||||
Below is an example of basic usage for changing some settings, and then pathing to an X/Z goal.
|
||||
|
||||
```
|
||||
BaritoneAPI.getSettings().allowSprint.value = true;
|
||||
@@ -81,12 +100,22 @@ BaritoneAPI.getProvider().getPrimaryBaritone().getCustomGoalProcess().setGoalAnd
|
||||
|
||||
## Can I use Baritone as a library in my custom utility client?
|
||||
|
||||
That's what it's for, sure! (As long as usage is in compliance with the LGPL 3.0 License)
|
||||
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.
|
||||
It's named for FitMC's deep sultry voice.
|
||||
|
||||
38
SETUP.md
38
SETUP.md
@@ -2,7 +2,7 @@
|
||||
|
||||
The easiest way to install Baritone is to install [Impact](https://impactclient.net/), which comes with Baritone.
|
||||
|
||||
For 1.14.4, [click here](https://www.dropbox.com/s/rkml3hjokd3qv0m/1.14.4-Baritone.zip?dl=1).
|
||||
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
|
||||
|
||||
@@ -49,6 +44,27 @@ On Mac OSX and Linux, use `./gradlew` instead of `gradlew`.
|
||||
|
||||
If you have errors with a package missing please make sure you have setup your environment, and are using Oracle JDK 8.
|
||||
|
||||
To check which java you are using do
|
||||
`java -version` in a command prompt or terminal.
|
||||
If you are using anything above OpenJDK 8, it might not work because the Java distributions above JDK 8 using may not have the needed javax classes.
|
||||
|
||||
Open JDK 8 download: https://openjdk.java.net/install/
|
||||
#### macOS guide
|
||||
In order to get JDK 8, Try running the following command:
|
||||
`% /usr/libexec/java_home -V`
|
||||
If it doesn't work try this guide: https://stackoverflow.com/questions/46513639/how-to-downgrade-java-from-9-to-8-on-a-macos-eclipse-is-not-running-with-java-9
|
||||
|
||||
If you see something like
|
||||
|
||||
`% 1.8.0_VERSION, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_VERSION.jdk/Contents/Home`
|
||||
|
||||
in the list then you've got JDK 8 installed.
|
||||
In order to get JDK 8 running in the **current terminal window** you will have to run this command:
|
||||
|
||||
`% export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)`
|
||||
|
||||
To add OpenJDK 8 to your PATH add the export line to the end of your `.zshrc / .bashrc` if you want it to apply to each new terminal. If you're using bash change the .bachrc and if you're using zsh change the .zshrc
|
||||
|
||||
Setting up the Environment:
|
||||
|
||||
```
|
||||
@@ -62,6 +78,12 @@ Building Baritone:
|
||||
$ gradlew build
|
||||
```
|
||||
|
||||
For minecraft 1.15.2+, run the following instead to include the Forge jars:
|
||||
|
||||
```
|
||||
$ gradlew build -Pbaritone.forge_build
|
||||
```
|
||||
|
||||
Running Baritone:
|
||||
|
||||
```
|
||||
|
||||
10
USAGE.md
10
USAGE.md
@@ -24,6 +24,8 @@ Watch this [showcase video](https://youtu.be/CZkLXWo4Fg4)!
|
||||
|
||||
# Commands
|
||||
|
||||
[Tutorial playlist](https://www.youtube.com/playlist?list=PLnwnJ1qsS7CoQl9Si-RTluuzCo_4Oulpa)
|
||||
|
||||
**All** of these commands may need a prefix before them, as above ^.
|
||||
|
||||
`help`
|
||||
@@ -38,21 +40,21 @@ Some common examples:
|
||||
- `goal clear` to clear the goal
|
||||
- `cancel` or `stop` to stop everything
|
||||
- `goto portal` or `goto ender_chest` or `goto block_type` to go to a block. (in Impact, `.goto` is an alias for `.b goto` for the most part)
|
||||
- `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine diamond_ore 64`.
|
||||
- `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it (either at foot level or eye level), and left click and drag to clear all blocks from an area.
|
||||
- `mine diamond_ore iron_ore` to mine diamond ore or iron ore (turn on the setting `legitMine` to only mine ores that it can actually see. It will explore randomly around y=11 until it finds them.) An amount of blocks can also be specified, for example, `mine 64 diamond_ore`.
|
||||
- `click` to click your destination on the screen. Right click path to on top of the block, left click to path into it (either at foot level or eye level), and left click and drag to select an area (`#help sel` to see what you can do with that selection).
|
||||
- `follow player playerName` to follow a player. `follow players` to follow any players in range (combine with Kill Aura for a fun time). `follow entities` to follow any entities. `follow entity pig` to follow entities of a specific type.
|
||||
- `wp` for waypoints. A "tag" is like "home" (created automatically on right clicking a bed) or "death" (created automatically on death) or "user" (has to be created manually). So you might want `#wp save user coolbiome`, then to set the goal `#wp goal coolbiome` then `#path` to path to it. For death, `#wp goal death` will list waypoints under the "death" tag (remember stuff is clickable!)
|
||||
- `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.
|
||||
- `version` to get the version of Baritone you're running
|
||||
- `damn` daniel
|
||||
|
||||
For the rest of the commands, you can take a look at the code [here](https://github.com/cabaletta/baritone/blob/master/src/api/java/baritone/api/utils/ExampleBaritoneControl.java).
|
||||
For the rest of the commands, you can take a look at the code [here](https://baritone.leijurv.com/baritone/api/Settings.html).
|
||||
|
||||
All the settings and documentation are <a href="https://github.com/cabaletta/baritone/blob/master/src/api/java/baritone/api/Settings.java">here</a>. If you find HTML easier to read than Javadoc, you can look <a href="https://baritone.leijurv.com/baritone/api/Settings.html#field.detail">here</a>.
|
||||
|
||||
|
||||
35
build.gradle
35
build.gradle
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
group 'baritone'
|
||||
version '1.2.13'
|
||||
version '1.2.15'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
@@ -26,7 +26,7 @@ buildscript {
|
||||
}
|
||||
maven {
|
||||
name = 'SpongePowered'
|
||||
url = 'http://repo.spongepowered.org/maven'
|
||||
url = 'https://repo.spongepowered.org/repository/maven-public/'
|
||||
}
|
||||
jcenter()
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import baritone.gradle.task.CreateDistTask
|
||||
import baritone.gradle.task.ProguardTask
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'maven'
|
||||
apply plugin: 'net.minecraftforge.gradle.tweaker-client'
|
||||
apply plugin: 'org.spongepowered.mixin'
|
||||
|
||||
@@ -80,7 +81,7 @@ repositories {
|
||||
|
||||
maven {
|
||||
name = 'spongepowered-repo'
|
||||
url = 'http://repo.spongepowered.org/maven/'
|
||||
url = 'https://repo.spongepowered.org/repository/maven-public/'
|
||||
}
|
||||
|
||||
maven {
|
||||
@@ -117,6 +118,12 @@ javadoc {
|
||||
|
||||
jar {
|
||||
from sourceSets.launch.output, sourceSets.api.output
|
||||
|
||||
if (!getProject().hasProperty("baritone.forge_build")) {
|
||||
exclude "**/BaritoneForgeModXD.class"
|
||||
exclude "**/mods.toml"
|
||||
}
|
||||
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
|
||||
@@ -138,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)
|
||||
|
||||
@@ -21,13 +21,21 @@ 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.JavaVersion;
|
||||
import org.gradle.api.NamedDomainObjectContainer;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.artifacts.Dependency;
|
||||
import org.gradle.api.internal.file.IdentityFileResolver;
|
||||
import org.gradle.api.internal.plugins.DefaultConvention;
|
||||
import org.gradle.api.tasks.Input;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.TaskCollection;
|
||||
import org.gradle.api.tasks.compile.ForkOptions;
|
||||
import org.gradle.api.tasks.compile.JavaCompile;
|
||||
import org.gradle.internal.Pair;
|
||||
import org.gradle.internal.jvm.Jvm;
|
||||
import org.gradle.internal.jvm.inspection.DefaultJvmVersionDetector;
|
||||
import org.gradle.process.internal.DefaultExecActionFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -101,6 +109,101 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
}
|
||||
}
|
||||
|
||||
private String getJavaBinPathForProguard() throws Exception {
|
||||
String path;
|
||||
try {
|
||||
path = findJavaPathByGradleConfig();
|
||||
if (path != null) return path;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
System.err.println("Unable to find java by javaCompile options");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
try {
|
||||
path = findJavaByJavaHome();
|
||||
if (path != null) return path;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
System.err.println("Unable to find java by JAVA_HOME");
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
path = findJavaByGradleCurrentRuntime();
|
||||
if (path != null) return path;
|
||||
|
||||
throw new Exception("Unable to find java to determine ProGuard libraryjars. Please specify forkOptions.executable in javaCompile," +
|
||||
" JAVA_HOME environment variable, or make sure to run Gradle with the correct JDK (a v1.8 only)");
|
||||
}
|
||||
|
||||
private String findJavaByGradleCurrentRuntime() {
|
||||
String path = Jvm.current().getJavaExecutable().getAbsolutePath();
|
||||
|
||||
if (this.validateJavaVersion(path)) {
|
||||
System.out.println("Using Gradle's runtime Java for ProGuard");
|
||||
return path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String findJavaByJavaHome() {
|
||||
final String javaHomeEnv = System.getenv("JAVA_HOME");
|
||||
if (javaHomeEnv != null) {
|
||||
|
||||
String path = Jvm.forHome(new File(javaHomeEnv)).getJavaExecutable().getAbsolutePath();
|
||||
if (this.validateJavaVersion(path)) {
|
||||
System.out.println("Detected Java path by JAVA_HOME");
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String findJavaPathByGradleConfig() {
|
||||
final TaskCollection<JavaCompile> javaCompiles = super.getProject().getTasks().withType(JavaCompile.class);
|
||||
|
||||
final JavaCompile compileTask = javaCompiles.iterator().next();
|
||||
final ForkOptions forkOptions = compileTask.getOptions().getForkOptions();
|
||||
|
||||
if (forkOptions != null) {
|
||||
String javacPath = forkOptions.getExecutable();
|
||||
if (javacPath != null) {
|
||||
File javacFile = new File(javacPath);
|
||||
if (javacFile.exists()) {
|
||||
File[] maybeJava = javacFile.getParentFile().listFiles(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return name.equals("java");
|
||||
}
|
||||
});
|
||||
|
||||
if (maybeJava != null && maybeJava.length > 0) {
|
||||
String path = maybeJava[0].getAbsolutePath();
|
||||
if (this.validateJavaVersion(path)) {
|
||||
System.out.println("Detected Java path by forkOptions");
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean validateJavaVersion(String java) {
|
||||
final JavaVersion javaVersion = new DefaultJvmVersionDetector(new DefaultExecActionFactory(new IdentityFileResolver())).getJavaVersion(java);
|
||||
|
||||
if (!javaVersion.getMajorVersion().equals("8")) {
|
||||
System.out.println("Failed to validate Java version " + javaVersion.toString() + " [" + java + "] for ProGuard libraryjars");
|
||||
// throw new RuntimeException("Java version incorrect: " + javaVersion.getMajorVersion() + " for " + java);
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println("Validated Java version " + javaVersion.toString() + " [" + java + "] for ProGuard libraryjars");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void generateConfigs() throws Exception {
|
||||
Files.copy(getRelativeFile(PROGUARD_CONFIG_TEMPLATE), getTemporaryFile(PROGUARD_CONFIG_DEST), REPLACE_EXISTING);
|
||||
|
||||
@@ -110,7 +213,7 @@ public class ProguardTask extends BaritoneGradleTask {
|
||||
template.add(1, "-outjars " + this.getTemporaryFile(PROGUARD_EXPORT_PATH));
|
||||
|
||||
// Acquire the RT jar using "java -verbose". This doesn't work on Java 9+
|
||||
Process p = new ProcessBuilder("java", "-verbose").start();
|
||||
Process p = new ProcessBuilder(this.getJavaBinPathForProguard(), "-verbose").start();
|
||||
String out = IOUtils.toString(p.getInputStream(), "UTF-8").split("\n")[0].split("Opened ")[1].replace("]", "");
|
||||
template.add(2, "-libraryjars '" + out + "'");
|
||||
|
||||
|
||||
11
scripts/proguard.pro
vendored
11
scripts/proguard.pro
vendored
@@ -23,6 +23,13 @@
|
||||
|
||||
-keep class baritone.api.utils.MyChunkPos { *; } # even in standalone we need to keep this for gson reflect
|
||||
|
||||
# Keep any class or member annotated with @KeepName so we dont have to put everything in the script
|
||||
-keep,allowobfuscation @interface baritone.KeepName
|
||||
-keep @baritone.KeepName class *
|
||||
-keepclassmembers class * {
|
||||
@baritone.KeepName *;
|
||||
}
|
||||
|
||||
# setting names are reflected from field names, so keep field names
|
||||
-keepclassmembers class baritone.api.Settings {
|
||||
public <fields>;
|
||||
@@ -74,7 +81,7 @@
|
||||
|
||||
-libraryjars 'tempLibraries/netty-all-4.1.9.Final.jar'
|
||||
-libraryjars 'tempLibraries/oshi-core-1.1.jar'
|
||||
-libraryjars 'tempLibraries/patchy-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'
|
||||
@@ -374,3 +381,5 @@
|
||||
public java.lang.String substring(int);
|
||||
public java.lang.String substring(int,int);
|
||||
}
|
||||
|
||||
-printmapping mapping.txt
|
||||
|
||||
Binary file not shown.
@@ -20,13 +20,13 @@ package baritone.api;
|
||||
import baritone.api.behavior.ILookBehavior;
|
||||
import baritone.api.behavior.IPathingBehavior;
|
||||
import baritone.api.cache.IWorldProvider;
|
||||
import baritone.api.command.manager.ICommandManager;
|
||||
import baritone.api.event.listener.IEventBus;
|
||||
import baritone.api.pathing.calc.IPathingControlManager;
|
||||
import baritone.api.process.*;
|
||||
import baritone.api.selection.ISelectionManager;
|
||||
import baritone.api.utils.IInputOverrideHandler;
|
||||
import baritone.api.utils.IPlayerContext;
|
||||
import baritone.api.command.manager.ICommandManager;
|
||||
|
||||
/**
|
||||
* @author Brady
|
||||
|
||||
@@ -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;
|
||||
@@ -30,9 +32,10 @@ import java.awt.*;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
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.
|
||||
@@ -61,6 +64,19 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> allowInventory = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Disable baritone's auto-tool at runtime, but still assume that another mod will provide auto tool functionality
|
||||
* <p>
|
||||
* Specifically, path calculation will still assume that an auto tool will run at execution time, even though
|
||||
* Baritone itself will not do that.
|
||||
*/
|
||||
public final Setting<Boolean> assumeExternalAutoTool = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Automatically select the best available tool
|
||||
*/
|
||||
public final Setting<Boolean> autoTool = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* It doesn't actually take twenty ticks to place a block, this cost is so high
|
||||
* because we want to generally conserve blocks which might be limited.
|
||||
@@ -193,6 +209,38 @@ 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>
|
||||
* If a schematic asks for a block on this list, only air will be accepted at that location (and nothing on buildIgnoreBlocks)
|
||||
*/
|
||||
public final Setting<List<Block>> okIfAir = new Setting<>(new ArrayList<>(Arrays.asList(
|
||||
|
||||
)));
|
||||
|
||||
/**
|
||||
* If this is true, the builder will treat all non-air blocks as correct. It will only place new blocks.
|
||||
*/
|
||||
@@ -430,6 +478,11 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> simplifyUnloadedYCoord = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Whenever a block changes, repack the whole chunk that it's in
|
||||
*/
|
||||
public final Setting<Boolean> repackOnAnyBlockChange = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* If a movement takes this many ticks more than its initial cost estimate, cancel it
|
||||
*/
|
||||
@@ -502,6 +555,18 @@ public final class Settings {
|
||||
*/
|
||||
public final Setting<Boolean> backfill = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Shows popup message in the upper right corner, similarly to when you make an advancement
|
||||
*/
|
||||
public final Setting<Boolean> logAsToast = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* The time of how long the message in the pop-up will display
|
||||
* <p>
|
||||
* If below 1000L (1sec), it's better to disable this
|
||||
*/
|
||||
public final Setting<Long> toastTimer = new Setting<>(5000L);
|
||||
|
||||
/**
|
||||
* Print all the debug messages to chat
|
||||
*/
|
||||
@@ -533,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
|
||||
*/
|
||||
@@ -602,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.
|
||||
*/
|
||||
@@ -657,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.
|
||||
@@ -700,7 +781,24 @@ public final class Settings {
|
||||
public final Setting<Integer> maxCachedWorldScanCount = new Setting<>(10);
|
||||
|
||||
/**
|
||||
* When GetToBlock doesn't know any locations for the desired block, explore randomly instead of giving up.
|
||||
* Sets the minimum y level whilst mining - set to 0 to turn off.
|
||||
*/
|
||||
public final Setting<Integer> minYLevelWhileMining = new Setting<>(0);
|
||||
|
||||
/**
|
||||
* This will only allow baritone to mine exposed ores, can be used to stop ore obfuscators on servers that use them.
|
||||
*/
|
||||
public final Setting<Boolean> allowOnlyExposedOres = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* When allowOnlyExposedOres is enabled this is the distance around to search.
|
||||
* <p>
|
||||
* It is recommended to keep this value low, as it dramatically increases calculation times.
|
||||
*/
|
||||
public final Setting<Integer> allowOnlyExposedOresDistance = new Setting<>(1);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@@ -752,6 +850,27 @@ 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
|
||||
*/
|
||||
public final Setting<Integer> startAtLayer = new Setting<>(0);
|
||||
|
||||
/**
|
||||
* If a layer is unable to be constructed, just skip it.
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -762,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>
|
||||
@@ -870,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);
|
||||
|
||||
@@ -956,6 +1083,20 @@ public final class Settings {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
@@ -1047,10 +1188,40 @@ public final class Settings {
|
||||
public final Setting<Boolean> renderSelectionCorners = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop Notifications
|
||||
* Use sword to mine.
|
||||
*/
|
||||
public final Setting<Boolean> useSwordToMine = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notifications
|
||||
*/
|
||||
public final Setting<Boolean> desktopNotifications = new Setting<>(false);
|
||||
|
||||
/**
|
||||
* Desktop notification on path complete
|
||||
*/
|
||||
public final Setting<Boolean> notificationOnPathComplete = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notification on farm fail
|
||||
*/
|
||||
public final Setting<Boolean> notificationOnFarmFail = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notification on build finished
|
||||
*/
|
||||
public final Setting<Boolean> notificationOnBuildFinished = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notification on explore finished
|
||||
*/
|
||||
public final Setting<Boolean> notificationOnExploreFinished = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* Desktop notification on mine fail
|
||||
*/
|
||||
public final Setting<Boolean> notificationOnMineFail = new Setting<>(true);
|
||||
|
||||
/**
|
||||
* A map of lowercase setting field names to their respective setting
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -88,7 +88,7 @@ public interface IWorldScanner {
|
||||
* Queues the chunks in a square formation around the specified player, using the specified
|
||||
* range, which represents 1/2 the square's dimensions, where the player is in the center.
|
||||
*
|
||||
* @param ctx The player, describing the origin
|
||||
* @param ctx The player, describing the origin
|
||||
* @param range The range to repack
|
||||
* @return The amount of chunks successfully queued for repacking
|
||||
*/
|
||||
|
||||
@@ -34,9 +34,8 @@ import java.util.stream.Stream;
|
||||
* So basically, you should use it because it provides a small amount of boilerplate,
|
||||
* but you're not forced to use it.
|
||||
*
|
||||
* @see ICommand
|
||||
*
|
||||
* @author LoganDark
|
||||
* @see ICommand
|
||||
*/
|
||||
public abstract class Command implements ICommand {
|
||||
|
||||
|
||||
@@ -46,8 +46,8 @@ public interface IArgParserManager {
|
||||
/**
|
||||
* Attempt to parse the specified argument with a stateless {@link IArgParser} that outputs the specified class.
|
||||
*
|
||||
* @param type The type to try and parse the argument into.
|
||||
* @param arg The argument to parse.
|
||||
* @param type The type to try and parse the argument into.
|
||||
* @param arg The argument to parse.
|
||||
* @return An instance of the specified class.
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
*/
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
package baritone.api.command.argument;
|
||||
|
||||
import baritone.api.command.ICommand;
|
||||
import baritone.api.command.exception.CommandTooManyArgumentsException;
|
||||
import baritone.api.utils.Helper;
|
||||
import baritone.api.command.argparser.IArgParser;
|
||||
import baritone.api.command.datatypes.IDatatype;
|
||||
import baritone.api.command.datatypes.IDatatypeFor;
|
||||
@@ -27,6 +25,8 @@ import baritone.api.command.datatypes.IDatatypePost;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.exception.CommandInvalidTypeException;
|
||||
import baritone.api.command.exception.CommandNotEnoughArgumentsException;
|
||||
import baritone.api.command.exception.CommandTooManyArgumentsException;
|
||||
import baritone.api.utils.Helper;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import java.util.Deque;
|
||||
@@ -223,7 +223,7 @@ public interface IArgConsumer {
|
||||
* @param type The type to peek as
|
||||
* @param index The index to peek
|
||||
* @return An instance of the specified type
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @see IArgParser
|
||||
* @see #peekAs(Class)
|
||||
* @see #peekAsOrDefault(Class, Object, int)
|
||||
@@ -240,7 +240,7 @@ public interface IArgConsumer {
|
||||
*
|
||||
* @param type The type to peek as
|
||||
* @return An instance of the specified type
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @see IArgParser
|
||||
* @see #peekAs(Class, int)
|
||||
* @see #peekAsOrDefault(Class, Object)
|
||||
@@ -458,7 +458,7 @@ public interface IArgConsumer {
|
||||
*
|
||||
* @param type The type to peek as
|
||||
* @return An instance of the specified type
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @see IArgParser
|
||||
* @see #get()
|
||||
* @see #getAsOrDefault(Class, Object)
|
||||
|
||||
@@ -87,7 +87,7 @@ public interface ICommandArgument {
|
||||
*
|
||||
* @param type The class to parse this argument into
|
||||
* @return An instance of the specified type
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
* @throws CommandInvalidTypeException If the parsing failed
|
||||
*/
|
||||
<T, S> T getAs(Class<T> type, Class<S> stateType, S state) throws CommandInvalidTypeException;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
@@ -32,7 +32,19 @@ public enum EntityClassById implements IDatatypeFor<Class<? extends Entity>> {
|
||||
public Class<? extends Entity> get(IDatatypeContext ctx) throws CommandException {
|
||||
ResourceLocation id = new ResourceLocation(ctx.getConsumer().getString());
|
||||
Class<? extends Entity> entity;
|
||||
if ((entity = EntityList.REGISTRY.getObject(id)) == null) {
|
||||
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;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.utils.BlockOptionalMeta;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.utils.BlockOptionalMeta;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
@@ -20,8 +20,8 @@ package baritone.api.command.datatypes;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.cache.IWaypoint;
|
||||
import baritone.api.cache.IWaypointCollection;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -24,9 +24,8 @@ import baritone.api.command.argument.IArgConsumer;
|
||||
* Provides an {@link IDatatype} with contextual information so
|
||||
* that it can perform the desired operation on the target level.
|
||||
*
|
||||
* @see IDatatype
|
||||
*
|
||||
* @author Brady
|
||||
* @see IDatatype
|
||||
* @since 9/26/2019
|
||||
*/
|
||||
public interface IDatatypeContext {
|
||||
|
||||
@@ -34,11 +34,10 @@ public interface IDatatypeFor<T> extends IDatatype {
|
||||
* if the expected input does not conform to a parseable value. As far as a {@link CommandException} being
|
||||
* thrown is concerned, see the note below for specifics.
|
||||
*
|
||||
* @see IDatatypeContext
|
||||
*
|
||||
* @param ctx The context
|
||||
* @return The parsed data-type
|
||||
* @throws CommandException If there was an issue parsing using another type or arguments could not be polled.
|
||||
* @see IDatatypeContext
|
||||
*/
|
||||
T get(IDatatypeContext ctx) throws CommandException;
|
||||
}
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.helpers.TabCompleteHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
@@ -18,16 +18,13 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.pathing.goals.Goal;
|
||||
import baritone.api.pathing.goals.GoalBlock;
|
||||
import baritone.api.pathing.goals.GoalXZ;
|
||||
import baritone.api.pathing.goals.GoalYLevel;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum RelativeGoal implements IDatatypePost<Goal, BetterBlockPos> {
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.pathing.goals.GoalBlock;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.pathing.goals.GoalXZ;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
package baritone.api.command.datatypes;
|
||||
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.pathing.goals.GoalYLevel;
|
||||
import baritone.api.utils.BetterBlockPos;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class CommandUnhandledException extends RuntimeException implements IComm
|
||||
@Override
|
||||
public void handle(ICommand command, List<ICommandArgument> args) {
|
||||
HELPER.logDirect("An unhandled exception occurred. " +
|
||||
"The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues",
|
||||
"The error is in your game's log, please report this at https://github.com/cabaletta/baritone/issues",
|
||||
TextFormatting.RED);
|
||||
|
||||
this.printStackTrace();
|
||||
|
||||
@@ -38,8 +38,8 @@ import static baritone.api.utils.Helper.HELPER;
|
||||
public interface ICommandException {
|
||||
|
||||
/**
|
||||
* @see Exception#getMessage()
|
||||
* @return The exception details
|
||||
* @see Exception#getMessage()
|
||||
*/
|
||||
String getMessage();
|
||||
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
|
||||
package baritone.api.command.helpers;
|
||||
|
||||
import baritone.api.utils.Helper;
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.exception.CommandException;
|
||||
import baritone.api.command.exception.CommandInvalidTypeException;
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.utils.Helper;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.TextComponentString;
|
||||
import net.minecraft.util.text.TextFormatting;
|
||||
|
||||
@@ -19,10 +19,10 @@ package baritone.api.command.helpers;
|
||||
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.Settings;
|
||||
import baritone.api.event.events.TabCompleteEvent;
|
||||
import baritone.api.utils.SettingsUtil;
|
||||
import baritone.api.command.argument.IArgConsumer;
|
||||
import baritone.api.command.manager.ICommandManager;
|
||||
import baritone.api.event.events.TabCompleteEvent;
|
||||
import baritone.api.utils.SettingsUtil;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import java.util.Comparator;
|
||||
@@ -236,7 +236,6 @@ public class TabCompleteHelper {
|
||||
* Appends every command in the specified {@link ICommandManager} to this {@link TabCompleteHelper}
|
||||
*
|
||||
* @param manager A command manager
|
||||
*
|
||||
* @return This {@link TabCompleteHelper}
|
||||
*/
|
||||
public TabCompleteHelper addCommands(ICommandManager manager) {
|
||||
@@ -254,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)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package baritone.api.event.events;
|
||||
|
||||
import baritone.api.event.events.type.Cancellable;
|
||||
import baritone.api.event.events.type.Overrideable;
|
||||
|
||||
/**
|
||||
* @author LoganDark
|
||||
|
||||
@@ -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;
|
||||
@@ -31,7 +33,7 @@ public class GoalRunAway implements Goal {
|
||||
|
||||
private final BlockPos[] from;
|
||||
|
||||
private final double distanceSq;
|
||||
private final int distanceSq;
|
||||
|
||||
private final Integer maintainY;
|
||||
|
||||
@@ -44,7 +46,7 @@ public class GoalRunAway implements Goal {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
this.from = from;
|
||||
this.distanceSq = distance * distance;
|
||||
this.distanceSq = (int) (distance * distance);
|
||||
this.maintainY = maintainY;
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ public class GoalRunAway implements Goal {
|
||||
for (BlockPos p : from) {
|
||||
int diffX = x - p.getX();
|
||||
int diffZ = z - p.getZ();
|
||||
double distSq = diffX * diffX + diffZ * diffZ;
|
||||
int distSq = diffX * diffX + diffZ * diffZ;
|
||||
if (distSq < distanceSq) {
|
||||
return false;
|
||||
}
|
||||
@@ -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(
|
||||
|
||||
@@ -75,7 +75,7 @@ public interface IBaritoneProcess {
|
||||
* to start eating this tick. {@code PauseForAutoEatProcess} should only actually right click once onTick is called with
|
||||
* {@code isSafeToCancel} true though.
|
||||
*
|
||||
* @return Whethor or not if this control is temporary
|
||||
* @return Whether or not if this control is temporary
|
||||
*/
|
||||
boolean isTemporary();
|
||||
|
||||
|
||||
@@ -17,7 +17,29 @@
|
||||
|
||||
package baritone.api.process;
|
||||
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
public interface IFarmProcess extends IBaritoneProcess {
|
||||
|
||||
void farm();
|
||||
/**
|
||||
* Begin to search for crops to farm with in specified aria
|
||||
* from specified location.
|
||||
*
|
||||
* @param range The distance from center to farm from
|
||||
* @param pos The center position to base the range from
|
||||
*/
|
||||
void farm(int range, BlockPos pos);
|
||||
|
||||
/**
|
||||
* Begin to search for nearby crops to farm.
|
||||
*/
|
||||
default void farm() {farm(0, null);}
|
||||
|
||||
/**
|
||||
* Begin to search for crops to farm with in specified aria
|
||||
* from the position the command was executed.
|
||||
*
|
||||
* @param range The distance to search for crops to farm
|
||||
*/
|
||||
default void farm(int range) {farm(range, null);}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -22,10 +22,9 @@ import net.minecraft.block.state.IBlockState;
|
||||
/**
|
||||
* A static schematic is capable of providing the desired state at a given position without
|
||||
* additional context. Schematics of this type are expected to have non-varying contents.
|
||||
*
|
||||
* @see #getDirect(int, int, int)
|
||||
*
|
||||
* @author Brady
|
||||
* @see #getDirect(int, int, int)
|
||||
* @since 12/24/2019
|
||||
*/
|
||||
public interface IStaticSchematic extends ISchematic {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,11 +234,10 @@ public final class BlockOptionalMeta {
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @see #normalize(IBlockState)
|
||||
*
|
||||
*
|
||||
* @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));
|
||||
|
||||
@@ -63,6 +63,77 @@ public interface Helper {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to display as a toast popup
|
||||
*
|
||||
* @param title The title to display in the popup
|
||||
* @param message The message to display in the popup
|
||||
*/
|
||||
default void logToast(ITextComponent title, ITextComponent message) {
|
||||
mc.addScheduledTask(() -> BaritoneAPI.getSettings().toaster.value.accept(title, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to display as a toast popup
|
||||
*
|
||||
* @param title The title to display in the popup
|
||||
* @param message The message to display in the popup
|
||||
*/
|
||||
default void logToast(String title, String message) {
|
||||
logToast(new TextComponentString(title), new TextComponentString(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to display as a toast popup
|
||||
*
|
||||
* @param message The message to display in the popup
|
||||
*/
|
||||
default void logToast(String message) {
|
||||
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
|
||||
*
|
||||
@@ -74,7 +145,31 @@ public interface Helper {
|
||||
//System.out.println(message);
|
||||
return;
|
||||
}
|
||||
logDirect(message);
|
||||
// We won't log debug chat into toasts
|
||||
// Because only a madman would want that extreme spam -_-
|
||||
logDirect(message, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send components to chat with the [Baritone] prefix
|
||||
*
|
||||
* @param logAsToast Whether to log as a toast notification
|
||||
* @param components The components to send
|
||||
*/
|
||||
default void logDirect(boolean logAsToast, ITextComponent... components) {
|
||||
ITextComponent component = new TextComponentString("");
|
||||
if (!logAsToast) {
|
||||
// If we are not logging as a Toast
|
||||
// Append the prefix to the base component line
|
||||
component.appendSibling(getPrefix());
|
||||
component.appendSibling(new TextComponentString(" "));
|
||||
}
|
||||
Arrays.asList(components).forEach(component::appendSibling);
|
||||
if (logAsToast) {
|
||||
logToast(getPrefix(), component);
|
||||
} else {
|
||||
mc.addScheduledTask(() -> BaritoneAPI.getSettings().logger.value.accept(component));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,11 +178,23 @@ public interface Helper {
|
||||
* @param components The components to send
|
||||
*/
|
||||
default void logDirect(ITextComponent... components) {
|
||||
ITextComponent component = new TextComponentString("");
|
||||
component.appendSibling(getPrefix());
|
||||
component.appendSibling(new TextComponentString(" "));
|
||||
Arrays.asList(components).forEach(component::appendSibling);
|
||||
Minecraft.getMinecraft().addScheduledTask(() -> BaritoneAPI.getSettings().logger.value.accept(component));
|
||||
logDirect(BaritoneAPI.getSettings().logAsToast.value, components);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
|
||||
* direct response to a chat command)
|
||||
*
|
||||
* @param message The message to display in chat
|
||||
* @param color The color to print that message in
|
||||
* @param logAsToast Whether to log as a toast notification
|
||||
*/
|
||||
default void logDirect(String message, TextFormatting color, boolean logAsToast) {
|
||||
Stream.of(message.split("\n")).forEach(line -> {
|
||||
ITextComponent component = new TextComponentString(line.replace("\t", " "));
|
||||
component.getStyle().setColor(color);
|
||||
logDirect(logAsToast, component);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -98,11 +205,18 @@ public interface Helper {
|
||||
* @param color The color to print that message in
|
||||
*/
|
||||
default void logDirect(String message, TextFormatting color) {
|
||||
Stream.of(message.split("\n")).forEach(line -> {
|
||||
ITextComponent component = new TextComponentString(line.replace("\t", " "));
|
||||
component.getStyle().setColor(color);
|
||||
logDirect(component);
|
||||
});
|
||||
logDirect(message, color, BaritoneAPI.getSettings().logAsToast.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to chat regardless of chatDebug (should only be used for critically important messages, or as a
|
||||
* direct response to a chat command)
|
||||
*
|
||||
* @param message The message to display in chat
|
||||
* @param logAsToast Whether to log as a toast notification
|
||||
*/
|
||||
default void logDirect(String message, boolean logAsToast) {
|
||||
logDirect(message, TextFormatting.GRAY, logAsToast);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,6 +226,6 @@ public interface Helper {
|
||||
* @param message The message to display in chat
|
||||
*/
|
||||
default void logDirect(String message) {
|
||||
logDirect(message, TextFormatting.GRAY);
|
||||
logDirect(message, BaritoneAPI.getSettings().logAsToast.value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ package baritone.api.utils;
|
||||
import baritone.api.cache.IWorldData;
|
||||
import net.minecraft.block.BlockSlab;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
@@ -97,17 +96,4 @@ public interface IPlayerContext {
|
||||
default boolean isLookingAt(BlockPos pos) {
|
||||
return getSelectedBlock().equals(Optional.of(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the entity that the crosshair is currently placed over. Updated once per tick.
|
||||
*
|
||||
* @return The entity
|
||||
*/
|
||||
default Optional<Entity> getSelectedEntity() {
|
||||
RayTraceResult result = objectMouseOver();
|
||||
if (result != null && result.typeOfHit == RayTraceResult.Type.ENTITY) {
|
||||
return Optional.of(result.entityHit);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -35,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;
|
||||
@@ -49,6 +50,7 @@ public class SettingsUtil {
|
||||
|
||||
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("//");
|
||||
@@ -110,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) {
|
||||
@@ -164,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) {
|
||||
@@ -261,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$$;
|
||||
|
||||
78
src/api/java/baritone/api/utils/gui/BaritoneToast.java
Normal file
78
src/api/java/baritone/api/utils/gui/BaritoneToast.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.utils.gui;
|
||||
|
||||
import net.minecraft.client.gui.toasts.GuiToast;
|
||||
import net.minecraft.client.gui.toasts.IToast;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.text.ITextComponent;
|
||||
|
||||
public class BaritoneToast implements IToast {
|
||||
private String title;
|
||||
private String subtitle;
|
||||
private long firstDrawTime;
|
||||
private boolean newDisplay;
|
||||
private long totalShowTime;
|
||||
|
||||
public BaritoneToast(ITextComponent titleComponent, ITextComponent subtitleComponent, long totalShowTime) {
|
||||
this.title = titleComponent.getFormattedText();
|
||||
this.subtitle = subtitleComponent == null ? null : subtitleComponent.getFormattedText();
|
||||
this.totalShowTime = totalShowTime;
|
||||
}
|
||||
|
||||
public Visibility draw(GuiToast toastGui, long delta) {
|
||||
if (this.newDisplay) {
|
||||
this.firstDrawTime = delta;
|
||||
this.newDisplay = false;
|
||||
}
|
||||
|
||||
toastGui.getMinecraft().getTextureManager().bindTexture(new ResourceLocation("textures/gui/toasts.png"));
|
||||
GlStateManager.color(1.0F, 1.0F, 1.0F, 255.0f);
|
||||
toastGui.drawTexturedModalRect(0, 0, 0, 32, 160, 32);
|
||||
|
||||
if (this.subtitle == null) {
|
||||
toastGui.getMinecraft().fontRenderer.drawString(this.title, 18, 12, -11534256);
|
||||
} else {
|
||||
toastGui.getMinecraft().fontRenderer.drawString(this.title, 18, 7, -11534256);
|
||||
toastGui.getMinecraft().fontRenderer.drawString(this.subtitle, 18, 18, -16777216);
|
||||
}
|
||||
|
||||
return delta - this.firstDrawTime < totalShowTime ? Visibility.SHOW : Visibility.HIDE;
|
||||
}
|
||||
|
||||
public void setDisplayedText(ITextComponent titleComponent, ITextComponent subtitleComponent) {
|
||||
this.title = titleComponent.getFormattedText();
|
||||
this.subtitle = subtitleComponent == null ? null : subtitleComponent.getFormattedText();
|
||||
this.newDisplay = true;
|
||||
}
|
||||
|
||||
public static void addOrUpdate(GuiToast toast, ITextComponent title, ITextComponent subtitle, long totalShowTime) {
|
||||
BaritoneToast baritonetoast = toast.getToast(BaritoneToast.class, new Object());
|
||||
|
||||
if (baritonetoast == null) {
|
||||
toast.add(new BaritoneToast(title, subtitle, totalShowTime));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import baritone.api.event.events.BlockInteractEvent;
|
||||
import baritone.api.event.events.TickEvent;
|
||||
import baritone.api.event.events.WorldEvent;
|
||||
import baritone.api.event.events.type.EventState;
|
||||
import baritone.utils.BaritoneAutoTest;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.GuiScreen;
|
||||
@@ -63,17 +62,6 @@ public class MixinMinecraft {
|
||||
BaritoneAPI.getProvider().getPrimaryBaritone();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "init",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/client/Minecraft.startTimerHackThread()V"
|
||||
)
|
||||
)
|
||||
private void preInit(CallbackInfo ci) {
|
||||
BaritoneAutoTest.INSTANCE.onPreInit();
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "runTick",
|
||||
at = @At(
|
||||
|
||||
@@ -17,14 +17,19 @@
|
||||
|
||||
package baritone.launch.mixins;
|
||||
|
||||
import baritone.Baritone;
|
||||
import baritone.api.BaritoneAPI;
|
||||
import baritone.api.IBaritone;
|
||||
import baritone.api.event.events.ChunkEvent;
|
||||
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.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;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
@@ -80,6 +85,68 @@ public class MixinNetHandlerPlayClient {
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "handleBlockChange",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postHandleBlockChange(SPacketBlockChange packetIn, CallbackInfo ci) {
|
||||
if (!Baritone.settings().repackOnAnyBlockChange.value) {
|
||||
return;
|
||||
}
|
||||
if (!CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(packetIn.getBlockState().getBlock())) {
|
||||
return;
|
||||
}
|
||||
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.POPULATE_FULL,
|
||||
packetIn.getBlockPosition().getX() >> 4,
|
||||
packetIn.getBlockPosition().getZ() >> 4
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "handleMultiBlockChange",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private void postHandleMultiBlockChange(SPacketMultiBlockChange packetIn, CallbackInfo ci) {
|
||||
if (!Baritone.settings().repackOnAnyBlockChange.value) {
|
||||
return;
|
||||
}
|
||||
if (packetIn.getChangedBlocks().length == 0) {
|
||||
return;
|
||||
}
|
||||
https://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.15
|
||||
{
|
||||
for (SPacketMultiBlockChange.BlockUpdateData update : packetIn.getChangedBlocks()) {
|
||||
if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(update.getBlockState().getBlock())) {
|
||||
break https;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
ChunkPos pos = new ChunkPos(packetIn.getChangedBlocks()[0].getPos());
|
||||
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.POPULATE_FULL,
|
||||
pos.x,
|
||||
pos.z
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "handleCombatEvent",
|
||||
at = @At(
|
||||
|
||||
@@ -24,12 +24,16 @@ 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;
|
||||
import baritone.process.*;
|
||||
import baritone.selection.SelectionManager;
|
||||
import baritone.utils.*;
|
||||
import baritone.command.manager.CommandManager;
|
||||
import baritone.utils.BlockStateInterface;
|
||||
import baritone.utils.GuiClick;
|
||||
import baritone.utils.InputOverrideHandler;
|
||||
import baritone.utils.PathingControlManager;
|
||||
import baritone.utils.player.PrimaryPlayerContext;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
@@ -104,22 +108,27 @@ public class Baritone implements IBaritone {
|
||||
|
||||
this.pathingControlManager = new PathingControlManager(this);
|
||||
{
|
||||
followProcess = new FollowProcess(this);
|
||||
mineProcess = new MineProcess(this);
|
||||
customGoalProcess = new CustomGoalProcess(this); // very high iq
|
||||
getToBlockProcess = new GetToBlockProcess(this);
|
||||
builderProcess = new BuilderProcess(this);
|
||||
exploreProcess = new ExploreProcess(this);
|
||||
backfillProcess = new BackfillProcess(this);
|
||||
farmProcess = new FarmProcess(this);
|
||||
this.pathingControlManager.registerProcess(followProcess = new FollowProcess(this));
|
||||
this.pathingControlManager.registerProcess(mineProcess = new MineProcess(this));
|
||||
this.pathingControlManager.registerProcess(customGoalProcess = new CustomGoalProcess(this)); // very high iq
|
||||
this.pathingControlManager.registerProcess(getToBlockProcess = new GetToBlockProcess(this));
|
||||
this.pathingControlManager.registerProcess(builderProcess = new BuilderProcess(this));
|
||||
this.pathingControlManager.registerProcess(exploreProcess = new ExploreProcess(this));
|
||||
this.pathingControlManager.registerProcess(backfillProcess = new BackfillProcess(this));
|
||||
this.pathingControlManager.registerProcess(farmProcess = new FarmProcess(this));
|
||||
}
|
||||
|
||||
this.worldProvider = new WorldProvider();
|
||||
this.selectionManager = new SelectionManager(this);
|
||||
this.commandManager = new CommandManager(this);
|
||||
|
||||
if (BaritoneAutoTest.ENABLE_AUTO_TEST) {
|
||||
this.gameEventHandler.registerEventListener(BaritoneAutoTest.INSTANCE);
|
||||
try {
|
||||
Main.main();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ import baritone.api.IBaritoneProvider;
|
||||
import baritone.api.cache.IWorldScanner;
|
||||
import baritone.api.command.ICommandSystem;
|
||||
import baritone.api.schematic.ISchematicSystem;
|
||||
import baritone.command.BaritoneChatControl;
|
||||
import baritone.cache.WorldScanner;
|
||||
import baritone.command.CommandSystem;
|
||||
import baritone.command.ExampleBaritoneControl;
|
||||
import baritone.utils.schematic.SchematicSystem;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -44,7 +44,7 @@ public final class BaritoneProvider implements IBaritoneProvider {
|
||||
this.all = Collections.singletonList(this.primary);
|
||||
|
||||
// Setup chat control, just for the primary instance
|
||||
new BaritoneChatControl(this.primary);
|
||||
new ExampleBaritoneControl(this.primary);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
21
src/main/java/baritone/KeepName.java
Normal file
21
src/main/java/baritone/KeepName.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;
|
||||
|
||||
// Annotation for classes and class members that should not be renamed by proguard
|
||||
public @interface KeepName {}
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
70
src/main/java/baritone/builder/PackedBlockStateCuboid.java
Normal file
70
src/main/java/baritone/builder/PackedBlockStateCuboid.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 PackedBlockStateCuboid {
|
||||
|
||||
public final Bounds bounds;
|
||||
private final BlockStateCachedData[] states;
|
||||
private final BlockStateCachedData[] statesWithScaffolding;
|
||||
|
||||
private PackedBlockStateCuboid(int x, int y, int z) {
|
||||
this.bounds = new CuboidBounds(x, y, z);
|
||||
this.states = new BlockStateCachedData[bounds.volume()];
|
||||
this.statesWithScaffolding = new BlockStateCachedData[bounds.volume()];
|
||||
}
|
||||
|
||||
public PackedBlockStateCuboid(int[][][] blockStates, BlockData data) {
|
||||
this(blockStates.length, blockStates[0].length, blockStates[0][0].length);
|
||||
bounds.forEach((x, y, z) -> states[bounds.toIndex(x, y, z)] = data.get(blockStates[x][y][z]));
|
||||
genScaffoldVariant();
|
||||
}
|
||||
|
||||
public PackedBlockStateCuboid(BlockStateCachedData[][][] blockStates) {
|
||||
this(blockStates.length, blockStates[0].length, blockStates[0][0].length);
|
||||
bounds.forEach((x, y, z) -> states[bounds.toIndex(x, y, z)] = blockStates[x][y][z]);
|
||||
genScaffoldVariant();
|
||||
}
|
||||
|
||||
public static void fillWithAir(BlockStateCachedData[][][] states) {
|
||||
for (BlockStateCachedData[][] layer : states) {
|
||||
for (BlockStateCachedData[] slice : layer) {
|
||||
Arrays.fill(slice, FakeStates.AIR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void genScaffoldVariant() {
|
||||
for (int i = 0; i < states.length; i++) {
|
||||
if (PlaceOrderDependencyGraph.treatedAsScaffolding(states[i])) {
|
||||
throw new IllegalStateException("including FakeStates.SCAFFOLDING will confuse the place order dependency graph. use an alternate block like FakeStates.SOLID");
|
||||
}
|
||||
statesWithScaffolding[i] = states[i].isAir ? FakeStates.SCAFFOLDING : states[i];
|
||||
}
|
||||
}
|
||||
|
||||
public BlockStateCachedData get(int index) {
|
||||
return states[index];
|
||||
}
|
||||
|
||||
public BlockStateCachedData getScaffoldingVariant(int index) {
|
||||
return statesWithScaffolding[index];
|
||||
}
|
||||
}
|
||||
151
src/main/java/baritone/builder/PlaceAgainstData.java
Normal file
151
src/main/java/baritone/builder/PlaceAgainstData.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.stream.DoubleStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* If you want to place against me, there's some things you gotta know
|
||||
*/
|
||||
public class PlaceAgainstData {
|
||||
|
||||
public final Face against;
|
||||
public final boolean mustSneak; // like if its a crafting table
|
||||
private final Vec3d[] hits;
|
||||
private final boolean top;
|
||||
private final boolean bottom;
|
||||
|
||||
public PlaceAgainstData(Face against, Vec3d[] hits, boolean mustSneak) {
|
||||
this.mustSneak = mustSneak;
|
||||
this.against = against;
|
||||
boolean top = false;
|
||||
boolean bottom = false;
|
||||
for (Vec3d hit : hits) {
|
||||
if (!validatePossibleHit(hit)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (!against.vertical) {
|
||||
if (BlockStatePlacementOption.hitOk(Half.BOTTOM, hit)) {
|
||||
bottom = true;
|
||||
}
|
||||
if (BlockStatePlacementOption.hitOk(Half.TOP, hit)) {
|
||||
top = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.top = top;
|
||||
this.bottom = bottom;
|
||||
this.hits = hits;
|
||||
if (!streamRelativeToMyself().allMatch(Vec3d::inOriginUnitVoxel)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (!streamRelativeToPlace().allMatch(Vec3d::inOriginUnitVoxel)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (hits.length == 0) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public PlaceAgainstData(Face against, Half half, boolean mustSneak) {
|
||||
this(against, project(against.opposite(), half), mustSneak);
|
||||
if (against.vertical && half != Half.EITHER) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<Vec3d> streamRelativeToPlace() {
|
||||
return Stream.of(hits);
|
||||
}
|
||||
|
||||
public Stream<Vec3d> streamRelativeToMyself() {
|
||||
return streamRelativeToPlace().map(v -> v.plus(against.x, against.y, against.z));
|
||||
}
|
||||
|
||||
private boolean validatePossibleHit(Vec3d hit) {
|
||||
double[] h = {hit.x, hit.y, hit.z};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
switch (against.vec[i]) {
|
||||
case -1: {
|
||||
if (h[i] != 1) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
if (h[i] < 0.05 || h[i] > 0.95) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
if (h[i] != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO for playerMustBeHorizontalFacing, do i need something like andThatOptionExtendsTheFullHorizontalSpaceOfTheVoxel()?
|
||||
|
||||
public boolean presentsAnOptionStrictlyInTheTopHalfOfTheStandardVoxelPlane() {
|
||||
if (Main.DEBUG && against.vertical) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
public boolean presentsAnOptionStrictlyInTheBottomHalfOfTheStandardVoxelPlane() {
|
||||
if (Main.DEBUG && against.vertical) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return bottom;
|
||||
}
|
||||
|
||||
private static final double[] LOCS = {0.1, 0.5, 0.9};
|
||||
|
||||
private static Vec3d[] project(Face ontoFace, Half filterHalf) {
|
||||
return DoubleStream
|
||||
.of(LOCS)
|
||||
.boxed()
|
||||
.flatMap(dx -> DoubleStream.of(LOCS).mapToObj(dz -> new double[]{dx, dz}))
|
||||
.map(faceHit -> project(faceHit, ontoFace))
|
||||
.map(Vec3d::new)
|
||||
.filter(vec -> ontoFace.vertical || BlockStatePlacementOption.hitOk(filterHalf, vec))
|
||||
.toArray(Vec3d[]::new);
|
||||
}
|
||||
|
||||
private static double[] project(double[] faceHit, Face ontoFace) {
|
||||
double[] ret = new double[3];
|
||||
int j = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (ontoFace.vec[i] == 0) {
|
||||
ret[i] = faceHit[j++];
|
||||
} else {
|
||||
if (ontoFace.vec[i] == 1) {
|
||||
ret[i] = 1;
|
||||
} // else leave it as zero
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
58
src/main/java/baritone/builder/PlaceOptions.java
Normal file
58
src/main/java/baritone/builder/PlaceOptions.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 PlaceOptions {
|
||||
|
||||
double blockReachDistance = 4;
|
||||
DependencyGraphScaffoldingOverlay overlay;
|
||||
IReachabilityProvider provider = IReachabilityProvider.get(overlay, new PlayerReachSphere(blockReachDistance));
|
||||
|
||||
public void whatCouldIDo(int playerX, int playerFeetBlips, int playerZ) {
|
||||
int playerEyeBlips = playerFeetBlips + Blip.FEET_TO_EYE_APPROX;
|
||||
// TODO ugh how tf to deal with sneaking UGH. maybe like if (playerEyeBlips % 16 < 2) { also run all candidates from one voxel lower down because if we snuck our eye would be in there}
|
||||
int voxelY = playerEyeBlips / Blip.FULL_BLOCK;
|
||||
long pos = BetterBlockPos.toLong(playerX, voxelY, playerZ);
|
||||
for (long blockPos : provider.candidates(pos)) {
|
||||
BlockStateCachedData placingAgainst = overlay.data(blockPos);
|
||||
outer:
|
||||
for (Face againstToPlace : Face.VALUES) {
|
||||
Face placeToAgainst = againstToPlace.opposite();
|
||||
if (overlay.outgoingEdge(blockPos, againstToPlace)) {
|
||||
long placingBlockAt = againstToPlace.offset(blockPos);
|
||||
BlockStateCachedData blockBeingPlaced = overlay.data(placingBlockAt);
|
||||
for (BlockStatePlacementOption option : blockBeingPlaced.placeMe) {
|
||||
if (option.against == placeToAgainst) {
|
||||
PlaceAgainstData againstData = placingAgainst.againstMe(option);
|
||||
int relativeX = playerX - BetterBlockPos.XfromLong(placingBlockAt);
|
||||
int relativeY = playerFeetBlips - Blip.FULL_BLOCK * BetterBlockPos.YfromLong(placingBlockAt);
|
||||
int relativeZ = playerZ - BetterBlockPos.ZfromLong(placingBlockAt);
|
||||
for (Raytracer.Raytrace trace : option.computeTraceOptions(againstData, relativeX, relativeY, relativeZ, PlayerVantage.LOOSE_CENTER, blockReachDistance)) {
|
||||
// yay, gold star
|
||||
}
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
src/main/java/baritone/builder/PlaceOrderDependencyGraph.java
Normal file
102
src/main/java/baritone/builder/PlaceOrderDependencyGraph.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An immutable graph representing block placement dependency order
|
||||
* <p>
|
||||
* Air blocks are treated as scaffolding!
|
||||
* (the idea is that treating air blocks as air would be boring - nothing can place against them and they can't be placed against anything)
|
||||
* <p>
|
||||
* Edge A --> B means that B can be placed against A
|
||||
*/
|
||||
public class PlaceOrderDependencyGraph {
|
||||
|
||||
private final PackedBlockStateCuboid states;
|
||||
private final byte[] edges;
|
||||
|
||||
public PlaceOrderDependencyGraph(PackedBlockStateCuboid states) {
|
||||
this.states = states;
|
||||
this.edges = new byte[bounds().volume()];
|
||||
|
||||
bounds().forEach(this::compute);
|
||||
}
|
||||
|
||||
private void compute(long pos) {
|
||||
byte val = 0;
|
||||
for (BlockStatePlacementOption option : data(pos).placeMe) {
|
||||
if (Main.STRICT_Y && option.against == Face.UP) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
long againstPos = option.against.offset(pos);
|
||||
BlockStateCachedData against;
|
||||
if (inRange(againstPos)) {
|
||||
against = data(againstPos);
|
||||
} else {
|
||||
against = FakeStates.SCAFFOLDING;
|
||||
}
|
||||
if (against.possibleAgainstMe(option)) {
|
||||
val |= 1 << option.against.index;
|
||||
}
|
||||
}
|
||||
edges[states.bounds.toIndex(pos)] = val;
|
||||
}
|
||||
|
||||
public BlockStateCachedData data(long pos) {
|
||||
return states.getScaffoldingVariant(bounds().toIndex(pos));
|
||||
}
|
||||
|
||||
// example: dirt at 0,0,0 torch at 0,1,0. outgoingEdge(0,0,0,UP) returns true, incomingEdge(0,1,0,DOWN) returns true
|
||||
|
||||
public boolean outgoingEdge(long pos, Face face) {
|
||||
if (!inRange(pos)) {
|
||||
return false;
|
||||
}
|
||||
return incomingEdge(face.offset(pos), face.opposite());
|
||||
}
|
||||
|
||||
public boolean incomingEdge(long pos, Face face) {
|
||||
if (!inRange(face.offset(pos))) {
|
||||
return false;
|
||||
}
|
||||
return incomingEdgePermitExterior(pos, face);
|
||||
}
|
||||
|
||||
public boolean incomingEdgePermitExterior(long pos, Face face) {
|
||||
if (!inRange(pos)) {
|
||||
return false;
|
||||
}
|
||||
return (edges[bounds().toIndex(pos)] & 1 << face.index) != 0;
|
||||
}
|
||||
|
||||
public boolean airTreatedAsScaffolding(long pos) {
|
||||
return treatedAsScaffolding(data(pos));
|
||||
}
|
||||
|
||||
private boolean inRange(long pos) {
|
||||
return bounds().inRangePos(pos);
|
||||
}
|
||||
|
||||
public Bounds bounds() {
|
||||
return states.bounds;
|
||||
}
|
||||
|
||||
public static boolean treatedAsScaffolding(BlockStateCachedData data) {
|
||||
return data == FakeStates.SCAFFOLDING;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user