Compare commits
451 Commits
1.12.2-R0.
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8a0951bd4c | ||
|
|
7dcfc130d2 | ||
|
|
0e9940771e | ||
|
|
ee8e09fe58 | ||
|
|
b74ec9aa2e | ||
|
|
ea8292aaf9 | ||
|
|
7b47710c42 | ||
|
|
d34531aa84 | ||
|
|
30bb7a43fb | ||
|
|
9a54ba116e | ||
|
|
a3cc630375 | ||
|
|
e5081d4575 | ||
|
|
852acc0d65 | ||
|
|
7e756e786d | ||
|
|
62848df6de | ||
|
|
f5a531a15c | ||
|
|
ba3d1c9672 | ||
|
|
fa08306751 | ||
|
|
9a8d4c35a9 | ||
|
|
adf961666a | ||
|
|
947b22892c | ||
|
|
0d9f04a748 | ||
|
|
26652d43fc | ||
|
|
ac24d10a57 | ||
|
|
f220a6a153 | ||
|
|
233aab099e | ||
|
|
5268e9a55a | ||
|
|
83c30311d0 | ||
|
|
6a7f2b8699 | ||
|
|
cb55572504 | ||
|
|
9d5c535d18 | ||
|
|
b46cddd84d | ||
|
|
936cf720f4 | ||
|
|
9b6412766f | ||
|
|
d847e87c65 | ||
|
|
ff8c0cd3be | ||
|
|
19991463bf | ||
|
|
c2931e362e | ||
|
|
e912498dac | ||
|
|
98a7b4d757 | ||
|
|
62aa216b48 | ||
|
|
1a56f7ac75 | ||
|
|
0080380486 | ||
|
|
e7419af0de | ||
|
|
371817ab85 | ||
|
|
9ad318d4bb | ||
|
|
e066c5e12c | ||
|
|
cfa99ef96e | ||
|
|
03bcac33d7 | ||
|
|
fcbda069fc | ||
|
|
27561af9d8 | ||
|
|
ecb47cc23f | ||
|
|
2fd9d75ba7 | ||
|
|
69c5da47a0 | ||
|
|
c46b459f08 | ||
|
|
a540b150aa | ||
|
|
16eb219b60 | ||
|
|
3dac5cebbf | ||
|
|
78a3fb3aad | ||
|
|
df97aca45f | ||
|
|
a0daa4f40e | ||
|
|
ebf0968fd3 | ||
|
|
a0d41a8dea | ||
|
|
8101d7b1a7 | ||
|
|
9a0558d218 | ||
|
|
6d023d0ff5 | ||
|
|
26d65b8875 | ||
|
|
b65309abd3 | ||
|
|
e07860fb92 | ||
|
|
9491da6b8f | ||
|
|
4d57eb6172 | ||
|
|
2920fa4993 | ||
|
|
48574c2eb4 | ||
|
|
233199054e | ||
|
|
fe6068d6de | ||
|
|
a9a9b933f5 | ||
|
|
d082c42898 | ||
|
|
d784d03289 | ||
|
|
3702d83f80 | ||
|
|
ebd4a75b88 | ||
|
|
d45dde48f1 | ||
|
|
55878409ac | ||
|
|
5f0eca806a | ||
|
|
5e656d02eb | ||
|
|
e407443ca9 | ||
|
|
345faccac7 | ||
|
|
0dcd33f41b | ||
|
|
10a0c82475 | ||
|
|
2871e0dc81 | ||
|
|
15ab29a426 | ||
|
|
93cac1aeea | ||
|
|
55bbbdfbf2 | ||
|
|
01555f49a0 | ||
|
|
4c40903958 | ||
|
|
74d0113b46 | ||
|
|
1db625034e | ||
|
|
6fbd890884 | ||
|
|
3411916593 | ||
|
|
9e2b072bc6 | ||
|
|
cf03617415 | ||
|
|
d2c3477e59 | ||
|
|
6c9c9bef6e | ||
|
|
4a48e19dcc | ||
|
|
0e44c002df | ||
|
|
cbe1830f1c | ||
|
|
c7e7d1a40b | ||
|
|
daf6af0269 | ||
|
|
b510185638 | ||
|
|
0eaeb3b28c | ||
|
|
a2fad07eb6 | ||
|
|
f07072102b | ||
|
|
94264a57a3 | ||
|
|
83f71f673b | ||
|
|
3794ab48c1 | ||
|
|
643f313789 | ||
|
|
573d187e38 | ||
|
|
2e9075e2d7 | ||
|
|
db9cf4e5a2 | ||
|
|
08c6269f3e | ||
|
|
d25176aff1 | ||
|
|
f5fc1e6b64 | ||
|
|
000ea6409b | ||
|
|
925e91c99e | ||
|
|
b1b3efae8d | ||
|
|
a00d3645e1 | ||
|
|
95d3bc3529 | ||
|
|
677914bd39 | ||
|
|
e82e06c760 | ||
|
|
e40656e7f2 | ||
|
|
676173300f | ||
|
|
2d45820922 | ||
|
|
bdbec60f02 | ||
|
|
433e18e7d2 | ||
|
|
21d24bdfa5 | ||
|
|
4eec855840 | ||
|
|
dbe68a55d4 | ||
|
|
08d22009b7 | ||
|
|
0f32cb2ee9 | ||
|
|
fc47e66e4c | ||
|
|
8778323b0c | ||
|
|
8751adbf1b | ||
|
|
742fe4e0a3 | ||
|
|
3d75fe1118 | ||
|
|
a1c3051508 | ||
|
|
da57bfbf57 | ||
|
|
54e05c84f6 | ||
|
|
9fd22502b2 | ||
|
|
5551f505a1 | ||
|
|
8f421ffb2a | ||
|
|
7fe2637206 | ||
|
|
362c9561dd | ||
|
|
fa1cf770f4 | ||
|
|
4758e8aeef | ||
|
|
998b18d2d8 | ||
|
|
b5b7684c58 | ||
|
|
48604bc9a6 | ||
|
|
2f4b0a7a88 | ||
|
|
16a91e1e31 | ||
|
|
d64d8eb5f8 | ||
|
|
e10f2ae716 | ||
|
|
e62e662196 | ||
|
|
6c425fa820 | ||
|
|
29385180ab | ||
|
|
295d11b87a | ||
|
|
fda0839d62 | ||
|
|
6002e2e6d2 | ||
|
|
b23baff1d8 | ||
|
|
05ea8eda44 | ||
|
|
84de7a43fc | ||
|
|
3a53619460 | ||
|
|
fd6f9ef9f3 | ||
|
|
bcea435a88 | ||
|
|
8d669910f6 | ||
|
|
5f3843eb14 | ||
|
|
c407e63047 | ||
|
|
c60d1f672e | ||
|
|
ff6b2d60ab | ||
|
|
1cde35cfc1 | ||
|
|
49cfe2d8e6 | ||
|
|
19f2be2d7d | ||
|
|
8ce60a6575 | ||
|
|
1fffe53720 | ||
|
|
c33f5cea3b | ||
|
|
bc27b6b11d | ||
|
|
ea9589df5c | ||
|
|
53e8b9cf2a | ||
|
|
1d0b02d8d3 | ||
|
|
7436817e08 | ||
|
|
7b6f58c979 | ||
|
|
7f12dd9c28 | ||
|
|
973d4d56c2 | ||
|
|
db31b8edde | ||
|
|
2aabcc3bb1 | ||
|
|
9e83056973 | ||
|
|
7d268c178c | ||
|
|
4ed193b1dd | ||
|
|
1445ea5401 | ||
|
|
950662e652 | ||
|
|
9686940a08 | ||
|
|
46c2fd8790 | ||
|
|
53b7a9095c | ||
|
|
1803ed497f | ||
|
|
9931749781 | ||
|
|
5721388795 | ||
|
|
7e195b8bd9 | ||
|
|
3929f61633 | ||
|
|
d9b608826a | ||
|
|
30f216b3c5 | ||
|
|
663953230d | ||
|
|
18868c715c | ||
|
|
26e0546b50 | ||
|
|
1708888915 | ||
|
|
a4c72a2762 | ||
|
|
a096eeadb1 | ||
|
|
47fc205ba4 | ||
|
|
8a9d3acc05 | ||
|
|
c4db74f13a | ||
|
|
044759f197 | ||
|
|
c120dafe85 | ||
|
|
3cf50c844d | ||
|
|
fbeb9840ea | ||
|
|
82106ee8b6 | ||
|
|
a74aa135ae | ||
|
|
0201722e69 | ||
|
|
cbf71003ff | ||
|
|
90f6d0e9ba | ||
|
|
4290216041 | ||
|
|
33b5d7fb22 | ||
|
|
ac1d99be3b | ||
|
|
d282f46a5b | ||
|
|
a1dee4e1b6 | ||
|
|
e8e6eb230d | ||
|
|
5fd8d633cb | ||
|
|
473190dc17 | ||
|
|
ce64d00b3a | ||
|
|
810c8a5d00 | ||
|
|
768ac2f14c | ||
|
|
183c5fc53d | ||
|
|
5e51b97d27 | ||
|
|
dbd6e66ab9 | ||
|
|
8cb2bc2356 | ||
|
|
48ff1679fe | ||
|
|
8f77cd21b5 | ||
|
|
a825d60641 | ||
|
|
64f74e19a0 | ||
|
|
a741a4336c | ||
|
|
6946210cf9 | ||
|
|
e412d13ccc | ||
|
|
a74603ce85 | ||
|
|
b9a14c436f | ||
|
|
3ff1a9d072 | ||
|
|
0174722008 | ||
|
|
7475c7fb8f | ||
|
|
41da0735ba | ||
|
|
843e612652 | ||
|
|
739614b286 | ||
|
|
04502d021b | ||
|
|
2bc7a3b190 | ||
|
|
1cbd868bdf | ||
|
|
c54d83cedc | ||
|
|
d9040bf6f3 | ||
|
|
a877b1903f | ||
|
|
a768ef41c6 | ||
|
|
22d100f9f4 | ||
|
|
1e57476b41 | ||
|
|
d5e59449fd | ||
|
|
3b20dbd3f8 | ||
|
|
ec977b85e9 | ||
|
|
c22d51f944 | ||
|
|
c05abe18b1 | ||
|
|
da6685793d | ||
|
|
55b7a1bfed | ||
|
|
0f9e51fd0b | ||
|
|
bfca47a782 | ||
|
|
f2563e1f76 | ||
|
|
a5d9181450 | ||
|
|
f1b3273cba | ||
|
|
1028ea8ff8 | ||
|
|
3d562bec84 | ||
|
|
610ec6d52e | ||
|
|
27a7b2a823 | ||
|
|
cb3fe4a4c2 | ||
|
|
b894a5256a | ||
|
|
b99ec14a99 | ||
|
|
4f2510bb51 | ||
|
|
24360a8085 | ||
|
|
367f5e2e87 | ||
|
|
91e0140620 | ||
|
|
24b26ab846 | ||
|
|
c0df425445 | ||
|
|
389c787eb9 | ||
|
|
685b07f51d | ||
|
|
43adb03349 | ||
|
|
d742c096c6 | ||
|
|
8071a603ba | ||
|
|
889b6ea5ba | ||
|
|
2df37be2e2 | ||
|
|
36543cabf5 | ||
|
|
2173b1e2bc | ||
|
|
6e9351caaf | ||
|
|
eb28c5ae38 | ||
|
|
ae6de9567f | ||
|
|
a6c252130f | ||
|
|
5b2147cef5 | ||
|
|
a4836cf014 | ||
|
|
a924263f2f | ||
|
|
eb4ac897c7 | ||
|
|
090ab24a6b | ||
|
|
445f6f185d | ||
|
|
3c198efd87 | ||
|
|
8fa3e82671 | ||
|
|
7381ae8793 | ||
|
|
ac5291d8e4 | ||
|
|
57b84f327b | ||
|
|
4529c0237a | ||
|
|
0dfabac80f | ||
|
|
7c4a81bcb6 | ||
|
|
263cf75f49 | ||
|
|
005b2d2097 | ||
|
|
13b5b7cc13 | ||
|
|
1bc90004ca | ||
|
|
8a41fa0401 | ||
|
|
043b87f4ef | ||
|
|
6da2ca1f67 | ||
|
|
f0044d7d07 | ||
|
|
ea18e73509 | ||
|
|
f24d488601 | ||
|
|
a3501fb8dd | ||
|
|
70b75489f2 | ||
|
|
abe730886a | ||
|
|
645293ed0e | ||
|
|
00859a2e18 | ||
|
|
d643cae29f | ||
|
|
9aa63c56ab | ||
|
|
b06e5f3e13 | ||
|
|
f2a81f8a1e | ||
|
|
7487ac4edf | ||
|
|
07a8521999 | ||
|
|
1879eebd8f | ||
|
|
679236cfb9 | ||
|
|
5fe93610c9 | ||
|
|
f73c20e342 | ||
|
|
8e6000c523 | ||
|
|
2f87a4c54e | ||
|
|
751fcb584d | ||
|
|
10d2d285d9 | ||
|
|
818f5559f7 | ||
|
|
bf7f6fe3bc | ||
|
|
7acc339704 | ||
|
|
f0d17e54e9 | ||
|
|
01c40ed0d3 | ||
|
|
4fdc9f0166 | ||
|
|
3b4926bed2 | ||
|
|
7e0f44f0af | ||
|
|
b41e4fd8c3 | ||
|
|
241a8ffefa | ||
|
|
c2748ea2df | ||
|
|
e563233ff3 | ||
|
|
9a5de6cf31 | ||
|
|
9f7b490f61 | ||
|
|
6dceb465c3 | ||
|
|
17ba164cbc | ||
|
|
bb19cf0f7a | ||
|
|
0a59c8ce5f | ||
|
|
5d5bb381c0 | ||
|
|
05cc09ff51 | ||
|
|
2ba4bc2755 | ||
|
|
9b5b40c002 | ||
|
|
a0545a756d | ||
|
|
6e62515d11 | ||
|
|
ff100c348e | ||
|
|
a25ff5dd93 | ||
|
|
c6dbae3c24 | ||
|
|
b2bafb826f | ||
|
|
fb20bb3113 | ||
|
|
927d946dba | ||
|
|
9ceec10f18 | ||
|
|
74353989e4 | ||
|
|
c2705d4722 | ||
|
|
b743d7dc4c | ||
|
|
601ec9e90d | ||
|
|
6dcd61f2c7 | ||
|
|
c42528f4f1 | ||
|
|
402d9d2536 | ||
|
|
6ba139a83e | ||
|
|
0690cb1c9f | ||
|
|
b8879db641 | ||
|
|
a3cc6062b6 | ||
|
|
dba9fad220 | ||
|
|
af7444df09 | ||
|
|
2def9e628a | ||
|
|
1641f2767f | ||
|
|
f2c15275e9 | ||
|
|
c58612f271 | ||
|
|
4d44c9f8cc | ||
|
|
3d34b1e011 | ||
|
|
032ffea438 | ||
|
|
2b6175edcb | ||
|
|
c06c7e2776 | ||
|
|
af678041f0 | ||
|
|
5c03cad4f2 | ||
|
|
6de709c4c6 | ||
|
|
bca8f20393 | ||
|
|
144c9e08f5 | ||
|
|
bd47cab2ff | ||
|
|
4c7da04d9f | ||
|
|
cf300713b4 | ||
|
|
4c93b2b13a | ||
|
|
78ddbbd7c7 | ||
|
|
ecb28a58a6 | ||
|
|
9e62b22fb8 | ||
|
|
2ace17105f | ||
|
|
528ab01026 | ||
|
|
87583d4e7c | ||
|
|
f77f039dbb | ||
|
|
3fbba5de34 | ||
|
|
79d3980be2 | ||
|
|
280db63b42 | ||
|
|
f93abbf076 | ||
|
|
03a5ef5149 | ||
|
|
58de950203 | ||
|
|
2887399202 | ||
|
|
cbfe35f062 | ||
|
|
1a330a4c07 | ||
|
|
c932a0d1b1 | ||
|
|
515e9d86b9 | ||
|
|
5f917f8253 | ||
|
|
cede9c99fa | ||
|
|
fd733df52a | ||
|
|
5f6671cd50 | ||
|
|
ff00b7556e | ||
|
|
3875266bb7 | ||
|
|
ca8e0d7c0f | ||
|
|
269e1c30f1 | ||
|
|
79d0207801 | ||
|
|
a2c09040b4 | ||
|
|
c50f6f2a68 | ||
|
|
9cfe108024 | ||
|
|
545ce61711 | ||
|
|
e881bb1c23 | ||
|
|
c926121dd6 | ||
|
|
b859bf6b4d | ||
|
|
5113e99853 | ||
|
|
68b4db992f | ||
|
|
2765bb8891 | ||
|
|
067b86b7cc | ||
|
|
08013d2b4a | ||
|
|
a6a51f9805 | ||
|
|
d223e4ad00 | ||
|
|
44d1491789 | ||
|
|
e82a99d4d8 |
50
.circleci/config.yml
Normal file
50
.circleci/config.yml
Normal file
@@ -0,0 +1,50 @@
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/Akarin-project/Akarin
|
||||
parallelism: 1
|
||||
shell: /bin/bash --login
|
||||
environment:
|
||||
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
|
||||
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
|
||||
docker:
|
||||
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
|
||||
command: /sbin/init
|
||||
steps:
|
||||
# Machine Setup
|
||||
- checkout
|
||||
# Prepare for artifact
|
||||
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
|
||||
- run:
|
||||
working_directory: ~/Akarin-project/Akarin
|
||||
command: sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java; sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac; echo -e "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> $BASH_ENV
|
||||
# Dependencies
|
||||
# Restore the dependency cache
|
||||
- restore_cache:
|
||||
keys:
|
||||
# This branch if available
|
||||
- v1-dep-{{ .Branch }}-
|
||||
# Default branch if not
|
||||
- v1-dep-ver/1.12.2-
|
||||
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
|
||||
- v1-dep-
|
||||
- run: git config --global user.email "circle@circleci.com"
|
||||
- run: git config --global user.name "CircleCI"
|
||||
- run: chmod +x scripts/inst.sh
|
||||
- run: ./scripts/inst.sh --setup --remote
|
||||
# Save dependency cache
|
||||
- save_cache:
|
||||
key: v1-dep-{{ .Branch }}-{{ epoch }}
|
||||
paths:
|
||||
- ~/.m2
|
||||
# Test
|
||||
- run: yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS
|
||||
# Teardown
|
||||
# Save test results
|
||||
- store_test_results:
|
||||
path: /tmp/circleci-test-results
|
||||
# Save artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-artifacts
|
||||
- store_artifacts:
|
||||
path: /tmp/circleci-test-results
|
||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,3 +1,3 @@
|
||||
[submodule "work/Paper"]
|
||||
path = work/Paper
|
||||
url = https://github.com/Akarin-project/Paper.git
|
||||
url = https://github.com/Akarin-project/Paper.git
|
||||
|
||||
24
Jenkinsfile
vendored
Normal file
24
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
pipeline {
|
||||
agent any
|
||||
stages {
|
||||
stage('Init Submodules') {
|
||||
steps {
|
||||
sh 'git submodule update --init --recursive'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh 'chmod +x scripts/inst.sh'
|
||||
sh './scripts/inst.sh --setup --fast --remote'
|
||||
}
|
||||
}
|
||||
|
||||
stage('Archive') {
|
||||
steps {
|
||||
archiveArtifacts(artifacts: '*.jar', fingerprint: true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,17 @@ As such, Akarin is licensed under the
|
||||
[GNU General Public License version 3](licenses/GPL.md); as it inherits it from Paper,
|
||||
who in turn inherits it from the original Spigot projects.
|
||||
|
||||
Any author who is _not_ listed below should be presumed to have released their work
|
||||
Any author who is _not_ listed below should be presumed to have their work released
|
||||
under the original [GPL](licenses/GPL.md) license.
|
||||
|
||||
In the interest of promoting a better Minecraft platform for everyone, contributors
|
||||
may choose to release their code under the more permissive [MIT License](licenses/MIT.md).
|
||||
|
||||
The authors listed below have chosen to release their code under that more permissive
|
||||
The authors listed below have chosen to release their code under the more permissive
|
||||
[MIT License](licenses/MIT.md). Any contributor who wants their name added below
|
||||
should submit a pull request to this project to add their name.
|
||||
should submit a Pull Request to this project and add their name.
|
||||
|
||||
```text
|
||||
Sotr <kira@kira.moe>
|
||||
```
|
||||
MatrixTunnel <https://github.com/MatrixTunnel>
|
||||
```
|
||||
|
||||
53
README.md
53
README.md
@@ -1,29 +1,31 @@
|
||||
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
|
||||
[](http://ci.ilummc.com/job/Akarin/)
|
||||
[](https://bstats.org/plugin/bukkit/Torch)
|
||||
[](https://akarin.io)
|
||||
[](https://discord.gg/fw2pJAj)
|
||||
[](https://bstats.org/plugin/bukkit/Torch)
|
||||
|
||||
Akarin is currently **under heavy development** and contributions are welcome!
|
||||
|
||||
Introduction
|
||||
---
|
||||
> Akarin is a powerful server software form the 'new dimension', formerly known as Torch.
|
||||
> Akarin is a powerful server software from the 'new dimension', formerly known as Torch.
|
||||
|
||||
As a [Paper](https://github.com/PaperMC/Paper) fork, it supports almost all plugins that [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse) can use.
|
||||
As a [Paper](https://github.com/PaperMC/Paper) fork, it should support almost all plugins that work on [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse).
|
||||
|
||||
It has a few key goals:
|
||||
* **Open Access** - Make more game mechanism configurable.
|
||||
* **Bedrock** - Safety and stable is important for a server.
|
||||
* **Fast** - Simplify the logic and import the multi-thread compute.
|
||||
Our project has a few key goals:
|
||||
|
||||
Akarin is **under heavy development** yet, contribution is welcome and run a test before putting into production.
|
||||
* **Open Access** - Make more game mechanics configurable.
|
||||
* **Bedrock** - Make the server more safe and stable.
|
||||
* **Fast** - Simplify the logic and implement multi-threaded computing.
|
||||
|
||||
*Issues and Pull Requests will be labeled accordingly*
|
||||
|
||||
Get Akarin
|
||||
---
|
||||
---
|
||||
### Download
|
||||
#### Recommended Sites
|
||||
+ [**Jenkins**](http://ci.ilummc.com/job/Akarin/) - *Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)*
|
||||
+ [ **Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build *Login required*
|
||||
#### Recommended
|
||||
+ [**Jenkins**](http://ci.josephworks.net/job/Akarin/job/ver%252F1.12.2/) - Kudos to [JosephWorks](https://github.com/josephworks)
|
||||
|
||||
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your website here*
|
||||
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
|
||||
|
||||
### Build
|
||||
#### Requirements
|
||||
@@ -34,20 +36,23 @@ Get Akarin
|
||||
```sh
|
||||
./scripts/inst.sh --setup --fast
|
||||
```
|
||||
*For non-modification compile, add `--fast` option to skip the test is recommended.*
|
||||
*Futhermore, if your machine have a insufficient memory, you may add `--remote` option to avoid decompile locally.*
|
||||
|
||||
Demonstration servers
|
||||
**Notes**
|
||||
* You must use `--setup` at least once to deploy necessary dependencies otherwise some imports cannot be organized.
|
||||
* For non-modified projects, it is recommended to add the `--fast` option to skip any tests.
|
||||
* If your machine has insufficient memory, you may want to add the `--remote` option to avoid decompiling locally.
|
||||
|
||||
Demo Servers
|
||||
---
|
||||
+ **demo.akarin.io**
|
||||
* `demo.akarin.io` (official)
|
||||
* `omc.hk` (auth required)
|
||||
|
||||
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your server here*
|
||||
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
|
||||
|
||||
Contributing
|
||||
---
|
||||
* Feel free to open an [Issue](https://github.com/Akarin-project/akarin/issues) if you have any problem with Akarin.
|
||||
* [Pull Request](https://github.com/Akarin-project/akarin/pulls) is welcomed, Akarin use [Mixin](https://github.com/SpongePowered/Mixin) to modify the code, you can checkout `sources` folder to see them. Moreover, add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
|
||||
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send an email to `kira@kira.moe` with your experience and necessary information. Besides, welcome to join our [Discord](https://discord.gg/D3Rsukh) to chat.
|
||||
* Note that you must `--setup` at least once to deploy necessary dependency otherwise some imports cannot be organized.
|
||||
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
|
||||
* Add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
|
||||
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can [send](mailto://kira@kira.moe) us an email with your experience and necessary information.
|
||||
|
||||

|
||||

|
||||
|
||||
16
circle.yml
16
circle.yml
@@ -1,16 +0,0 @@
|
||||
machine:
|
||||
java:
|
||||
version: openjdk8
|
||||
|
||||
dependencies:
|
||||
cache-directories:
|
||||
- "/home/ubuntu/Akarin/work/Paper/work/Minecraft"
|
||||
override:
|
||||
- git config --global user.email "circle@circleci.com"
|
||||
- git config --global user.name "CircleCI"
|
||||
- chmod +x scripts/inst.sh
|
||||
- ./scripts/inst.sh --setup --remote
|
||||
|
||||
test:
|
||||
post:
|
||||
- yes|cp -rf ./akarin-1.12.2.jar $CIRCLE_ARTIFACTS
|
||||
62
removed/com/destroystokyo/paper/antixray/DataBitsReader.java
Normal file
62
removed/com/destroystokyo/paper/antixray/DataBitsReader.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class DataBitsReader {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerValue;
|
||||
private int mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
|
||||
public void setDataBits(ByteBuf dataBits) { // Akarin
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int bitsPerValue) {
|
||||
this.bitsPerValue = bitsPerValue;
|
||||
mask = (1 << bitsPerValue) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
dataBits.getLong(longInDataBitsIndex);
|
||||
/*
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
*/ // Akarin end
|
||||
}
|
||||
}
|
||||
|
||||
public int read() {
|
||||
int value = (int) (current >>> bitInLongIndex) & mask;
|
||||
bitInLongIndex += bitsPerValue;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
value |= current << bitsPerValue - bitInLongIndex & mask;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
94
removed/com/destroystokyo/paper/antixray/DataBitsWriter.java
Normal file
94
removed/com/destroystokyo/paper/antixray/DataBitsWriter.java
Normal file
@@ -0,0 +1,94 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class DataBitsWriter {
|
||||
|
||||
private ByteBuf dataBits; // Akarin
|
||||
private int bitsPerValue;
|
||||
private long mask;
|
||||
private int longInDataBitsIndex;
|
||||
private int bitInLongIndex;
|
||||
private long current;
|
||||
private boolean dirty;
|
||||
|
||||
public void setDataBits(ByteBuf dataBits) { // Akarin
|
||||
this.dataBits = dataBits;
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int bitsPerValue) {
|
||||
this.bitsPerValue = bitsPerValue;
|
||||
mask = (1 << bitsPerValue) - 1;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.longInDataBitsIndex = index;
|
||||
bitInLongIndex = 0;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
current = dataBits.getLong(longInDataBitsIndex);
|
||||
/*
|
||||
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
|
||||
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
|
||||
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
|
||||
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
|
||||
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
|
||||
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
|
||||
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
|
||||
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
|
||||
*/ // Akarin end
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
if (dirty && dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
|
||||
// Akarin start
|
||||
dataBits.setLong(longInDataBitsIndex, current);
|
||||
/*
|
||||
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
|
||||
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
|
||||
*/ // Akarin end
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int value) {
|
||||
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
|
||||
dirty = true;
|
||||
bitInLongIndex += bitsPerValue;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
|
||||
if (bitInLongIndex > 0) {
|
||||
current = current & ~(mask >>> bitsPerValue - bitInLongIndex) | (value & mask) >>> bitsPerValue - bitInLongIndex;
|
||||
dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
bitInLongIndex += bitsPerValue;
|
||||
|
||||
if (bitInLongIndex > 63) {
|
||||
finish();
|
||||
bitInLongIndex -= 64;
|
||||
longInDataBitsIndex += 8;
|
||||
init();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.destroystokyo.paper.antixray;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.DataPalette;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) byte[] -> ByteBuf (compatibility)
|
||||
*/
|
||||
public class PacketPlayOutMapChunkInfo {
|
||||
|
||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||
private final Chunk chunk;
|
||||
private final int chunkSectionSelector;
|
||||
private ByteBuf data; // Akarin
|
||||
private final int[] bitsPerValue = new int[16];
|
||||
private final DataPalette[] dataPalettes = new DataPalette[16];
|
||||
private final int[] dataBitsIndexes = new int[16];
|
||||
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
|
||||
|
||||
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||
this.chunk = chunk;
|
||||
this.chunkSectionSelector = chunkSectionSelector;
|
||||
}
|
||||
|
||||
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||
return packetPlayOutMapChunk;
|
||||
}
|
||||
|
||||
public Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public int getChunkSectionSelector() {
|
||||
return chunkSectionSelector;
|
||||
}
|
||||
|
||||
public ByteBuf getData() { // Akarin
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(ByteBuf data) { // Akarin
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getBitsPerValue(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
|
||||
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
|
||||
}
|
||||
|
||||
public DataPalette getDataPalette(int chunkSectionIndex) {
|
||||
return dataPalettes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
|
||||
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||
}
|
||||
|
||||
public int getDataBitsIndex(int chunkSectionIndex) {
|
||||
return dataBitsIndexes[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||
}
|
||||
|
||||
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
|
||||
return predefinedBlockData[chunkSectionIndex];
|
||||
}
|
||||
|
||||
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
|
||||
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
|
||||
}
|
||||
|
||||
public boolean isWritten(int chunkSectionIndex) {
|
||||
return bitsPerValue[chunkSectionIndex] != 0;
|
||||
}
|
||||
}
|
||||
193
removed/net/minecraft/server/PacketPlayOutMapChunk.java
Normal file
193
removed/net/minecraft/server/PacketPlayOutMapChunk.java
Normal file
@@ -0,0 +1,193 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
// Paper start
|
||||
import com.destroystokyo.paper.antixray.PacketPlayOutMapChunkInfo; // Anti-Xray
|
||||
// Paper end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) WrappedByteBuf -> ByteBuf (compatibility)
|
||||
*/
|
||||
public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||
|
||||
private int a;
|
||||
private int b;
|
||||
private int c;
|
||||
private ByteBuf d; // Akarin - byte[] -> ByteBuf
|
||||
private List<NBTTagCompound> e;
|
||||
private boolean f;
|
||||
private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
|
||||
|
||||
// Paper start - Async-Anti-Xray - Set the ready flag to true
|
||||
public PacketPlayOutMapChunk() {
|
||||
this.ready = true;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public PacketPlayOutMapChunk(Chunk chunk, int i) {
|
||||
PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo = chunk.world.chunkPacketBlockController.getPacketPlayOutMapChunkInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
|
||||
this.a = chunk.locX;
|
||||
this.b = chunk.locZ;
|
||||
this.f = i == '\uffff';
|
||||
boolean flag = chunk.getWorld().worldProvider.m();
|
||||
|
||||
this.d = allocateBuffer(this.a(chunk, flag, i)); // Akarin
|
||||
|
||||
// Paper start - Anti-Xray - Add chunk packet info
|
||||
if (packetPlayOutMapChunkInfo != null) {
|
||||
packetPlayOutMapChunkInfo.setData(this.d);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
this.c = this.writeChunk(new PacketDataSerializer(this.d), chunk, flag, i, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
|
||||
this.e = Lists.newArrayList();
|
||||
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entry entry = (Entry) iterator.next();
|
||||
BlockPosition blockposition = (BlockPosition) entry.getKey();
|
||||
TileEntity tileentity = (TileEntity) entry.getValue();
|
||||
int j = blockposition.getY() >> 4;
|
||||
|
||||
if (this.e() || (i & 1 << j) != 0) {
|
||||
NBTTagCompound nbttagcompound = tileentity.d();
|
||||
|
||||
this.e.add(nbttagcompound);
|
||||
}
|
||||
}
|
||||
|
||||
chunk.world.chunkPacketBlockController.modifyBlocks(this, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Modify blocks
|
||||
}
|
||||
|
||||
// Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
|
||||
public boolean isReady() {
|
||||
return this.ready;
|
||||
}
|
||||
|
||||
public void setReady(boolean ready) {
|
||||
this.ready = ready;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public void a(PacketDataSerializer packetdataserializer) throws IOException {
|
||||
this.a = packetdataserializer.readInt();
|
||||
this.b = packetdataserializer.readInt();
|
||||
this.f = packetdataserializer.readBoolean();
|
||||
this.c = packetdataserializer.g();
|
||||
int i = packetdataserializer.g();
|
||||
|
||||
if (i > 2097152) {
|
||||
throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
|
||||
} else {
|
||||
this.d = Unpooled.buffer(i); // Akarin
|
||||
packetdataserializer.readBytes(this.d);
|
||||
int j = packetdataserializer.g();
|
||||
|
||||
this.e = Lists.newArrayList();
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
this.e.add(packetdataserializer.j());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void b(PacketDataSerializer packetdataserializer) throws IOException {
|
||||
packetdataserializer.writeInt(this.a);
|
||||
packetdataserializer.writeInt(this.b);
|
||||
packetdataserializer.writeBoolean(this.f);
|
||||
packetdataserializer.d(this.c);
|
||||
packetdataserializer.d(this.d.capacity()); // Akarin
|
||||
packetdataserializer.writeBytes(this.d.array()); // Akarin
|
||||
packetdataserializer.d(this.e.size());
|
||||
Iterator iterator = this.e.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
NBTTagCompound nbttagcompound = (NBTTagCompound) iterator.next();
|
||||
|
||||
packetdataserializer.a(nbttagcompound);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(PacketListenerPlayOut packetlistenerplayout) {
|
||||
packetlistenerplayout.a(this);
|
||||
}
|
||||
|
||||
private ByteBuf g() { return allocateBuffer(-1); } // Akarin
|
||||
private ByteBuf allocateBuffer(int expectedCapacity) { // Akarin - added argument
|
||||
ByteBuf bytebuf = expectedCapacity == -1 ? Unpooled.buffer() : Unpooled.buffer(expectedCapacity); // Akarin
|
||||
|
||||
bytebuf.writerIndex(0);
|
||||
return bytebuf;
|
||||
}
|
||||
|
||||
// Paper start - Anti-Xray - Support default method
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // OBFHELPER
|
||||
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) {
|
||||
return this.a(packetdataserializer, chunk, flag, i, null);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, packetPlayOutMapChunkInfo); } // Paper - Anti-Xray - OBFHELPER
|
||||
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { // Paper - Anti-Xray - Add chunk packet info
|
||||
int j = 0;
|
||||
ChunkSection[] achunksection = chunk.getSections();
|
||||
int k = 0;
|
||||
|
||||
for (int l = achunksection.length; k < l; ++k) {
|
||||
ChunkSection chunksection = achunksection[k];
|
||||
|
||||
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
j |= 1 << k;
|
||||
chunksection.getBlocks().writeBlocks(packetdataserializer, packetPlayOutMapChunkInfo, k); // Paper - Anti-Xray - Add chunk packet info
|
||||
packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes());
|
||||
if (flag) {
|
||||
packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e()) {
|
||||
packetdataserializer.writeBytes(chunk.getBiomeIndex());
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
protected int a(Chunk chunk, boolean flag, int i) {
|
||||
int j = 0;
|
||||
ChunkSection[] achunksection = chunk.getSections();
|
||||
int k = 0;
|
||||
|
||||
for (int l = achunksection.length; k < l; ++k) {
|
||||
ChunkSection chunksection = achunksection[k];
|
||||
|
||||
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||
j += chunksection.getBlocks().a();
|
||||
j += chunksection.getEmittedLightArray().asBytes().length;
|
||||
if (flag) {
|
||||
j += chunksection.getSkyLightArray().asBytes().length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.e()) {
|
||||
j += chunk.getBiomeIndex().length;
|
||||
}
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
public boolean e() {
|
||||
return this.f;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
|
||||
if [ -d "Minecraft" ]; then
|
||||
rm Minecraft/ -r
|
||||
fi
|
||||
git clone https://github.com/Akarin-project/Minecraft.git
|
||||
git clone https://github.com/LegacyGamerHD/Minecraft.git
|
||||
fi
|
||||
|
||||
cd "$paperbasedir"
|
||||
@@ -51,4 +51,4 @@ echo "[Akarin] Ready to build"
|
||||
echo "[Akarin] Migrated final jar to $basedir/akarin-$minecraftversion.jar"
|
||||
)
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
set -e
|
||||
basedir="$pwd"
|
||||
|
||||
(git submodule update --init --remote && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
|
||||
(chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
|
||||
echo "Failed to build Akarin"
|
||||
exit 1
|
||||
) || exit 1
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
130
sources/pom.xml
130
sources/pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>akarin</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.12.2-R0.3-SNAPSHOT</version>
|
||||
<version>1.12.2-R0.4-SNAPSHOT</version>
|
||||
<name>Akarin</name>
|
||||
<url>https://github.com/Akarin-project/Akarin</url>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.1.24.Final</version>
|
||||
<version>4.1.78.Final</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -45,6 +45,12 @@
|
||||
<version>${minecraft.version}-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>minecraft-server</artifactId>
|
||||
<version>${minecraft.version}-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.jopt-simple</groupId>
|
||||
<artifactId>jopt-simple</artifactId>
|
||||
@@ -60,7 +66,7 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.45</version>
|
||||
<version>8.0.28</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -69,16 +75,15 @@
|
||||
<version>3.0.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.minecrell</groupId>
|
||||
<artifactId>terminalconsoleappender</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>4.4.0</version>
|
||||
<version>4.5.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -91,7 +96,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<version>2.17.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
@@ -99,13 +104,21 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<version>2.17.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-iostreams</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<version>2.17.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Paper - Async loggers -->
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<version>3.4.4</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- testing -->
|
||||
@@ -121,28 +134,34 @@
|
||||
<version>1.3</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Akarin -->
|
||||
<dependency>
|
||||
<groupId>io.akarin</groupId>
|
||||
<artifactId>legacylauncher</artifactId>
|
||||
<version>1.23</version>
|
||||
<version>1.26</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.spongepowered</groupId>
|
||||
<artifactId>mixin</artifactId>
|
||||
<version>0.7.10-SNAPSHOT</version>
|
||||
<version>0.7.11-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.concurrent-locks</groupId>
|
||||
<artifactId>concurrent-locks</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>2.9.3</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>akarin-repo</id>
|
||||
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
|
||||
<id>elmakers-repo</id>
|
||||
<url>http://maven.elmakers.com/repository/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spongepowered-repo</id>
|
||||
@@ -152,12 +171,36 @@
|
||||
<id>nallar-repo</id>
|
||||
<url>http://repo.nallar.me/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>dmulloy2-repo</id>
|
||||
<url>https://repo.dmulloy2.net/repository/public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>josephworks</id>
|
||||
<url>http://repo.josephworks.net/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>sonatype-nexusg</id>
|
||||
<url>https://oss.sonatype.org/content/repositories</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spigotmc-public</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||
<id>paper</id>
|
||||
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
@@ -198,6 +241,7 @@
|
||||
<Specification-Title>Bukkit</Specification-Title>
|
||||
<Specification-Version>${api.version}</Specification-Version>
|
||||
<Specification-Vendor>Bukkit Team</Specification-Vendor>
|
||||
<Multi-Release>true</Multi-Release> <!-- Paper start - update log4j -->
|
||||
</manifestEntries>
|
||||
<manifestSections>
|
||||
<manifestSection>
|
||||
@@ -225,7 +269,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.4</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
@@ -234,6 +278,25 @@
|
||||
</goals>
|
||||
<configuration>
|
||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
|
||||
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>org.spigotmc:minecraft-server:**</artifact>
|
||||
<excludes>
|
||||
<exclude>io/netty/**</exclude>
|
||||
<exclude>org/apache/logging/log4j/**</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
|
||||
<relocations>
|
||||
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
|
||||
<!--<relocation>-->
|
||||
@@ -265,27 +328,16 @@
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||
<resource>META-INF/services/java.sql.Driver</resource>
|
||||
</transformer>
|
||||
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
|
||||
<transformer implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
|
||||
</transformers>
|
||||
<!-- Akarin - Avoid signature failure -->
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.edwgiz</groupId>
|
||||
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
|
||||
<version>2.8.1</version>
|
||||
<groupId>io.github.edwgiz</groupId>
|
||||
<artifactId>log4j-maven-shade-plugin-extensions</artifactId>
|
||||
<version>2.17.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
@@ -293,6 +345,12 @@
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>org/spigotmc/CaseInsensitiveHashingStrategy.java</exclude>
|
||||
<exclude>org/spigotmc/CaseInsensitiveMap.java</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
||||
128
sources/src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
128
sources/src/main/java/co/aikar/timings/MinecraftTimings.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import com.google.common.collect.MapMaker;
|
||||
import net.minecraft.server.*;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import org.bukkit.craftbukkit.scheduler.CraftTask;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public final class MinecraftTimings {
|
||||
|
||||
public static final Timing playerListTimer = Timings.ofSafe("Player List");
|
||||
public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions");
|
||||
public static final Timing connectionTimer = Timings.ofSafe("Connection Handler");
|
||||
public static final Timing tickablesTimer = Timings.ofSafe("Tickables");
|
||||
public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler");
|
||||
public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler");
|
||||
public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending");
|
||||
public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing");
|
||||
public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick");
|
||||
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
|
||||
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
|
||||
public static final Timing savePlayers = Timings.ofSafe("Save Players");
|
||||
|
||||
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
|
||||
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
|
||||
public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing");
|
||||
public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks");
|
||||
public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation");
|
||||
|
||||
public static final Timing processQueueTimer = Timings.ofSafe("processQueue");
|
||||
|
||||
public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand");
|
||||
|
||||
public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck");
|
||||
|
||||
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
|
||||
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
|
||||
|
||||
private static final Map<Class<? extends Runnable>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private MinecraftTimings() {}
|
||||
|
||||
/**
|
||||
* Gets a timer associated with a plugins tasks.
|
||||
* @param bukkitTask
|
||||
* @param period
|
||||
* @return
|
||||
*/
|
||||
public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) {
|
||||
if (!bukkitTask.isSync()) {
|
||||
return NullTimingHandler.NULL;
|
||||
}
|
||||
Plugin plugin;
|
||||
|
||||
Runnable task = ((CraftTask) bukkitTask).task;
|
||||
|
||||
final Class<? extends Runnable> taskClass = task.getClass();
|
||||
if (bukkitTask.getOwner() != null) {
|
||||
plugin = bukkitTask.getOwner();
|
||||
} else {
|
||||
plugin = TimingsManager.getPluginByClassloader(taskClass);
|
||||
}
|
||||
|
||||
final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz ->
|
||||
clazz.isAnonymousClass() || clazz.isLocalClass()
|
||||
? clazz.getName()
|
||||
: clazz.getCanonicalName());
|
||||
|
||||
StringBuilder name = new StringBuilder(64);
|
||||
name.append("Task: ").append(taskname);
|
||||
if (period > 0) {
|
||||
name.append(" (interval:").append(period).append(")");
|
||||
} else {
|
||||
name.append(" (Single)");
|
||||
}
|
||||
|
||||
if (plugin == null) {
|
||||
return Timings.ofSafe(null, name.toString());
|
||||
}
|
||||
|
||||
return Timings.ofSafe(plugin, name.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getEntityTimings(Entity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a named timer for the specified tile entity type to track type specific timings.
|
||||
* @param entity
|
||||
* @return
|
||||
*/
|
||||
public static Timing getTileEntityTimings(TileEntity entity) {
|
||||
String entityType = entity.getClass().getName();
|
||||
return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer);
|
||||
}
|
||||
public static Timing getCancelTasksTimer() {
|
||||
return Timings.ofSafe("Cancel Tasks");
|
||||
}
|
||||
public static Timing getCancelTasksTimer(Plugin plugin) {
|
||||
return Timings.ofSafe(plugin, "Cancel Tasks");
|
||||
}
|
||||
|
||||
public static void stopServer() {
|
||||
TimingsManager.stopServer();
|
||||
}
|
||||
|
||||
public static Timing getBlockTiming(Block block) {
|
||||
return Timings.ofSafe("## Scheduled Block: " + block.getName(), scheduledBlocksTimer);
|
||||
}
|
||||
|
||||
public static Timing getStructureTiming(StructureGenerator structureGenerator) {
|
||||
return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer);
|
||||
}
|
||||
|
||||
public static Timing getPacketTiming(Packet packet) {
|
||||
return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer);
|
||||
}
|
||||
}
|
||||
131
sources/src/main/java/co/aikar/timings/TimedChunkGenerator.java
Normal file
131
sources/src/main/java/co/aikar/timings/TimedChunkGenerator.java
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) 2014-2016 Daniel Ennis <http://aikar.co>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package co.aikar.timings;
|
||||
|
||||
import net.minecraft.server.BiomeBase.BiomeMeta;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumCreatureType;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.craftbukkit.generator.InternalChunkGenerator;
|
||||
import org.bukkit.generator.BlockPopulator;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class TimedChunkGenerator extends InternalChunkGenerator {
|
||||
private final WorldServer world;
|
||||
private final InternalChunkGenerator timedGenerator;
|
||||
|
||||
public TimedChunkGenerator(WorldServer worldServer, InternalChunkGenerator gen) {
|
||||
world = worldServer;
|
||||
timedGenerator = gen;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[] generate(org.bukkit.World world, Random random, int x, int z) {
|
||||
return timedGenerator.generate(world, random, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public short[][] generateExtBlockSections(org.bukkit.World world, Random random, int x, int z,
|
||||
BiomeGrid biomes) {
|
||||
return timedGenerator.generateExtBlockSections(world, random, x, z, biomes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[][] generateBlockSections(org.bukkit.World world, Random random, int x, int z,
|
||||
BiomeGrid biomes) {
|
||||
return timedGenerator.generateBlockSections(world, random, x, z, biomes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) {
|
||||
return timedGenerator.generateChunkData(world, random, x, z, biome);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSpawn(org.bukkit.World world, int x, int z) {
|
||||
return timedGenerator.canSpawn(world, x, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockPopulator> getDefaultPopulators(org.bukkit.World world) {
|
||||
return timedGenerator.getDefaultPopulators(world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getFixedSpawnLocation(org.bukkit.World world, Random random) {
|
||||
return timedGenerator.getFixedSpawnLocation(world, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getOrCreateChunk(int i, int j) {
|
||||
try (Timing ignored = world.timings.chunkGeneration.startTiming()) {
|
||||
return timedGenerator.getOrCreateChunk(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateStructures(int i, int j) {
|
||||
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
|
||||
timedGenerator.recreateStructures(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean a(Chunk chunk, int i, int j) {
|
||||
return timedGenerator.a(chunk, i, j);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BiomeMeta> getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||
return timedGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, boolean flag) {
|
||||
return timedGenerator.findNearestMapFeature(world, s, blockposition, flag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recreateStructures(Chunk chunk, int i, int j) {
|
||||
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
|
||||
timedGenerator.recreateStructures(chunk, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean a(World world, String s, BlockPosition blockPosition) {
|
||||
return timedGenerator.a(world, s, blockPosition);
|
||||
}
|
||||
}
|
||||
@@ -24,23 +24,26 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import co.aikar.util.LoadingIntMap;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.Akari.AssignableThread;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.bukkit.Bukkit;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add volatile to fields<br>
|
||||
* @author cakoyo
|
||||
*/
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
class TimingHandler implements Timing {
|
||||
String name;
|
||||
private static AtomicInteger idPool = new AtomicInteger(1);
|
||||
static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
|
||||
final int id = idPool.getAndIncrement();
|
||||
|
||||
private static int idPool = 1;
|
||||
final int id = idPool++;
|
||||
|
||||
final String name;
|
||||
final TimingIdentifier identifier;
|
||||
private final boolean verbose;
|
||||
|
||||
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
||||
@@ -48,22 +51,15 @@ class TimingHandler implements Timing {
|
||||
final TimingData record;
|
||||
private final TimingHandler groupHandler;
|
||||
|
||||
private volatile long start = 0; // Akarin - volatile
|
||||
private volatile int timingDepth = 0; // Akarin - volatile
|
||||
private long start = 0;
|
||||
private int timingDepth = 0;
|
||||
private boolean added;
|
||||
private boolean timed;
|
||||
private boolean enabled;
|
||||
private TimingHandler parent;
|
||||
|
||||
TimingHandler(TimingIdentifier id) {
|
||||
if (id.name.startsWith("##")) {
|
||||
verbose = true;
|
||||
this.name = id.name.substring(3);
|
||||
} else {
|
||||
this.name = id.name;
|
||||
verbose = false;
|
||||
}
|
||||
|
||||
this.identifier = id;
|
||||
this.verbose = id.name.startsWith("##");
|
||||
this.record = new TimingData(this.id);
|
||||
this.groupHandler = id.groupHandler;
|
||||
|
||||
@@ -88,59 +84,61 @@ class TimingHandler implements Timing {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timing startTimingIfSync() {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
startTiming();
|
||||
}
|
||||
return this;
|
||||
startTiming();
|
||||
return (Timing) this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void stopTimingIfSync() {
|
||||
if (Bukkit.isPrimaryThread()) {
|
||||
stopTiming();
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
stopTiming(true); // Avoid twice thread check
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Timing startTiming() {
|
||||
if (enabled && ++timingDepth == 1) {
|
||||
if (enabled && Bukkit.isPrimaryThread() && ++timingDepth == 1) {
|
||||
start = System.nanoTime();
|
||||
parent = TimingsManager.CURRENT;
|
||||
TimingsManager.CURRENT = this;
|
||||
TIMING_STACK.addLast(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTiming() {
|
||||
if (enabled && --timingDepth == 0 && start != 0) {
|
||||
if (!Bukkit.isPrimaryThread()) {
|
||||
stopTiming(false);
|
||||
}
|
||||
|
||||
public void stopTiming(long start) {
|
||||
if (enabled) addDiff(System.nanoTime() - start);
|
||||
}
|
||||
|
||||
public void stopTiming(boolean alreadySync) {
|
||||
if (!enabled) return;
|
||||
if (!alreadySync) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (curThread.getClass() == AssignableThread.class) return;
|
||||
if (curThread != MinecraftServer.getServer().primaryThread) {
|
||||
if (AkarinGlobalConfig.silentAsyncTimings) return;
|
||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
||||
new Throwable().printStackTrace();
|
||||
start = 0;
|
||||
return;
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
|
||||
// Main thread ensured
|
||||
if (--timingDepth == 0 && start != 0) {
|
||||
addDiff(System.nanoTime() - start);
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
if (enabled && timingDepth > 0) {
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
public final void abort() {
|
||||
|
||||
}
|
||||
void addDiff(long diff) {
|
||||
if (TimingsManager.CURRENT == this) {
|
||||
TimingsManager.CURRENT = parent;
|
||||
if (parent != null) {
|
||||
parent.children.get(id).add(diff);
|
||||
}
|
||||
if (this != null) {
|
||||
this.children.get(id).add(diff);
|
||||
}
|
||||
|
||||
record.add(diff);
|
||||
if (!added) {
|
||||
added = true;
|
||||
@@ -148,15 +146,29 @@ class TimingHandler implements Timing {
|
||||
TimingsManager.HANDLERS.add(this);
|
||||
}
|
||||
if (groupHandler != null) {
|
||||
groupHandler.addDiff(diff);
|
||||
groupHandler.addDiff(diff, this);
|
||||
groupHandler.children.get(id).add(diff);
|
||||
}
|
||||
}
|
||||
void addDiff(long diff, TimingHandler parent) {
|
||||
if (parent != null) {
|
||||
parent.children.get(id).add(diff);
|
||||
}
|
||||
|
||||
record.add(diff);
|
||||
if (!added) {
|
||||
added = true;
|
||||
timed = true;
|
||||
TimingsManager.HANDLERS.add(this);
|
||||
}
|
||||
if (groupHandler != null) {
|
||||
groupHandler.addDiff(diff, parent);
|
||||
groupHandler.children.get(id).add(diff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset this timer, setting all values to zero.
|
||||
*
|
||||
* @param full
|
||||
*/
|
||||
void reset(boolean full) {
|
||||
record.reset();
|
||||
@@ -186,8 +198,7 @@ class TimingHandler implements Timing {
|
||||
}
|
||||
|
||||
/**
|
||||
* This is simply for the Closeable interface so it can be used with
|
||||
* try-with-resources ()
|
||||
* This is simply for the Closeable interface so it can be used with try-with-resources ()
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
103
sources/src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
103
sources/src/main/java/co/aikar/timings/WorldTimingsHandler.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package co.aikar.timings;
|
||||
|
||||
import net.minecraft.server.World;
|
||||
|
||||
/**
|
||||
* Set of timers per world, to track world specific timings.
|
||||
*/
|
||||
public class WorldTimingsHandler {
|
||||
public final Timing mobSpawn;
|
||||
public final Timing doChunkUnload;
|
||||
public final Timing doPortalForcer;
|
||||
public final Timing scheduledBlocks;
|
||||
public final Timing scheduledBlocksCleanup;
|
||||
public final Timing scheduledBlocksTicking;
|
||||
public final Timing chunkTicks;
|
||||
public final Timing lightChunk;
|
||||
public final Timing chunkTicksBlocks;
|
||||
public final Timing doVillages;
|
||||
public final Timing doChunkMap;
|
||||
public final Timing doChunkMapUpdate;
|
||||
public final Timing doChunkMapToUpdate;
|
||||
public final Timing doChunkMapSortMissing;
|
||||
public final Timing doChunkMapSortSendToPlayers;
|
||||
public final Timing doChunkMapPlayersNeedingChunks;
|
||||
public final Timing doChunkMapPendingSendToPlayers;
|
||||
public final Timing doChunkMapUnloadChunks;
|
||||
public final Timing doChunkGC;
|
||||
public final Timing doSounds;
|
||||
public final Timing entityRemoval;
|
||||
public final Timing entityTick;
|
||||
public final Timing tileEntityTick;
|
||||
public final Timing tileEntityPending;
|
||||
public final Timing tracker1;
|
||||
public final Timing tracker2;
|
||||
public final Timing doTick;
|
||||
public final Timing tickEntities;
|
||||
|
||||
public final Timing syncChunkLoadTimer;
|
||||
public final Timing syncChunkLoadDataTimer;
|
||||
public final Timing syncChunkLoadStructuresTimer;
|
||||
public final Timing syncChunkLoadPostTimer;
|
||||
public final Timing syncChunkLoadNBTTimer;
|
||||
public final Timing syncChunkLoadPopulateNeighbors;
|
||||
public final Timing chunkGeneration;
|
||||
public final Timing chunkIOStage1;
|
||||
public final Timing chunkIOStage2;
|
||||
public final Timing worldSave;
|
||||
public final Timing worldSaveChunks;
|
||||
public final Timing worldSaveLevel;
|
||||
public final Timing chunkSaveData;
|
||||
|
||||
public final Timing lightingQueueTimer;
|
||||
|
||||
public WorldTimingsHandler(World server) {
|
||||
String name = server.worldData.getName() +" - ";
|
||||
|
||||
mobSpawn = Timings.ofSafe(name + "mobSpawn");
|
||||
doChunkUnload = Timings.ofSafe(name + "doChunkUnload");
|
||||
scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks");
|
||||
scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup");
|
||||
scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking");
|
||||
chunkTicks = Timings.ofSafe(name + "Chunk Ticks");
|
||||
lightChunk = Timings.ofSafe(name + "Light Chunk");
|
||||
chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks");
|
||||
doVillages = Timings.ofSafe(name + "doVillages");
|
||||
doChunkMap = Timings.ofSafe(name + "doChunkMap");
|
||||
doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update");
|
||||
doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update");
|
||||
doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing");
|
||||
doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players");
|
||||
doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks");
|
||||
doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players");
|
||||
doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks");
|
||||
doSounds = Timings.ofSafe(name + "doSounds");
|
||||
doChunkGC = Timings.ofSafe(name + "doChunkGC");
|
||||
doPortalForcer = Timings.ofSafe(name + "doPortalForcer");
|
||||
entityTick = Timings.ofSafe(name + "entityTick");
|
||||
entityRemoval = Timings.ofSafe(name + "entityRemoval");
|
||||
tileEntityTick = Timings.ofSafe(name + "tileEntityTick");
|
||||
tileEntityPending = Timings.ofSafe(name + "tileEntityPending");
|
||||
|
||||
syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad");
|
||||
syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data");
|
||||
syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures");
|
||||
syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post");
|
||||
syncChunkLoadNBTTimer = Timings.ofSafe(name + "chunkLoad - NBT");
|
||||
syncChunkLoadPopulateNeighbors = Timings.ofSafe(name + "chunkLoad - Populate Neighbors");
|
||||
chunkGeneration = Timings.ofSafe(name + "chunkGeneration");
|
||||
chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO");
|
||||
chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load");
|
||||
worldSave = Timings.ofSafe(name + "World Save");
|
||||
worldSaveLevel = Timings.ofSafe(name + "World Save - Level");
|
||||
worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks");
|
||||
chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data");
|
||||
|
||||
tracker1 = Timings.ofSafe(name + "tracker stage 1");
|
||||
tracker2 = Timings.ofSafe(name + "tracker stage 2");
|
||||
doTick = Timings.ofSafe(name + "doTick");
|
||||
tickEntities = Timings.ofSafe(name + "tickEntities");
|
||||
|
||||
lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,12 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import com.google.common.collect.Queues;
|
||||
@@ -13,8 +17,15 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
import co.aikar.timings.Timings;
|
||||
import io.akarin.api.internal.Akari.AssignableFactory;
|
||||
import io.akarin.api.internal.Akari.TimingSignal;
|
||||
import io.akarin.api.internal.utils.ReentrantSpinningLock;
|
||||
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
|
||||
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public abstract class Akari {
|
||||
@@ -37,22 +48,65 @@ public abstract class Akari {
|
||||
public AssignableThread(Runnable run) {
|
||||
super(run);
|
||||
}
|
||||
public AssignableThread() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
||||
public static class AssignableFactory implements ThreadFactory {
|
||||
private final String threadName;
|
||||
private int threadNumber;
|
||||
|
||||
public AssignableFactory(String name) {
|
||||
threadName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable run) {
|
||||
Thread thread = new AssignableThread(run);
|
||||
thread.setName("Akarin Parallel Schedule Thread");
|
||||
thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
|
||||
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A common tick pool
|
||||
*/
|
||||
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
|
||||
public static class TimingSignal {
|
||||
public final World tickedWorld;
|
||||
public final boolean isEntities;
|
||||
|
||||
public TimingSignal(World world, boolean entities) {
|
||||
tickedWorld = world;
|
||||
isEntities = entities;
|
||||
}
|
||||
}
|
||||
|
||||
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
|
||||
|
||||
static {
|
||||
resizeTickExecutors(3);
|
||||
}
|
||||
|
||||
public static void resizeTickExecutors(int worlds) {
|
||||
int parallelism;
|
||||
switch (AkarinGlobalConfig.parallelMode) {
|
||||
case -1:
|
||||
return;
|
||||
case 0:
|
||||
parallelism = 2;
|
||||
break;
|
||||
case 1:
|
||||
parallelism = worlds + 1;
|
||||
break;
|
||||
case 2:
|
||||
default:
|
||||
parallelism = worlds * 2;
|
||||
break;
|
||||
}
|
||||
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<Runnable>(),
|
||||
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
|
||||
}
|
||||
|
||||
public static boolean isPrimaryThread() {
|
||||
return isPrimaryThread(true);
|
||||
@@ -60,7 +114,7 @@ public abstract class Akari {
|
||||
|
||||
public static boolean isPrimaryThread(boolean assign) {
|
||||
Thread current = Thread.currentThread();
|
||||
return current == MinecraftServer.getServer().primaryThread || (assign ? current instanceof AssignableThread : false);
|
||||
return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
|
||||
}
|
||||
|
||||
public static final String EMPTY_STRING = "";
|
||||
@@ -92,9 +146,7 @@ public abstract class Akari {
|
||||
*/
|
||||
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
|
||||
|
||||
public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Callback");
|
||||
|
||||
public final static Timing callbackTiming = getTiming("Akarin - Callback");
|
||||
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
|
||||
|
||||
private static Timing getTiming(String name) {
|
||||
try {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
|
||||
public interface IMixinChunk {
|
||||
AtomicInteger getPendingLightUpdates();
|
||||
|
||||
long getLightUpdateTime();
|
||||
|
||||
boolean areNeighborsLoaded();
|
||||
|
||||
@Nullable Chunk getNeighborChunk(int index);
|
||||
|
||||
CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type);
|
||||
|
||||
List<Chunk> getNeighbors();
|
||||
|
||||
void setNeighborChunk(int index, @Nullable Chunk chunk);
|
||||
|
||||
void setLightUpdateTime(long time);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
public interface IMixinLockProvider {
|
||||
public Object lock();
|
||||
}
|
||||
@@ -1,13 +1,8 @@
|
||||
package io.akarin.api.internal.mixin;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import java.util.Random;
|
||||
|
||||
public interface IMixinWorldServer {
|
||||
boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk);
|
||||
|
||||
ExecutorService getLightingExecutor();
|
||||
public Object lock();
|
||||
public Random rand();
|
||||
}
|
||||
@@ -33,7 +33,7 @@
|
||||
* at http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
package io.akarin.api.internal.collections;
|
||||
package io.akarin.api.internal.utils;
|
||||
|
||||
import java.util.AbstractQueue;
|
||||
import java.util.ArrayList;
|
||||
@@ -0,0 +1,102 @@
|
||||
package io.akarin.api.internal.utils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ReentrantSpinningLock {
|
||||
/*
|
||||
* Impl Note:
|
||||
* A write lock can reentrant as a read lock, while a
|
||||
* read lock is not allowed to reentrant as a write lock.
|
||||
* READ LOCK IS UNTESTED, USE WITH CATION.
|
||||
*/
|
||||
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
|
||||
|
||||
// --------- Thread local restricted fields ---------
|
||||
private long heldThreadId = 0;
|
||||
private int reentrantLocks = 0;
|
||||
|
||||
/**
|
||||
* Lock as a typical reentrant write lock
|
||||
*/
|
||||
public void lock() {
|
||||
long currentThreadId = Thread.currentThread().getId();
|
||||
if (heldThreadId == currentThreadId) {
|
||||
reentrantLocks++;
|
||||
} else {
|
||||
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
|
||||
heldThreadId = currentThreadId;
|
||||
}
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
if (reentrantLocks == 0) {
|
||||
heldThreadId = 0;
|
||||
//if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
|
||||
writeLocked.set(false);
|
||||
//}
|
||||
} else {
|
||||
--reentrantLocks;
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicInteger readerThreads = new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* Lock as a typical reentrant read lock
|
||||
*/
|
||||
@Deprecated
|
||||
public void lockWeak() {
|
||||
long currentThreadId = Thread.currentThread().getId();
|
||||
if (heldThreadId == currentThreadId) {
|
||||
reentrantLocks++;
|
||||
} else {
|
||||
if (readerThreads.get() == 0) {
|
||||
while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock
|
||||
}
|
||||
heldThreadId = currentThreadId;
|
||||
readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void unlockWeak() {
|
||||
if (reentrantLocks == 0) {
|
||||
heldThreadId = 0;
|
||||
writeLocked.set(false);
|
||||
} else {
|
||||
--reentrantLocks;
|
||||
}
|
||||
}
|
||||
|
||||
// --------- Wrappers to allow typical usages ---------
|
||||
private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock();
|
||||
private SpinningReadLock wrappedReadLock = new SpinningReadLock();
|
||||
|
||||
public class SpinningWriteLock {
|
||||
public void lock() {
|
||||
lock();
|
||||
}
|
||||
public void unlock() {
|
||||
unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public class SpinningReadLock {
|
||||
public void lock() {
|
||||
lockWeak();
|
||||
}
|
||||
public void unlock() {
|
||||
unlockWeak();
|
||||
}
|
||||
}
|
||||
|
||||
public SpinningWriteLock writeLock() {
|
||||
return wrappedWriteLock;
|
||||
}
|
||||
|
||||
public SpinningReadLock readLock() {
|
||||
return wrappedReadLock;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.akarin.api.internal.collections;
|
||||
package io.akarin.api.internal.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectOutputStream;
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
Written in 2015 by Sebastiano Vigna (vigna@acm.org)
|
||||
|
||||
To the extent possible under law, the author has dedicated all copyright
|
||||
and related and neighboring rights to this software to the public domain
|
||||
worldwide. This software is distributed without any warranty.
|
||||
|
||||
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
|
||||
package io.akarin.api.internal.utils.random;
|
||||
|
||||
/**
|
||||
* This is a SplittableRandom-style generator, meant to have a tiny state
|
||||
* that permits storing many different generators with low overhead.
|
||||
* It should be rather fast, though no guarantees can be made on all hardware.
|
||||
* <br>
|
||||
* Benchmarking on a Windows laptop with an i7-4700MQ processor running OpenJDK 8
|
||||
* reports generation of 64-bit random long output as 17.8x faster than generating
|
||||
* an equivalent number of random longs with java.util.Random, and generation of
|
||||
* 32-bit random int output as 9.8x faster. Specifically, generating 1 billion longs
|
||||
* took about 1.28 nanoseconds per long (1.277 seconds for the whole group) with
|
||||
* LightRNG, while java.util.Random (which is meant to produce int, to be fair) took
|
||||
* about 22.8 nanoseconds per long (22.797 seconds for the whole group). XorRNG
|
||||
* appears to be occasionally faster on int output than LightRNG, but it isn't clear
|
||||
* why or what causes that (JIT or GC internals, possibly). XorRNG is slightly
|
||||
* slower at generating 64-bit random data, including long and double, but not by
|
||||
* a significant degree (a multiplier between 0.9 and 1.2 times). The only deciding
|
||||
* factor then is state size, where LightRNG is as small as possible for any JVM
|
||||
* object with even a single field: 16 bytes (on a 64-bit JVM; 8-byte objects with
|
||||
* 4 bytes or less of non-static members may be possible on 32-bit JVMs but I can't
|
||||
* find anything confirming that guess).
|
||||
* <br>
|
||||
* So yes, this should be very fast, and with only a single long used per LightRNG,
|
||||
* it is about as memory-efficient as these generators get.
|
||||
* <br>
|
||||
* Written in 2015 by Sebastiano Vigna (vigna@acm.org)
|
||||
* @author Sebastiano Vigna
|
||||
* @author Tommy Ettinger
|
||||
*/
|
||||
public class LightRNG implements RandomnessSource, StatefulRandomness
|
||||
{
|
||||
/** 2 raised to the 53, - 1. */
|
||||
private static final long DOUBLE_MASK = ( 1L << 53 ) - 1;
|
||||
/** 2 raised to the -53. */
|
||||
private static final double NORM_53 = 1. / ( 1L << 53 );
|
||||
/** 2 raised to the 24, -1. */
|
||||
private static final long FLOAT_MASK = ( 1L << 24 ) - 1;
|
||||
/** 2 raised to the -24. */
|
||||
private static final double NORM_24 = 1. / ( 1L << 24 );
|
||||
|
||||
private static final long serialVersionUID = -374415589203474497L;
|
||||
|
||||
public long state; /* The state can be seeded with any value. */
|
||||
|
||||
/** Creates a new generator seeded using Math.random. */
|
||||
public LightRNG() {
|
||||
this((long) Math.floor(Math.random() * Long.MAX_VALUE));
|
||||
}
|
||||
|
||||
public LightRNG( final long seed ) {
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int next( int bits ) {
|
||||
return (int)( nextLong() & ( 1L << bits ) - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.
|
||||
* @return any long, all 64 bits are random
|
||||
*/
|
||||
@Override
|
||||
public long nextLong() {
|
||||
long z = ( state += 0x9E3779B97F4A7C15L );
|
||||
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
|
||||
return z ^ (z >>> 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
|
||||
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
|
||||
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
|
||||
*
|
||||
* @return a copy of this RandomnessSource
|
||||
*/
|
||||
@Override
|
||||
public RandomnessSource copy() {
|
||||
return new LightRNG(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
|
||||
* @return any int, all 32 bits are random
|
||||
*/
|
||||
public int nextInt() {
|
||||
return (int)nextLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive on the upper bound. The lower bound is 0.
|
||||
* @param bound the upper bound; should be positive
|
||||
* @return a random int less than n and at least equal to 0
|
||||
*/
|
||||
public int nextInt( final int bound ) {
|
||||
if ( bound <= 0 ) return 0;
|
||||
int threshold = (0x7fffffff - bound + 1) % bound;
|
||||
for (;;) {
|
||||
int bits = (int)(nextLong() & 0x7fffffff);
|
||||
if (bits >= threshold)
|
||||
return bits % bound;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Inclusive lower, exclusive upper.
|
||||
* @param lower the lower bound, inclusive, can be positive or negative
|
||||
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
|
||||
* @return a random int at least equal to lower and less than upper
|
||||
*/
|
||||
public int nextInt( final int lower, final int upper ) {
|
||||
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
|
||||
return lower + nextInt(upper - lower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclusive on the upper bound. The lower bound is 0.
|
||||
* @param bound the upper bound; should be positive
|
||||
* @return a random long less than n
|
||||
*/
|
||||
public long nextLong( final long bound ) {
|
||||
if ( bound <= 0 ) return 0;
|
||||
long threshold = (0x7fffffffffffffffL - bound + 1) % bound;
|
||||
for (;;) {
|
||||
long bits = nextLong() & 0x7fffffffffffffffL;
|
||||
if (bits >= threshold)
|
||||
return bits % bound;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inclusive lower, exclusive upper.
|
||||
* @param lower the lower bound, inclusive, can be positive or negative
|
||||
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
|
||||
* @return a random long at least equal to lower and less than upper
|
||||
*/
|
||||
public long nextLong( final long lower, final long upper ) {
|
||||
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
|
||||
return lower + nextLong(upper - lower);
|
||||
}
|
||||
/**
|
||||
* Gets a uniform random double in the range [0.0,1.0)
|
||||
* @return a random double at least equal to 0.0 and less than 1.0
|
||||
*/
|
||||
public double nextDouble() {
|
||||
return ( nextLong() & DOUBLE_MASK ) * NORM_53;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer
|
||||
* is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound.
|
||||
* @param outer the exclusive outer bound, can be negative
|
||||
* @return a random double between 0.0 (inclusive) and outer (exclusive)
|
||||
*/
|
||||
public double nextDouble(final double outer) {
|
||||
return nextDouble() * outer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a uniform random float in the range [0.0,1.0)
|
||||
* @return a random float at least equal to 0.0 and less than 1.0
|
||||
*/
|
||||
public float nextFloat() {
|
||||
return (float)( ( nextLong() & FLOAT_MASK ) * NORM_24 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random value, true or false.
|
||||
* Calls nextLong() once.
|
||||
* @return a random true or false value.
|
||||
*/
|
||||
public boolean nextBoolean() {
|
||||
return ( nextLong() & 1 ) != 0L;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a byte array as a parameter, this will fill the array with random bytes (modifying it
|
||||
* in-place). Calls nextLong() {@code Math.ceil(bytes.length / 8.0)} times.
|
||||
* @param bytes a byte array that will have its contents overwritten with random bytes.
|
||||
*/
|
||||
public void nextBytes( final byte[] bytes ) {
|
||||
int i = bytes.length, n = 0;
|
||||
while( i != 0 ) {
|
||||
n = Math.min( i, 8 );
|
||||
for ( long bits = nextLong(); n-- != 0; bits >>= 8 ) bytes[ --i ] = (byte)bits;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the seed of this generator (which is also the current state).
|
||||
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
|
||||
*/
|
||||
public void setSeed( final long seed ) {
|
||||
state = seed;
|
||||
}
|
||||
/**
|
||||
* Sets the seed (also the current state) of this generator.
|
||||
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
|
||||
*/
|
||||
@Override
|
||||
public void setState( final long seed ) {
|
||||
state = seed;
|
||||
}
|
||||
/**
|
||||
* Gets the current state of this generator.
|
||||
* @return the current seed of this LightRNG, changed once per call to nextLong()
|
||||
*/
|
||||
@Override
|
||||
public long getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances or rolls back the LightRNG's state without actually generating numbers. Skip forward
|
||||
* or backward a number of steps specified by advance, where a step is equal to one call to nextInt().
|
||||
* @param advance Number of future generations to skip past. Can be negative to backtrack.
|
||||
* @return the state after skipping.
|
||||
*/
|
||||
public long skip(long advance)
|
||||
{
|
||||
return state += 0x9E3779B97F4A7C15L * advance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LightRNG (" + state + ")"; // Akarin
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.akarin.api.internal.utils.random;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This is a "fake" LightRandom, backed by the LightRNG.
|
||||
*
|
||||
* This is useful if you want to quickly replace a Random variable with
|
||||
* LightRNG without breaking every code.
|
||||
*/
|
||||
public class LightRandom extends Random {
|
||||
public static LightRNG light = new LightRNG(); // LightRNG, static.
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public int next(int bits) {
|
||||
return light.next(bits);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextBytes(byte[] bytes) {
|
||||
light.nextBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt() {
|
||||
return light.nextInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextInt(int n) {
|
||||
return light.nextInt(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextLong() {
|
||||
return light.nextLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nextBoolean() {
|
||||
return light.nextBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float nextFloat() {
|
||||
return light.nextFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextDouble() {
|
||||
return light.nextDouble();
|
||||
}
|
||||
|
||||
// Akarin start
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
light.setSeed(seed);
|
||||
}
|
||||
// Akarin end
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.akarin.api.internal.utils.random;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* This interface defines the interactions required of a random number
|
||||
* generator. It is a replacement for Java's built-in Random because for
|
||||
* improved performance.
|
||||
*
|
||||
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
|
||||
*/
|
||||
public interface RandomnessSource extends Serializable {
|
||||
|
||||
/**
|
||||
* Using this method, any algorithm that might use the built-in Java Random
|
||||
* can interface with this randomness source.
|
||||
*
|
||||
* @param bits the number of bits to be returned
|
||||
* @return the integer containing the appropriate number of bits
|
||||
*/
|
||||
int next(int bits);
|
||||
|
||||
/**
|
||||
*
|
||||
* Using this method, any algorithm that needs to efficiently generate more
|
||||
* than 32 bits of random data can interface with this randomness source.
|
||||
*
|
||||
* Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
|
||||
* @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
|
||||
*/
|
||||
long nextLong();
|
||||
|
||||
/**
|
||||
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
|
||||
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
|
||||
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
|
||||
* @return a copy of this RandomnessSource
|
||||
*/
|
||||
RandomnessSource copy();
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.akarin.api.internal.utils.random;
|
||||
|
||||
/**
|
||||
* A simple interface for RandomnessSources that have the additional property of a state that can be re-set.
|
||||
* Created by Tommy Ettinger on 9/15/2015.
|
||||
*/
|
||||
public interface StatefulRandomness extends RandomnessSource {
|
||||
/**
|
||||
* Get the current internal state of the StatefulRandomness as a long.
|
||||
* @return the current internal state of this object.
|
||||
*/
|
||||
long getState();
|
||||
|
||||
/**
|
||||
* Set the current internal state of this StatefulRandomness with a long.
|
||||
*
|
||||
* @param state a 64-bit long. You should avoid passing 0, even though some implementations can handle that.
|
||||
*/
|
||||
void setState(long state);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.akarin.api.internal.utils.thread;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class OpenExecutionException extends ExecutionException {
|
||||
private static final long serialVersionUID = 7830266012832686185L;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||
* Expert Group and released to the public domain, as explained at
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
package io.akarin.api.internal.utils.thread;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.RunnableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class SuspendableExecutorCompletionService<V> implements CompletionService<V> {
|
||||
private final SuspendableThreadPoolExecutor executor;
|
||||
private final BlockingQueue<Future<V>> completionQueue;
|
||||
|
||||
public void suspend() {
|
||||
executor.suspend();
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
executor.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* FutureTask extension to enqueue upon completion
|
||||
*/
|
||||
private class QueueingFuture extends FutureTask<Void> {
|
||||
QueueingFuture(RunnableFuture<V> task) {
|
||||
super(task, null);
|
||||
this.task = task;
|
||||
}
|
||||
protected void done() { completionQueue.add(task); }
|
||||
private final Future<V> task;
|
||||
}
|
||||
|
||||
private RunnableFuture<V> newTaskFor(Callable<V> task) {
|
||||
return new FutureTask<V>(task);
|
||||
}
|
||||
|
||||
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
|
||||
return new FutureTask<V>(task, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ExecutorCompletionService using the supplied
|
||||
* executor for base task execution and a
|
||||
* {@link LinkedBlockingQueue} as a completion queue.
|
||||
*
|
||||
* @param executor the executor to use
|
||||
* @throws NullPointerException if executor is {@code null}
|
||||
*/
|
||||
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) {
|
||||
if (executor == null)
|
||||
throw new NullPointerException();
|
||||
this.executor = executor;
|
||||
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an ExecutorCompletionService using the supplied
|
||||
* executor for base task execution and the supplied queue as its
|
||||
* completion queue.
|
||||
*
|
||||
* @param executor the executor to use
|
||||
* @param completionQueue the queue to use as the completion queue
|
||||
* normally one dedicated for use by this service. This
|
||||
* queue is treated as unbounded -- failed attempted
|
||||
* {@code Queue.add} operations for completed tasks cause
|
||||
* them not to be retrievable.
|
||||
* @throws NullPointerException if executor or completionQueue are {@code null}
|
||||
*/
|
||||
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor,
|
||||
BlockingQueue<Future<V>> completionQueue) {
|
||||
if (executor == null || completionQueue == null)
|
||||
throw new NullPointerException();
|
||||
this.executor = executor;
|
||||
this.completionQueue = completionQueue;
|
||||
}
|
||||
|
||||
public Future<V> submit(Callable<V> task) {
|
||||
if (task == null) throw new NullPointerException();
|
||||
RunnableFuture<V> f = newTaskFor(task);
|
||||
executor.execute(new QueueingFuture(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
public Future<V> submit(Runnable task, V result) {
|
||||
if (task == null) throw new NullPointerException();
|
||||
RunnableFuture<V> f = newTaskFor(task, result);
|
||||
executor.execute(new QueueingFuture(f));
|
||||
return f;
|
||||
}
|
||||
|
||||
public Future<V> take() throws InterruptedException {
|
||||
return completionQueue.take();
|
||||
}
|
||||
|
||||
public Future<V> poll() {
|
||||
return completionQueue.poll();
|
||||
}
|
||||
|
||||
public Future<V> poll(long timeout, TimeUnit unit)
|
||||
throws InterruptedException {
|
||||
return completionQueue.poll(timeout, unit);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -165,11 +165,6 @@ public class AkarinGlobalConfig {
|
||||
silentAsyncTimings = getBoolean("core.always-silent-async-timing", false);
|
||||
}
|
||||
|
||||
public static boolean legacyWorldTimings;
|
||||
private static void legacyWorldTimings() {
|
||||
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
|
||||
}
|
||||
|
||||
public static long timeUpdateInterval;
|
||||
private static void timeUpdateInterval() {
|
||||
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
||||
@@ -201,52 +196,25 @@ public class AkarinGlobalConfig {
|
||||
}
|
||||
|
||||
public static String messageKick;
|
||||
private static void messageKick() {
|
||||
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
|
||||
}
|
||||
|
||||
public static String messageBan;
|
||||
private static void messageBan() {
|
||||
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
|
||||
}
|
||||
|
||||
public static String messageBanReason;
|
||||
private static void messageBanReason() {
|
||||
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
|
||||
}
|
||||
|
||||
public static String messageBanExpires;
|
||||
private static void messageBanExpires() {
|
||||
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
|
||||
}
|
||||
|
||||
public static String messageBanIp;
|
||||
private static void messageBanIp() {
|
||||
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
|
||||
}
|
||||
|
||||
public static String messageDupLogin;
|
||||
private static void messageDupLogin() {
|
||||
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
|
||||
}
|
||||
|
||||
public static String messageJoin;
|
||||
private static void messageJoin() {
|
||||
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
|
||||
}
|
||||
|
||||
public static String messageJoinRenamed;
|
||||
private static void messageJoinRenamed() {
|
||||
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
|
||||
}
|
||||
|
||||
public static String messageKickKeepAlive;
|
||||
private static void messagekickKeepAlive() {
|
||||
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
|
||||
}
|
||||
|
||||
public static String messagePlayerQuit;
|
||||
private static void messagePlayerQuit() {
|
||||
private static void messagekickKeepAlive() {
|
||||
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
|
||||
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
|
||||
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
|
||||
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
|
||||
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
|
||||
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
|
||||
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
|
||||
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
|
||||
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
|
||||
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
|
||||
}
|
||||
|
||||
@@ -279,4 +247,19 @@ public class AkarinGlobalConfig {
|
||||
private static void sendLightOnlyChunkSection() {
|
||||
sendLightOnlyChunkSection = getBoolean("core.send-light-only-chunk-sections", true);
|
||||
}
|
||||
|
||||
public static boolean forceHardcoreDifficulty;
|
||||
private static void forceHardcoreDifficulty() {
|
||||
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
||||
}
|
||||
|
||||
public static int fileIOThreads;
|
||||
private static void fileIOThreads() {
|
||||
fileIOThreads = getInt("core.chunk-save-threads", 2);
|
||||
}
|
||||
|
||||
public static int parallelMode;
|
||||
private static void parallelMode() {
|
||||
parallelMode = getInt("core.parallel-mode", 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,10 @@ public class AkarinSlackScheduler extends Thread {
|
||||
return Singleton.instance;
|
||||
}
|
||||
|
||||
public static void boot() {
|
||||
Singleton.instance.setName("Akarin Slack Scheduler Thread");
|
||||
Singleton.instance.setPriority(MIN_PRIORITY);
|
||||
Singleton.instance.setDaemon(true);
|
||||
Singleton.instance.start();
|
||||
public void boot() {
|
||||
setName("Akarin Slack Scheduler Thread");
|
||||
setDaemon(true);
|
||||
start();
|
||||
Akari.logger.info("Slack scheduler service started");
|
||||
}
|
||||
|
||||
@@ -41,11 +40,13 @@ public class AkarinSlackScheduler extends Thread {
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
while (server.isRunning()) {
|
||||
long startProcessTiming = System.currentTimeMillis();
|
||||
// Send time updates to everyone, it will get the right time from the world the player is in.
|
||||
// Time update, from MinecraftServer#D
|
||||
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
|
||||
for (EntityPlayer player : server.getPlayerList().players) {
|
||||
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time
|
||||
// Add support for per player time
|
||||
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle")));
|
||||
}
|
||||
updateTime = 0;
|
||||
}
|
||||
@@ -70,12 +71,13 @@ public class AkarinSlackScheduler extends Thread {
|
||||
conn.setPendingPing(true);
|
||||
conn.setLastPing(currentTime);
|
||||
conn.setKeepAliveID(currentTime);
|
||||
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID()));
|
||||
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); // 15s lagg you should stop your server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Force hardcore difficulty, from WorldServer#doTick
|
||||
if (AkarinGlobalConfig.forceHardcoreDifficulty)
|
||||
for (WorldServer world : server.worlds) {
|
||||
if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) {
|
||||
world.getWorldData().setDifficulty(EnumDifficulty.HARD);
|
||||
@@ -84,11 +86,11 @@ public class AkarinSlackScheduler extends Thread {
|
||||
|
||||
// Update player info, from PlayerList#tick
|
||||
if (++resendPlayersInfo > AkarinGlobalConfig.playersInfoUpdateInterval) {
|
||||
for (EntityPlayer target : server.getPlayerList().players) {
|
||||
target.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(server.getPlayerList().players, new Predicate<EntityPlayer>() {
|
||||
for (EntityPlayer player : server.getPlayerList().players) {
|
||||
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(server.getPlayerList().players, new Predicate<EntityPlayer>() {
|
||||
@Override
|
||||
public boolean apply(EntityPlayer input) {
|
||||
return target.getBukkitEntity().canSee(input.getBukkitEntity());
|
||||
public boolean apply(EntityPlayer each) {
|
||||
return player.getBukkitEntity().canSee(each.getBukkitEntity());
|
||||
}
|
||||
})));
|
||||
}
|
||||
@@ -96,10 +98,10 @@ public class AkarinSlackScheduler extends Thread {
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
|
||||
ex.printStackTrace();
|
||||
long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming);
|
||||
if (sleepFixed > 0) Thread.sleep(sleepFixed);
|
||||
} catch (InterruptedException interrupted) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.spigotmc.RestartCommand;
|
||||
import org.spigotmc.WatchdogThread;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
|
||||
public abstract class Watchcat extends Thread {
|
||||
@Shadow private static WatchdogThread instance;
|
||||
@Shadow private @Final long timeoutTime;
|
||||
@Shadow private @Final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting
|
||||
@Shadow private @Final long earlyWarningDelay; // Paper
|
||||
@Shadow public static volatile boolean hasStarted; // Paper
|
||||
@Shadow private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps
|
||||
@Shadow private @Final boolean restart;
|
||||
@Shadow private volatile long lastTick;
|
||||
@Shadow private volatile boolean stopping;
|
||||
@@ -38,48 +41,73 @@ public abstract class Watchcat extends Thread {
|
||||
@Overwrite
|
||||
public void run() {
|
||||
while (!stopping) {
|
||||
//
|
||||
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
|
||||
Logger log = Bukkit.getServer().getLogger();
|
||||
log.log(Level.SEVERE, "Server has stopped responding!");
|
||||
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues");
|
||||
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
||||
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion());
|
||||
//
|
||||
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
|
||||
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
|
||||
}
|
||||
// Paper start - Warn in watchdog if an excessive velocity was ever set
|
||||
if (CraftServer.excessiveVelEx != null) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
|
||||
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
|
||||
log.log(Level.SEVERE, CraftServer.excessiveVelEx.getMessage());
|
||||
for (StackTraceElement stack : CraftServer.excessiveVelEx.getStackTrace()) {
|
||||
log.log(Level.SEVERE, "\t\t" + stack);
|
||||
}
|
||||
}
|
||||
// Paper start
|
||||
long currentTime = System.currentTimeMillis();
|
||||
if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") )
|
||||
{
|
||||
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
|
||||
// Don't spam early warning dumps
|
||||
if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
|
||||
continue;
|
||||
lastEarlyWarning = currentTime;
|
||||
// Paper end
|
||||
Logger log = Bukkit.getServer().getLogger();
|
||||
// Paper start - Different message when it's a short timeout
|
||||
if (isLongTimeout) {
|
||||
log.log(Level.SEVERE, "The server has stopped responding!");
|
||||
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues"); // Akarin
|
||||
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
||||
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion()); // Akarin
|
||||
//
|
||||
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
|
||||
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
|
||||
}
|
||||
// Paper start - Warn in watchdog if an excessive velocity was ever set
|
||||
if (org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null) {
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
|
||||
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
|
||||
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
|
||||
for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) {
|
||||
log.log(Level.SEVERE, "\t\t" + stack);
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
} else {
|
||||
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
|
||||
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
|
||||
}
|
||||
// Paper end - Different message for short timeout
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Akarin!):");
|
||||
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
//
|
||||
log.log(Level.SEVERE, "Entire Thread Dump:");
|
||||
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
for (ThreadInfo thread : threads) {
|
||||
dumpThread(thread, log);
|
||||
// Paper start - Only print full dump on long timeouts
|
||||
if (isLongTimeout) {
|
||||
log.log(Level.SEVERE, "Entire Thread Dump:");
|
||||
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
for (ThreadInfo thread : threads) {
|
||||
dumpThread(thread, log);
|
||||
}
|
||||
} else {
|
||||
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
|
||||
}
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
|
||||
if (restart) RestartCommand.restart(); // GC Inlined
|
||||
break;
|
||||
log.log(Level.SEVERE, "------------------------------");
|
||||
|
||||
if ( isLongTimeout )
|
||||
{
|
||||
if (restart) {
|
||||
RestartCommand.restart();
|
||||
}
|
||||
break;
|
||||
} // Paper end
|
||||
}
|
||||
|
||||
try {
|
||||
sleep(9000); // Akarin
|
||||
sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
|
||||
} catch (InterruptedException ex) {
|
||||
interrupt();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.PersistentCollection;
|
||||
|
||||
@Mixin(value = AsyncCatcher.class, remap = false)
|
||||
public abstract class MixinAsyncCatcher {
|
||||
@@ -23,7 +22,6 @@ public abstract class MixinAsyncCatcher {
|
||||
} else {
|
||||
Akari.logger.warn("Asynchronous " + reason + "!");
|
||||
Thread.dumpStack();
|
||||
PersistentCollection.class.getName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import net.minecraft.server.ChunkSection;
|
||||
public abstract class MixinChunkSection {
|
||||
@Shadow private int nonEmptyBlockCount;
|
||||
|
||||
@Overwrite // PAIL: isEmpty
|
||||
@Overwrite // OBFHELPER: isEmpty
|
||||
public boolean a() {
|
||||
return AkarinGlobalConfig.sendLightOnlyChunkSection ? false : nonEmptyBlockCount == 0;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public abstract class MixinCommandBan {
|
||||
entityplayer.playerConnection.disconnect(hasReason ? message : AkarinGlobalConfig.messageBan);
|
||||
}
|
||||
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // PAIL: notifyCommandListener
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // OBFHELPER: notifyCommandListener
|
||||
}
|
||||
} else {
|
||||
throw new ExceptionUsage("commands.ban.usage");
|
||||
|
||||
@@ -19,7 +19,7 @@ import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@Mixin(value = CommandBanIp.class, remap = false)
|
||||
public abstract class MixinCommandBanIp {
|
||||
@Overwrite // PAIL: banIp
|
||||
@Overwrite // OBFHELPER: banIp
|
||||
protected void a(MinecraftServer server, ICommandListener sender, String args, @Nullable String banReason) {
|
||||
// Akarin start - modify message
|
||||
boolean hasReason = true;
|
||||
@@ -31,7 +31,7 @@ public abstract class MixinCommandBanIp {
|
||||
IpBanEntry ipbanentry = new IpBanEntry(args, (Date) null, sender.getName(), (Date) null, banReason);
|
||||
|
||||
server.getPlayerList().getIPBans().add(ipbanentry);
|
||||
List<EntityPlayer> withIpPlayers = server.getPlayerList().b(args); // PAIL: getPlayersMatchingAddress
|
||||
List<EntityPlayer> withIpPlayers = server.getPlayerList().b(args); // OBFHELPER: getPlayersMatchingAddress
|
||||
String[] banPlayerNames = new String[withIpPlayers.size()];
|
||||
|
||||
for (int i = 0; i < banPlayerNames.length; i++) {
|
||||
@@ -41,9 +41,9 @@ public abstract class MixinCommandBanIp {
|
||||
}
|
||||
|
||||
if (withIpPlayers.isEmpty()) {
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // PAIL: notifyCommandListener
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // OBFHELPER: notifyCommandListener
|
||||
} else {
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // PAIL: notifyCommandListener - joinNiceString
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // OBFHELPER: notifyCommandListener - joinNiceString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ public abstract class MixinCommandKick {
|
||||
message = message + args[i];
|
||||
}
|
||||
target.playerConnection.disconnect(message);
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // PAIL: notifyCommandListener
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // OBFHELPER: notifyCommandListener
|
||||
// Akarin end
|
||||
} else {
|
||||
target.playerConnection.disconnect(AkarinGlobalConfig.messageKick); // Akarin
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // PAIL: notifyCommandListener
|
||||
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // OBFHELPER: notifyCommandListener
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.FileIOThread;
|
||||
import net.minecraft.server.IAsyncChunkSaver;
|
||||
|
||||
@Mixin(value = FileIOThread.class, remap = false)
|
||||
public abstract class MixinFileIOThread {
|
||||
private final Executor executor = Executors.newFixedThreadPool(AkarinGlobalConfig.fileIOThreads, new ThreadFactoryBuilder().setNameFormat("Akarin File IO Thread - %1$d").setPriority(1).build());
|
||||
private final AtomicInteger queuedChunkCounter = new AtomicInteger(0);
|
||||
|
||||
@Shadow(aliases = "e") private volatile boolean isAwaitFinish;
|
||||
|
||||
@Overwrite // OBFHELPER: saveChunk
|
||||
public void a(IAsyncChunkSaver iasyncchunksaver) {
|
||||
queuedChunkCounter.incrementAndGet();
|
||||
executor.execute(() -> writeChunk(iasyncchunksaver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a chunk, re-add to the queue if unsuccessful
|
||||
*/
|
||||
private void writeChunk(IAsyncChunkSaver iasyncchunksaver) {
|
||||
if (!iasyncchunksaver.a()) { // PAIL: WriteNextIO() -> Returns if the write was unsuccessful
|
||||
queuedChunkCounter.decrementAndGet();
|
||||
|
||||
if (PaperConfig.enableFileIOThreadSleep) {
|
||||
try {
|
||||
Thread.sleep(isAwaitFinish ? 0L : 2L);
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeChunk(iasyncchunksaver);
|
||||
}
|
||||
}
|
||||
|
||||
@Overwrite // OBFHELPER: waitForFinish
|
||||
public void b() throws InterruptedException {
|
||||
isAwaitFinish = true;
|
||||
while (queuedChunkCounter.get() != 0) Thread.sleep(9L);
|
||||
isAwaitFinish = false;
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,14 @@ package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
import org.apache.commons.lang.WordUtils;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||
@@ -21,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import co.aikar.timings.MinecraftTimings;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.Akari.AssignableFactory;
|
||||
import io.akarin.api.internal.mixin.IMixinLockProvider;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import io.akarin.server.core.AkarinSlackScheduler;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
@@ -40,6 +45,8 @@ import net.minecraft.server.WorldServer;
|
||||
@Mixin(value = MinecraftServer.class, remap = false)
|
||||
public abstract class MixinMinecraftServer {
|
||||
@Shadow @Final public Thread primaryThread;
|
||||
private boolean tickedPrimaryEntities;
|
||||
private int cachedWorldSize;
|
||||
|
||||
@Overwrite
|
||||
public String getServerModName() {
|
||||
@@ -54,13 +61,13 @@ public abstract class MixinMinecraftServer {
|
||||
private void prerun(CallbackInfo info) {
|
||||
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
|
||||
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
|
||||
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
|
||||
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
|
||||
//TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
|
||||
}
|
||||
AkarinSlackScheduler.boot();
|
||||
|
||||
AkarinSlackScheduler.get().boot();
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
@@ -108,7 +115,7 @@ public abstract class MixinMinecraftServer {
|
||||
|
||||
@Overwrite
|
||||
protected void l() throws InterruptedException {
|
||||
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory()));
|
||||
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
|
||||
|
||||
for (int index = 0; index < worlds.size(); index++) {
|
||||
WorldServer world = this.worlds.get(index);
|
||||
@@ -120,7 +127,11 @@ public abstract class MixinMinecraftServer {
|
||||
|
||||
for (WorldServer world : this.worlds) {
|
||||
if (world.getWorld().getKeepSpawnInMemory()) executor.take();
|
||||
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
|
||||
}
|
||||
if (WorldLoadEvent.getHandlerList().getRegisteredListeners().length != 0) {
|
||||
for (WorldServer world : this.worlds) {
|
||||
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
|
||||
}
|
||||
}
|
||||
|
||||
enablePluginsPostWorld();
|
||||
@@ -143,7 +154,11 @@ public abstract class MixinMinecraftServer {
|
||||
|
||||
private boolean tickEntities(WorldServer world) {
|
||||
try {
|
||||
world.timings.tickEntities.startTiming();
|
||||
world.tickEntities();
|
||||
world.timings.tickEntities.stopTiming();
|
||||
world.getTracker().updatePlayers();
|
||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport;
|
||||
try {
|
||||
@@ -159,7 +174,9 @@ public abstract class MixinMinecraftServer {
|
||||
|
||||
private void tickWorld(WorldServer world) {
|
||||
try {
|
||||
world.timings.doTick.startTiming();
|
||||
world.doTick();
|
||||
world.timings.doTick.stopTiming();
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport;
|
||||
try {
|
||||
@@ -173,8 +190,12 @@ public abstract class MixinMinecraftServer {
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void D() throws InterruptedException {
|
||||
public void D() throws InterruptedException, ExecutionException, CancellationException {
|
||||
Runnable runnable;
|
||||
Akari.callbackTiming.startTiming();
|
||||
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||
Akari.callbackTiming.stopTiming();
|
||||
|
||||
MinecraftTimings.bukkitSchedulerTimer.startTiming();
|
||||
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
|
||||
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
|
||||
@@ -195,54 +216,92 @@ public abstract class MixinMinecraftServer {
|
||||
ChunkIOExecutor.tick();
|
||||
MinecraftTimings.chunkIOTickTimer.stopTiming();
|
||||
|
||||
Akari.worldTiming.startTiming();
|
||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
worlds.get(i).timings.tickEntities.startTiming();
|
||||
worlds.get(i).timings.doTick.startTiming();
|
||||
}
|
||||
}
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
// Never tick one world concurrently!
|
||||
for (int i = 1; i <= worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
|
||||
synchronized (((IMixinLockProvider) world).lock()) {
|
||||
if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
|
||||
switch (AkarinGlobalConfig.parallelMode) {
|
||||
case 1:
|
||||
case 2:
|
||||
default:
|
||||
// Never tick one world concurrently!
|
||||
for (int i = 0; i < cachedWorldSize; i++) {
|
||||
// Impl Note:
|
||||
// Entities ticking: index 1 -> ... -> 0 (parallel)
|
||||
// World ticking: index 0 -> ... (parallel)
|
||||
int interlace = i + 1;
|
||||
WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
synchronized (((IMixinWorldServer) entityWorld).lock()) {
|
||||
tickEntities(entityWorld);
|
||||
}
|
||||
}, null/*new TimingSignal(entityWorld, true)*/);
|
||||
|
||||
if (AkarinGlobalConfig.parallelMode != 1) {
|
||||
int fi = i;
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
WorldServer world = worlds.get(fi);
|
||||
synchronized (((IMixinWorldServer) world).lock()) {
|
||||
tickWorld(world);
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (AkarinGlobalConfig.parallelMode == 1)
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
for (int i = 0; i < cachedWorldSize; i++) {
|
||||
WorldServer world = worlds.get(i);
|
||||
synchronized (((IMixinWorldServer) world).lock()) {
|
||||
tickWorld(world);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize + 1 : cachedWorldSize * 2); i --> 0 ;) {
|
||||
Akari.STAGE_TICK.take();
|
||||
}
|
||||
|
||||
/* for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize : cachedWorldSize * 2); i --> 0 ;) {
|
||||
long startTiming = System.nanoTime();
|
||||
TimingSignal signal = Akari.STAGE_TICK.take().get();
|
||||
IMixinTimingHandler timing = (IMixinTimingHandler) (signal.isEntities ? signal.tickedWorld.timings.tickEntities : signal.tickedWorld.timings.doTick);
|
||||
timing.stopTiming(startTiming); // The overlap will be ignored
|
||||
} */
|
||||
|
||||
break;
|
||||
case 0:
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
for (int i = 1; i <= cachedWorldSize; ++i) {
|
||||
WorldServer world = worlds.get(i < cachedWorldSize ? i : 0);
|
||||
synchronized (((IMixinWorldServer) world).lock()) {
|
||||
tickEntities(world);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
Akari.STAGE_TICK.submit(() -> {
|
||||
for (int i = 0; i < cachedWorldSize; ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
synchronized (((IMixinWorldServer) world).lock()) {
|
||||
tickWorld(world);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
Akari.STAGE_TICK.take();
|
||||
Akari.STAGE_TICK.take();
|
||||
break;
|
||||
case -1:
|
||||
for (int i = 0; i < cachedWorldSize; ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
tickWorld(world);
|
||||
tickEntities(world);
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
synchronized (((IMixinLockProvider) world).lock()) {
|
||||
tickWorld(world);
|
||||
}
|
||||
}
|
||||
|
||||
Akari.entityCallbackTiming.startTiming();
|
||||
Akari.STAGE_TICK.take();
|
||||
Akari.entityCallbackTiming.stopTiming();
|
||||
|
||||
Akari.worldTiming.stopTiming();
|
||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
worlds.get(i).timings.tickEntities.stopTiming();
|
||||
worlds.get(i).timings.doTick.startTiming();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Akari.callbackTiming.startTiming();
|
||||
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||
Akari.callbackTiming.stopTiming();
|
||||
|
||||
for (int i = 0; i < worlds.size(); ++i) {
|
||||
WorldServer world = worlds.get(i);
|
||||
tickUnsafeSync(world);
|
||||
|
||||
world.getTracker().updatePlayers();
|
||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||
}
|
||||
|
||||
MinecraftTimings.connectionTimer.startTiming();
|
||||
serverConnection().c();
|
||||
MinecraftTimings.connectionTimer.stopTiming();
|
||||
@@ -261,10 +320,4 @@ public abstract class MixinMinecraftServer {
|
||||
}
|
||||
MinecraftTimings.tickablesTimer.stopTiming();
|
||||
}
|
||||
|
||||
public void tickUnsafeSync(WorldServer world) {
|
||||
world.timings.doChunkMap.startTiming();
|
||||
world.manager.flush();
|
||||
world.timings.doChunkMap.stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
||||
import net.minecraft.server.PlayerList;
|
||||
|
||||
@Mixin(value = PlayerList.class, remap = false)
|
||||
public abstract class MixinPlayerList {
|
||||
@Overwrite
|
||||
public void tick() {} // Migrated to slack service
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import co.aikar.timings.Timing;
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.Akari.AssignableThread;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
|
||||
public abstract class MixinTimingHandler {
|
||||
@Shadow @Final String name;
|
||||
@Shadow private boolean enabled;
|
||||
@Shadow private volatile long start;
|
||||
@Shadow private volatile int timingDepth;
|
||||
|
||||
@Shadow abstract void addDiff(long diff);
|
||||
@Shadow public abstract Timing startTiming();
|
||||
|
||||
@Overwrite
|
||||
public Timing startTimingIfSync() {
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
startTiming();
|
||||
}
|
||||
return (Timing) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
|
||||
public void onStartTiming(CallbackInfoReturnable ci) {
|
||||
if (!Akari.isPrimaryThread(false)) ci.setReturnValue(this); // Avoid modify any field
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void stopTimingIfSync() {
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
stopTiming(true); // Avoid twice thread check
|
||||
}
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public void stopTiming() {
|
||||
stopTiming(false);
|
||||
}
|
||||
|
||||
public void stopTiming(boolean alreadySync) {
|
||||
Thread curThread = Thread.currentThread();
|
||||
if (!enabled || curThread instanceof AssignableThread) return;
|
||||
if (!alreadySync && curThread != MinecraftServer.getServer().primaryThread) {
|
||||
if (AkarinGlobalConfig.silentAsyncTimings) return;
|
||||
|
||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
||||
Thread.dumpStack();
|
||||
}
|
||||
|
||||
// Main thread ensured
|
||||
if (--timingDepth == 0 && start != 0) {
|
||||
addDiff(System.nanoTime() - start);
|
||||
start = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,58 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.defaults.VersionCommand;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.MCUtil;
|
||||
|
||||
@Mixin(value = VersionCommand.class, remap = false)
|
||||
public abstract class MixinVersionCommand {
|
||||
@Shadow private static int getFromRepo(String repo, String hash) { return 0; }
|
||||
@Overwrite
|
||||
private static int getFromRepo(String repo, String hash) {
|
||||
try {
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/ver/1.12.2..." + hash).openConnection();
|
||||
connection.connect();
|
||||
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit
|
||||
try (
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))
|
||||
) {
|
||||
JSONObject obj = (JSONObject) new JSONParser().parse(reader);
|
||||
String status = (String) obj.get("status");
|
||||
switch (status) {
|
||||
case "identical":
|
||||
return 0;
|
||||
case "behind":
|
||||
return ((Number) obj.get("behind_by")).intValue();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
} catch (ParseException | NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match current version with repository and calculate the distance
|
||||
@@ -59,6 +96,9 @@ public abstract class MixinVersionCommand {
|
||||
// This should be lying in 'obtainVersion' method, but bump for faster returning
|
||||
if (customVersion) return;
|
||||
|
||||
synchronized (versionWaiters) {
|
||||
versionWaiters.add(sender);
|
||||
}
|
||||
if (versionObtaining) return;
|
||||
// The volatile guarantees the safety between different threads.
|
||||
// Remembers that we are still on main thread now,
|
||||
@@ -100,8 +140,6 @@ public abstract class MixinVersionCommand {
|
||||
// We post all things because a custom version is rare (expiring is not rare),
|
||||
// and we'd better post this task as early as we can, since it's a will (horrible destiny).
|
||||
MCUtil.scheduleAsyncTask(() -> {
|
||||
// This should be lying in 'sendVersion' method, but comes here for relax main thread
|
||||
versionWaiters.add(sender);
|
||||
sender.sendMessage("Checking version, please wait...");
|
||||
|
||||
String version = Akari.getServerVersion();
|
||||
@@ -140,9 +178,11 @@ public abstract class MixinVersionCommand {
|
||||
versionMessage = message;
|
||||
hasVersion = true;
|
||||
|
||||
for (CommandSender sender : versionWaiters) {
|
||||
sender.sendMessage(versionMessage);
|
||||
synchronized (versionWaiters) {
|
||||
for (CommandSender sender : versionWaiters) {
|
||||
sender.sendMessage(versionMessage);
|
||||
}
|
||||
versionWaiters.clear();
|
||||
}
|
||||
versionWaiters.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.AxisAlignedBB;
|
||||
@@ -21,11 +22,12 @@ public abstract class MixinWorld {
|
||||
/**
|
||||
* Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity
|
||||
*/
|
||||
public boolean a(AxisAlignedBB box, @Nullable Entity target) { // PAIL: checkNoEntityCollision
|
||||
@Overwrite
|
||||
public boolean a(AxisAlignedBB box, @Nullable Entity target) { // OBFHELPER: checkNoEntityCollision
|
||||
List<Entity> list = this.getEntities(null, box);
|
||||
|
||||
for (Entity each : list) {
|
||||
if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // PAIL: preventEntitySpawning - isRidingSameEntity
|
||||
if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // OBFHELPER: preventEntitySpawning - isRidingSameEntity
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.EntityPlayer;
|
||||
import net.minecraft.server.WorldManager;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@Mixin(value = WorldManager.class, remap = false)
|
||||
public abstract class MixinWorldManager {
|
||||
@Shadow @Final private WorldServer world;
|
||||
|
||||
@Overwrite
|
||||
public void a(Entity entity) {
|
||||
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
|
||||
this.world.getTracker().track(entity);
|
||||
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
|
||||
|
||||
if (entity instanceof EntityPlayer) {
|
||||
this.world.worldProvider.a((EntityPlayer) entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,37 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import java.util.Random;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinLockProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@Mixin(value = WorldServer.class, remap = false)
|
||||
public abstract class MixinWorldServer implements IMixinLockProvider {
|
||||
@Redirect(method = "doTick", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "net/minecraft/server/PlayerChunkMap.flush()V"
|
||||
))
|
||||
public void onFlush() {} // Migrated to main thread
|
||||
|
||||
public abstract class MixinWorldServer implements IMixinWorldServer {
|
||||
private final Object tickLock = new Object();
|
||||
|
||||
@Override
|
||||
public Object lock() {
|
||||
return tickLock;
|
||||
}
|
||||
|
||||
private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private boolean locked = false;
|
||||
@Override
|
||||
public synchronized void setSeed(long seed) {
|
||||
if (locked) {
|
||||
LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
|
||||
} else {
|
||||
super.setSeed(seed);
|
||||
locked = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public Random rand() {
|
||||
return sharedRandom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package io.akarin.server.mixin.core;
|
||||
|
||||
import org.spongepowered.asm.lib.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.ItemMonsterEgg;
|
||||
|
||||
@Mixin(value = ItemMonsterEgg.class, remap = false)
|
||||
public abstract class MonsterEggGuardian {
|
||||
@Redirect(method = "a", at = @At(
|
||||
value = "FIELD",
|
||||
target = "net/minecraft/server/Blocks.MOB_SPAWNER:Lnet/minecraft/server/Block;",
|
||||
opcode = Opcodes.GETSTATIC
|
||||
))
|
||||
private boolean configurable(Block target) {
|
||||
return target == Blocks.MOB_SPAWNER && AkarinGlobalConfig.allowSpawnerModify;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* This file is part of Sponge, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package io.akarin.server.mixin.cps;
|
||||
|
||||
import java.util.List;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = Chunk.class, remap = false)
|
||||
public abstract class MixinChunk implements IMixinChunk {
|
||||
private Chunk[] neighborChunks = new Chunk[4];
|
||||
private static final EnumDirection[] CARDINAL_DIRECTIONS = new EnumDirection[] {EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.EAST, EnumDirection.WEST};
|
||||
|
||||
@Shadow @Final public World world;
|
||||
@Shadow @Final public int locX;
|
||||
@Shadow @Final public int locZ;
|
||||
|
||||
@Override
|
||||
public Chunk getNeighborChunk(int index) {
|
||||
return this.neighborChunks[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeighborChunk(int index, @Nullable Chunk chunk) {
|
||||
this.neighborChunks[index] = chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Chunk> getNeighbors() {
|
||||
List<Chunk> neighborList = Lists.newArrayList();
|
||||
for (Chunk neighbor : this.neighborChunks) {
|
||||
if (neighbor != null) {
|
||||
neighborList.add(neighbor);
|
||||
}
|
||||
}
|
||||
return neighborList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areNeighborsLoaded() {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (this.neighborChunks[i] == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int directionToIndex(EnumDirection direction) {
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
return 0;
|
||||
case SOUTH:
|
||||
return 1;
|
||||
case EAST:
|
||||
return 2;
|
||||
case WEST:
|
||||
return 3;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unexpected direction");
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "addEntities", at = @At("RETURN"))
|
||||
public void onLoadReturn(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
this.setNeighborChunk(neighborIndex, neighbor);
|
||||
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, (Chunk) (Object) this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "removeEntities", at = @At("RETURN"))
|
||||
public void onUnload(CallbackInfo ci) {
|
||||
BlockPosition origin = new BlockPosition(locX, 0, locZ);
|
||||
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
|
||||
BlockPosition shift = origin.shift(direction);
|
||||
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
|
||||
if (neighbor != null) {
|
||||
int neighborIndex = directionToIndex(direction);
|
||||
int oppositeNeighborIndex = directionToIndex(direction.opposite());
|
||||
this.setNeighborChunk(neighborIndex, null);
|
||||
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public abstract class MixinChunkProviderServer {
|
||||
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
|
||||
activityAccountant.startActivity(0.5);
|
||||
|
||||
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
|
||||
int remainingChunks = chunks.size();
|
||||
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||
@@ -47,27 +48,23 @@ public abstract class MixinChunkProviderServer {
|
||||
while (it.hasNext()) {
|
||||
Entry<Chunk> entry = it.next();
|
||||
Chunk chunk = entry.getValue();
|
||||
if (chunk == null) continue;
|
||||
|
||||
if (chunk.isUnloading()) {
|
||||
if (chunk != null && chunk.isUnloading()) {
|
||||
if (chunk.scheduledForUnload != null) {
|
||||
if (now - chunk.scheduledForUnload > unloadAfter) {
|
||||
chunk.scheduledForUnload = null;
|
||||
} else continue;
|
||||
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
|
||||
}
|
||||
|
||||
if (!unloadChunk(chunk, true)) { // Event cancelled
|
||||
// If a plugin cancelled it, we shouldn't trying unload it for a while
|
||||
chunk.setShouldUnload(false);
|
||||
continue;
|
||||
if (unloadChunk(chunk, true)) {
|
||||
it.remove();
|
||||
}
|
||||
chunk.setShouldUnload(false);
|
||||
chunk.scheduledForUnload = null;
|
||||
|
||||
it.remove();
|
||||
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
|
||||
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
|
||||
}
|
||||
}
|
||||
activityAccountant.endActivity();
|
||||
this.chunkLoader.b(); // PAIL: chunkTick
|
||||
this.chunkLoader.b(); // OBFHELPER: chunkTick
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,640 +0,0 @@
|
||||
/*
|
||||
* This file is part of Sponge, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package io.akarin.server.mixin.lighting;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Blocks;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkSection;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.TileEntity;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.BlockPosition.MutableBlockPosition;
|
||||
|
||||
@Mixin(value = Chunk.class, remap = false, priority = 1001)
|
||||
public abstract class MixinChunk implements IMixinChunk {
|
||||
|
||||
// Keeps track of block positions in this chunk currently queued for sky light update
|
||||
private CopyOnWriteArrayList<Short> queuedSkyLightingUpdates = new CopyOnWriteArrayList<>();
|
||||
// Keeps track of block positions in this chunk currently queued for block light update
|
||||
private CopyOnWriteArrayList<Short> queuedBlockLightingUpdates = new CopyOnWriteArrayList<>();
|
||||
private AtomicInteger pendingLightUpdates = new AtomicInteger();
|
||||
private long lightUpdateTime;
|
||||
private ExecutorService lightExecutorService;
|
||||
|
||||
@Shadow(aliases = "m") private boolean isGapLightingUpdated;
|
||||
@Shadow(aliases = "r") private boolean ticked;
|
||||
@Shadow @Final private ChunkSection[] sections;
|
||||
@Shadow @Final public int locX;
|
||||
@Shadow @Final public int locZ;
|
||||
@Shadow @Final public World world;
|
||||
@Shadow @Final public int[] heightMap;
|
||||
/** Which columns need their skylightMaps updated. */
|
||||
@Shadow(aliases = "i") @Final private boolean[] updateSkylightColumns;
|
||||
/** Queue containing the BlockPosition of tile entities queued for creation */
|
||||
@Shadow(aliases = "y") @Final private ConcurrentLinkedQueue<BlockPosition> tileEntityPosQueue;
|
||||
/** Boolean value indicating if the terrain is populated. */
|
||||
@Shadow(aliases = "done") private boolean isTerrainPopulated;
|
||||
@Shadow(aliases = "lit") private boolean isLightPopulated;
|
||||
/** Lowest value in the heightmap. */
|
||||
@Shadow(aliases = "v") private int heightMapMinimum;
|
||||
|
||||
@Shadow(aliases = "b") public abstract int getHeightValue(int x, int z);
|
||||
@Shadow(aliases = "g") @Nullable public abstract TileEntity createNewTileEntity(BlockPosition pos);
|
||||
@Shadow(aliases = "a") @Nullable public abstract TileEntity getTileEntity(BlockPosition pos, Chunk.EnumTileEntityState state);
|
||||
@Shadow @Final public abstract IBlockData getBlockData(BlockPosition pos);
|
||||
@Shadow @Final public abstract IBlockData getBlockData(int x, int y, int z);
|
||||
@Shadow public abstract boolean isUnloading();
|
||||
/** Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary */
|
||||
@Shadow(aliases = "b") public abstract void checkSkylightNeighborHeight(int x, int z, int maxValue);
|
||||
@Shadow(aliases = "a") public abstract void updateSkylightNeighborHeight(int x, int z, int startY, int endY);
|
||||
@Shadow(aliases = "z") public abstract void setSkylightUpdated();
|
||||
@Shadow(aliases = "g") public abstract int getTopFilledSegment();
|
||||
@Shadow public abstract void markDirty();
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
public void onConstruct(World worldIn, int x, int z, CallbackInfo ci) {
|
||||
lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicInteger getPendingLightUpdates() {
|
||||
return this.pendingLightUpdates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLightUpdateTime() {
|
||||
return this.lightUpdateTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLightUpdateTime(long time) {
|
||||
this.lightUpdateTime = time;
|
||||
}
|
||||
|
||||
@Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) {
|
||||
final List<Chunk> neighbors = this.getSurroundingChunks();
|
||||
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // PAIL: hasSkyLight
|
||||
lightExecutorService.execute(() -> {
|
||||
this.recheckGapsAsync(neighbors);
|
||||
});
|
||||
this.isGapLightingUpdated = false;
|
||||
}
|
||||
|
||||
this.ticked = true;
|
||||
|
||||
if (!this.isLightPopulated && this.isTerrainPopulated && !neighbors.isEmpty()) {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(neighbors);
|
||||
});
|
||||
// set to true to avoid requeuing the same task when not finished
|
||||
this.isLightPopulated = true;
|
||||
}
|
||||
|
||||
while (!this.tileEntityPosQueue.isEmpty()) {
|
||||
BlockPosition blockpos = this.tileEntityPosQueue.poll();
|
||||
|
||||
if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // PAIL: getTileEntity
|
||||
TileEntity tileentity = this.createNewTileEntity(blockpos);
|
||||
this.world.setTileEntity(blockpos, tileentity);
|
||||
this.world.b(blockpos, blockpos); // PAIL: markBlockRangeForRenderUpdate
|
||||
}
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
@Redirect(method = "b(III)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getHighestBlockYAt(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/BlockPosition;"))
|
||||
private BlockPosition onCheckSkylightGetHeight(World world, BlockPosition pos) {
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
return BlockPosition.ZERO;
|
||||
}
|
||||
|
||||
return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // PAIL: getHeightValue
|
||||
}
|
||||
|
||||
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.areChunksLoaded(Lnet/minecraft/server/BlockPosition;I)Z"))
|
||||
private boolean onAreaLoadedSkyLightNeighbor(World world, BlockPosition pos, int radius) {
|
||||
return this.isAreaLoaded();
|
||||
}
|
||||
|
||||
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.c(Lnet/minecraft/server/EnumSkyBlock;Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onCheckLightForSkylightNeighbor(World world, EnumSkyBlock enumSkyBlock, BlockPosition pos) {
|
||||
return this.checkWorldLightFor(enumSkyBlock, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rechecks chunk gaps async.
|
||||
*
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
*/
|
||||
private void recheckGapsAsync(List<Chunk> neighbors) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (this.updateSkylightColumns[i + j * 16]) {
|
||||
this.updateSkylightColumns[i + j * 16] = false;
|
||||
int k = this.getHeightValue(i, j);
|
||||
int l = this.locX * 16 + i;
|
||||
int i1 = this.locZ * 16 + j;
|
||||
int j1 = Integer.MAX_VALUE;
|
||||
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
final Chunk chunk = this.getLightChunk((l + enumfacing.getAdjacentX()) >> 4, (i1 + enumfacing.getAdjacentZ()) >> 4, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
continue;
|
||||
}
|
||||
j1 = Math.min(j1, chunk.w()); // PAIL: getLowestHeight
|
||||
}
|
||||
|
||||
this.checkSkylightNeighborHeight(l, i1, j1);
|
||||
|
||||
for (EnumDirection enumfacing1 : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
this.checkSkylightNeighborHeight(l + enumfacing1.getAdjacentX(), i1 + enumfacing1.getAdjacentZ(), k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this.isGapLightingUpdated = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getType(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/IBlockData;"))
|
||||
private IBlockData onRelightChecksGetBlockData(World world, BlockPosition pos) {
|
||||
Chunk chunk = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), pos.getX() >> 4, pos.getZ() >> 4);
|
||||
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) chunk;
|
||||
if (chunk == null || chunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
|
||||
return Blocks.AIR.getBlockData();
|
||||
}
|
||||
|
||||
return chunk.getBlockData(pos);
|
||||
}
|
||||
|
||||
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onRelightChecksCheckLight(World world, BlockPosition pos) {
|
||||
return this.checkWorldLight(pos);
|
||||
}
|
||||
|
||||
// Avoids grabbing chunk async during light check
|
||||
@Redirect(method = "e(II)Z", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
|
||||
private boolean onCheckLightWorld(World world, BlockPosition pos) {
|
||||
return this.checkWorldLight(pos);
|
||||
}
|
||||
|
||||
@Inject(method = "o()V", at = @At("HEAD"), cancellable = true)
|
||||
private void checkLightHead(CallbackInfo ci) {
|
||||
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isUnloading()) {
|
||||
return;
|
||||
}
|
||||
final List<Chunk> neighborChunks = this.getSurroundingChunks();
|
||||
if (neighborChunks.isEmpty()) {
|
||||
this.isLightPopulated = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Akari.isPrimaryThread(false)) {
|
||||
try {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(neighborChunks);
|
||||
});
|
||||
} catch (RejectedExecutionException ex) {
|
||||
// This could happen if ServerHangWatchdog kills the server
|
||||
// between the start of the method and the execute() call.
|
||||
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.checkLightAsync(neighborChunks);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks light async.
|
||||
*
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
*/
|
||||
private void checkLightAsync(List<Chunk> neighbors) {
|
||||
this.isTerrainPopulated = true;
|
||||
this.isLightPopulated = true;
|
||||
BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4);
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
reCheckLight:
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (!this.checkLightAsync(i, j, neighbors)) {
|
||||
this.isLightPopulated = false;
|
||||
break reCheckLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isLightPopulated) {
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // PAIL: getAxisDirection
|
||||
final BlockPosition pos = blockpos.shift(enumfacing, k);
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
chunk.checkLightSide(enumfacing.opposite());
|
||||
}
|
||||
|
||||
this.setSkylightUpdated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks light async.
|
||||
*
|
||||
* @param x The x position of chunk
|
||||
* @param z The z position of chunk
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkLightAsync(int x, int z, List<Chunk> neighbors) {
|
||||
int i = this.getTopFilledSegment();
|
||||
boolean flag = false;
|
||||
boolean flag1 = false;
|
||||
MutableBlockPosition blockpos$mutableblockpos = new MutableBlockPosition((this.locX << 4) + x, 0, (this.locZ << 4) + z);
|
||||
|
||||
for (int j = i + 16 - 1; j > this.world.getSeaLevel() || j > 0 && !flag1; --j) {
|
||||
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), j, blockpos$mutableblockpos.getZ());
|
||||
int k = this.getBlockData(blockpos$mutableblockpos).c(); // PAIL: getLightOpacity
|
||||
|
||||
if (k == 255 && blockpos$mutableblockpos.getY() < this.world.getSeaLevel()) {
|
||||
flag1 = true;
|
||||
}
|
||||
|
||||
if (!flag && k > 0) {
|
||||
flag = true;
|
||||
} else if (flag && k == 0 && !this.checkWorldLight(blockpos$mutableblockpos, neighbors)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int l = blockpos$mutableblockpos.getY(); l > 0; --l) {
|
||||
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), l, blockpos$mutableblockpos.getZ());
|
||||
|
||||
if (this.getBlockData(blockpos$mutableblockpos).d() > 0) { // getLightValue
|
||||
this.checkWorldLight(blockpos$mutableblockpos, neighbors);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thread-safe method to retrieve a chunk during async light updates.
|
||||
*
|
||||
* @param chunkX The x position of chunk.
|
||||
* @param chunkZ The z position of chunk.
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return The chunk if available, null if not
|
||||
*/
|
||||
private Chunk getLightChunk(int chunkX, int chunkZ, List<Chunk> neighbors) {
|
||||
final Chunk currentChunk = (Chunk) (Object) this;
|
||||
if (currentChunk.a(chunkX, chunkZ)) { // PAIL: isAtLocation
|
||||
if (currentChunk.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return currentChunk;
|
||||
}
|
||||
if (neighbors == null) {
|
||||
neighbors = this.getSurroundingChunks();
|
||||
if (neighbors.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
for (Chunk neighbor : neighbors) {
|
||||
if (neighbor.a(chunkX, chunkZ)) { // PAIL: isAtLocation
|
||||
if (neighbor.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return neighbor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if surrounding chunks are loaded thread-safe.
|
||||
*
|
||||
* @return True if surrounded chunks are loaded, false if not
|
||||
*/
|
||||
private boolean isAreaLoaded() {
|
||||
if (!this.areNeighborsLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// add diagonal chunks
|
||||
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
if (southEastChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
if (southWestChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
if (northEastChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (northWestChunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets surrounding chunks thread-safe.
|
||||
*
|
||||
* @return The list of surrounding chunks, empty list if not loaded
|
||||
*/
|
||||
private List<Chunk> getSurroundingChunks() {
|
||||
if (!this.areNeighborsLoaded()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// add diagonal chunks
|
||||
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
if (southEastChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
if (southWestChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
if (northEastChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (northWestChunk == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<Chunk> chunkList = Lists.newArrayList();
|
||||
chunkList = this.getNeighbors();
|
||||
chunkList.add(southEastChunk);
|
||||
chunkList.add(southWestChunk);
|
||||
chunkList.add(northEastChunk);
|
||||
chunkList.add(northWestChunk);
|
||||
return chunkList;
|
||||
}
|
||||
|
||||
@Inject(method = "c(III)V", at = @At("HEAD"), cancellable = true)
|
||||
private void onRelightBlock(int x, int y, int z, CallbackInfo ci) {
|
||||
lightExecutorService.execute(() -> {
|
||||
this.relightBlockAsync(x, y, z);
|
||||
});
|
||||
ci.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relight's a block async.
|
||||
*
|
||||
* @param x The x position
|
||||
* @param y The y position
|
||||
* @param z The z position
|
||||
*/
|
||||
private void relightBlockAsync(int x, int y, int z) {
|
||||
int i = this.heightMap[z << 4 | x] & 255;
|
||||
int j = i;
|
||||
|
||||
if (y > i) {
|
||||
j = y;
|
||||
}
|
||||
|
||||
while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) { // PAIL: getLightOpacity
|
||||
--j;
|
||||
}
|
||||
|
||||
if (j != i) {
|
||||
this.markBlocksDirtyVerticalAsync(x + this.locX * 16, z + this.locZ * 16, j, i);
|
||||
this.heightMap[z << 4 | x] = j;
|
||||
int k = this.locX * 16 + x;
|
||||
int l = this.locZ * 16 + z;
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
if (j < i) {
|
||||
for (int j1 = j; j1 < i; ++j1) {
|
||||
ChunkSection extendedblockstorage2 = this.sections[j1 >> 4];
|
||||
|
||||
if (extendedblockstorage2 != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage2.a(x, j1 & 15, z, 15); // PAIL: setSkyLight
|
||||
// this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // PAIL: notifyLightSet - client side
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i1 = i; i1 < j; ++i1) {
|
||||
ChunkSection extendedblockstorage = this.sections[i1 >> 4];
|
||||
|
||||
if (extendedblockstorage != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage.a(x, i1 & 15, z, 0); // PAIL: setSkyLight
|
||||
// this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // PAIL: notifyLightSet - client side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int k1 = 15;
|
||||
|
||||
while (j > 0 && k1 > 0) {
|
||||
--j;
|
||||
int i2 = this.getBlockData(x, j, z).c();
|
||||
|
||||
if (i2 == 0) {
|
||||
i2 = 1;
|
||||
}
|
||||
|
||||
k1 -= i2;
|
||||
|
||||
if (k1 < 0) {
|
||||
k1 = 0;
|
||||
}
|
||||
|
||||
ChunkSection extendedblockstorage1 = this.sections[j >> 4];
|
||||
|
||||
if (extendedblockstorage1 != Chunk.EMPTY_CHUNK_SECTION) {
|
||||
extendedblockstorage1.a(x, j & 15, z, k1); // PAIL: setSkyLight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int l1 = this.heightMap[z << 4 | x];
|
||||
int j2 = i;
|
||||
int k2 = l1;
|
||||
|
||||
if (l1 < i) {
|
||||
j2 = l1;
|
||||
k2 = i;
|
||||
}
|
||||
|
||||
if (l1 < this.heightMapMinimum) {
|
||||
this.heightMapMinimum = l1;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // PAIL: updateSkylightNeighborHeight
|
||||
}
|
||||
|
||||
this.updateSkylightNeighborHeight(k, l, j2, k2);
|
||||
}
|
||||
|
||||
this.markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a vertical line of blocks as dirty async.
|
||||
* Instead of calling world directly, we pass chunk safely for async light method.
|
||||
*
|
||||
* @param x1
|
||||
* @param z1
|
||||
* @param x2
|
||||
* @param z2
|
||||
*/
|
||||
private void markBlocksDirtyVerticalAsync(int x1, int z1, int x2, int z2) {
|
||||
if (x2 > z2) {
|
||||
int i = z2;
|
||||
z2 = x2;
|
||||
x2 = i;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
for (int j = x2; j <= z2; ++j) {
|
||||
final BlockPosition pos = new BlockPosition(x1, j, z1);
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
continue;
|
||||
}
|
||||
((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, new BlockPosition(x1, j, z1), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
this.world.b(x1, x2, z1, x1, z2, z1); // PAIL: markBlockRangeForRenderUpdate
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks world light thread-safe.
|
||||
*
|
||||
* @param lightType The type of light to check
|
||||
* @param pos The block position
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkWorldLightFor(EnumSkyBlock lightType, BlockPosition pos) {
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((IMixinWorldServer) this.world).updateLightAsync(lightType, pos, chunk);
|
||||
}
|
||||
|
||||
private boolean checkWorldLight(BlockPosition pos) {
|
||||
return this.checkWorldLight(pos, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks world light async.
|
||||
*
|
||||
* @param pos The block position
|
||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
||||
* @return True if light update was successful, false if not
|
||||
*/
|
||||
private boolean checkWorldLight(BlockPosition pos, List<Chunk> neighbors) {
|
||||
boolean flag = false;
|
||||
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
|
||||
flag |= ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, pos, chunk);
|
||||
}
|
||||
|
||||
flag = flag | ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.BLOCK, pos, chunk);
|
||||
return flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of block positions currently queued for lighting updates.
|
||||
*
|
||||
* @param type The light type
|
||||
* @return The list of queued block positions, empty if none
|
||||
*/
|
||||
@Override
|
||||
public CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type) {
|
||||
if (type == EnumSkyBlock.SKY) {
|
||||
return this.queuedSkyLightingUpdates;
|
||||
}
|
||||
return this.queuedBlockLightingUpdates;
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* This file is part of Sponge, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package io.akarin.server.mixin.lighting;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import net.minecraft.server.ChunkProviderServer;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
@Mixin(value = ChunkProviderServer.class, remap = false, priority = 1001)
|
||||
public abstract class MixinChunkProviderServer {
|
||||
@Shadow @Final public WorldServer world;
|
||||
|
||||
@Redirect(method = "unloadChunks", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/server/Chunk;isUnloading()Z"
|
||||
))
|
||||
public boolean shouldUnload(IMixinChunk chunk) {
|
||||
if (chunk.getPendingLightUpdates().get() > 0 || this.world.getTime() - chunk.getLightUpdateTime() < 20) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This file is part of Sponge, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package io.akarin.server.mixin.lighting;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IChunkProvider;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = World.class, remap = false, priority = 1001)
|
||||
public abstract class MixinWorld {
|
||||
@Shadow protected IChunkProvider chunkProvider;
|
||||
@Shadow int[] J; // PAIL: lightUpdateBlockList
|
||||
|
||||
@Shadow(aliases = "c") public abstract boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos);
|
||||
@Shadow public abstract MinecraftServer getMinecraftServer();
|
||||
@Shadow public abstract boolean areChunksLoaded(BlockPosition center, int radius, boolean allowEmpty);
|
||||
@Shadow public abstract Chunk getChunkIfLoaded(int x, int z);
|
||||
}
|
||||
@@ -1,374 +0,0 @@
|
||||
/*
|
||||
* This file is part of Sponge, licensed under the MIT License (MIT).
|
||||
*
|
||||
* Copyright (c) SpongePowered <https://www.spongepowered.org>
|
||||
* Copyright (c) contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package io.akarin.server.mixin.lighting;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import io.akarin.api.internal.mixin.IMixinChunk;
|
||||
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.EnumDirection;
|
||||
import net.minecraft.server.EnumSkyBlock;
|
||||
import net.minecraft.server.IBlockData;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MathHelper;
|
||||
import net.minecraft.server.WorldServer;
|
||||
import net.minecraft.server.BlockPosition.PooledBlockPosition;
|
||||
|
||||
@Mixin(value = WorldServer.class, remap = false)
|
||||
public abstract class MixinWorldServer extends MixinWorld implements IMixinWorldServer {
|
||||
|
||||
private static final int NUM_XZ_BITS = 4;
|
||||
private static final int NUM_SHORT_Y_BITS = 8;
|
||||
private static final short XZ_MASK = 0xF;
|
||||
private static final short Y_SHORT_MASK = 0xFF;
|
||||
|
||||
private final static ThreadFactory SERVICE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Async Lighting Thread - %1$d").build();
|
||||
private final ExecutorService lightExecutorService = getExecutorService();
|
||||
|
||||
private ExecutorService getExecutorService() {
|
||||
return Executors.newFixedThreadPool(1, SERVICE_FACTORY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // PAIL: checkLightFor
|
||||
return updateLightAsync(lightType, pos, null);
|
||||
}
|
||||
|
||||
public boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
// Sponge - This check is not needed as neighbors are checked in updateLightAsync
|
||||
if (false && !this.areChunksLoaded(pos, 17, false)) {
|
||||
return false;
|
||||
} else {
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
int current = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int rawLight = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
int z = pos.getZ();
|
||||
|
||||
if (rawLight > current) {
|
||||
this.J[j++] = 133152; // PAIL: lightUpdateBlockList
|
||||
} else if (rawLight < current) {
|
||||
this.J[j++] = 133152 | current << 18; // PAIL: lightUpdateBlockList
|
||||
|
||||
while (i < j) {
|
||||
int l1 = this.J[i++]; // PAIL: lightUpdateBlockList
|
||||
int i2 = (l1 & 63) - 32 + x;
|
||||
int j2 = (l1 >> 6 & 63) - 32 + y;
|
||||
int k2 = (l1 >> 12 & 63) - 32 + z;
|
||||
int l2 = l1 >> 18 & 15;
|
||||
BlockPosition blockpos = new BlockPosition(i2, j2, k2);
|
||||
int lightLevel = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (lightLevel == l2) {
|
||||
this.setLightForAsync(lightType, blockpos, 0, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (l2 > 0) {
|
||||
int j3 = MathHelper.a(i2 - x); // abs
|
||||
int k3 = MathHelper.a(j2 - y);
|
||||
int l3 = MathHelper.a(k2 - z);
|
||||
|
||||
if (j3 + k3 + l3 < 17) {
|
||||
PooledBlockPosition mutableBlockpos = PooledBlockPosition.aquire();
|
||||
|
||||
for (EnumDirection enumfacing : EnumDirection.values()) {
|
||||
int i4 = i2 + enumfacing.getAdjacentX();
|
||||
int j4 = j2 + enumfacing.getAdjacentX();
|
||||
int k4 = k2 + enumfacing.getAdjacentX();
|
||||
mutableBlockpos.setValues(i4, j4, k4);
|
||||
// Sponge start - get chunk safely
|
||||
final Chunk pooledChunk = this.getLightChunk(mutableBlockpos, currentChunk, neighbors);
|
||||
if (pooledChunk == null) {
|
||||
continue;
|
||||
}
|
||||
int opacity = Math.max(1, pooledChunk.getBlockData(mutableBlockpos).c()); // PAIL: getLightOpacity
|
||||
lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors);
|
||||
// Sponge end
|
||||
|
||||
if (lightLevel == l2 - opacity && j < this.J.length) { // PAIL: lightUpdateBlockList
|
||||
this.J[j++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // PAIL: lightUpdateBlockList
|
||||
}
|
||||
}
|
||||
|
||||
mutableBlockpos.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i = 0;
|
||||
}
|
||||
|
||||
while (i < j) {
|
||||
int i5 = this.J[i++]; // PAIL: lightUpdateBlockList
|
||||
int j5 = (i5 & 63) - 32 + x;
|
||||
int k5 = (i5 >> 6 & 63) - 32 + y;
|
||||
int l5 = (i5 >> 12 & 63) - 32 + z;
|
||||
BlockPosition blockpos1 = new BlockPosition(j5, k5, l5);
|
||||
int i6 = this.getLightForAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
int j6 = this.getRawBlockLightAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (j6 != i6) {
|
||||
this.setLightForAsync(lightType, blockpos1, j6, currentChunk, neighbors); // Sponge - use thread safe method
|
||||
|
||||
if (j6 > i6) {
|
||||
int k6 = Math.abs(j5 - x);
|
||||
int l6 = Math.abs(k5 - y);
|
||||
int i7 = Math.abs(l5 - z);
|
||||
boolean flag = j < this.J.length - 6; // PAIL: lightUpdateBlockList
|
||||
|
||||
if (k6 + l6 + i7 < 17 && flag) {
|
||||
// Sponge start - use thread safe method getLightForAsync
|
||||
if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
|
||||
if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) {
|
||||
this.J[j++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // PAIL: lightUpdateBlockList
|
||||
}
|
||||
// Sponge end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sponge start - Asynchronous light updates
|
||||
spongeChunk.getQueuedLightingUpdates(lightType).remove((Short) this.blockPosToShort(pos));
|
||||
spongeChunk.getPendingLightUpdates().decrementAndGet();
|
||||
for (Chunk neighborChunk : neighbors) {
|
||||
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
|
||||
neighbor.getPendingLightUpdates().decrementAndGet();
|
||||
}
|
||||
// Sponge end
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, @Nullable Chunk currentChunk) {
|
||||
if (this.getMinecraftServer().isStopped() || this.lightExecutorService.isShutdown()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentChunk == null) {
|
||||
currentChunk = MCUtil.getLoadedChunkWithoutMarkingActive(chunkProvider, pos.getX() >> 4, pos.getZ() >> 4);
|
||||
}
|
||||
|
||||
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
|
||||
if (currentChunk == null || currentChunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final short shortPos = this.blockPosToShort(pos);
|
||||
if (spongeChunk.getQueuedLightingUpdates(lightType).contains(shortPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final Chunk chunk = currentChunk;
|
||||
spongeChunk.getQueuedLightingUpdates(lightType).add(shortPos);
|
||||
spongeChunk.getPendingLightUpdates().incrementAndGet();
|
||||
spongeChunk.setLightUpdateTime(chunk.getWorld().getTime());
|
||||
|
||||
List<Chunk> neighbors = spongeChunk.getNeighbors();
|
||||
// add diagonal chunks
|
||||
Chunk southEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(2);
|
||||
Chunk southWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(3);
|
||||
Chunk northEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(2);
|
||||
Chunk northWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(3);
|
||||
if (southEastChunk != null) {
|
||||
neighbors.add(southEastChunk);
|
||||
}
|
||||
if (southWestChunk != null) {
|
||||
neighbors.add(southWestChunk);
|
||||
}
|
||||
if (northEastChunk != null) {
|
||||
neighbors.add(northEastChunk);
|
||||
}
|
||||
if (northWestChunk != null) {
|
||||
neighbors.add(northWestChunk);
|
||||
}
|
||||
|
||||
for (Chunk neighborChunk : neighbors) {
|
||||
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
|
||||
neighbor.getPendingLightUpdates().incrementAndGet();
|
||||
neighbor.setLightUpdateTime(chunk.getWorld().getTime());
|
||||
}
|
||||
|
||||
if (Akari.isPrimaryThread()) { // Akarin
|
||||
this.lightExecutorService.execute(() -> {
|
||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
||||
});
|
||||
} else {
|
||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutorService getLightingExecutor() {
|
||||
return this.lightExecutorService;
|
||||
}
|
||||
|
||||
// Thread safe methods to retrieve a chunk during async light updates
|
||||
// Each method avoids calling getLoadedChunk and instead accesses the passed neighbor chunk list to avoid concurrency issues
|
||||
public Chunk getLightChunk(BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // PAIL: isAtLocation
|
||||
if (currentChunk.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return currentChunk;
|
||||
}
|
||||
for (Chunk neighbor : neighbors) {
|
||||
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // PAIL: isAtLocation
|
||||
if (neighbor.isUnloading()) {
|
||||
return null;
|
||||
}
|
||||
return neighbor;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private int getLightForAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (pos.getY() < 0) {
|
||||
pos = new BlockPosition(pos.getX(), 0, pos.getZ());
|
||||
}
|
||||
if (!pos.isValidLocation()) {
|
||||
return lightType.c; // PAIL: defaultLightValue
|
||||
}
|
||||
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
return lightType.c; // PAIL: defaultLightValue
|
||||
}
|
||||
|
||||
return chunk.getBrightness(lightType, pos);
|
||||
}
|
||||
|
||||
private int getRawBlockLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
final Chunk chunk = getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk == null || chunk.isUnloading()) {
|
||||
return lightType.c; // PAIL: defaultLightValue
|
||||
}
|
||||
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // PAIL: canSeeSky
|
||||
return 15;
|
||||
} else {
|
||||
IBlockData blockData = chunk.getBlockData(pos);
|
||||
int blockLight = blockData.d(); // getLightValue
|
||||
int rawLight = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
|
||||
int opacity = blockData.c(); // PAIL: getLightOpacity
|
||||
|
||||
if (opacity >= 15 && blockLight > 0) {
|
||||
opacity = 1;
|
||||
}
|
||||
|
||||
if (opacity < 1) {
|
||||
opacity = 1;
|
||||
}
|
||||
|
||||
if (opacity >= 15) {
|
||||
return 0;
|
||||
} else if (rawLight >= 14) {
|
||||
return rawLight;
|
||||
} else {
|
||||
for (EnumDirection facing : EnumDirection.values()) {
|
||||
BlockPosition blockpos = pos.shift(facing);
|
||||
int current = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - opacity;
|
||||
|
||||
if (current > rawLight) {
|
||||
rawLight = current;
|
||||
}
|
||||
|
||||
if (rawLight >= 14) {
|
||||
return rawLight;
|
||||
}
|
||||
}
|
||||
|
||||
return rawLight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLightForAsync(EnumSkyBlock type, BlockPosition pos, int lightValue, Chunk currentChunk, List<Chunk> neighbors) {
|
||||
if (pos.isValidLocation()) {
|
||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
||||
if (chunk != null && !chunk.isUnloading()) {
|
||||
chunk.a(type, pos, lightValue); // PAIL: setLightFor
|
||||
// this.notifyLightSet(pos); // client side
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private short blockPosToShort(BlockPosition pos) {
|
||||
short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS);
|
||||
serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS);
|
||||
serialized = (short) setNibble(serialized, pos.getZ() & XZ_MASK, 3, NUM_XZ_BITS);
|
||||
return serialized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies bits in an integer.
|
||||
*
|
||||
* @param num Integer to modify
|
||||
* @param data Bits of data to add
|
||||
* @param which Index of nibble to start at
|
||||
* @param bitsToReplace The number of bits to replace starting from nibble index
|
||||
* @return The modified integer
|
||||
*/
|
||||
private int setNibble(int num, int data, int which, int bitsToReplace) {
|
||||
return (num & ~(bitsToReplace << (which * 4)) | (data << (which * 4)));
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ public abstract class NonblockingServerConnection {
|
||||
Class<? extends ServerChannel> channelClass;
|
||||
EventLoopGroup loopGroup;
|
||||
|
||||
if (Epoll.isAvailable() && this.server.af()) { // PAIL: MinecraftServer::useNativeTransport
|
||||
if (Epoll.isAvailable() && this.server.af()) { // OBFHELPER: MinecraftServer::useNativeTransport
|
||||
channelClass = EpollServerSocketChannel.class;
|
||||
loopGroup = ServerConnection.b.c();
|
||||
logger.info("Using epoll channel type");
|
||||
@@ -122,7 +122,7 @@ public abstract class NonblockingServerConnection {
|
||||
}
|
||||
}
|
||||
|
||||
@Shadow public volatile boolean d; // PAIL: neverTerminate
|
||||
@Shadow public volatile boolean d; // OBFHELPER: neverTerminate
|
||||
/**
|
||||
* Shuts down all open endpoints
|
||||
*/
|
||||
@@ -139,7 +139,7 @@ public abstract class NonblockingServerConnection {
|
||||
|
||||
public void processPackets(NetworkManager manager) {
|
||||
try {
|
||||
manager.a(); // PAIL: NetworkManager::processReceivedPackets
|
||||
manager.a(); // OBFHELPER: NetworkManager::processReceivedPackets
|
||||
} catch (Exception ex) {
|
||||
logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex);
|
||||
final ChatComponentText message = new ChatComponentText("Internal server error");
|
||||
@@ -168,7 +168,7 @@ public abstract class NonblockingServerConnection {
|
||||
Iterator<NetworkManager> it = networkManagers.iterator();
|
||||
while (it.hasNext()) {
|
||||
NetworkManager manager = it.next();
|
||||
if (manager.h()) continue; // PAIL: NetworkManager::hasNoChannel
|
||||
if (manager.h()) continue; // OBFHELPER: NetworkManager::hasNoChannel
|
||||
|
||||
if (manager.isConnected()) {
|
||||
processPackets(manager);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.akarin.server.mixin.nsc;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import io.akarin.api.internal.collections.CheckedConcurrentLinkedQueue;
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
|
||||
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
@@ -21,21 +21,22 @@ import net.minecraft.server.PacketPlayOutMapChunk;
|
||||
public abstract class OptimisticNetworkManager {
|
||||
@Shadow public Channel channel;
|
||||
@Shadow(aliases = "i") @Final private Queue<NetworkManager.QueuedPacket> packets;
|
||||
@Shadow(aliases = "j") @Final private ReentrantReadWriteLock queueLock;
|
||||
@Shadow(aliases = "j") @Final private ReentrantReadWriteUpdateLock queueLock;
|
||||
|
||||
@Shadow public abstract Queue<NetworkManager.QueuedPacket> getPacketQueue();
|
||||
@Shadow public abstract void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners);
|
||||
|
||||
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null, null);
|
||||
@SuppressWarnings("unchecked")
|
||||
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null);
|
||||
|
||||
@Overwrite // PAIL: trySendQueue
|
||||
@Overwrite // OBFHELPER: trySendQueue
|
||||
private boolean m() {
|
||||
if (this.channel != null && this.channel.isOpen()) {
|
||||
if (this.packets.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
|
||||
return true;
|
||||
}
|
||||
|
||||
this.queueLock.readLock().lock();
|
||||
this.queueLock.updateLock().lock();
|
||||
try {
|
||||
while (!this.packets.isEmpty()) {
|
||||
NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue<QueuedPacket>) getPacketQueue()).poll(item -> {
|
||||
@@ -51,7 +52,7 @@ public abstract class OptimisticNetworkManager {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.queueLock.readLock().unlock();
|
||||
this.queueLock.updateLock().unlock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.akarin.server.mixin.optimization;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import net.minecraft.server.AxisAlignedBB;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.Material;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = Entity.class, remap = false)
|
||||
public abstract class MixinEntity {
|
||||
@Shadow public World world;
|
||||
@Shadow public abstract AxisAlignedBB getBoundingBox();
|
||||
|
||||
private boolean isInLava;
|
||||
private int lastLavaCheck = Integer.MIN_VALUE;
|
||||
|
||||
@Overwrite // OBFHELPER: isInLava
|
||||
public boolean au() {
|
||||
/*
|
||||
* This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285)
|
||||
* Thanks @Poweruser
|
||||
*/
|
||||
int currentTick = MinecraftServer.currentTick;
|
||||
if (this.lastLavaCheck != currentTick) {
|
||||
this.lastLavaCheck = currentTick;
|
||||
this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
|
||||
}
|
||||
return this.isInLava;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.akarin.api.internal.Akari;
|
||||
import net.minecraft.server.BaseBlockPosition;
|
||||
import net.minecraft.server.Block;
|
||||
import net.minecraft.server.BlockDiodeAbstract;
|
||||
@@ -148,10 +147,11 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
|
||||
while (!turnOff.isEmpty()) {
|
||||
BlockPosition pos = turnOff.remove(0);
|
||||
if (pos == null) continue; // Akarin
|
||||
IBlockData state = worldIn.getType(pos);
|
||||
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||
this.canProvidePower = false;
|
||||
int blockPower = worldIn.z(pos); // PAIL: isBlockIndirectlyGettingPowered
|
||||
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
|
||||
this.canProvidePower = true;
|
||||
int wirePower = this.getSurroundingWirePower(worldIn, pos);
|
||||
|
||||
@@ -186,10 +186,11 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
|
||||
while (!this.turnOn.isEmpty()) {
|
||||
BlockPosition pos = this.turnOn.remove(0);
|
||||
if (pos == null) continue; // Akarin
|
||||
IBlockData state = worldIn.getType(pos);
|
||||
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||
this.canProvidePower = false;
|
||||
int blockPower = worldIn.z(pos); // PAIL: isBlockIndirectlyGettingPowered
|
||||
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
|
||||
this.canProvidePower = true;
|
||||
int wirePower = this.getSurroundingWirePower(worldIn, pos);
|
||||
// Lower the strength as it moved a block
|
||||
@@ -265,11 +266,11 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
}
|
||||
for (EnumDirection facingVertical : facingsVertical) {
|
||||
BlockPosition offsetPos = pos.shift(facingVertical);
|
||||
boolean solidBlock = worldIn.getType(offsetPos).k(); // PAIL: isBlockNormalCube
|
||||
boolean solidBlock = worldIn.getType(offsetPos).k(); // OBFHELPER: isBlockNormalCube
|
||||
for (EnumDirection facingHorizontal : facingsHorizontal) {
|
||||
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
|
||||
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
|
||||
if ((facingVertical == EnumDirection.UP && !solidBlock) || (facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).k())) { // PAIL: isBlockNormalCube
|
||||
if ((facingVertical == EnumDirection.UP && !solidBlock) || (facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).k())) { // OBFHELPER: isBlockNormalCube
|
||||
this.addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
|
||||
}
|
||||
}
|
||||
@@ -293,10 +294,10 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
wirePower = this.getPower(worldIn, offsetPos, wirePower);
|
||||
|
||||
// Block below the wire need to be solid (Upwards diode of slabs/stairs/glowstone) and no block should cut the wire
|
||||
if(worldIn.getType(offsetPos).l() && !worldIn.getType(pos.up()).l()) { // PAIL: isNormalCube
|
||||
if(worldIn.getType(offsetPos).l() && !worldIn.getType(pos.up()).l()) { // OBFHELPER: isNormalCube
|
||||
wirePower = this.getPower(worldIn, offsetPos.up(), wirePower);
|
||||
// Only get from power below if no block is cutting the wire
|
||||
} else if (!worldIn.getType(offsetPos).l()) { // PAIL: isNormalCube
|
||||
} else if (!worldIn.getType(offsetPos).l()) { // OBFHELPER: isNormalCube
|
||||
wirePower = this.getPower(worldIn, offsetPos.down(), wirePower);
|
||||
}
|
||||
}
|
||||
@@ -328,7 +329,7 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
// Later add blocks around the surrounding blocks that get powered
|
||||
for (EnumDirection facing : facings) {
|
||||
BlockPosition offsetPos = pos.shift(facing);
|
||||
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN || !worldIn.getType(offsetPos).l()) continue; // PAIL: isNormalCube
|
||||
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN || !worldIn.getType(offsetPos).l()) continue; // OBFHELPER: isNormalCube
|
||||
for (EnumDirection facing1 : facings) {
|
||||
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false))
|
||||
set.add(offsetPos.shift(facing1));
|
||||
@@ -413,7 +414,7 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
*/
|
||||
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
|
||||
for (BaseBlockPosition vect : surroundingBlocksOffset) {
|
||||
set.add(pos.a(vect)); // PAIL: add
|
||||
set.add(pos.a(vect)); // OBFHELPER: add
|
||||
}
|
||||
}
|
||||
|
||||
@@ -446,7 +447,7 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
public void onPlace(World world, BlockPosition pos, IBlockData state) {
|
||||
this.updateSurroundingRedstone(world, pos);
|
||||
for (BaseBlockPosition vec : surroundingBlocksOffset) {
|
||||
world.applyPhysics(pos.a(vec), this, false); // PAIL: add
|
||||
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +464,7 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
super.remove(world, pos, state);
|
||||
this.updateSurroundingRedstone(world, pos);
|
||||
for (BaseBlockPosition vec : surroundingBlocksOffset) {
|
||||
world.applyPhysics(pos.a(vec), this, false); // PAIL: add
|
||||
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,7 +479,7 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
*/
|
||||
@Override
|
||||
@Overwrite
|
||||
public int b(IBlockData blockState, IBlockAccess blockAccess, BlockPosition pos, EnumDirection side) { // PAIL: getWeakPower
|
||||
public int b(IBlockData blockState, IBlockAccess blockAccess, BlockPosition pos, EnumDirection side) { // OBFHELPER: getWeakPower
|
||||
if (!this.canProvidePower) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -495,14 +496,14 @@ public abstract class PandaRedstoneWire extends Block {
|
||||
|
||||
if (block == Blocks.REDSTONE_WIRE) {
|
||||
return true;
|
||||
} else if (Blocks.UNPOWERED_REPEATER.D(blockState)) { // PAIL: isSameDiode
|
||||
} else if (Blocks.UNPOWERED_REPEATER.D(blockState)) { // OBFHELPER: isSameDiode
|
||||
EnumDirection enumdirection1 = blockState.get(BlockRepeater.FACING);
|
||||
|
||||
return enumdirection1 == side || enumdirection1.opposite() == side;
|
||||
} else if (Blocks.dk == blockState.getBlock()) {
|
||||
return side == blockState.get(BlockObserver.FACING); // PAIL: OBSERVER
|
||||
return side == blockState.get(BlockObserver.FACING); // OBFHELPER: OBSERVER
|
||||
} else {
|
||||
return blockState.m() && side != null; // PAIL: canProvidePower
|
||||
return blockState.m() && side != null; // OBFHELPER: canProvidePower
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,23 +50,23 @@ public abstract class WeakEnchantmentManager {
|
||||
|
||||
@Overwrite
|
||||
public static int a(Iterable<ItemStack> iterable, DamageSource damageSource) {
|
||||
protection.a = 0; // PAIL: damageModifier
|
||||
protection.a = 0; // OBFHELPER: damageModifier
|
||||
protection.b = damageSource;
|
||||
a(protection, iterable); // PAIL: applyEnchantmentModifierArray
|
||||
a(protection, iterable); // OBFHELPER: applyEnchantmentModifierArray
|
||||
protection.b = null; // Akarin - Remove reference to Damagesource
|
||||
return protection.a;
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public static void a(EntityLiving user, Entity attacker) { // PAIL: applyThornEnchantments
|
||||
public static void a(EntityLiving user, Entity attacker) { // OBFHELPER: applyThornEnchantments
|
||||
thorns.b = attacker;
|
||||
thorns.a = user;
|
||||
if (user != null) {
|
||||
a(thorns, user.aQ()); // PAIL: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||
a(thorns, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||
}
|
||||
|
||||
if (attacker instanceof EntityHuman) {
|
||||
a(thorns, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
|
||||
a(thorns, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
|
||||
}
|
||||
|
||||
// Akarin Start - remove references to entity objects to avoid memory leaks
|
||||
@@ -76,15 +76,15 @@ public abstract class WeakEnchantmentManager {
|
||||
}
|
||||
|
||||
@Overwrite
|
||||
public static void b(EntityLiving user, Entity target) { // PAIL: applyArthropodEnchantments
|
||||
public static void b(EntityLiving user, Entity target) { // OBFHELPER: applyArthropodEnchantments
|
||||
arthropods.a = user;
|
||||
arthropods.b = target;
|
||||
if (user != null) {
|
||||
a(arthropods, user.aQ()); // PAIL: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||
a(arthropods, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||
}
|
||||
|
||||
if (user instanceof EntityHuman) {
|
||||
a(arthropods, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
|
||||
a(arthropods, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
|
||||
}
|
||||
|
||||
// Akarin Start - remove references to entity objects to avoid memory leaks
|
||||
|
||||
@@ -33,7 +33,7 @@ import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||
import net.minecraft.server.Entity;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = Entity.class, remap = false)
|
||||
@Mixin(value = Entity.class, remap = false, priority = 1001)
|
||||
public abstract class MixinEntity {
|
||||
private static final String ENTITY_RIDABLE_COOLDOWN_FIELD = "Lnet/minecraft/entity/Entity;j:I"; // PUTFIELD: rideCooldown
|
||||
private static final String ENTITY_PORTAL_COUNTER_FIELD = "Lnet/minecraft/entity/Entity;al:I"; // PUTFIELD: portalCounter
|
||||
@@ -41,16 +41,16 @@ public abstract class MixinEntity {
|
||||
@Shadow protected int al;
|
||||
@Shadow public World world;
|
||||
|
||||
// PAIL: onEntityUpdate
|
||||
// OBFHELPER: onEntityUpdate
|
||||
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_RIDABLE_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupEntityCooldown(Entity self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
|
||||
this.j = Math.max(0, this.j - ticks); // PAIL: rideCooldown
|
||||
this.j = Math.max(0, this.j - ticks); // OBFHELPER: rideCooldown
|
||||
}
|
||||
|
||||
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_PORTAL_COUNTER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupPortalCounter(Entity self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
|
||||
this.al += ticks; // PAIL: portalCounter
|
||||
this.al += ticks; // OBFHELPER: portalCounter
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import net.minecraft.server.EntityAgeable;
|
||||
public abstract class MixinEntityAgeable {
|
||||
private static final String ENTITY_AGEABLE_SET_GROWING_AGE_METHOD = "Lnet/minecraft/entity/EntityAgeable;setAgeRaw(I)V";
|
||||
|
||||
// PAIL: onLivingUpdate
|
||||
// OBFHELPER: onLivingUpdate
|
||||
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 0))
|
||||
public void fixupGrowingUp(EntityAgeable self, int age) {
|
||||
// Subtract the one the original update method added
|
||||
|
||||
@@ -37,19 +37,19 @@ import net.minecraft.server.EntityExperienceOrb;
|
||||
public abstract class MixinEntityExperienceOrb {
|
||||
private static final String ENTITY_XP_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;c:I"; // PUTFIELD: delayBeforeCanPickup
|
||||
private static final String ENTITY_XP_AGE_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;b:I"; // PUTFIELD: xpOrbAge
|
||||
@Shadow public int c; // PAIL: delayBeforeCanPickup
|
||||
@Shadow public int b; // PAIL: xpOrbAge
|
||||
@Shadow public int c; // OBFHELPER: delayBeforeCanPickup
|
||||
@Shadow public int b; // OBFHELPER: xpOrbAge
|
||||
|
||||
// PAIL: onUpdate
|
||||
// OBFHELPER: onUpdate
|
||||
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupPickupDelay(EntityExperienceOrb self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
this.c = Math.max(0, this.c - ticks); // PAIL: delayBeforeCanPickup
|
||||
this.c = Math.max(0, this.c - ticks); // OBFHELPER: delayBeforeCanPickup
|
||||
}
|
||||
|
||||
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupAge(EntityExperienceOrb self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
this.b += ticks; // PAIL: xpOrbAge
|
||||
this.b += ticks; // OBFHELPER: xpOrbAge
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +40,11 @@ public abstract class MixinEntityHuman {
|
||||
@Shadow public int bD;
|
||||
@Shadow private int sleepTicks;
|
||||
|
||||
// PAIL: onUpdate
|
||||
// OBFHELPER: onUpdate
|
||||
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_XP_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupXpCooldown(EntityHuman self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
this.bD = Math.max(0, this.bD - ticks); // PAIL: xpCooldown
|
||||
this.bD = Math.max(0, this.bD - ticks); // OBFHELPER: xpCooldown
|
||||
}
|
||||
|
||||
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
|
||||
@@ -42,7 +42,7 @@ public abstract class MixinEntityInsentient extends EntityLiving {
|
||||
super(world);
|
||||
}
|
||||
|
||||
@Redirect(method = "doTick", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
@Redirect(method = "doTick()V", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupEntityDespawnAge(EntityInsentient self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
this.ticksFarFromPlayer += ticks;
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class MixinEntityItem {
|
||||
@Shadow public int age;
|
||||
@Shadow private int pickupDelay;
|
||||
|
||||
// PAIL: onUpdate
|
||||
// OBFHELPER: onUpdate
|
||||
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupPickupDelay(EntityItem self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
|
||||
@@ -42,7 +42,7 @@ public abstract class MixinEntityPlayer extends Entity {
|
||||
super(worldIn);
|
||||
}
|
||||
|
||||
// PAIL: decrementTimeUntilPortal
|
||||
// OBFHELPER: decrementTimeUntilPortal
|
||||
@Redirect(method = "I()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupPortalCooldown(EntityPlayer self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
|
||||
@@ -38,7 +38,7 @@ public abstract class MixinEntityZombieVillager {
|
||||
|
||||
@Shadow(aliases = "du") protected abstract int getConversionProgress();
|
||||
|
||||
// PAIL: onUpdate
|
||||
// OBFHELPER: onUpdate
|
||||
@Redirect(method = "B_()V", at = @At(value = "INVOKE", target = ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD, ordinal = 0))
|
||||
public int fixupConversionTimeBoost(EntityZombieVillager self) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||
|
||||
@@ -13,7 +13,7 @@ public abstract class MixinMinecraftServer implements IMixinRealTimeTicking {
|
||||
private static long lastTickNanos = System.nanoTime();
|
||||
private static long realTimeTicks = 1;
|
||||
|
||||
@Inject(method = "C()V", at = @At("HEAD")) // PAIL: fullTick
|
||||
@Inject(method = "C()V", at = @At("HEAD")) // OBFHELPER: fullTick
|
||||
public void onTickUpdateRealTimeTicks(CallbackInfo ci) {
|
||||
long currentNanos = System.nanoTime();
|
||||
realTimeTicks = (currentNanos - lastTickNanos) / 50000000;
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class MixinPlayerConnection {
|
||||
@Shadow(aliases = "j") private int itemDropThreshold;
|
||||
@Shadow @Final private MinecraftServer minecraftServer;
|
||||
|
||||
// PAIL: update
|
||||
// OBFHELPER: update
|
||||
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_CHAT_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupChatSpamCheck(PlayerConnection self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
|
||||
|
||||
@@ -40,7 +40,7 @@ public abstract class MixinPlayerInteractManager {
|
||||
@Shadow public World world;
|
||||
@Shadow private int currentTick;
|
||||
|
||||
// PAIL: updateBlockRemoving
|
||||
// OBFHELPER: updateBlockRemoving
|
||||
@Redirect(method = "a()V", at = @At(value = "FIELD", target = PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupDiggingTime(PlayerInteractManager self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.world.getMinecraftServer()).getRealTimeTicks();
|
||||
|
||||
@@ -39,7 +39,7 @@ public abstract class MixinTileEntityBrewingStand extends TileEntity {
|
||||
private static final String BREWING_STAND_BREW_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityBrewingStand;brewTime:I";
|
||||
@Shadow private int brewTime;
|
||||
|
||||
// PAIL: update
|
||||
// OBFHELPER: update
|
||||
@Redirect(method = "e()V", at = @At(value = "FIELD", target = BREWING_STAND_BREW_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupBrewTime(TileEntityBrewingStand self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class MixinTileEntityFurnace extends TileEntity {
|
||||
@Shadow private int cookTime;
|
||||
@Shadow private int cookTimeTotal;
|
||||
|
||||
// PAIL: update
|
||||
// OBFHELPER: update
|
||||
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_BURN_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||
public void fixupBurnTime(TileEntityFurnace self, int modifier) {
|
||||
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||
|
||||
@@ -32,7 +32,7 @@ import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.World;
|
||||
|
||||
@Mixin(value = World.class, remap = false, priority = 1002)
|
||||
@Mixin(value = World.class, remap = false, priority = 1001)
|
||||
public abstract class MixinWorld implements IMixinRealTimeTicking {
|
||||
@Shadow @Nullable public abstract MinecraftServer getMinecraftServer();
|
||||
|
||||
|
||||
479
sources/src/main/java/net/minecraft/server/BlockChest.java
Normal file
479
sources/src/main/java/net/minecraft/server/BlockChest.java
Normal file
@@ -0,0 +1,479 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockChest extends BlockTileEntity {
|
||||
|
||||
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
|
||||
protected static final AxisAlignedBB b = new AxisAlignedBB(0.0625D, 0.0D, 0.0D, 0.9375D, 0.875D, 0.9375D);
|
||||
protected static final AxisAlignedBB c = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 1.0D);
|
||||
protected static final AxisAlignedBB d = new AxisAlignedBB(0.0D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D);
|
||||
protected static final AxisAlignedBB e = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 1.0D, 0.875D, 0.9375D);
|
||||
protected static final AxisAlignedBB f = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D);
|
||||
public final BlockChest.Type g;
|
||||
|
||||
protected BlockChest(BlockChest.Type blockchest_type) {
|
||||
super(Material.WOOD);
|
||||
this.w(this.blockStateList.getBlockData().set(BlockChest.FACING, EnumDirection.NORTH));
|
||||
this.g = blockchest_type;
|
||||
this.a(blockchest_type == BlockChest.Type.TRAP ? CreativeModeTab.d : CreativeModeTab.c);
|
||||
}
|
||||
|
||||
public boolean b(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean c(IBlockData iblockdata) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public EnumRenderType a(IBlockData iblockdata) {
|
||||
return EnumRenderType.ENTITYBLOCK_ANIMATED;
|
||||
}
|
||||
|
||||
public AxisAlignedBB b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
|
||||
return iblockaccess.getType(blockposition.north()).getBlock() == this ? BlockChest.b : (iblockaccess.getType(blockposition.south()).getBlock() == this ? BlockChest.c : (iblockaccess.getType(blockposition.west()).getBlock() == this ? BlockChest.d : (iblockaccess.getType(blockposition.east()).getBlock() == this ? BlockChest.e : BlockChest.f)));
|
||||
}
|
||||
|
||||
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
this.e(world, blockposition, iblockdata);
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection = (EnumDirection) iterator.next();
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
// NeonPaper start - Dont load chunks for chests
|
||||
final IBlockData iblockdata1 = world.isLoaded(blockposition1) ? world.getType(blockposition1) : null;
|
||||
if (iblockdata1 == null) {
|
||||
continue;
|
||||
}
|
||||
// NeonPaper end
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
this.e(world, blockposition1, iblockdata1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData getPlacedState(World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2, int i, EntityLiving entityliving) {
|
||||
return this.getBlockData().set(BlockChest.FACING, entityliving.getDirection());
|
||||
}
|
||||
|
||||
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = EnumDirection.fromType2(MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3).opposite();
|
||||
|
||||
iblockdata = iblockdata.set(BlockChest.FACING, enumdirection);
|
||||
BlockPosition blockposition1 = blockposition.north();
|
||||
BlockPosition blockposition2 = blockposition.south();
|
||||
BlockPosition blockposition3 = blockposition.west();
|
||||
BlockPosition blockposition4 = blockposition.east();
|
||||
boolean flag = this == world.getType(blockposition1).getBlock();
|
||||
boolean flag1 = this == world.getType(blockposition2).getBlock();
|
||||
boolean flag2 = this == world.getType(blockposition3).getBlock();
|
||||
boolean flag3 = this == world.getType(blockposition4).getBlock();
|
||||
|
||||
if (!flag && !flag1 && !flag2 && !flag3) {
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
} else if (enumdirection.k() == EnumDirection.EnumAxis.X && (flag || flag1)) {
|
||||
if (flag) {
|
||||
world.setTypeAndData(blockposition1, iblockdata, 3);
|
||||
} else {
|
||||
world.setTypeAndData(blockposition2, iblockdata, 3);
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
} else if (enumdirection.k() == EnumDirection.EnumAxis.Z && (flag2 || flag3)) {
|
||||
if (flag2) {
|
||||
world.setTypeAndData(blockposition3, iblockdata, 3);
|
||||
} else {
|
||||
world.setTypeAndData(blockposition4, iblockdata, 3);
|
||||
}
|
||||
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
}
|
||||
|
||||
if (itemstack.hasName()) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityChest) {
|
||||
((TileEntityChest) tileentity).setCustomName(itemstack.getName());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public IBlockData e(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
if (world.isClientSide) {
|
||||
return iblockdata;
|
||||
} else {
|
||||
IBlockData iblockdata1 = world.getType(blockposition.north());
|
||||
IBlockData iblockdata2 = world.getType(blockposition.south());
|
||||
IBlockData iblockdata3 = world.getType(blockposition.west());
|
||||
IBlockData iblockdata4 = world.getType(blockposition.east());
|
||||
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING);
|
||||
|
||||
if (iblockdata1.getBlock() != this && iblockdata2.getBlock() != this) {
|
||||
boolean flag = iblockdata1.b();
|
||||
boolean flag1 = iblockdata2.b();
|
||||
|
||||
if (iblockdata3.getBlock() == this || iblockdata4.getBlock() == this) {
|
||||
BlockPosition blockposition1 = iblockdata3.getBlock() == this ? blockposition.west() : blockposition.east();
|
||||
IBlockData iblockdata5 = world.getType(blockposition1.north());
|
||||
IBlockData iblockdata6 = world.getType(blockposition1.south());
|
||||
|
||||
enumdirection = EnumDirection.SOUTH;
|
||||
EnumDirection enumdirection1;
|
||||
|
||||
if (iblockdata3.getBlock() == this) {
|
||||
enumdirection1 = (EnumDirection) iblockdata3.get(BlockChest.FACING);
|
||||
} else {
|
||||
enumdirection1 = (EnumDirection) iblockdata4.get(BlockChest.FACING);
|
||||
}
|
||||
|
||||
if (enumdirection1 == EnumDirection.NORTH) {
|
||||
enumdirection = EnumDirection.NORTH;
|
||||
}
|
||||
|
||||
if ((flag || iblockdata5.b()) && !flag1 && !iblockdata6.b()) {
|
||||
enumdirection = EnumDirection.SOUTH;
|
||||
}
|
||||
|
||||
if ((flag1 || iblockdata6.b()) && !flag && !iblockdata5.b()) {
|
||||
enumdirection = EnumDirection.NORTH;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
BlockPosition blockposition2 = iblockdata1.getBlock() == this ? blockposition.north() : blockposition.south();
|
||||
IBlockData iblockdata7 = world.getType(blockposition2.west());
|
||||
IBlockData iblockdata8 = world.getType(blockposition2.east());
|
||||
|
||||
enumdirection = EnumDirection.EAST;
|
||||
EnumDirection enumdirection2;
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
enumdirection2 = (EnumDirection) iblockdata1.get(BlockChest.FACING);
|
||||
} else {
|
||||
enumdirection2 = (EnumDirection) iblockdata2.get(BlockChest.FACING);
|
||||
}
|
||||
|
||||
if (enumdirection2 == EnumDirection.WEST) {
|
||||
enumdirection = EnumDirection.WEST;
|
||||
}
|
||||
|
||||
if ((iblockdata3.b() || iblockdata7.b()) && !iblockdata4.b() && !iblockdata8.b()) {
|
||||
enumdirection = EnumDirection.EAST;
|
||||
}
|
||||
|
||||
if ((iblockdata4.b() || iblockdata8.b()) && !iblockdata3.b() && !iblockdata7.b()) {
|
||||
enumdirection = EnumDirection.WEST;
|
||||
}
|
||||
}
|
||||
|
||||
iblockdata = iblockdata.set(BlockChest.FACING, enumdirection);
|
||||
world.setTypeAndData(blockposition, iblockdata, 3);
|
||||
return iblockdata;
|
||||
}
|
||||
}
|
||||
|
||||
public IBlockData f(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
EnumDirection enumdirection = null;
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection1 = (EnumDirection) iterator.next();
|
||||
IBlockData iblockdata1 = world.getType(blockposition.shift(enumdirection1));
|
||||
|
||||
if (iblockdata1.getBlock() == this) {
|
||||
return iblockdata;
|
||||
}
|
||||
|
||||
if (iblockdata1.b()) {
|
||||
if (enumdirection != null) {
|
||||
enumdirection = null;
|
||||
break;
|
||||
}
|
||||
|
||||
enumdirection = enumdirection1;
|
||||
}
|
||||
}
|
||||
|
||||
if (enumdirection != null) {
|
||||
return iblockdata.set(BlockChest.FACING, enumdirection.opposite());
|
||||
} else {
|
||||
EnumDirection enumdirection2 = (EnumDirection) iblockdata.get(BlockChest.FACING);
|
||||
|
||||
if (world.getType(blockposition.shift(enumdirection2)).b()) {
|
||||
enumdirection2 = enumdirection2.opposite();
|
||||
}
|
||||
|
||||
if (world.getType(blockposition.shift(enumdirection2)).b()) {
|
||||
enumdirection2 = enumdirection2.e();
|
||||
}
|
||||
|
||||
if (world.getType(blockposition.shift(enumdirection2)).b()) {
|
||||
enumdirection2 = enumdirection2.opposite();
|
||||
}
|
||||
|
||||
return iblockdata.set(BlockChest.FACING, enumdirection2);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canPlace(World world, BlockPosition blockposition) {
|
||||
int i = 0;
|
||||
BlockPosition blockposition1 = blockposition.west();
|
||||
BlockPosition blockposition2 = blockposition.east();
|
||||
BlockPosition blockposition3 = blockposition.north();
|
||||
BlockPosition blockposition4 = blockposition.south();
|
||||
|
||||
if (world.getType(blockposition1).getBlock() == this) {
|
||||
if (this.d(world, blockposition1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (world.getType(blockposition2).getBlock() == this) {
|
||||
if (this.d(world, blockposition2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (world.getType(blockposition3).getBlock() == this) {
|
||||
if (this.d(world, blockposition3)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
if (world.getType(blockposition4).getBlock() == this) {
|
||||
if (this.d(world, blockposition4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
|
||||
return i <= 1;
|
||||
}
|
||||
|
||||
private boolean d(World world, BlockPosition blockposition) {
|
||||
if (world.getType(blockposition).getBlock() != this) {
|
||||
return false;
|
||||
} else {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
EnumDirection enumdirection;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enumdirection = (EnumDirection) iterator.next();
|
||||
} while (world.getType(blockposition.shift(enumdirection)).getBlock() != this);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
super.a(iblockdata, world, blockposition, block, blockposition1);
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityChest) {
|
||||
tileentity.invalidateBlockCache();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof IInventory) {
|
||||
InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity);
|
||||
world.updateAdjacentComparators(blockposition, this);
|
||||
}
|
||||
|
||||
super.remove(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
public boolean interact(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
if (world.isClientSide) {
|
||||
return true;
|
||||
} else {
|
||||
ITileInventory itileinventory = this.getInventory(world, blockposition);
|
||||
|
||||
if (itileinventory != null) {
|
||||
entityhuman.openContainer(itileinventory);
|
||||
if (this.g == BlockChest.Type.BASIC) {
|
||||
entityhuman.b(StatisticList.aa);
|
||||
} else if (this.g == BlockChest.Type.TRAP) {
|
||||
entityhuman.b(StatisticList.U);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ITileInventory getInventory(World world, BlockPosition blockposition) {
|
||||
return this.a(world, blockposition, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ITileInventory a(World world, BlockPosition blockposition, boolean flag) {
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (!(tileentity instanceof TileEntityChest)) {
|
||||
return null;
|
||||
} else {
|
||||
Object object = (TileEntityChest) tileentity;
|
||||
|
||||
if (!flag && this.e(world, blockposition)) {
|
||||
return null;
|
||||
} else {
|
||||
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EnumDirection enumdirection = (EnumDirection) iterator.next();
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
// Paper start - don't load chunks if the other side of the chest is in unloaded chunk
|
||||
final IBlockData type = world.getTypeIfLoaded(blockposition1); // Paper
|
||||
if (type == null) {
|
||||
continue;
|
||||
}
|
||||
Block block = type.getBlock();
|
||||
// Paper end
|
||||
|
||||
if (block == this) {
|
||||
if (!flag && this.e(world, blockposition1)) { // Paper - check for allowBlocked flag - MC-99321
|
||||
return null;
|
||||
}
|
||||
|
||||
TileEntity tileentity1 = world.getTileEntity(blockposition1);
|
||||
|
||||
if (tileentity1 instanceof TileEntityChest) {
|
||||
if (enumdirection != EnumDirection.WEST && enumdirection != EnumDirection.NORTH) {
|
||||
object = new InventoryLargeChest("container.chestDouble", (ITileInventory) object, (TileEntityChest) tileentity1);
|
||||
} else {
|
||||
object = new InventoryLargeChest("container.chestDouble", (TileEntityChest) tileentity1, (ITileInventory) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ITileInventory) object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TileEntity a(World world, int i) {
|
||||
return new TileEntityChest();
|
||||
}
|
||||
|
||||
public boolean isPowerSource(IBlockData iblockdata) {
|
||||
return this.g == BlockChest.Type.TRAP;
|
||||
}
|
||||
|
||||
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
if (!iblockdata.m()) {
|
||||
return 0;
|
||||
} else {
|
||||
int i = 0;
|
||||
TileEntity tileentity = iblockaccess.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityChest) {
|
||||
i = ((TileEntityChest) tileentity).l;
|
||||
}
|
||||
|
||||
return MathHelper.clamp(i, 0, 15);
|
||||
}
|
||||
}
|
||||
|
||||
public int c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return enumdirection == EnumDirection.UP ? iblockdata.a(iblockaccess, blockposition, enumdirection) : 0;
|
||||
}
|
||||
|
||||
private boolean e(World world, BlockPosition blockposition) {
|
||||
return this.i(world, blockposition) || this.j(world, blockposition);
|
||||
}
|
||||
|
||||
private boolean i(World world, BlockPosition blockposition) {
|
||||
return world.getType(blockposition.up()).l();
|
||||
}
|
||||
|
||||
private boolean j(World world, BlockPosition blockposition) {
|
||||
// Paper start - Option ti dsiable chest cat detection
|
||||
if (world.paperConfig.disableChestCatDetection) {
|
||||
return false;
|
||||
}
|
||||
// Paper end
|
||||
Iterator iterator = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1))).iterator();
|
||||
|
||||
EntityOcelot entityocelot;
|
||||
|
||||
do {
|
||||
if (!iterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Entity entity = (Entity) iterator.next();
|
||||
|
||||
entityocelot = (EntityOcelot) entity;
|
||||
} while (!entityocelot.isSitting());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isComplexRedstone(IBlockData iblockdata) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int c(IBlockData iblockdata, World world, BlockPosition blockposition) {
|
||||
return Container.b((IInventory) this.getInventory(world, blockposition));
|
||||
}
|
||||
|
||||
public IBlockData fromLegacyData(int i) {
|
||||
EnumDirection enumdirection = EnumDirection.fromType1(i);
|
||||
|
||||
if (enumdirection.k() == EnumDirection.EnumAxis.Y) {
|
||||
enumdirection = EnumDirection.NORTH;
|
||||
}
|
||||
|
||||
return this.getBlockData().set(BlockChest.FACING, enumdirection);
|
||||
}
|
||||
|
||||
public int toLegacyData(IBlockData iblockdata) {
|
||||
return ((EnumDirection) iblockdata.get(BlockChest.FACING)).a();
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
|
||||
return iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
|
||||
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
|
||||
}
|
||||
|
||||
protected BlockStateList getStateList() {
|
||||
return new BlockStateList(this, new IBlockState[] { BlockChest.FACING});
|
||||
}
|
||||
|
||||
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
|
||||
return EnumBlockFaceShape.UNDEFINED;
|
||||
}
|
||||
|
||||
public static enum Type {
|
||||
|
||||
BASIC, TRAP;
|
||||
|
||||
private Type() {}
|
||||
}
|
||||
}
|
||||
118
sources/src/main/java/net/minecraft/server/BlockStationary.java
Normal file
118
sources/src/main/java/net/minecraft/server/BlockStationary.java
Normal file
@@ -0,0 +1,118 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
|
||||
|
||||
public class BlockStationary extends BlockFluids {
|
||||
|
||||
protected BlockStationary(Material material) {
|
||||
super(material);
|
||||
this.a(false);
|
||||
if (material == Material.LAVA) {
|
||||
this.a(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
|
||||
if (!this.e(world, blockposition, iblockdata)) {
|
||||
this.f(world, blockposition, iblockdata);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void f(World world, BlockPosition blockposition, IBlockData iblockdata) {
|
||||
BlockFlowing blockflowing = a(this.material);
|
||||
|
||||
world.setTypeAndData(blockposition, blockflowing.getBlockData().set(BlockStationary.LEVEL, iblockdata.get(BlockStationary.LEVEL)), 2);
|
||||
world.a(blockposition, (Block) blockflowing, this.a(world));
|
||||
}
|
||||
|
||||
public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) {
|
||||
if (this.material == Material.LAVA) {
|
||||
if (world.getGameRules().getBoolean("doFireTick")) {
|
||||
int i = random.nextInt(3);
|
||||
|
||||
if (i > 0) {
|
||||
BlockPosition blockposition1 = blockposition;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
blockposition1 = blockposition1.a(random.nextInt(3) - 1, 1, random.nextInt(3) - 1);
|
||||
if (blockposition1.getY() >= 0 && blockposition1.getY() < 256 && !world.isLoaded(blockposition1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block block = world.getType(blockposition1).getBlock();
|
||||
|
||||
if (block.material == Material.AIR) {
|
||||
if (this.c(world, blockposition1)) {
|
||||
// CraftBukkit start - Prevent lava putting something on fire
|
||||
if (world.getType(blockposition1) != Blocks.FIRE) {
|
||||
if (CraftEventFactory.callBlockIgniteEvent(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData());
|
||||
return;
|
||||
}
|
||||
} else if (block.material.isSolid()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, 0, random.nextInt(3) - 1);
|
||||
|
||||
if (blockposition2.getY() >= 0 && blockposition2.getY() < 256 && !world.isLoaded(blockposition2)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (world.isEmpty(blockposition2.up()) && this.d(world, blockposition2)) {
|
||||
// CraftBukkit start - Prevent lava putting something on fire
|
||||
BlockPosition up = blockposition2.up();
|
||||
if (world.getType(up) != Blocks.FIRE) {
|
||||
if (CraftEventFactory.callBlockIgniteEvent(world, up.getX(), up.getY(), up.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
world.setTypeUpdate(blockposition2.up(), Blocks.FIRE.getBlockData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean c(World world, BlockPosition blockposition) {
|
||||
EnumDirection[] aenumdirection = EnumDirection.values();
|
||||
int i = aenumdirection.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumDirection enumdirection = aenumdirection[j];
|
||||
|
||||
if (this.d(world, blockposition.shift(enumdirection))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean d(World world, BlockPosition blockposition) {
|
||||
// Dionysus start - improve fire spread checks
|
||||
if (blockposition.getY() >= 0 && blockposition.getY() < 256) {
|
||||
IBlockData blockData = world.getTypeIfLoaded(blockposition);
|
||||
|
||||
if (blockData != null) {
|
||||
return blockData.getMaterial().isBurnable();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
// Dionysus end
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// Paper start
|
||||
import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
// Paper end
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Maps;
|
||||
@@ -19,13 +24,6 @@ import com.google.common.collect.Lists; // CraftBukkit
|
||||
import org.bukkit.Server; // CraftBukkit
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers; // Paper
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add volatile to fields<br>
|
||||
* 2) Add OBFHELPER<br>
|
||||
* @author cakoyo
|
||||
*/
|
||||
public class Chunk {
|
||||
|
||||
private static final Logger e = LogManager.getLogger();
|
||||
@@ -38,6 +36,7 @@ public class Chunk {
|
||||
public final World world;
|
||||
public final int[] heightMap;
|
||||
public Long scheduledForUnload; // Paper - delay chunk unloads
|
||||
private static final Logger logger = LogManager.getLogger(); // Paper
|
||||
public final int locX;
|
||||
public final int locZ;
|
||||
private boolean m;
|
||||
@@ -52,11 +51,11 @@ public class Chunk {
|
||||
TileEntity replaced = super.put(key, value);
|
||||
if (replaced != null) {
|
||||
replaced.setCurrentChunk(null);
|
||||
tileEntityCounts.decrement(replaced.tileEntityKeyString);
|
||||
tileEntityCounts.decrement(replaced.getMinecraftKeyString());
|
||||
}
|
||||
if (value != null) {
|
||||
value.setCurrentChunk(Chunk.this);
|
||||
tileEntityCounts.increment(value.tileEntityKeyString);
|
||||
tileEntityCounts.increment(value.getMinecraftKeyString());
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
@@ -66,16 +65,16 @@ public class Chunk {
|
||||
TileEntity removed = super.remove(key);
|
||||
if (removed != null) {
|
||||
removed.setCurrentChunk(null);
|
||||
tileEntityCounts.decrement(removed.tileEntityKeyString);
|
||||
tileEntityCounts.decrement(removed.getMinecraftKeyString());
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
}
|
||||
public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public
|
||||
final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
|
||||
// Paper end
|
||||
private volatile boolean done; // Akarin - volatile
|
||||
private volatile boolean lit; // Akarin - volatile
|
||||
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
|
||||
private boolean done;
|
||||
private boolean lit;
|
||||
private boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER
|
||||
private boolean s;
|
||||
private boolean t;
|
||||
private long lastSaved;
|
||||
@@ -275,14 +274,7 @@ public class Chunk {
|
||||
|
||||
private void h(boolean flag) {
|
||||
this.world.methodProfiler.a("recheckGaps");
|
||||
if (this.world.areChunksLoaded(new BlockPosition(this.locX * 16 + 8, 0, this.locZ * 16 + 8), 16)) {
|
||||
this.runOrQueueLightUpdate(() -> recheckGaps(flag)); // Paper - Queue light update
|
||||
}
|
||||
}
|
||||
|
||||
private void recheckGaps(boolean flag) {
|
||||
if (true) {
|
||||
// Paper end
|
||||
if (this.areNeighborsLoaded(1)) { // Paper
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
for (int j = 0; j < 16; ++j) {
|
||||
if (this.i[i + j * 16]) {
|
||||
@@ -333,7 +325,7 @@ public class Chunk {
|
||||
}
|
||||
|
||||
private void a(int i, int j, int k, int l) {
|
||||
if (l > k && this.world.areChunksLoaded(new BlockPosition(i, 0, j), 16)) {
|
||||
if (l > k && this.areNeighborsLoaded(1)) { // Paper
|
||||
for (int i1 = k; i1 < l; ++i1) {
|
||||
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
|
||||
}
|
||||
@@ -665,6 +657,7 @@ public class Chunk {
|
||||
if (i != this.locX || j != this.locZ) {
|
||||
Chunk.e.warn("Wrong location! ({}, {}) should be ({}, {}), {}", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(this.locX), Integer.valueOf(this.locZ), entity);
|
||||
entity.die();
|
||||
return; // Paper
|
||||
}
|
||||
|
||||
int k = MathHelper.floor(entity.locY / 16.0D);
|
||||
@@ -681,8 +674,30 @@ public class Chunk {
|
||||
entity.ab = this.locX;
|
||||
entity.ac = k;
|
||||
entity.ad = this.locZ;
|
||||
this.entitySlices[k].add(entity);
|
||||
// Paper start - update count
|
||||
|
||||
// Paper start
|
||||
List<Entity> entitySlice = this.entitySlices[k];
|
||||
boolean inThis = entitySlice.contains(entity);
|
||||
List<Entity> currentSlice = entity.entitySlice;
|
||||
if (inThis || (currentSlice != null && currentSlice.contains(entity))) {
|
||||
if (currentSlice == entitySlice || inThis) {
|
||||
return;
|
||||
} else {
|
||||
Chunk chunk = entity.getCurrentChunk();
|
||||
if (chunk != null) {
|
||||
chunk.removeEntity(entity);
|
||||
} else {
|
||||
removeEntity(entity);
|
||||
}
|
||||
currentSlice.remove(entity); // Just incase the above did not remove from this target slice
|
||||
}
|
||||
}
|
||||
entity.entitySlice = entitySlice;
|
||||
entitySlice.add(entity);
|
||||
|
||||
this.markDirty();
|
||||
entity.setCurrentChunk(this);
|
||||
entityCounts.increment(entity.getMinecraftKeyString());
|
||||
if (entity instanceof EntityItem) {
|
||||
itemCounts[k]++;
|
||||
} else if (entity instanceof IInventory) {
|
||||
@@ -704,10 +719,6 @@ public class Chunk {
|
||||
this.entityCount.adjustOrPutValue( creatureType.a(), 1, 1 );
|
||||
}
|
||||
}
|
||||
// Paper start
|
||||
entity.setCurrentChunk(this);
|
||||
entityCounts.increment(entity.entityKeyString);
|
||||
// Paper end
|
||||
// Spigot end
|
||||
}
|
||||
|
||||
@@ -725,8 +736,14 @@ public class Chunk {
|
||||
i = this.entitySlices.length - 1;
|
||||
}
|
||||
|
||||
if (!this.entitySlices[i].remove(entity)) { return; } // Paper
|
||||
// Paper start - update counts
|
||||
// Paper start
|
||||
if (entity.entitySlice == null || !entity.entitySlice.contains(entity) || entitySlices[i] == entity.entitySlice) {
|
||||
entity.entitySlice = null;
|
||||
}
|
||||
if (!this.entitySlices[i].remove(entity)) { return; }
|
||||
this.markDirty();
|
||||
entity.setCurrentChunk(null);
|
||||
entityCounts.decrement(entity.getMinecraftKeyString());
|
||||
if (entity instanceof EntityItem) {
|
||||
itemCounts[i]--;
|
||||
} else if (entity instanceof IInventory) {
|
||||
@@ -748,10 +765,6 @@ public class Chunk {
|
||||
this.entityCount.adjustValue( creatureType.a(), -1 );
|
||||
}
|
||||
}
|
||||
// Paper start
|
||||
entity.setCurrentChunk(null);
|
||||
entityCounts.decrement(entity.entityKeyString);
|
||||
// Paper end
|
||||
// Spigot end
|
||||
}
|
||||
|
||||
@@ -859,12 +872,59 @@ public class Chunk {
|
||||
this.world.b(this.tileEntities.values());
|
||||
List[] aentityslice = this.entitySlices; // Spigot
|
||||
int i = aentityslice.length;
|
||||
List<Entity> toAdd = new java.util.ArrayList<>(32); // Paper
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
List entityslice = aentityslice[j]; // Spigot
|
||||
// Paper start
|
||||
DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode;
|
||||
if (mode == DuplicateUUIDMode.WARN || mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.SAFE_REGEN) {
|
||||
Map<UUID, Entity> thisChunk = new HashMap<>();
|
||||
for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) {
|
||||
Entity entity = iterator.next();
|
||||
if (entity.dead || entity.valid) continue;
|
||||
Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID);
|
||||
if (other == null || other.dead || world.getEntityUnloadQueue().contains(other)) {
|
||||
other = thisChunk.get(entity.uniqueID);
|
||||
}
|
||||
|
||||
this.world.a((Collection) entityslice);
|
||||
if (mode == DuplicateUUIDMode.SAFE_REGEN && other != null && !other.dead &&
|
||||
!world.getEntityUnloadQueue().contains(other)
|
||||
&& java.util.Objects.equals(other.getSaveID(), entity.getSaveID())
|
||||
&& entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < world.paperConfig.duplicateUUIDDeleteRange
|
||||
) {
|
||||
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
||||
entity.die();
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (other != null && !other.dead) {
|
||||
switch (mode) {
|
||||
case SAFE_REGEN: {
|
||||
entity.setUUID(UUID.randomUUID());
|
||||
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
||||
break;
|
||||
}
|
||||
case DELETE: {
|
||||
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
||||
entity.die();
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
thisChunk.put(entity.uniqueID, entity);
|
||||
}
|
||||
}
|
||||
|
||||
//this.world.a((Collection) entityslice); // Move down, add all entities at same time
|
||||
toAdd.addAll(entityslice);
|
||||
// Paper end
|
||||
}
|
||||
this.world.addChunkEntities(toAdd); // Paper - add all at same time to avoid entities adding to world modifying slice state
|
||||
|
||||
}
|
||||
|
||||
@@ -911,6 +971,8 @@ public class Chunk {
|
||||
}
|
||||
}
|
||||
// Spigot End
|
||||
entity.setCurrentChunk(null); // Paper
|
||||
entity.entitySlice = null; // Paper
|
||||
|
||||
// Do not pass along players, as doing so can get them stuck outside of time.
|
||||
// (which for example disables inventory icon updates and prevents block breaking)
|
||||
@@ -938,7 +1000,6 @@ public class Chunk {
|
||||
|
||||
for (int k = i; k <= j; ++k) {
|
||||
if (!this.entitySlices[k].isEmpty()) {
|
||||
Iterator iterator = this.entitySlices[k].iterator();
|
||||
|
||||
// Paper start - Don't search for inventories if we have none, and that is all we want
|
||||
/*
|
||||
@@ -949,6 +1010,7 @@ public class Chunk {
|
||||
*/
|
||||
if (predicate == IEntitySelector.c && inventoryEntityCounts[k] <= 0) continue;
|
||||
// Paper end
|
||||
Iterator iterator = this.entitySlices[k].iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity1 = (Entity) iterator.next();
|
||||
|
||||
@@ -1157,7 +1219,7 @@ public class Chunk {
|
||||
}
|
||||
|
||||
this.r = true;
|
||||
if (!this.lit && this.done && this.world.spigotConfig.randomLightUpdates) { // Spigot - also use random light updates setting to determine if we should relight
|
||||
if (!this.lit && this.done) {
|
||||
this.o();
|
||||
}
|
||||
|
||||
@@ -1344,7 +1406,6 @@ public class Chunk {
|
||||
this.h(false);
|
||||
}
|
||||
|
||||
public void checkLightSide(EnumDirection direction) { a(direction); } // Akarin - OBFHELPER
|
||||
private void a(EnumDirection enumdirection) {
|
||||
if (this.done) {
|
||||
int i;
|
||||
@@ -1487,4 +1548,14 @@ public class Chunk {
|
||||
|
||||
private EnumTileEntityState() {}
|
||||
}
|
||||
// FlamePaper start - Hopper item lookup optimization
|
||||
public int getItemCount(BlockPosition blockPosition) {
|
||||
int k = MathHelper.floor(blockPosition.getY() / 16.0D);
|
||||
|
||||
k = Math.max(0, k);
|
||||
k = Math.min(this.entitySlices.length - 1, k);
|
||||
|
||||
return itemCounts[k];
|
||||
}
|
||||
// FlamePaper end - Hopper item lookup optimization
|
||||
}
|
||||
|
||||
@@ -0,0 +1,458 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.PaperConfig;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongArraySet;
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||
import org.bukkit.event.world.ChunkUnloadEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class ChunkProviderServer implements IChunkProvider {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
public final LongArraySet unloadQueue = new LongArraySet(512); // Dionysus
|
||||
public final ChunkGenerator chunkGenerator;
|
||||
private final IChunkLoader chunkLoader;
|
||||
// Paper start - chunk save stats
|
||||
private long lastQueuedSaves = 0L; // Paper
|
||||
private long lastProcessedSaves = 0L; // Paper
|
||||
private long lastSaveStatPrinted = System.currentTimeMillis();
|
||||
// Paper end
|
||||
// Paper start
|
||||
protected Chunk lastChunkByPos = null;
|
||||
public Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<Chunk>(8192) {
|
||||
|
||||
@Override
|
||||
public Chunk get(long key) {
|
||||
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||
return lastChunkByPos;
|
||||
}
|
||||
return lastChunkByPos = super.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk remove(long key) {
|
||||
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
|
||||
lastChunkByPos = null;
|
||||
}
|
||||
return super.remove(key);
|
||||
}
|
||||
}; // CraftBukkit
|
||||
// Paper end
|
||||
public final WorldServer world;
|
||||
|
||||
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator chunkgenerator) {
|
||||
this.world = worldserver;
|
||||
this.chunkLoader = ichunkloader;
|
||||
this.chunkGenerator = chunkgenerator;
|
||||
}
|
||||
|
||||
public Collection<Chunk> a() {
|
||||
return this.chunks.values();
|
||||
}
|
||||
|
||||
public void unload(Chunk chunk) {
|
||||
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
||||
this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
||||
chunk.d = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void b() {
|
||||
ObjectIterator objectiterator = this.chunks.values().iterator();
|
||||
|
||||
while (objectiterator.hasNext()) {
|
||||
Chunk chunk = (Chunk) objectiterator.next();
|
||||
|
||||
this.unload(chunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Paper start
|
||||
public boolean isChunkGenerated(int x, int z) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.asLong(x, z)) || this.chunkLoader.chunkExists(x, z);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Nullable
|
||||
public Chunk getLoadedChunkAt(int i, int j) {
|
||||
long k = ChunkCoordIntPair.a(i, j);
|
||||
Chunk chunk = (Chunk) this.chunks.get(k);
|
||||
|
||||
if (chunk != null) {
|
||||
chunk.d = false;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk getOrLoadChunkAt(int i, int j) {
|
||||
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||
|
||||
if (chunk == null) {
|
||||
// CraftBukkit start
|
||||
ChunkRegionLoader loader = null;
|
||||
|
||||
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||
}
|
||||
if (loader != null && loader.chunkExists(i, j)) {
|
||||
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk originalGetOrLoadChunkAt(int i, int j) {
|
||||
// CraftBukkit end
|
||||
Chunk chunk = this.getLoadedChunkAt(i, j);
|
||||
|
||||
if (chunk == null) {
|
||||
chunk = this.loadChunk(i, j);
|
||||
if (chunk != null) {
|
||||
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk);
|
||||
chunk.addEntities();
|
||||
chunk.loadNearby(this, this.chunkGenerator, false); // CraftBukkit
|
||||
}
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public Chunk getChunkIfLoaded(int x, int z) {
|
||||
return chunks.get(ChunkCoordIntPair.a(x, z));
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public Chunk getChunkAt(int i, int j) {
|
||||
return getChunkAt(i, j, null);
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(int i, int j, Runnable runnable) {
|
||||
return getChunkAt(i, j, runnable, true);
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(int i, int j, Runnable runnable, boolean generate) {
|
||||
Chunk chunk = world.paperConfig.allowPermaChunkLoaders ? getLoadedChunkAt(i, j) : getChunkIfLoaded(i, j); // Paper - Configurable perma chunk loaders
|
||||
ChunkRegionLoader loader = null;
|
||||
|
||||
if (this.chunkLoader instanceof ChunkRegionLoader) {
|
||||
loader = (ChunkRegionLoader) this.chunkLoader;
|
||||
|
||||
}
|
||||
// We can only use the queue for already generated chunks
|
||||
if (chunk == null && loader != null && loader.chunkExists(i, j)) {
|
||||
if (runnable != null) {
|
||||
ChunkIOExecutor.queueChunkLoad(world, loader, this, i, j, runnable);
|
||||
return null;
|
||||
} else {
|
||||
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
|
||||
|
||||
// Paper start - If there was an issue loading the chunk from region, stage1 will fail and stage2 will load it sync
|
||||
// all we need to do is fetch an instance
|
||||
if (chunk == null) {
|
||||
chunk = getChunkIfLoaded(i, j);
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
} else if (chunk == null && generate) {
|
||||
chunk = originalGetChunkAt(i, j);
|
||||
}
|
||||
|
||||
// If we didn't load the chunk async and have a callback run it now
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public Chunk originalGetChunkAt(int i, int j) {
|
||||
Chunk chunk = this.originalGetOrLoadChunkAt(i, j);
|
||||
// CraftBukkit end
|
||||
|
||||
if (chunk == null) {
|
||||
world.timings.syncChunkLoadTimer.startTiming(); // Spigot
|
||||
long k = ChunkCoordIntPair.a(i, j);
|
||||
|
||||
try {
|
||||
chunk = this.chunkGenerator.getOrCreateChunk(i, j);
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
|
||||
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
|
||||
|
||||
crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)}));
|
||||
crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(k));
|
||||
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
|
||||
this.chunks.put(k, chunk);
|
||||
chunk.addEntities();
|
||||
chunk.loadNearby(this, this.chunkGenerator, true); // CraftBukkit
|
||||
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Chunk loadChunk(int i, int j) {
|
||||
try {
|
||||
Chunk chunk = this.chunkLoader.a(this.world, i, j);
|
||||
|
||||
if (chunk != null) {
|
||||
chunk.setLastSaved(this.world.getTime());
|
||||
this.chunkGenerator.recreateStructures(chunk, i, j);
|
||||
}
|
||||
|
||||
return chunk;
|
||||
} catch (Exception exception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t load chunk";
|
||||
ChunkProviderServer.a.error(msg, exception);
|
||||
ServerInternalException.reportInternalException(exception);
|
||||
// Paper end
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveChunkNOP(Chunk chunk) {
|
||||
try {
|
||||
// this.chunkLoader.b(this.world, chunk); // Spigot
|
||||
} catch (Exception exception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t save entities";
|
||||
ChunkProviderServer.a.error(msg, exception);
|
||||
ServerInternalException.reportInternalException(exception);
|
||||
// Paper end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void saveChunk(Chunk chunk, boolean unloaded) { // Spigot
|
||||
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) {
|
||||
chunk.setLastSaved(this.world.getTime());
|
||||
this.chunkLoader.saveChunk(this.world, chunk, unloaded); // Spigot
|
||||
} catch (IOException ioexception) {
|
||||
// Paper start
|
||||
String msg = "Couldn\'t save chunk";
|
||||
ChunkProviderServer.a.error(msg, ioexception);
|
||||
ServerInternalException.reportInternalException(ioexception);
|
||||
} catch (ExceptionWorldConflict exceptionworldconflict) {
|
||||
String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?";
|
||||
ChunkProviderServer.a.error(msg, exceptionworldconflict);
|
||||
ServerInternalException.reportInternalException(exceptionworldconflict);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean a(boolean flag) {
|
||||
int i = 0;
|
||||
|
||||
// CraftBukkit start
|
||||
// Paper start
|
||||
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
|
||||
final int queueSize = chunkLoader.getQueueSize();
|
||||
|
||||
final long now = System.currentTimeMillis();
|
||||
final long timeSince = (now - lastSaveStatPrinted) / 1000;
|
||||
final Integer printRateSecs = Integer.getInteger("printSaveStats");
|
||||
if (printRateSecs != null && timeSince >= printRateSecs) {
|
||||
final String timeStr = "/" + timeSince +"s";
|
||||
final long queuedSaves = chunkLoader.getQueuedSaves();
|
||||
long queuedDiff = queuedSaves - lastQueuedSaves;
|
||||
lastQueuedSaves = queuedSaves;
|
||||
|
||||
final long processedSaves = chunkLoader.getProcessedSaves();
|
||||
long processedDiff = processedSaves - lastProcessedSaves;
|
||||
lastProcessedSaves = processedSaves;
|
||||
|
||||
lastSaveStatPrinted = now;
|
||||
if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) {
|
||||
System.out.println("[Chunk Save Stats] " + world.worldData.getName() +
|
||||
" - Current: " + queueSize +
|
||||
" - Queued: " + queuedDiff + timeStr +
|
||||
" - Processed: " +processedDiff + timeStr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
|
||||
return false;
|
||||
}
|
||||
final int autoSaveLimit = world.paperConfig.maxAutoSaveChunksPerTick;
|
||||
// Paper end
|
||||
Iterator iterator = this.chunks.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Chunk chunk = (Chunk) iterator.next();
|
||||
// CraftBukkit end
|
||||
|
||||
if (flag) {
|
||||
this.saveChunkNOP(chunk);
|
||||
}
|
||||
|
||||
if (chunk.a(flag)) {
|
||||
this.saveChunk(chunk, false); // Spigot
|
||||
chunk.f(false);
|
||||
++i;
|
||||
if (!flag && i >= autoSaveLimit) { // Spigot - // Paper - Incremental Auto Save - cap max per tick
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.chunkLoader.c();
|
||||
}
|
||||
|
||||
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96;
|
||||
|
||||
public boolean unloadChunks() {
|
||||
if (!this.world.savingDisabled) {
|
||||
if (!this.unloadQueue.isEmpty()) {
|
||||
// Spigot start
|
||||
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||
activityAccountant.startActivity(0.5);
|
||||
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||
// Spigot end
|
||||
|
||||
LongIterator iterator = this.unloadQueue.iterator();
|
||||
|
||||
while (iterator.hasNext()) { // Spigot
|
||||
Long chunkKey = iterator.nextLong();
|
||||
iterator.remove(); // Spigot
|
||||
Chunk chunk = (Chunk) this.chunks.get(chunkKey);
|
||||
|
||||
if (chunk != null && chunk.d) {
|
||||
// CraftBukkit start - move unload logic to own method
|
||||
chunk.setShouldUnload(false); // Paper
|
||||
if (!unloadChunk(chunk, true)) {
|
||||
continue;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
// Spigot start
|
||||
if (this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
|
||||
break;
|
||||
}
|
||||
// Spigot end
|
||||
}
|
||||
}
|
||||
|
||||
activityAccountant.endActivity(); // Spigot
|
||||
}
|
||||
// Paper start - delayed chunk unloads
|
||||
long now = System.currentTimeMillis();
|
||||
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||
if (unloadAfter > 0) {
|
||||
//noinspection Convert2streamapi
|
||||
for (Chunk chunk : chunks.values()) {
|
||||
if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) {
|
||||
chunk.scheduledForUnload = null;
|
||||
unload(chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
this.chunkLoader.b();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public boolean unloadChunk(Chunk chunk, boolean save) {
|
||||
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
save = event.isSaveChunk();
|
||||
chunk.lightingQueue.processUnload(); // Paper
|
||||
|
||||
// Update neighbor counts
|
||||
for (int x = -2; x < 3; x++) {
|
||||
for (int z = -2; z < 3; z++) {
|
||||
if (x == 0 && z == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z);
|
||||
if (neighbor != null) {
|
||||
neighbor.setNeighborUnloaded(-x, -z);
|
||||
chunk.setNeighborUnloaded(x, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Moved from unloadChunks above
|
||||
chunk.removeEntities();
|
||||
if (save) {
|
||||
this.saveChunk(chunk, true); // Spigot
|
||||
this.saveChunkNOP(chunk);
|
||||
}
|
||||
this.chunks.remove(chunk.chunkKey);
|
||||
return true;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
public boolean e() {
|
||||
return !this.world.savingDisabled;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size();
|
||||
}
|
||||
|
||||
public List<BiomeBase.BiomeMeta> a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public BlockPosition a(World world, String s, BlockPosition blockposition, boolean flag) {
|
||||
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, flag);
|
||||
}
|
||||
|
||||
public boolean a(World world, String s, BlockPosition blockposition) {
|
||||
return this.chunkGenerator.a(world, s, blockposition);
|
||||
}
|
||||
|
||||
public int g() {
|
||||
return this.chunks.size();
|
||||
}
|
||||
|
||||
public boolean isLoaded(int i, int j) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
|
||||
}
|
||||
|
||||
public boolean e(int i, int j) {
|
||||
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j)) || this.chunkLoader.chunkExists(i, j);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,623 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue; // Paper
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
// Spigot start
|
||||
import java.util.function.Supplier;
|
||||
import org.spigotmc.SupplierUtils;
|
||||
// Spigot end
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Removes unneed synchronization (performance)
|
||||
*/
|
||||
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
|
||||
|
||||
private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>(); // Paper - Chunk queue improvements
|
||||
private final Object lock = new Object(); // Paper - Chunk queue improvements
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
private final Map<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = Maps.newConcurrentMap(); // Spigot
|
||||
// CraftBukkit
|
||||
// private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(Maps.newConcurrentMap());
|
||||
private final File d;
|
||||
private final DataConverterManager e;
|
||||
// private boolean f;
|
||||
// CraftBukkit
|
||||
private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot
|
||||
|
||||
public ChunkRegionLoader(File file, DataConverterManager dataconvertermanager) {
|
||||
this.d = file;
|
||||
this.e = dataconvertermanager;
|
||||
}
|
||||
|
||||
// Paper start
|
||||
private long queuedSaves = 0;
|
||||
private final java.util.concurrent.atomic.AtomicLong processedSaves = new java.util.concurrent.atomic.AtomicLong(0L);
|
||||
public int getQueueSize() { return queue.size(); }
|
||||
public long getQueuedSaves() { return queuedSaves; }
|
||||
public long getProcessedSaves() { return processedSaves.longValue(); }
|
||||
// Paper end
|
||||
|
||||
// CraftBukkit start - Add async variant, provide compatibility
|
||||
@Nullable
|
||||
public Chunk a(World world, int i, int j) throws IOException {
|
||||
world.timings.syncChunkLoadDataTimer.startTiming(); // Spigot
|
||||
Object[] data = loadChunk(world, i, j);
|
||||
world.timings.syncChunkLoadDataTimer.stopTiming(); // Spigot
|
||||
if (data != null) {
|
||||
Chunk chunk = (Chunk) data[0];
|
||||
NBTTagCompound nbttagcompound = (NBTTagCompound) data[1];
|
||||
loadEntities(chunk, nbttagcompound.getCompound("Level"), world);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object[] loadChunk(World world, int i, int j) throws IOException {
|
||||
// CraftBukkit end
|
||||
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
|
||||
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(chunkcoordintpair)); // Spigot
|
||||
|
||||
if (nbttagcompound == null) {
|
||||
// CraftBukkit start
|
||||
nbttagcompound = RegionFileCache.d(this.d, i, j);
|
||||
|
||||
if (nbttagcompound == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
nbttagcompound = this.e.a((DataConverterType) DataConverterTypes.CHUNK, nbttagcompound);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return this.a(world, i, j, nbttagcompound);
|
||||
}
|
||||
|
||||
public boolean chunkExists(int i, int j) {
|
||||
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
|
||||
Supplier<NBTTagCompound> nbttagcompound = this.b.get(chunkcoordintpair); // Spigot
|
||||
|
||||
return nbttagcompound != null ? true : RegionFileCache.chunkExists(this.d, i, j);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[]
|
||||
if (!nbttagcompound.hasKeyOfType("Level", 10)) {
|
||||
ChunkRegionLoader.a.error("Chunk file at {},{} is missing level data, skipping", Integer.valueOf(i), Integer.valueOf(j));
|
||||
return null;
|
||||
} else {
|
||||
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
|
||||
|
||||
if (!nbttagcompound1.hasKeyOfType("Sections", 9)) {
|
||||
ChunkRegionLoader.a.error("Chunk file at {},{} is missing block data, skipping", Integer.valueOf(i), Integer.valueOf(j));
|
||||
return null;
|
||||
} else {
|
||||
Chunk chunk = this.a(world, nbttagcompound1);
|
||||
|
||||
if (!chunk.a(i, j)) {
|
||||
ChunkRegionLoader.a.error("Chunk file at {},{} is in the wrong location; relocating. (Expected {}, {}, got {}, {})", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(chunk.locX), Integer.valueOf(chunk.locZ));
|
||||
nbttagcompound1.setInt("xPos", i);
|
||||
nbttagcompound1.setInt("zPos", j);
|
||||
|
||||
// CraftBukkit start - Have to move tile entities since we don't load them at this stage
|
||||
NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10);
|
||||
if (tileEntities != null) {
|
||||
for (int te = 0; te < tileEntities.size(); te++) {
|
||||
NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te);
|
||||
int x = tileEntity.getInt("x") - chunk.locX * 16;
|
||||
int z = tileEntity.getInt("z") - chunk.locZ * 16;
|
||||
tileEntity.setInt("x", i * 16 + x);
|
||||
tileEntity.setInt("z", j * 16 + z);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
chunk = this.a(world, nbttagcompound1);
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
Object[] data = new Object[2];
|
||||
data[0] = chunk;
|
||||
data[1] = nbttagcompound;
|
||||
return data;
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveChunk(World world, Chunk chunk, boolean unloaded) throws IOException, ExceptionWorldConflict { // Spigot
|
||||
world.checkSession();
|
||||
|
||||
try {
|
||||
NBTTagCompound nbttagcompound = new NBTTagCompound();
|
||||
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
||||
|
||||
nbttagcompound.set("Level", nbttagcompound1);
|
||||
nbttagcompound.setInt("DataVersion", 1343);
|
||||
|
||||
// Spigot start
|
||||
final long worldTime = world.getTime();
|
||||
final boolean worldHasSkyLight = world.worldProvider.m();
|
||||
saveEntities(nbttagcompound1, chunk, world);
|
||||
Supplier<NBTTagCompound> completion = new Supplier<NBTTagCompound>() {
|
||||
public NBTTagCompound get() {
|
||||
saveBody(nbttagcompound1, chunk, worldTime, worldHasSkyLight);
|
||||
return nbttagcompound;
|
||||
}
|
||||
};
|
||||
|
||||
this.a(chunk.k(), SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE));
|
||||
// Spigot end
|
||||
} catch (Exception exception) {
|
||||
ChunkRegionLoader.a.error("Failed to save chunk", exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
|
||||
// CraftBukkit
|
||||
// if (!this.c.contains(chunkcoordintpair))
|
||||
synchronized (lock) { // Paper - Chunk queue improvements
|
||||
this.b.put(chunkcoordintpair, nbttagcompound);
|
||||
}
|
||||
queuedSaves++; // Paper
|
||||
queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
|
||||
|
||||
FileIOThread.a().a(this);
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
// CraftBukkit start
|
||||
return this.processSaveQueueEntry(false);
|
||||
}
|
||||
|
||||
private /*synchronized*/ boolean processSaveQueueEntry(boolean logCompletion) { // Akarin - remove synchronization
|
||||
// CraftBukkit start
|
||||
// Paper start - Chunk queue improvements
|
||||
QueuedChunk chunk = queue.poll();
|
||||
if (chunk == null) {
|
||||
// Paper - end
|
||||
if (logCompletion) {
|
||||
// CraftBukkit end
|
||||
ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.d.getName());
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
|
||||
processedSaves.incrementAndGet(); // Paper
|
||||
|
||||
boolean flag;
|
||||
|
||||
try {
|
||||
// this.c.add(chunkcoordintpair);
|
||||
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(chunk.compoundSupplier); // Spigot // Paper
|
||||
// CraftBukkit
|
||||
|
||||
if (nbttagcompound != null) {
|
||||
int attempts = 0; Exception laste = null; while (attempts++ < 5) { // Paper
|
||||
try {
|
||||
this.b(chunkcoordintpair, nbttagcompound);
|
||||
laste = null; break; // Paper
|
||||
} catch (Exception exception) {
|
||||
//ChunkRegionLoader.a.error("Failed to save chunk", exception); // Paper
|
||||
laste = exception; // Paper
|
||||
}
|
||||
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} } // Paper
|
||||
if (laste != null) { com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste); MinecraftServer.LOGGER.error("Failed to save chunk", laste); } // Paper
|
||||
}
|
||||
synchronized (lock) { if (this.b.get(chunkcoordintpair) == chunk.compoundSupplier) { this.b.remove(chunkcoordintpair); } }// Paper - This will not equal if a newer version is still pending
|
||||
|
||||
flag = true;
|
||||
} finally {
|
||||
//this.b.remove(chunkcoordintpair, value); // CraftBukkit // Spigot // Paper
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
private void b(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException {
|
||||
// CraftBukkit start
|
||||
RegionFileCache.e(this.d, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound);
|
||||
|
||||
/*
|
||||
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
|
||||
dataoutputstream.close();
|
||||
*/
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public void b(World world, Chunk chunk) throws IOException {}
|
||||
|
||||
public void b() {}
|
||||
|
||||
public void c() {
|
||||
try {
|
||||
// this.f = true; // CraftBukkit
|
||||
|
||||
while (true) {
|
||||
if (this.processSaveQueueEntry(true)) { // CraftBukkit
|
||||
continue;
|
||||
}
|
||||
break; // CraftBukkit - Fix infinite loop when saving chunks
|
||||
}
|
||||
} finally {
|
||||
// this.f = false; // CraftBukkit
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void a(DataConverterManager dataconvertermanager) {
|
||||
dataconvertermanager.a(DataConverterTypes.CHUNK, new DataInspector() {
|
||||
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
|
||||
if (nbttagcompound.hasKeyOfType("Level", 10)) {
|
||||
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
|
||||
NBTTagList nbttaglist;
|
||||
int j;
|
||||
|
||||
if (nbttagcompound1.hasKeyOfType("Entities", 9)) {
|
||||
nbttaglist = nbttagcompound1.getList("Entities", 10);
|
||||
|
||||
for (j = 0; j < nbttaglist.size(); ++j) {
|
||||
nbttaglist.a(j, dataconverter.a(DataConverterTypes.ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
|
||||
}
|
||||
}
|
||||
|
||||
if (nbttagcompound1.hasKeyOfType("TileEntities", 9)) {
|
||||
nbttaglist = nbttagcompound1.getList("TileEntities", 10);
|
||||
|
||||
for (j = 0; j < nbttaglist.size(); ++j) {
|
||||
nbttaglist.a(j, dataconverter.a(DataConverterTypes.BLOCK_ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nbttagcompound;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void saveBody(NBTTagCompound nbttagcompound, Chunk chunk, long worldTime, boolean worldHasSkyLight) { // Spigot
|
||||
nbttagcompound.setInt("xPos", chunk.locX);
|
||||
nbttagcompound.setInt("zPos", chunk.locZ);
|
||||
nbttagcompound.setLong("LastUpdate", worldTime); // Spigot
|
||||
nbttagcompound.setIntArray("HeightMap", chunk.r());
|
||||
nbttagcompound.setBoolean("TerrainPopulated", chunk.isDone());
|
||||
nbttagcompound.setBoolean("LightPopulated", chunk.v());
|
||||
nbttagcompound.setLong("InhabitedTime", chunk.x());
|
||||
ChunkSection[] achunksection = chunk.getSections();
|
||||
NBTTagList nbttaglist = new NBTTagList();
|
||||
boolean flag = worldHasSkyLight; // Spigot
|
||||
ChunkSection[] achunksection1 = achunksection;
|
||||
int i = achunksection.length;
|
||||
|
||||
NBTTagCompound nbttagcompound1;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
ChunkSection chunksection = achunksection1[j];
|
||||
|
||||
if (chunksection != Chunk.a) {
|
||||
nbttagcompound1 = new NBTTagCompound();
|
||||
nbttagcompound1.setByte("Y", (byte) (chunksection.getYPosition() >> 4 & 255));
|
||||
byte[] abyte = new byte[4096];
|
||||
NibbleArray nibblearray = new NibbleArray();
|
||||
NibbleArray nibblearray1 = chunksection.getBlocks().exportData(abyte, nibblearray);
|
||||
|
||||
nbttagcompound1.setByteArray("Blocks", abyte);
|
||||
nbttagcompound1.setByteArray("Data", nibblearray.asBytes());
|
||||
if (nibblearray1 != null) {
|
||||
nbttagcompound1.setByteArray("Add", nibblearray1.asBytes());
|
||||
}
|
||||
|
||||
nbttagcompound1.setByteArray("BlockLight", chunksection.getEmittedLightArray().asBytes());
|
||||
if (flag) {
|
||||
nbttagcompound1.setByteArray("SkyLight", chunksection.getSkyLightArray().asBytes());
|
||||
} else {
|
||||
nbttagcompound1.setByteArray("SkyLight", new byte[chunksection.getEmittedLightArray().asBytes().length]);
|
||||
}
|
||||
|
||||
nbttaglist.add(nbttagcompound1);
|
||||
}
|
||||
}
|
||||
|
||||
nbttagcompound.set("Sections", nbttaglist);
|
||||
nbttagcompound.setByteArray("Biomes", chunk.getBiomeIndex());
|
||||
|
||||
// Spigot start - End this method here and split off entity saving to another method
|
||||
}
|
||||
|
||||
private static void saveEntities(NBTTagCompound nbttagcompound, Chunk chunk, World world) {
|
||||
int i;
|
||||
NBTTagCompound nbttagcompound1;
|
||||
// Spigot end
|
||||
|
||||
chunk.g(false);
|
||||
NBTTagList nbttaglist1 = new NBTTagList();
|
||||
|
||||
Iterator iterator;
|
||||
|
||||
List<Entity> toUpdate = new java.util.ArrayList<>(); // Paper
|
||||
for (i = 0; i < chunk.getEntitySlices().length; ++i) {
|
||||
iterator = chunk.getEntitySlices()[i].iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity = (Entity) iterator.next();
|
||||
// Paper start
|
||||
if ((int)Math.floor(entity.locX) >> 4 != chunk.locX || (int)Math.floor(entity.locZ) >> 4 != chunk.locZ) {
|
||||
LogManager.getLogger().warn(entity + " is not in this chunk, skipping save. This a bug fix to a vanilla bug. Do not report this to PaperMC please.");
|
||||
toUpdate.add(entity);
|
||||
continue;
|
||||
}
|
||||
if (entity.dead) {
|
||||
continue;
|
||||
}
|
||||
// Paper end
|
||||
|
||||
nbttagcompound1 = new NBTTagCompound();
|
||||
if (entity.d(nbttagcompound1)) {
|
||||
chunk.g(true);
|
||||
nbttaglist1.add(nbttagcompound1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper start - move entities to the correct chunk
|
||||
for (Entity entity : toUpdate) {
|
||||
world.entityJoinedWorld(entity, false);
|
||||
}
|
||||
// Paper end
|
||||
|
||||
nbttagcompound.set("Entities", nbttaglist1);
|
||||
NBTTagList nbttaglist2 = new NBTTagList();
|
||||
|
||||
iterator = chunk.getTileEntities().values().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
TileEntity tileentity = (TileEntity) iterator.next();
|
||||
|
||||
nbttagcompound1 = tileentity.save(new NBTTagCompound());
|
||||
nbttaglist2.add(nbttagcompound1);
|
||||
}
|
||||
|
||||
nbttagcompound.set("TileEntities", nbttaglist2);
|
||||
List list = world.a(chunk, false);
|
||||
|
||||
if (list != null) {
|
||||
long k = world.getTime();
|
||||
NBTTagList nbttaglist3 = new NBTTagList();
|
||||
Iterator iterator1 = list.iterator();
|
||||
|
||||
while (iterator1.hasNext()) {
|
||||
NextTickListEntry nextticklistentry = (NextTickListEntry) iterator1.next();
|
||||
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
|
||||
MinecraftKey minecraftkey = (MinecraftKey) Block.REGISTRY.b(nextticklistentry.a());
|
||||
|
||||
nbttagcompound2.setString("i", minecraftkey == null ? "" : minecraftkey.toString());
|
||||
nbttagcompound2.setInt("x", nextticklistentry.a.getX());
|
||||
nbttagcompound2.setInt("y", nextticklistentry.a.getY());
|
||||
nbttagcompound2.setInt("z", nextticklistentry.a.getZ());
|
||||
nbttagcompound2.setInt("t", (int) (nextticklistentry.b - k));
|
||||
nbttagcompound2.setInt("p", nextticklistentry.c);
|
||||
nbttaglist3.add(nbttagcompound2);
|
||||
}
|
||||
|
||||
nbttagcompound.set("TileTicks", nbttaglist3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Chunk a(World world, NBTTagCompound nbttagcompound) {
|
||||
int i = nbttagcompound.getInt("xPos");
|
||||
int j = nbttagcompound.getInt("zPos");
|
||||
Chunk chunk = new Chunk(world, i, j);
|
||||
|
||||
chunk.a(nbttagcompound.getIntArray("HeightMap"));
|
||||
chunk.d(nbttagcompound.getBoolean("TerrainPopulated"));
|
||||
chunk.e(nbttagcompound.getBoolean("LightPopulated"));
|
||||
chunk.c(nbttagcompound.getLong("InhabitedTime"));
|
||||
NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10);
|
||||
boolean flag = true;
|
||||
ChunkSection[] achunksection = new ChunkSection[16];
|
||||
boolean flag1 = world.worldProvider.m();
|
||||
|
||||
for (int k = 0; k < nbttaglist.size(); ++k) {
|
||||
NBTTagCompound nbttagcompound1 = nbttaglist.get(k);
|
||||
byte b0 = nbttagcompound1.getByte("Y");
|
||||
ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, world.chunkPacketBlockController.getPredefinedBlockData(chunk, b0)); // Paper - Anti-Xray - Add predefined block data
|
||||
byte[] abyte = nbttagcompound1.getByteArray("Blocks");
|
||||
NibbleArray nibblearray = new NibbleArray(nbttagcompound1.getByteArray("Data"));
|
||||
NibbleArray nibblearray1 = nbttagcompound1.hasKeyOfType("Add", 7) ? new NibbleArray(nbttagcompound1.getByteArray("Add")) : null;
|
||||
|
||||
chunksection.getBlocks().a(abyte, nibblearray, nibblearray1);
|
||||
chunksection.a(new NibbleArray(nbttagcompound1.getByteArray("BlockLight")));
|
||||
if (flag1) {
|
||||
chunksection.b(new NibbleArray(nbttagcompound1.getByteArray("SkyLight")));
|
||||
}
|
||||
|
||||
chunksection.recalcBlockCounts();
|
||||
achunksection[b0] = chunksection;
|
||||
}
|
||||
|
||||
chunk.a(achunksection);
|
||||
if (nbttagcompound.hasKeyOfType("Biomes", 7)) {
|
||||
chunk.a(nbttagcompound.getByteArray("Biomes"));
|
||||
}
|
||||
|
||||
// CraftBukkit start - End this method here and split off entity loading to another method
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public void loadEntities(Chunk chunk, NBTTagCompound nbttagcompound, World world) {
|
||||
// CraftBukkit end
|
||||
world.timings.syncChunkLoadNBTTimer.startTiming(); // Spigot
|
||||
NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10);
|
||||
|
||||
for (int l = 0; l < nbttaglist1.size(); ++l) {
|
||||
NBTTagCompound nbttagcompound2 = nbttaglist1.get(l);
|
||||
|
||||
a(nbttagcompound2, world, chunk);
|
||||
chunk.g(true);
|
||||
}
|
||||
NBTTagList nbttaglist2 = nbttagcompound.getList("TileEntities", 10);
|
||||
|
||||
for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) {
|
||||
NBTTagCompound nbttagcompound3 = nbttaglist2.get(i1);
|
||||
TileEntity tileentity = TileEntity.create(world, nbttagcompound3);
|
||||
|
||||
if (tileentity != null) {
|
||||
chunk.a(tileentity);
|
||||
}
|
||||
}
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("TileTicks", 9)) {
|
||||
NBTTagList nbttaglist3 = nbttagcompound.getList("TileTicks", 10);
|
||||
|
||||
for (int j1 = 0; j1 < nbttaglist3.size(); ++j1) {
|
||||
NBTTagCompound nbttagcompound4 = nbttaglist3.get(j1);
|
||||
Block block;
|
||||
|
||||
if (nbttagcompound4.hasKeyOfType("i", 8)) {
|
||||
block = Block.getByName(nbttagcompound4.getString("i"));
|
||||
} else {
|
||||
block = Block.getById(nbttagcompound4.getInt("i"));
|
||||
}
|
||||
|
||||
world.b(new BlockPosition(nbttagcompound4.getInt("x"), nbttagcompound4.getInt("y"), nbttagcompound4.getInt("z")), block, nbttagcompound4.getInt("t"), nbttagcompound4.getInt("p"));
|
||||
}
|
||||
}
|
||||
world.timings.syncChunkLoadNBTTimer.stopTiming(); // Spigot
|
||||
|
||||
// return chunk; // CraftBukkit
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Entity a(NBTTagCompound nbttagcompound, World world, Chunk chunk) {
|
||||
Entity entity = a(nbttagcompound, world);
|
||||
|
||||
if (entity == null) {
|
||||
return null;
|
||||
} else {
|
||||
chunk.a(entity);
|
||||
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
|
||||
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
Entity entity1 = a(nbttaglist.get(i), world, chunk);
|
||||
|
||||
if (entity1 != null) {
|
||||
entity1.a(entity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
// CraftBukkit start
|
||||
public static Entity a(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag) {
|
||||
return spawnEntity(nbttagcompound, world, d0, d1, d2, flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||
}
|
||||
|
||||
public static Entity spawnEntity(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
|
||||
// CraftBukkit end
|
||||
Entity entity = a(nbttagcompound, world);
|
||||
|
||||
if (entity == null) {
|
||||
return null;
|
||||
} else {
|
||||
entity.setPositionRotation(d0, d1, d2, entity.yaw, entity.pitch);
|
||||
if (flag && !world.addEntity(entity, spawnReason)) { // CraftBukkit
|
||||
return null;
|
||||
} else {
|
||||
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
|
||||
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
Entity entity1 = a(nbttaglist.get(i), world, d0, d1, d2, flag);
|
||||
|
||||
if (entity1 != null) {
|
||||
entity1.a(entity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected static Entity a(NBTTagCompound nbttagcompound, World world) {
|
||||
try {
|
||||
return EntityTypes.a(nbttagcompound, world);
|
||||
} catch (RuntimeException runtimeexception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
public static void a(Entity entity, World world) {
|
||||
a(entity, world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
|
||||
}
|
||||
|
||||
public static void a(Entity entity, World world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
|
||||
if (!entity.valid && world.addEntity(entity, reason) && entity.isVehicle()) { // Paper
|
||||
// CraftBukkit end
|
||||
Iterator iterator = entity.bF().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity1 = (Entity) iterator.next();
|
||||
|
||||
a(entity1, world);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Entity a(NBTTagCompound nbttagcompound, World world, boolean flag) {
|
||||
Entity entity = a(nbttagcompound, world);
|
||||
|
||||
if (entity == null) {
|
||||
return null;
|
||||
} else if (flag && !world.addEntity(entity)) {
|
||||
return null;
|
||||
} else {
|
||||
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
|
||||
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
|
||||
|
||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||
Entity entity1 = a(nbttaglist.get(i), world, flag);
|
||||
|
||||
if (entity1 != null) {
|
||||
entity1.a(entity, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// Paper start - Chunk queue improvements
|
||||
private static class QueuedChunk {
|
||||
public ChunkCoordIntPair coords;
|
||||
public Supplier<NBTTagCompound> compoundSupplier;
|
||||
|
||||
public QueuedChunk(ChunkCoordIntPair coords, Supplier<NBTTagCompound> compoundSupplier) {
|
||||
this.coords = coords;
|
||||
this.compoundSupplier = compoundSupplier;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
115
sources/src/main/java/net/minecraft/server/ContainerHorse.java
Normal file
115
sources/src/main/java/net/minecraft/server/ContainerHorse.java
Normal file
@@ -0,0 +1,115 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
// CraftBukkit end
|
||||
|
||||
public class ContainerHorse extends Container {
|
||||
|
||||
private final IInventory a;
|
||||
private final EntityHorseAbstract f;
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity;
|
||||
PlayerInventory player;
|
||||
|
||||
@Override
|
||||
public InventoryView getBukkitView() {
|
||||
if (bukkitEntity != null) {
|
||||
return bukkitEntity;
|
||||
}
|
||||
|
||||
return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), a.getOwner().getInventory(), this);
|
||||
}
|
||||
|
||||
public ContainerHorse(IInventory iinventory, final IInventory iinventory1, final EntityHorseAbstract entityhorseabstract, EntityHuman entityhuman) {
|
||||
player = (PlayerInventory) iinventory;
|
||||
// CraftBukkit end
|
||||
this.a = iinventory1;
|
||||
this.f = entityhorseabstract;
|
||||
boolean flag = true;
|
||||
|
||||
iinventory1.startOpen(entityhuman);
|
||||
boolean flag1 = true;
|
||||
|
||||
this.a(new Slot(iinventory1, 0, 8, 18) {
|
||||
public boolean isAllowed(ItemStack itemstack) {
|
||||
return itemstack.getItem() == Items.SADDLE && !this.hasItem() && entityhorseabstract.dF();
|
||||
}
|
||||
});
|
||||
this.a(new Slot(iinventory1, 1, 8, 36) {
|
||||
public boolean isAllowed(ItemStack itemstack) {
|
||||
return entityhorseabstract.f(itemstack);
|
||||
}
|
||||
|
||||
public int getMaxStackSize() {
|
||||
return 1;
|
||||
}
|
||||
});
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (entityhorseabstract instanceof EntityHorseChestedAbstract && ((EntityHorseChestedAbstract) entityhorseabstract).isCarryingChest()) {
|
||||
for (i = 0; i < 3; ++i) {
|
||||
for (j = 0; j < ((EntityHorseChestedAbstract) entityhorseabstract).dt(); ++j) {
|
||||
this.a(new Slot(iinventory1, 2 + j + i * ((EntityHorseChestedAbstract) entityhorseabstract).dt(), 80 + j * 18, 18 + i * 18));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i) {
|
||||
for (j = 0; j < 9; ++j) {
|
||||
this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 9; ++i) {
|
||||
this.a(new Slot(iinventory, i, 8 + i * 18, 142));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean canUse(EntityHuman entityhuman) {
|
||||
return this.a.a(entityhuman) && this.f.isAlive() && this.f.valid && this.f.g((Entity) entityhuman) < 8.0F; // NeonPaper! - Fix MC-161754
|
||||
}
|
||||
|
||||
public ItemStack shiftClick(EntityHuman entityhuman, int i) {
|
||||
ItemStack itemstack = ItemStack.a;
|
||||
Slot slot = (Slot) this.slots.get(i);
|
||||
|
||||
if (slot != null && slot.hasItem()) {
|
||||
ItemStack itemstack1 = slot.getItem();
|
||||
|
||||
itemstack = itemstack1.cloneItemStack();
|
||||
if (i < this.a.getSize()) {
|
||||
if (!this.a(itemstack1, this.a.getSize(), this.slots.size(), true)) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
} else if (this.getSlot(1).isAllowed(itemstack1) && !this.getSlot(1).hasItem()) {
|
||||
if (!this.a(itemstack1, 1, 2, false)) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
} else if (this.getSlot(0).isAllowed(itemstack1)) {
|
||||
if (!this.a(itemstack1, 0, 1, false)) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
} else if (this.a.getSize() <= 2 || !this.a(itemstack1, 2, this.a.getSize(), false)) {
|
||||
return ItemStack.a;
|
||||
}
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
slot.set(ItemStack.a);
|
||||
} else {
|
||||
slot.f();
|
||||
}
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
public void b(EntityHuman entityhuman) {
|
||||
super.b(entityhuman);
|
||||
this.a.closeContainer(entityhuman);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,868 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import java.util.List;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.TreeType;
|
||||
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||
import org.bukkit.event.block.BlockDispenseEvent;
|
||||
import org.bukkit.event.world.StructureGrowEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class DispenserRegistry {
|
||||
|
||||
public static final PrintStream a = System.out;
|
||||
private static boolean c;
|
||||
public static boolean b;
|
||||
private static final Logger d = LogManager.getLogger();
|
||||
|
||||
public static boolean a() {
|
||||
return DispenserRegistry.c;
|
||||
}
|
||||
|
||||
static void b() {
|
||||
BlockDispenser.REGISTRY.a(Items.ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entitytippedarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.TIPPED_ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entitytippedarrow.a(itemstack);
|
||||
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entitytippedarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPECTRAL_ARROW, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
|
||||
entityspectralarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
|
||||
return entityspectralarrow;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.EGG, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SNOWBALL, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.EXPERIENCE_BOTTLE, new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
|
||||
return new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPLASH_POTION, new IDispenseBehavior() {
|
||||
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
|
||||
return (new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
|
||||
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
}).a(isourceblock, itemstack);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.LINGERING_POTION, new IDispenseBehavior() {
|
||||
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
|
||||
return (new DispenseBehaviorProjectile() {
|
||||
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
|
||||
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
|
||||
}
|
||||
|
||||
protected float a() {
|
||||
return super.a() * 0.5F;
|
||||
}
|
||||
|
||||
protected float getPower() {
|
||||
return super.getPower() * 1.25F;
|
||||
}
|
||||
}).a(isourceblock, itemstack);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SPAWN_EGG, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
|
||||
double d1 = (double) ((float) (isourceblock.getBlockPosition().getY() + enumdirection.getAdjacentY()) + 0.2F);
|
||||
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
|
||||
// Entity entity = ItemMonsterEgg.a(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), d0, d1, d2);
|
||||
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
|
||||
|
||||
Entity entity = ItemMonsterEgg.spawnCreature(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG);
|
||||
|
||||
if (entity instanceof EntityLiving && itemstack.hasName()) {
|
||||
entity.setCustomName(itemstack.getName());
|
||||
}
|
||||
|
||||
ItemMonsterEgg.a(isourceblock.getWorld(), (EntityHuman) null, itemstack, entity);
|
||||
// itemstack.subtract(1);// Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FIREWORKS, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
|
||||
double d1 = (double) ((float) isourceblock.getBlockPosition().getY() + 0.2F);
|
||||
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
|
||||
EntityFireworks entityfireworks = new EntityFireworks(isourceblock.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1);
|
||||
|
||||
isourceblock.getWorld().addEntity(entityfireworks);
|
||||
// itemstack.subtract(1); // Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1004, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FIRE_CHARGE, new DispenseBehaviorItem() {
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
IPosition iposition = BlockDispenser.a(isourceblock);
|
||||
double d0 = iposition.getX() + (double) ((float) enumdirection.getAdjacentX() * 0.3F);
|
||||
double d1 = iposition.getY() + (double) ((float) enumdirection.getAdjacentY() * 0.3F);
|
||||
double d2 = iposition.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 0.3F);
|
||||
World world = isourceblock.getWorld();
|
||||
Random random = world.random;
|
||||
double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentX();
|
||||
double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY();
|
||||
double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ();
|
||||
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntitySmallFireball fireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
|
||||
fireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity());
|
||||
|
||||
world.addEntity(fireball);
|
||||
// itemstack.subtract(1); // Handled during event processing
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1018, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.aH, new DispenserRegistry.a(EntityBoat.EnumBoatType.OAK));
|
||||
BlockDispenser.REGISTRY.a(Items.aI, new DispenserRegistry.a(EntityBoat.EnumBoatType.SPRUCE));
|
||||
BlockDispenser.REGISTRY.a(Items.aJ, new DispenserRegistry.a(EntityBoat.EnumBoatType.BIRCH));
|
||||
BlockDispenser.REGISTRY.a(Items.aK, new DispenserRegistry.a(EntityBoat.EnumBoatType.JUNGLE));
|
||||
BlockDispenser.REGISTRY.a(Items.aM, new DispenserRegistry.a(EntityBoat.EnumBoatType.DARK_OAK));
|
||||
BlockDispenser.REGISTRY.a(Items.aL, new DispenserRegistry.a(EntityBoat.EnumBoatType.ACACIA));
|
||||
DispenseBehaviorItem dispensebehavioritem = new DispenseBehaviorItem() {
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
ItemBucket itembucket = (ItemBucket) itemstack.getItem();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
// CraftBukkit start
|
||||
World world = isourceblock.getWorld();
|
||||
int x = blockposition.getX();
|
||||
int y = blockposition.getY();
|
||||
int z = blockposition.getZ();
|
||||
if (world.isEmpty(blockposition) || !world.getType(blockposition).getMaterial().isBuildable()) {
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem();
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (itembucket.a((EntityHuman) null, isourceblock.getWorld(), blockposition)) {
|
||||
// CraftBukkit start - Handle stacked buckets
|
||||
Item item = Items.BUCKET;
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
itemstack.setItem(Items.BUCKET);
|
||||
itemstack.setCount(1);
|
||||
} else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
|
||||
this.b.a(isourceblock, new ItemStack(item));
|
||||
}
|
||||
// CraftBukkit end
|
||||
return itemstack;
|
||||
} else {
|
||||
return this.b.a(isourceblock, itemstack);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BlockDispenser.REGISTRY.a(Items.LAVA_BUCKET, dispensebehavioritem);
|
||||
BlockDispenser.REGISTRY.a(Items.WATER_BUCKET, dispensebehavioritem);
|
||||
BlockDispenser.REGISTRY.a(Items.BUCKET, new DispenseBehaviorItem() {
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
Material material = iblockdata.getMaterial();
|
||||
Item item;
|
||||
|
||||
if (Material.WATER.equals(material) && block instanceof BlockFluids && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0) {
|
||||
item = Items.WATER_BUCKET;
|
||||
} else {
|
||||
if (!Material.LAVA.equals(material) || !(block instanceof BlockFluids) || ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() != 0) {
|
||||
return super.b(isourceblock, itemstack);
|
||||
}
|
||||
|
||||
item = Items.LAVA_BUCKET;
|
||||
}
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
world.setAir(blockposition);
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
return new ItemStack(item);
|
||||
} else {
|
||||
if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
|
||||
this.b.a(isourceblock, new ItemStack(item));
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.FLINT_AND_STEEL, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
if (world.isEmpty(blockposition)) {
|
||||
// CraftBukkit start - Ignition by dispensing flint and steel
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()).isCancelled()) {
|
||||
world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
|
||||
if (itemstack.isDamaged(1, world.random, (EntityPlayer) null)) {
|
||||
itemstack.setCount(0);
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
} else if (world.getType(blockposition).getBlock() == Blocks.TNT) {
|
||||
Blocks.TNT.postBreak(world, blockposition, Blocks.TNT.getBlockData().set(BlockTNT.EXPLODE, Boolean.valueOf(true)));
|
||||
world.setAir(blockposition);
|
||||
} else {
|
||||
this.b = false;
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.DYE, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
this.b = true;
|
||||
if (EnumColor.WHITE == EnumColor.fromInvColorIndex(itemstack.getData())) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
world.captureTreeGeneration = true;
|
||||
// CraftBukkit end
|
||||
|
||||
if (ItemDye.a(itemstack, world, blockposition)) {
|
||||
if (!world.isClientSide) {
|
||||
world.triggerEffect(2005, blockposition, 0);
|
||||
}
|
||||
} else {
|
||||
this.b = false;
|
||||
}
|
||||
// CraftBukkit start
|
||||
world.captureTreeGeneration = false;
|
||||
if (world.capturedBlockStates.size() > 0) {
|
||||
TreeType treeType = BlockSapling.treeType;
|
||||
BlockSapling.treeType = null;
|
||||
Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ());
|
||||
List<org.bukkit.block.BlockState> blocks = (List<org.bukkit.block.BlockState>) world.capturedBlockStates.clone();
|
||||
world.capturedBlockStates.clear();
|
||||
StructureGrowEvent structureEvent = null;
|
||||
if (treeType != null) {
|
||||
structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks);
|
||||
org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
|
||||
}
|
||||
if (structureEvent == null || !structureEvent.isCancelled()) {
|
||||
for (org.bukkit.block.BlockState blockstate : blocks) {
|
||||
blockstate.update(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
return itemstack;
|
||||
} else {
|
||||
return super.b(isourceblock, itemstack);
|
||||
}
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.TNT), new DispenseBehaviorItem() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
// EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
|
||||
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null);
|
||||
// CraftBukkit end
|
||||
|
||||
world.addEntity(entitytntprimed);
|
||||
world.a((EntityHuman) null, entitytntprimed.locX, entitytntprimed.locY, entitytntprimed.locZ, SoundEffects.hW, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||
// itemstack.subtract(1); // CraftBukkit - handled above
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Items.SKULL, new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
BlockSkull blockskull = Blocks.SKULL;
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
if (world.isEmpty(blockposition) && blockskull.b(world, blockposition, itemstack)) {
|
||||
if (!world.isClientSide) {
|
||||
world.setTypeAndData(blockposition, blockskull.getBlockData().set(BlockSkull.FACING, EnumDirection.UP), 3);
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntitySkull) {
|
||||
if (itemstack.getData() == 3) {
|
||||
GameProfile gameprofile = null;
|
||||
|
||||
if (itemstack.hasTag()) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("SkullOwner", 10)) {
|
||||
gameprofile = GameProfileSerializer.deserialize(nbttagcompound.getCompound("SkullOwner"));
|
||||
} else if (nbttagcompound.hasKeyOfType("SkullOwner", 8)) {
|
||||
String s = nbttagcompound.getString("SkullOwner");
|
||||
|
||||
if (!UtilColor.b(s)) {
|
||||
gameprofile = new GameProfile((UUID) null, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
((TileEntitySkull) tileentity).setGameProfile(gameprofile);
|
||||
} else {
|
||||
((TileEntitySkull) tileentity).setSkullType(itemstack.getData());
|
||||
}
|
||||
|
||||
((TileEntitySkull) tileentity).setRotation(enumdirection.opposite().get2DRotationValue() * 4);
|
||||
Blocks.SKULL.a(world, blockposition, (TileEntitySkull) tileentity);
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
} else if (ItemArmor.a(isourceblock, itemstack).isEmpty()) {
|
||||
this.b = false;
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.PUMPKIN), new DispenserRegistry.b() {
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
World world = isourceblock.getWorld();
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
|
||||
BlockPumpkin blockpumpkin = (BlockPumpkin) Blocks.PUMPKIN;
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = true;
|
||||
if (world.isEmpty(blockposition) && blockpumpkin.b(world, blockposition)) {
|
||||
if (!world.isClientSide) {
|
||||
world.setTypeAndData(blockposition, blockpumpkin.getBlockData(), 3);
|
||||
}
|
||||
|
||||
itemstack.subtract(1);
|
||||
} else {
|
||||
ItemStack itemstack1 = ItemArmor.a(isourceblock, itemstack);
|
||||
|
||||
if (itemstack1.isEmpty()) {
|
||||
this.b = false;
|
||||
}
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
});
|
||||
EnumColor[] aenumcolor = EnumColor.values();
|
||||
int i = aenumcolor.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumColor enumcolor = aenumcolor[j];
|
||||
|
||||
BlockDispenser.REGISTRY.a(Item.getItemOf(BlockShulkerBox.a(enumcolor)), new DispenserRegistry.c(null));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void c() {
|
||||
if (!DispenserRegistry.c) {
|
||||
DispenserRegistry.c = true;
|
||||
d();
|
||||
SoundEffect.b();
|
||||
Block.w();
|
||||
BlockFire.e();
|
||||
MobEffectList.k();
|
||||
Enchantment.g();
|
||||
Item.t();
|
||||
PotionRegistry.b();
|
||||
PotionBrewer.a();
|
||||
EntityTypes.c();
|
||||
BiomeBase.q();
|
||||
b();
|
||||
if (!CraftingManager.init()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in recipes!");
|
||||
}
|
||||
|
||||
StatisticList.a();
|
||||
if (DispenserRegistry.d.isDebugEnabled()) {
|
||||
if ((new AdvancementDataWorld((File) null)).b()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in advancements!");
|
||||
}
|
||||
|
||||
if (!LootTables.b()) {
|
||||
DispenserRegistry.b = true;
|
||||
DispenserRegistry.d.error("Errors with built-in loot tables");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void d() {
|
||||
if (DispenserRegistry.d.isDebugEnabled()) {
|
||||
System.setErr(new DebugOutputStream("STDERR", System.err));
|
||||
System.setOut(new DebugOutputStream("STDOUT", DispenserRegistry.a));
|
||||
} else {
|
||||
System.setErr(new RedirectStream("STDERR", System.err));
|
||||
System.setOut(new RedirectStream("STDOUT", DispenserRegistry.a));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class c extends DispenserRegistry.b {
|
||||
|
||||
private c() {}
|
||||
|
||||
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
Block block = Block.asBlock(itemstack.getItem());
|
||||
World world = isourceblock.getWorld();
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
|
||||
// CraftBukkit start
|
||||
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.b = world.a(block, blockposition, false, EnumDirection.DOWN, (Entity) null);
|
||||
if (this.b) {
|
||||
EnumDirection enumdirection1 = world.isEmpty(blockposition.down()) ? enumdirection : EnumDirection.UP;
|
||||
IBlockData iblockdata = block.getBlockData().set(BlockShulkerBox.a, enumdirection1);
|
||||
// Dionysus start - fix Dispenser crashes
|
||||
boolean wasPlaced = world.setTypeUpdate(blockposition, iblockdata);
|
||||
if (!wasPlaced) {
|
||||
return itemstack;
|
||||
}
|
||||
// Dionysus end
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
|
||||
if (itemstack1.hasTag()) {
|
||||
((TileEntityShulkerBox) tileentity).e(itemstack1.getTag().getCompound("BlockEntityTag"));
|
||||
}
|
||||
|
||||
if (itemstack1.hasName()) {
|
||||
((TileEntityShulkerBox) tileentity).setCustomName(itemstack1.getName());
|
||||
}
|
||||
|
||||
world.updateAdjacentComparators(blockposition, iblockdata.getBlock());
|
||||
}
|
||||
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
c(Object object) {
|
||||
this();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class b extends DispenseBehaviorItem {
|
||||
|
||||
protected boolean b = true;
|
||||
|
||||
public b() {}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(this.b ? 1000 : 1001, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static class a extends DispenseBehaviorItem {
|
||||
|
||||
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
|
||||
private final EntityBoat.EnumBoatType c;
|
||||
|
||||
public a(EntityBoat.EnumBoatType entityboat_enumboattype) {
|
||||
this.c = entityboat_enumboattype;
|
||||
}
|
||||
|
||||
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
|
||||
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
|
||||
World world = isourceblock.getWorld();
|
||||
double d0 = isourceblock.getX() + (double) ((float) enumdirection.getAdjacentX() * 1.125F);
|
||||
double d1 = isourceblock.getY() + (double) ((float) enumdirection.getAdjacentY() * 1.125F);
|
||||
double d2 = isourceblock.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 1.125F);
|
||||
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
|
||||
Material material = world.getType(blockposition).getMaterial();
|
||||
double d3;
|
||||
|
||||
if (Material.WATER.equals(material)) {
|
||||
d3 = 1.0D;
|
||||
} else {
|
||||
if (!Material.AIR.equals(material) || !Material.WATER.equals(world.getType(blockposition.down()).getMaterial())) {
|
||||
return this.b.a(isourceblock, itemstack);
|
||||
}
|
||||
|
||||
d3 = 0.0D;
|
||||
}
|
||||
|
||||
// EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2);
|
||||
// CraftBukkit start
|
||||
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
|
||||
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
|
||||
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
|
||||
|
||||
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2));
|
||||
if (!BlockDispenser.eventFired) {
|
||||
world.getServer().getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (event.isCancelled()) {
|
||||
itemstack.add(1);
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
if (!event.getItem().equals(craftItem)) {
|
||||
itemstack.add(1);
|
||||
// Chain to handler for new item
|
||||
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
|
||||
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
|
||||
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
|
||||
idispensebehavior.a(isourceblock, eventStack);
|
||||
return itemstack;
|
||||
}
|
||||
}
|
||||
|
||||
EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
|
||||
// CraftBukkit end
|
||||
|
||||
entityboat.setType(this.c);
|
||||
entityboat.yaw = enumdirection.l();
|
||||
if (!world.addEntity(entityboat)) itemstack.add(1); // CraftBukkit
|
||||
// itemstack.subtract(1); // CraftBukkit - handled during event processing
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
protected void a(ISourceBlock isourceblock) {
|
||||
isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,8 @@ import java.util.Random;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Expose private members<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 1) Expose private members (cause mixin errors)
|
||||
*/
|
||||
public class EnchantmentManager {
|
||||
|
||||
@@ -120,6 +118,7 @@ public class EnchantmentManager {
|
||||
EnchantmentManager.a.a = 0;
|
||||
EnchantmentManager.a.b = damagesource;
|
||||
a(EnchantmentManager.a, iterable);
|
||||
EnchantmentManager.a.b = null; // Reaper - Fix MC-128547
|
||||
return EnchantmentManager.a.a;
|
||||
}
|
||||
|
||||
@@ -146,6 +145,11 @@ public class EnchantmentManager {
|
||||
if (entity instanceof EntityHuman) {
|
||||
a(EnchantmentManager.c, entityliving.getItemInMainHand());
|
||||
}
|
||||
|
||||
// Reaper start - Fix MC-128547
|
||||
EnchantmentManager.c.b = null;
|
||||
EnchantmentManager.c.a = null;
|
||||
// Reaper end
|
||||
|
||||
}
|
||||
|
||||
@@ -159,7 +163,10 @@ public class EnchantmentManager {
|
||||
if (entityliving instanceof EntityHuman) {
|
||||
a(EnchantmentManager.d, entityliving.getItemInMainHand());
|
||||
}
|
||||
|
||||
// Reaper start - Fix MC-128547
|
||||
EnchantmentManager.d.b = null;
|
||||
EnchantmentManager.d.a = null;
|
||||
// Reaper end
|
||||
}
|
||||
|
||||
public static int a(Enchantment enchantment, EntityLiving entityliving) {
|
||||
|
||||
2985
sources/src/main/java/net/minecraft/server/Entity.java
Normal file
2985
sources/src/main/java/net/minecraft/server/Entity.java
Normal file
File diff suppressed because it is too large
Load Diff
476
sources/src/main/java/net/minecraft/server/EntityEnderman.java
Normal file
476
sources/src/main/java/net/minecraft/server/EntityEnderman.java
Normal file
@@ -0,0 +1,476 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.EndermanEscapeEvent;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.event.entity.EntityTargetEvent;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class EntityEnderman extends EntityMonster {
|
||||
|
||||
private static final UUID a = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0");
|
||||
private static final AttributeModifier b = (new AttributeModifier(EntityEnderman.a, "Attacking speed boost", 0.15000000596046448D, 0)).a(false);
|
||||
private static final Set<Block> c = Sets.newIdentityHashSet();
|
||||
private static final DataWatcherObject<Optional<IBlockData>> bx = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.g);
|
||||
private static final DataWatcherObject<Boolean> by = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.h);
|
||||
private int bz;
|
||||
private int bA;
|
||||
|
||||
public EntityEnderman(World world) {
|
||||
super(world);
|
||||
this.setSize(0.6F, 2.9F);
|
||||
this.P = 1.0F;
|
||||
this.a(PathType.WATER, -1.0F);
|
||||
}
|
||||
|
||||
protected void r() {
|
||||
this.goalSelector.a(0, new PathfinderGoalFloat(this));
|
||||
this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, 1.0D, false));
|
||||
this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D, 0.0F));
|
||||
this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
|
||||
this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
|
||||
this.goalSelector.a(10, new EntityEnderman.PathfinderGoalEndermanPlaceBlock(this));
|
||||
this.goalSelector.a(11, new EntityEnderman.PathfinderGoalEndermanPickupBlock(this));
|
||||
this.targetSelector.a(1, new EntityEnderman.PathfinderGoalPlayerWhoLookedAtTarget(this));
|
||||
this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0]));
|
||||
this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityEndermite.class, 10, true, false, new Predicate() {
|
||||
public boolean a(@Nullable EntityEndermite entityendermite) {
|
||||
return entityendermite.p();
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable Object object) {
|
||||
return this.a((EntityEndermite) object);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
protected void initAttributes() {
|
||||
super.initAttributes();
|
||||
this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D);
|
||||
this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D);
|
||||
this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(7.0D);
|
||||
this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(64.0D);
|
||||
}
|
||||
|
||||
public void setGoalTarget(@Nullable EntityLiving entityliving) {
|
||||
// CraftBukkit start - fire event
|
||||
setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true);
|
||||
}
|
||||
|
||||
// Paper start
|
||||
private boolean tryEscape(EndermanEscapeEvent.Reason reason) {
|
||||
return new EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent();
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@Override
|
||||
public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) {
|
||||
if (!super.setGoalTarget(entityliving, reason, fireEvent)) {
|
||||
return false;
|
||||
}
|
||||
entityliving = getGoalTarget();
|
||||
// CraftBukkit end
|
||||
AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED);
|
||||
|
||||
if (entityliving == null) {
|
||||
this.bA = 0;
|
||||
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(false));
|
||||
attributeinstance.c(EntityEnderman.b);
|
||||
} else {
|
||||
this.bA = this.ticksLived;
|
||||
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(true));
|
||||
if (!attributeinstance.a(EntityEnderman.b)) {
|
||||
attributeinstance.b(EntityEnderman.b);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
protected void i() {
|
||||
super.i();
|
||||
this.datawatcher.register(EntityEnderman.bx, Optional.absent());
|
||||
this.datawatcher.register(EntityEnderman.by, Boolean.valueOf(false));
|
||||
}
|
||||
|
||||
public void p() {
|
||||
if (this.ticksLived >= this.bz + 400) {
|
||||
this.bz = this.ticksLived;
|
||||
if (!this.isSilent()) {
|
||||
this.world.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, SoundEffects.bh, this.bK(), 2.5F, 1.0F, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(DataWatcherObject<?> datawatcherobject) {
|
||||
if (EntityEnderman.by.equals(datawatcherobject) && this.do_() && this.world.isClientSide) {
|
||||
this.p();
|
||||
}
|
||||
|
||||
super.a(datawatcherobject);
|
||||
}
|
||||
|
||||
public static void a(DataConverterManager dataconvertermanager) {
|
||||
EntityInsentient.a(dataconvertermanager, EntityEnderman.class);
|
||||
}
|
||||
|
||||
public void b(NBTTagCompound nbttagcompound) {
|
||||
super.b(nbttagcompound);
|
||||
IBlockData iblockdata = this.getCarried();
|
||||
|
||||
if (iblockdata != null) {
|
||||
nbttagcompound.setShort("carried", (short) Block.getId(iblockdata.getBlock()));
|
||||
nbttagcompound.setShort("carriedData", (short) iblockdata.getBlock().toLegacyData(iblockdata));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(NBTTagCompound nbttagcompound) {
|
||||
super.a(nbttagcompound);
|
||||
IBlockData iblockdata;
|
||||
|
||||
if (nbttagcompound.hasKeyOfType("carried", 8)) {
|
||||
iblockdata = Block.getByName(nbttagcompound.getString("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
|
||||
} else {
|
||||
iblockdata = Block.getById(nbttagcompound.getShort("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
|
||||
}
|
||||
|
||||
if (iblockdata == null || iblockdata.getBlock() == null || iblockdata.getMaterial() == Material.AIR) {
|
||||
iblockdata = null;
|
||||
}
|
||||
|
||||
this.setCarried(iblockdata);
|
||||
}
|
||||
|
||||
// Paper start - OBFHELPER - ok not really, but verify this on updates
|
||||
private boolean f(EntityHuman entityhuman) {
|
||||
boolean shouldAttack = f_real(entityhuman);
|
||||
com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) entityhuman.getBukkitEntity());
|
||||
event.setCancelled(!shouldAttack);
|
||||
return event.callEvent();
|
||||
}
|
||||
private boolean f_real(EntityHuman entityhuman) {
|
||||
// Paper end
|
||||
ItemStack itemstack = (ItemStack) entityhuman.inventory.armor.get(3);
|
||||
|
||||
if (itemstack.getItem() == Item.getItemOf(Blocks.PUMPKIN)) {
|
||||
return false;
|
||||
} else {
|
||||
Vec3D vec3d = entityhuman.e(1.0F).a();
|
||||
Vec3D vec3d1 = new Vec3D(this.locX - entityhuman.locX, this.getBoundingBox().b + (double) this.getHeadHeight() - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ);
|
||||
double d0 = vec3d1.b();
|
||||
|
||||
vec3d1 = vec3d1.a();
|
||||
double d1 = vec3d.b(vec3d1);
|
||||
|
||||
return d1 > 1.0D - 0.025D / d0 ? entityhuman.hasLineOfSight(this) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public float getHeadHeight() {
|
||||
return 2.55F;
|
||||
}
|
||||
|
||||
public void n() {
|
||||
if (this.world.isClientSide) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
this.world.addParticle(EnumParticle.PORTAL, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D, new int[0]);
|
||||
}
|
||||
}
|
||||
|
||||
this.bd = false;
|
||||
super.n();
|
||||
}
|
||||
|
||||
protected void M() {
|
||||
if (this.an()) {
|
||||
this.damageEntity(DamageSource.DROWN, 1.0F);
|
||||
}
|
||||
|
||||
if (this.world.D() && this.ticksLived >= this.bA + 600) {
|
||||
float f = this.aw();
|
||||
|
||||
if (f > 0.5F && this.world.h(new BlockPosition(this)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && tryEscape(EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper
|
||||
this.setGoalTarget((EntityLiving) null);
|
||||
this.dm();
|
||||
}
|
||||
}
|
||||
|
||||
super.M();
|
||||
}
|
||||
|
||||
public boolean teleportRandomly() { return dm(); } // Paper - OBFHELPER
|
||||
protected boolean dm() {
|
||||
double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D;
|
||||
double d1 = this.locY + (double) (this.random.nextInt(64) - 32);
|
||||
double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D;
|
||||
|
||||
return this.k(d0, d1, d2);
|
||||
}
|
||||
|
||||
protected boolean a(Entity entity) {
|
||||
Vec3D vec3d = new Vec3D(this.locX - entity.locX, this.getBoundingBox().b + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ);
|
||||
|
||||
vec3d = vec3d.a();
|
||||
double d0 = 16.0D;
|
||||
double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D;
|
||||
double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D;
|
||||
double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D;
|
||||
|
||||
return this.k(d1, d2, d3);
|
||||
}
|
||||
|
||||
private boolean k(double d0, double d1, double d2) {
|
||||
boolean flag = this.j(d0, d1, d2);
|
||||
|
||||
if (flag) {
|
||||
this.world.a((EntityHuman) null, this.lastX, this.lastY, this.lastZ, SoundEffects.bi, this.bK(), 1.0F, 1.0F);
|
||||
this.a(SoundEffects.bi, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
protected SoundEffect F() {
|
||||
return this.do_() ? SoundEffects.bg : SoundEffects.bd;
|
||||
}
|
||||
|
||||
protected SoundEffect d(DamageSource damagesource) {
|
||||
return SoundEffects.bf;
|
||||
}
|
||||
|
||||
protected SoundEffect cf() {
|
||||
return SoundEffects.be;
|
||||
}
|
||||
|
||||
protected void dropEquipment(boolean flag, int i) {
|
||||
super.dropEquipment(flag, i);
|
||||
IBlockData iblockdata = this.getCarried();
|
||||
|
||||
if (iblockdata != null) {
|
||||
Item item = Item.getItemOf(iblockdata.getBlock());
|
||||
int j = item.k() ? iblockdata.getBlock().toLegacyData(iblockdata) : 0;
|
||||
|
||||
this.a(new ItemStack(item, 1, j), 0.0F);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MinecraftKey J() {
|
||||
return LootTables.w;
|
||||
}
|
||||
|
||||
public void setCarried(@Nullable IBlockData iblockdata) {
|
||||
this.datawatcher.set(EntityEnderman.bx, Optional.fromNullable(iblockdata));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public IBlockData getCarried() {
|
||||
return (IBlockData) ((Optional) this.datawatcher.get(EntityEnderman.bx)).orNull();
|
||||
}
|
||||
|
||||
public boolean damageEntity(DamageSource damagesource, float f) {
|
||||
if (this.isInvulnerable(damagesource)) {
|
||||
return false;
|
||||
} else if (damagesource instanceof EntityDamageSourceIndirect && tryEscape(EndermanEscapeEvent.Reason.INDIRECT)) { // Paper
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
if (this.dm()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
boolean flag = super.damageEntity(damagesource, f);
|
||||
|
||||
if (damagesource.ignoresArmor() && this.random.nextInt(10) != 0 && tryEscape(damagesource == DamageSource.DROWN ? EndermanEscapeEvent.Reason.DROWN : EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper
|
||||
this.dm();
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean do_() {
|
||||
return ((Boolean) this.datawatcher.get(EntityEnderman.by)).booleanValue();
|
||||
}
|
||||
|
||||
static {
|
||||
EntityEnderman.c.add(Blocks.GRASS);
|
||||
EntityEnderman.c.add(Blocks.DIRT);
|
||||
EntityEnderman.c.add(Blocks.SAND);
|
||||
EntityEnderman.c.add(Blocks.GRAVEL);
|
||||
EntityEnderman.c.add(Blocks.YELLOW_FLOWER);
|
||||
EntityEnderman.c.add(Blocks.RED_FLOWER);
|
||||
EntityEnderman.c.add(Blocks.BROWN_MUSHROOM);
|
||||
EntityEnderman.c.add(Blocks.RED_MUSHROOM);
|
||||
EntityEnderman.c.add(Blocks.TNT);
|
||||
EntityEnderman.c.add(Blocks.CACTUS);
|
||||
EntityEnderman.c.add(Blocks.CLAY);
|
||||
EntityEnderman.c.add(Blocks.PUMPKIN);
|
||||
EntityEnderman.c.add(Blocks.MELON_BLOCK);
|
||||
EntityEnderman.c.add(Blocks.MYCELIUM);
|
||||
EntityEnderman.c.add(Blocks.NETHERRACK);
|
||||
}
|
||||
|
||||
static class PathfinderGoalEndermanPickupBlock extends PathfinderGoal {
|
||||
|
||||
private final EntityEnderman enderman;
|
||||
|
||||
public PathfinderGoalEndermanPickupBlock(EntityEnderman entityenderman) {
|
||||
this.enderman = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
return this.enderman.getCarried() != null ? false : (!this.enderman.world.getGameRules().getBoolean("mobGriefing") ? false : this.enderman.getRandom().nextInt(20) == 0);
|
||||
}
|
||||
|
||||
public void e() {
|
||||
Random random = this.enderman.getRandom();
|
||||
World world = this.enderman.world;
|
||||
int i = MathHelper.floor(this.enderman.locX - 2.0D + random.nextDouble() * 4.0D);
|
||||
int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D);
|
||||
int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D);
|
||||
BlockPosition blockposition = new BlockPosition(i, j, k);
|
||||
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
|
||||
if (iblockdata == null) return; // NeonPaper
|
||||
Block block = iblockdata.getBlock();
|
||||
MovingObjectPosition movingobjectposition = world.rayTrace(new Vec3D((double) ((float) MathHelper.floor(this.enderman.locX) + 0.5F), (double) ((float) j + 0.5F), (double) ((float) MathHelper.floor(this.enderman.locZ) + 0.5F)), new Vec3D((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)), false, true, false);
|
||||
boolean flag = movingobjectposition != null && movingobjectposition.a().equals(blockposition);
|
||||
|
||||
if (EntityEnderman.c.contains(block) && flag) {
|
||||
// CraftBukkit start - Pickup event
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, this.enderman.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), org.bukkit.Material.AIR).isCancelled()) {
|
||||
this.enderman.setCarried(iblockdata);
|
||||
world.setAir(blockposition);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static class PathfinderGoalEndermanPlaceBlock extends PathfinderGoal {
|
||||
|
||||
private final EntityEnderman a;
|
||||
|
||||
public PathfinderGoalEndermanPlaceBlock(EntityEnderman entityenderman) {
|
||||
this.a = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
return this.a.getCarried() == null ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : this.a.getRandom().nextInt(2000) == 0);
|
||||
}
|
||||
|
||||
public void e() {
|
||||
Random random = this.a.getRandom();
|
||||
World world = this.a.world;
|
||||
int i = MathHelper.floor(this.a.locX - 1.0D + random.nextDouble() * 2.0D);
|
||||
int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D);
|
||||
int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D);
|
||||
BlockPosition blockposition = new BlockPosition(i, j, k);
|
||||
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
|
||||
if (iblockdata == null) return; // NeonPaper
|
||||
IBlockData iblockdata1 = world.getType(blockposition.down());
|
||||
IBlockData iblockdata2 = this.a.getCarried();
|
||||
|
||||
if (iblockdata2 != null && this.a(world, blockposition, iblockdata2.getBlock(), iblockdata, iblockdata1)) {
|
||||
// CraftBukkit start - Place event
|
||||
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, this.a.getCarried().getBlock(), this.a.getCarried().getBlock().toLegacyData(this.a.getCarried())).isCancelled()) {
|
||||
world.setTypeAndData(blockposition, iblockdata2, 3);
|
||||
this.a.setCarried((IBlockData) null);
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean a(World world, BlockPosition blockposition, Block block, IBlockData iblockdata, IBlockData iblockdata1) {
|
||||
return !block.canPlace(world, blockposition) ? false : (iblockdata.getMaterial() != Material.AIR ? false : (iblockdata1.getMaterial() == Material.AIR ? false : iblockdata1.g()));
|
||||
}
|
||||
}
|
||||
|
||||
static class PathfinderGoalPlayerWhoLookedAtTarget extends PathfinderGoalNearestAttackableTarget<EntityHuman> {
|
||||
|
||||
private final EntityEnderman i; public EntityEnderman getEnderman() { return i; } // Paper - OBFHELPER
|
||||
private EntityHuman j;
|
||||
private int k;
|
||||
private int l;
|
||||
|
||||
public PathfinderGoalPlayerWhoLookedAtTarget(EntityEnderman entityenderman) {
|
||||
super(entityenderman, EntityHuman.class, false);
|
||||
this.i = entityenderman;
|
||||
}
|
||||
|
||||
public boolean a() {
|
||||
double d0 = this.i();
|
||||
|
||||
this.j = this.i.world.a(this.i.locX, this.i.locY, this.i.locZ, d0, d0, (Function) null, new Predicate() {
|
||||
public boolean a(@Nullable EntityHuman entityhuman) {
|
||||
return entityhuman != null && PathfinderGoalPlayerWhoLookedAtTarget.this.i.f(entityhuman);
|
||||
}
|
||||
|
||||
public boolean apply(@Nullable Object object) {
|
||||
return this.a((EntityHuman) object);
|
||||
}
|
||||
});
|
||||
return this.j != null;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.k = 5;
|
||||
this.l = 0;
|
||||
}
|
||||
|
||||
public void d() {
|
||||
this.j = null;
|
||||
super.d();
|
||||
}
|
||||
|
||||
public boolean b() {
|
||||
if (this.j != null) {
|
||||
if (!this.i.f(this.j)) {
|
||||
return false;
|
||||
} else {
|
||||
this.i.a((Entity) this.j, 10.0F, 10.0F);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return this.d != null && ((EntityHuman) this.d).isAlive() ? true : super.b();
|
||||
}
|
||||
}
|
||||
|
||||
public void e() {
|
||||
if (this.j != null) {
|
||||
if (--this.k <= 0) {
|
||||
this.d = this.j;
|
||||
this.j = null;
|
||||
super.c();
|
||||
}
|
||||
} else {
|
||||
if (this.d != null) {
|
||||
if (this.i.f((EntityHuman) this.d)) {
|
||||
if (((EntityHuman) this.d).h(this.i) < 16.0D && this.getEnderman().tryEscape(EndermanEscapeEvent.Reason.STARE)) { // Paper
|
||||
this.i.dm();
|
||||
}
|
||||
|
||||
this.l = 0;
|
||||
} else if (((EntityHuman) this.d).h(this.i) > 256.0D && this.l++ >= 30 && this.i.a((Entity) this.d)) {
|
||||
this.l = 0;
|
||||
}
|
||||
}
|
||||
|
||||
super.e();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
// CraftBukkit start
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Fish;
|
||||
import org.bukkit.event.player.PlayerFishEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class EntityFishingHook extends Entity {
|
||||
|
||||
private static final DataWatcherObject<Integer> b = DataWatcher.a(EntityFishingHook.class, DataWatcherRegistry.b);
|
||||
private boolean isInGround;
|
||||
private int d;
|
||||
public EntityHuman owner;
|
||||
private int f;
|
||||
private int g;
|
||||
private int h;
|
||||
private int at;
|
||||
private float au;
|
||||
public Entity hooked;
|
||||
private EntityFishingHook.HookState av;
|
||||
private int aw;
|
||||
private int ax;
|
||||
|
||||
public EntityFishingHook(World world, EntityHuman entityhuman) {
|
||||
super(world);
|
||||
this.av = EntityFishingHook.HookState.FLYING;
|
||||
this.a(entityhuman);
|
||||
this.n();
|
||||
}
|
||||
|
||||
private void a(EntityHuman entityhuman) {
|
||||
this.setSize(0.25F, 0.25F);
|
||||
this.ah = true;
|
||||
this.owner = entityhuman;
|
||||
this.owner.hookedFish = this;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.ax = i;
|
||||
}
|
||||
|
||||
public void c(int i) {
|
||||
this.aw = i;
|
||||
}
|
||||
|
||||
private void n() {
|
||||
float f = this.owner.lastPitch + (this.owner.pitch - this.owner.lastPitch);
|
||||
float f1 = this.owner.lastYaw + (this.owner.yaw - this.owner.lastYaw);
|
||||
float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F);
|
||||
float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F);
|
||||
float f4 = -MathHelper.cos(-f * 0.017453292F);
|
||||
float f5 = MathHelper.sin(-f * 0.017453292F);
|
||||
double d0 = this.owner.lastX + (this.owner.locX - this.owner.lastX) - (double) f3 * 0.3D;
|
||||
double d1 = this.owner.lastY + (this.owner.locY - this.owner.lastY) + (double) this.owner.getHeadHeight();
|
||||
double d2 = this.owner.lastZ + (this.owner.locZ - this.owner.lastZ) - (double) f2 * 0.3D;
|
||||
|
||||
this.setPositionRotation(d0, d1, d2, f1, f);
|
||||
this.motX = (double) (-f3);
|
||||
this.motY = (double) MathHelper.a(-(f5 / f4), -5.0F, 5.0F);
|
||||
this.motZ = (double) (-f2);
|
||||
float f6 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ);
|
||||
|
||||
this.motX *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
this.motY *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
this.motZ *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
|
||||
float f7 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
|
||||
|
||||
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
|
||||
this.pitch = (float) (MathHelper.c(this.motY, (double) f7) * 57.2957763671875D);
|
||||
this.lastYaw = this.yaw;
|
||||
this.lastPitch = this.pitch;
|
||||
}
|
||||
|
||||
protected void i() {
|
||||
this.getDataWatcher().register(EntityFishingHook.b, Integer.valueOf(0));
|
||||
}
|
||||
|
||||
public void a(DataWatcherObject<?> datawatcherobject) {
|
||||
if (EntityFishingHook.b.equals(datawatcherobject)) {
|
||||
int i = ((Integer) this.getDataWatcher().get(EntityFishingHook.b)).intValue();
|
||||
|
||||
this.hooked = i > 0 ? this.world.getEntity(i - 1) : null;
|
||||
}
|
||||
|
||||
super.a(datawatcherobject);
|
||||
}
|
||||
|
||||
public void B_() {
|
||||
super.B_();
|
||||
if (this.owner == null) {
|
||||
this.die();
|
||||
} else if (this.world.isClientSide || !this.p()) {
|
||||
if (this.isInGround) {
|
||||
++this.d;
|
||||
if (this.d >= 1200) {
|
||||
this.die();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float f = 0.0F;
|
||||
BlockPosition blockposition = new BlockPosition(this);
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
|
||||
if (iblockdata.getMaterial() == Material.WATER) {
|
||||
f = BlockFluids.g(iblockdata, this.world, blockposition);
|
||||
}
|
||||
|
||||
double d0;
|
||||
|
||||
if (this.av == EntityFishingHook.HookState.FLYING) {
|
||||
if (this.hooked != null) {
|
||||
this.motX = 0.0D;
|
||||
this.motY = 0.0D;
|
||||
this.motZ = 0.0D;
|
||||
this.av = EntityFishingHook.HookState.HOOKED_IN_ENTITY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (f > 0.0F) {
|
||||
this.motX *= 0.3D;
|
||||
this.motY *= 0.2D;
|
||||
this.motZ *= 0.3D;
|
||||
this.av = EntityFishingHook.HookState.BOBBING;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.world.isClientSide) {
|
||||
this.r();
|
||||
}
|
||||
|
||||
if (!this.isInGround && !this.onGround && !this.positionChanged) {
|
||||
++this.f;
|
||||
} else {
|
||||
this.f = 0;
|
||||
this.motX = 0.0D;
|
||||
this.motY = 0.0D;
|
||||
this.motZ = 0.0D;
|
||||
}
|
||||
} else {
|
||||
if (this.av == EntityFishingHook.HookState.HOOKED_IN_ENTITY) {
|
||||
if (this.hooked != null) {
|
||||
if (this.hooked.dead) {
|
||||
this.hooked = null;
|
||||
this.av = EntityFishingHook.HookState.FLYING;
|
||||
} else {
|
||||
this.locX = this.hooked.locX;
|
||||
double d1 = (double) this.hooked.length;
|
||||
|
||||
this.locY = this.hooked.getBoundingBox().b + d1 * 0.8D;
|
||||
this.locZ = this.hooked.locZ;
|
||||
this.setPosition(this.locX, this.locY, this.locZ);
|
||||
if (this.ak) this.die(); // NeonPaper - Prevent going through portals
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.av == EntityFishingHook.HookState.BOBBING) {
|
||||
this.motX *= 0.9D;
|
||||
this.motZ *= 0.9D;
|
||||
d0 = this.locY + this.motY - (double) blockposition.getY() - (double) f;
|
||||
if (Math.abs(d0) < 0.01D) {
|
||||
d0 += Math.signum(d0) * 0.1D;
|
||||
}
|
||||
|
||||
this.motY -= d0 * (double) this.random.nextFloat() * 0.2D;
|
||||
if (!this.world.isClientSide && f > 0.0F) {
|
||||
this.a(blockposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iblockdata.getMaterial() != Material.WATER) {
|
||||
this.motY -= 0.03D;
|
||||
}
|
||||
|
||||
this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ);
|
||||
this.q();
|
||||
d0 = 0.92D;
|
||||
this.motX *= 0.92D;
|
||||
this.motY *= 0.92D;
|
||||
this.motZ *= 0.92D;
|
||||
this.setPosition(this.locX, this.locY, this.locZ);
|
||||
|
||||
// Paper start - These shouldn't be going through portals
|
||||
if (this.inPortal()) {
|
||||
this.die();
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
}
|
||||
|
||||
private boolean p() {
|
||||
ItemStack itemstack = this.owner.getItemInMainHand();
|
||||
ItemStack itemstack1 = this.owner.getItemInOffHand();
|
||||
boolean flag = itemstack.getItem() == Items.FISHING_ROD;
|
||||
boolean flag1 = itemstack1.getItem() == Items.FISHING_ROD;
|
||||
|
||||
if (!this.owner.dead && this.owner.isAlive() && (flag || flag1) && this.h(this.owner) <= 1024.0D) {
|
||||
return false;
|
||||
} else {
|
||||
this.die();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void q() {
|
||||
float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
|
||||
|
||||
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
|
||||
|
||||
for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) {
|
||||
;
|
||||
}
|
||||
|
||||
while (this.pitch - this.lastPitch >= 180.0F) {
|
||||
this.lastPitch += 360.0F;
|
||||
}
|
||||
|
||||
while (this.yaw - this.lastYaw < -180.0F) {
|
||||
this.lastYaw -= 360.0F;
|
||||
}
|
||||
|
||||
while (this.yaw - this.lastYaw >= 180.0F) {
|
||||
this.lastYaw += 360.0F;
|
||||
}
|
||||
|
||||
this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F;
|
||||
this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F;
|
||||
}
|
||||
|
||||
private void r() {
|
||||
Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ);
|
||||
Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
|
||||
MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, false, true, false);
|
||||
|
||||
vec3d = new Vec3D(this.locX, this.locY, this.locZ);
|
||||
vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
|
||||
|
||||
// Paper start - Call ProjectileCollideEvent
|
||||
if (movingobjectposition != null && movingobjectposition.entity != null) {
|
||||
com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition);
|
||||
if (event.isCancelled()) {
|
||||
movingobjectposition = null;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
|
||||
if (movingobjectposition != null) {
|
||||
vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z);
|
||||
}
|
||||
|
||||
Entity entity = null;
|
||||
List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D));
|
||||
double d0 = 0.0D;
|
||||
Iterator iterator = list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
Entity entity1 = (Entity) iterator.next();
|
||||
|
||||
if (this.a(entity1) && (entity1 != this.owner || this.f >= 5)) {
|
||||
AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D);
|
||||
MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1);
|
||||
|
||||
if (movingobjectposition1 != null) {
|
||||
double d1 = vec3d.distanceSquared(movingobjectposition1.pos);
|
||||
|
||||
if (d1 < d0 || d0 == 0.0D) {
|
||||
entity = entity1;
|
||||
d0 = d1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity != null) {
|
||||
movingobjectposition = new MovingObjectPosition(entity);
|
||||
}
|
||||
|
||||
if (movingobjectposition != null && movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.MISS) {
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event
|
||||
if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.ENTITY) {
|
||||
this.hooked = movingobjectposition.entity;
|
||||
this.s();
|
||||
} else {
|
||||
this.isInGround = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void s() {
|
||||
this.getDataWatcher().set(EntityFishingHook.b, Integer.valueOf(this.hooked.getId() + 1));
|
||||
}
|
||||
|
||||
private void a(BlockPosition blockposition) {
|
||||
WorldServer worldserver = (WorldServer) this.world;
|
||||
int i = 1;
|
||||
BlockPosition blockposition1 = blockposition.up();
|
||||
|
||||
if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(blockposition1)) {
|
||||
++i;
|
||||
}
|
||||
|
||||
if (this.random.nextFloat() < 0.5F && !this.world.h(blockposition1)) {
|
||||
--i;
|
||||
}
|
||||
|
||||
if (this.g > 0) {
|
||||
--this.g;
|
||||
if (this.g <= 0) {
|
||||
this.h = 0;
|
||||
this.at = 0;
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
// CraftBukkit end
|
||||
} else {
|
||||
this.motY -= 0.2D * (double) this.random.nextFloat() * (double) this.random.nextFloat();
|
||||
}
|
||||
} else {
|
||||
float f;
|
||||
float f1;
|
||||
float f2;
|
||||
double d0;
|
||||
double d1;
|
||||
double d2;
|
||||
Block block;
|
||||
|
||||
if (this.at > 0) {
|
||||
this.at -= i;
|
||||
if (this.at > 0) {
|
||||
this.au = (float) ((double) this.au + this.random.nextGaussian() * 4.0D);
|
||||
f = this.au * 0.017453292F;
|
||||
f1 = MathHelper.sin(f);
|
||||
f2 = MathHelper.cos(f);
|
||||
d0 = this.locX + (double) (f1 * (float) this.at * 0.1F);
|
||||
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
|
||||
d2 = this.locZ + (double) (f2 * (float) this.at * 0.1F);
|
||||
block = worldserver.getType(new BlockPosition(d0, d1 - 1.0D, d2)).getBlock();
|
||||
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
|
||||
if (this.random.nextFloat() < 0.15F) {
|
||||
worldserver.a(EnumParticle.WATER_BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D, new int[0]);
|
||||
}
|
||||
|
||||
float f3 = f1 * 0.04F;
|
||||
float f4 = f2 * 0.04F;
|
||||
|
||||
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D, new int[0]);
|
||||
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D, new int[0]);
|
||||
}
|
||||
} else {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.BITE);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.motY = (double) (-0.4F * MathHelper.a(this.random, 0.6F, 1.0F));
|
||||
this.a(SoundEffects.K, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
|
||||
double d3 = this.getBoundingBox().b + 0.5D;
|
||||
|
||||
worldserver.a(EnumParticle.WATER_BUBBLE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
|
||||
worldserver.a(EnumParticle.WATER_WAKE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
|
||||
this.g = MathHelper.nextInt(this.random, 20, 40);
|
||||
}
|
||||
} else if (this.h > 0) {
|
||||
this.h -= i;
|
||||
f = 0.15F;
|
||||
if (this.h < 20) {
|
||||
f = (float) ((double) f + (double) (20 - this.h) * 0.05D);
|
||||
} else if (this.h < 40) {
|
||||
f = (float) ((double) f + (double) (40 - this.h) * 0.02D);
|
||||
} else if (this.h < 60) {
|
||||
f = (float) ((double) f + (double) (60 - this.h) * 0.01D);
|
||||
}
|
||||
|
||||
if (this.random.nextFloat() < f) {
|
||||
f1 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F;
|
||||
f2 = MathHelper.a(this.random, 25.0F, 60.0F);
|
||||
d0 = this.locX + (double) (MathHelper.sin(f1) * f2 * 0.1F);
|
||||
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
|
||||
d2 = this.locZ + (double) (MathHelper.cos(f1) * f2 * 0.1F);
|
||||
block = worldserver.getType(new BlockPosition((int) d0, (int) d1 - 1, (int) d2)).getBlock();
|
||||
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
|
||||
worldserver.a(EnumParticle.WATER_SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.h <= 0) {
|
||||
this.au = MathHelper.a(this.random, 0.0F, 360.0F);
|
||||
this.at = MathHelper.nextInt(this.random, 20, 80);
|
||||
}
|
||||
} else {
|
||||
this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper
|
||||
this.h -= this.ax * 20 * 5;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean a(Entity entity) {
|
||||
return entity.isInteractable() || entity instanceof EntityItem;
|
||||
}
|
||||
|
||||
public void b(NBTTagCompound nbttagcompound) {}
|
||||
|
||||
public void a(NBTTagCompound nbttagcompound) {}
|
||||
|
||||
public int j() {
|
||||
if (!this.world.isClientSide && this.owner != null) {
|
||||
int i = 0;
|
||||
|
||||
if (this.hooked != null) {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.k();
|
||||
this.world.broadcastEntityEffect(this, (byte) 31);
|
||||
i = this.hooked instanceof EntityItem ? 3 : 5;
|
||||
} else if (this.g > 0) {
|
||||
LootTableInfo.a loottableinfo_a = new LootTableInfo.a((WorldServer) this.world);
|
||||
|
||||
loottableinfo_a.a((float) this.aw + this.owner.du());
|
||||
Iterator iterator = this.world.getLootTableRegistry().a(LootTables.aA).a(this.random, loottableinfo_a.a()).iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
ItemStack itemstack = (ItemStack) iterator.next();
|
||||
EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack);
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH);
|
||||
playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
double d0 = this.owner.locX - this.locX;
|
||||
double d1 = this.owner.locY - this.locY;
|
||||
double d2 = this.owner.locZ - this.locZ;
|
||||
double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
||||
double d4 = 0.1D;
|
||||
|
||||
entityitem.motX = d0 * 0.1D;
|
||||
entityitem.motY = d1 * 0.1D + (double) MathHelper.sqrt(d3) * 0.08D;
|
||||
entityitem.motZ = d2 * 0.1D;
|
||||
this.world.addEntity(entityitem);
|
||||
// CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
|
||||
if (playerFishEvent.getExpToDrop() > 0) {
|
||||
this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper
|
||||
}
|
||||
// CraftBukkit end
|
||||
Item item = itemstack.getItem();
|
||||
|
||||
if (item == Items.FISH || item == Items.COOKED_FISH) {
|
||||
this.owner.a(StatisticList.E, 1);
|
||||
}
|
||||
}
|
||||
|
||||
i = 1;
|
||||
}
|
||||
|
||||
if (this.isInGround) {
|
||||
// CraftBukkit start
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
// CraftBukkit end
|
||||
i = 2;
|
||||
}
|
||||
// CraftBukkit start
|
||||
if (i == 0) {
|
||||
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
|
||||
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
|
||||
if (playerFishEvent.isCancelled()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
this.die();
|
||||
return i;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected void k() {
|
||||
if (this.owner != null) {
|
||||
double d0 = this.owner.locX - this.locX;
|
||||
double d1 = this.owner.locY - this.locY;
|
||||
double d2 = this.owner.locZ - this.locZ;
|
||||
double d3 = 0.1D;
|
||||
|
||||
this.hooked.motX += d0 * 0.1D;
|
||||
this.hooked.motY += d1 * 0.1D;
|
||||
this.hooked.motZ += d2 * 0.1D;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean playStepSound() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void die() {
|
||||
super.die();
|
||||
if (this.owner != null) {
|
||||
this.owner.hookedFish = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public EntityHuman l() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
static enum HookState {
|
||||
|
||||
FLYING, HOOKED_IN_ENTITY, BOBBING;
|
||||
|
||||
private HookState() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit
|
||||
|
||||
public class EntityMushroomCow extends EntityCow {
|
||||
|
||||
public EntityMushroomCow(World world) {
|
||||
super(world);
|
||||
this.setSize(0.9F, 1.4F);
|
||||
this.bA = Blocks.MYCELIUM;
|
||||
}
|
||||
|
||||
public static void c(DataConverterManager dataconvertermanager) {
|
||||
EntityInsentient.a(dataconvertermanager, EntityMushroomCow.class);
|
||||
}
|
||||
|
||||
public boolean a(EntityHuman entityhuman, EnumHand enumhand) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
if (itemstack.isEmpty()) {
|
||||
entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW));
|
||||
} else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) {
|
||||
entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) {
|
||||
if (this.dead) return false; // Reaper - Fix cow dupe
|
||||
// CraftBukkit start
|
||||
PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity());
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
return false;
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.die();
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D, new int[0]);
|
||||
if (!this.world.isClientSide) {
|
||||
EntityCow entitycow = new EntityCow(this.world);
|
||||
|
||||
entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
|
||||
entitycow.setHealth(this.getHealth());
|
||||
entitycow.aN = this.aN;
|
||||
if (this.hasCustomName()) {
|
||||
entitycow.setCustomName(this.getCustomName());
|
||||
}
|
||||
|
||||
this.world.addEntity(entitycow);
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM)));
|
||||
}
|
||||
|
||||
itemstack.damage(1, entityhuman);
|
||||
this.a(SoundEffects.ei, 1.0F, 1.0F);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return super.a(entityhuman, enumhand);
|
||||
}
|
||||
}
|
||||
|
||||
public EntityMushroomCow c(EntityAgeable entityageable) {
|
||||
return new EntityMushroomCow(this.world);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected MinecraftKey J() {
|
||||
return LootTables.M;
|
||||
}
|
||||
|
||||
public EntityCow b(EntityAgeable entityageable) {
|
||||
return this.c(entityageable);
|
||||
}
|
||||
|
||||
public EntityAgeable createChild(EntityAgeable entityageable) {
|
||||
return this.c(entityageable);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.util.ArrayDeque; // Paper
|
||||
import java.util.ArrayList;
|
||||
@@ -31,10 +32,8 @@ import org.bukkit.inventory.MainHand;
|
||||
// CraftBukkit end
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add volatile to fields<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 2) Add lock to player track (safety issue)
|
||||
*/
|
||||
public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
|
||||
@@ -85,6 +84,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
}
|
||||
// Paper end
|
||||
private int containerUpdateDelay; // Paper
|
||||
// Paper start - cancellable death event
|
||||
public boolean queueHealthUpdatePacket = false;
|
||||
public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
|
||||
// Paper end
|
||||
|
||||
// CraftBukkit start
|
||||
public String displayName;
|
||||
@@ -442,9 +445,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
public void die(DamageSource damagesource) {
|
||||
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
|
||||
|
||||
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag));
|
||||
//this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
|
||||
// CraftBukkit start - fire PlayerDeathEvent
|
||||
if (this.dead) {
|
||||
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
|
||||
return;
|
||||
}
|
||||
java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.inventory.getSize());
|
||||
@@ -462,6 +466,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
|
||||
String deathmessage = chatmessage.toPlainText();
|
||||
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
|
||||
// Paper start - cancellable death event
|
||||
if (event.isCancelled()) {
|
||||
// make compatible with plugins that might have already set the health in an event listener
|
||||
if (this.getHealth() <= 0) {
|
||||
this.setHealth((float) event.getReviveHealth());
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag));
|
||||
// Paper end
|
||||
|
||||
String deathMessage = event.getDeathMessage();
|
||||
|
||||
@@ -615,7 +629,17 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
}
|
||||
}
|
||||
|
||||
return super.damageEntity(damagesource, f);
|
||||
// Paper start - cancellable death events
|
||||
//return super.damageEntity(damagesource, f);
|
||||
this.queueHealthUpdatePacket = true;
|
||||
boolean damaged = super.damageEntity(damagesource, f);
|
||||
this.queueHealthUpdatePacket = false;
|
||||
if (this.queuedHealthUpdatePacket != null) {
|
||||
this.playerConnection.sendPacket(this.queuedHealthUpdatePacket);
|
||||
this.queuedHealthUpdatePacket = null;
|
||||
}
|
||||
return damaged;
|
||||
// Paper end
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -745,7 +769,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
if (entity instanceof EntityPlayer) {
|
||||
WorldServer worldServer = (WorldServer) entity.getWorld();
|
||||
worldServer.tracker.untrackEntity(this);
|
||||
worldServer.tracker.entriesLock.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
|
||||
worldServer.tracker.track(this);
|
||||
worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
|
||||
}
|
||||
// Paper end
|
||||
|
||||
@@ -758,7 +784,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
|
||||
|
||||
protected void b(BlockPosition blockposition) {
|
||||
if (!this.isSpectator()) {
|
||||
if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Dionysus - don't tick dead players that are not in the world currently (pending respawn)
|
||||
super.b(blockposition);
|
||||
}
|
||||
|
||||
@@ -1130,6 +1156,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
this.getDataWatcher().set(EntityPlayer.br, entityplayer.getDataWatcher().get(EntityPlayer.br));
|
||||
this.lastSentExp = -1;
|
||||
this.lastHealthSent = -1.0F;
|
||||
setSneaking(false); // NeonPaper - fix MC-10657
|
||||
this.ch = -1;
|
||||
// this.cr.a((RecipeBook) entityplayer.cr); // CraftBukkit
|
||||
// Paper start - Optimize remove queue
|
||||
@@ -1420,8 +1447,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||
}
|
||||
|
||||
// CraftBukkit start - Add per-player time and weather.
|
||||
public volatile long timeOffset = 0; // Akarin - volatile
|
||||
public volatile boolean relativeTime = true; // Akarin - volatile
|
||||
public long timeOffset = 0;
|
||||
public boolean relativeTime = true;
|
||||
|
||||
public long getPlayerTime() {
|
||||
if (this.relativeTime) {
|
||||
|
||||
@@ -2,28 +2,28 @@ package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Make entries-set concurrent<br>
|
||||
* 2) Add lock for entries-set operations<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 1) Made collections and entry access thread-safe (safety issue)
|
||||
*/
|
||||
@ThreadSafe // Akarin
|
||||
public class EntityTracker {
|
||||
|
||||
private static final Logger a = LogManager.getLogger();
|
||||
private final WorldServer world;
|
||||
private final Set<EntityTrackerEntry> c = Sets.newConcurrentHashSet(); // Akarin - make concurrent
|
||||
private final ReentrantReadWriteLock entriesLock = new ReentrantReadWriteLock(); // Akarin - add lock
|
||||
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
|
||||
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
|
||||
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
|
||||
private int e;
|
||||
|
||||
@@ -40,8 +40,8 @@ public class EntityTracker {
|
||||
if (entity instanceof EntityPlayer) {
|
||||
this.addEntity(entity, 512, 2);
|
||||
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
// entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
@@ -50,7 +50,7 @@ public class EntityTracker {
|
||||
entitytrackerentry.updatePlayer(entityplayer);
|
||||
}
|
||||
}
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
// entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
|
||||
} else if (entity instanceof EntityFishingHook) {
|
||||
this.addEntity(entity, 64, 5, true);
|
||||
} else if (entity instanceof EntityArrow) {
|
||||
@@ -121,18 +121,18 @@ public class EntityTracker {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
|
||||
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
||||
try {
|
||||
// entriesLock.writeLock().lock(); // Akarin - locked from track method
|
||||
if (this.trackedEntities.b(entity.getId())) {
|
||||
throw new IllegalStateException("Entity is already tracked!");
|
||||
}
|
||||
|
||||
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
|
||||
|
||||
entriesLock.writeLock().lock(); // Akarin
|
||||
this.c.add(entitytrackerentry);
|
||||
entriesLock.writeLock().unlock(); // Akarin
|
||||
|
||||
|
||||
this.trackedEntities.a(entity.getId(), entitytrackerentry);
|
||||
entitytrackerentry.scanPlayers(this.world.players);
|
||||
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
|
||||
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
|
||||
@@ -192,9 +192,9 @@ public class EntityTracker {
|
||||
|
||||
public void updatePlayers() {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
world.timings.tracker1.startTiming(); // Spigot
|
||||
entriesLock.writeLock().lock(); // Akarin
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
@@ -207,7 +207,6 @@ public class EntityTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
world.timings.tracker1.stopTiming(); // Spigot
|
||||
|
||||
world.timings.tracker2.startTiming(); // Spigot
|
||||
@@ -223,13 +222,14 @@ public class EntityTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
entriesLock.writeLock().unlock(); // Akarin
|
||||
world.timings.tracker2.stopTiming(); // Spigot
|
||||
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer) {
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.writeLock().lock(); // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
@@ -240,11 +240,13 @@ public class EntityTracker {
|
||||
entitytrackerentry.updatePlayer(entityplayer);
|
||||
}
|
||||
}
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
entriesLock.writeLock().unlock(); // Akarin
|
||||
}
|
||||
|
||||
public void a(Entity entity, Packet<?> packet) {
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
|
||||
if (entitytrackerentry != null) {
|
||||
entitytrackerentry.broadcast(packet);
|
||||
@@ -253,7 +255,9 @@ public class EntityTracker {
|
||||
}
|
||||
|
||||
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
|
||||
if (entitytrackerentry != null) {
|
||||
entitytrackerentry.broadcastIncludingSelf(packet);
|
||||
@@ -262,22 +266,22 @@ public class EntityTracker {
|
||||
}
|
||||
|
||||
public void untrackPlayer(EntityPlayer entityplayer) {
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.writeLock().lock();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
entitytrackerentry.clear(entityplayer);
|
||||
}
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
entriesLock.writeLock().unlock();
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer, Chunk chunk) {
|
||||
ArrayList arraylist = Lists.newArrayList();
|
||||
ArrayList arraylist1 = Lists.newArrayList();
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.writeLock().lock(); // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
@@ -294,7 +298,7 @@ public class EntityTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
entriesLock.readLock().unlock(); // Akarin
|
||||
entriesLock.writeLock().unlock(); // Akarin
|
||||
|
||||
Entity entity1;
|
||||
|
||||
@@ -320,8 +324,8 @@ public class EntityTracker {
|
||||
|
||||
public void a(int i) {
|
||||
this.e = (i - 1) * 16;
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
Iterator iterator = this.c.iterator();
|
||||
entriesLock.readLock().lock(); // Akarin
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||
|
||||
@@ -0,0 +1,654 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.player.PlayerVelocityEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class EntityTrackerEntry {
|
||||
|
||||
private static final Logger c = LogManager.getLogger();
|
||||
private final Entity tracker;
|
||||
private final int e;
|
||||
private int f;
|
||||
private final int g;
|
||||
private long xLoc;
|
||||
private long yLoc;
|
||||
private long zLoc;
|
||||
private int yRot;
|
||||
private int xRot;
|
||||
private int headYaw;
|
||||
private double n;
|
||||
private double o;
|
||||
private double p;
|
||||
public int a;
|
||||
private double q;
|
||||
private double r;
|
||||
private double s;
|
||||
private boolean isMoving;
|
||||
private final boolean u;
|
||||
private int v;
|
||||
private List<Entity> w = Collections.emptyList();
|
||||
private boolean x;
|
||||
private boolean y;
|
||||
public boolean b;
|
||||
// Paper start
|
||||
// Replace trackedPlayers Set with a Map. The value is true until the player receives
|
||||
// their first update (which is forced to have absolute coordinates), false afterward.
|
||||
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<EntityPlayer, Boolean>();
|
||||
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
|
||||
// Paper end
|
||||
|
||||
public EntityTrackerEntry(Entity entity, int i, int j, int k, boolean flag) {
|
||||
entity.tracker = this; // Paper
|
||||
this.tracker = entity;
|
||||
this.e = i;
|
||||
this.f = j;
|
||||
this.g = k;
|
||||
this.u = flag;
|
||||
this.xLoc = EntityTracker.a(entity.locX);
|
||||
this.yLoc = EntityTracker.a(entity.locY);
|
||||
this.zLoc = EntityTracker.a(entity.locZ);
|
||||
this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F);
|
||||
this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F);
|
||||
this.headYaw = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F);
|
||||
this.y = entity.onGround;
|
||||
}
|
||||
|
||||
public boolean equals(Object object) {
|
||||
return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.tracker.getId();
|
||||
}
|
||||
|
||||
public void track(List<EntityHuman> list) {
|
||||
this.b = false;
|
||||
if (!this.isMoving || this.tracker.d(this.q, this.r, this.s) > 16.0D) {
|
||||
this.q = this.tracker.locX;
|
||||
this.r = this.tracker.locY;
|
||||
this.s = this.tracker.locZ;
|
||||
this.isMoving = true;
|
||||
this.b = true;
|
||||
this.scanPlayers(list);
|
||||
}
|
||||
|
||||
List list1 = this.tracker.bF();
|
||||
|
||||
if (!list1.equals(this.w)) {
|
||||
this.w = list1;
|
||||
this.broadcastIncludingSelf(new PacketPlayOutMount(this.tracker)); // CraftBukkit
|
||||
}
|
||||
|
||||
// PAIL : rename
|
||||
if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper
|
||||
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
|
||||
ItemStack itemstack = entityitemframe.getItem();
|
||||
|
||||
if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // Paper - moved back up
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world);
|
||||
Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityHuman entityhuman = (EntityHuman) iterator.next();
|
||||
EntityPlayer entityplayer = (EntityPlayer) entityhuman;
|
||||
|
||||
worldmap.a(entityplayer, itemstack);
|
||||
Packet packet = Items.FILLED_MAP.a(itemstack, this.tracker.world, (EntityHuman) entityplayer);
|
||||
|
||||
if (packet != null) {
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.d();
|
||||
}
|
||||
|
||||
if (this.a % this.g == 0 || this.tracker.impulse || this.tracker.getDataWatcher().a()) {
|
||||
int i;
|
||||
|
||||
if (this.tracker.isPassenger()) {
|
||||
i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
|
||||
int j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
|
||||
boolean flag = Math.abs(i - this.yRot) >= 1 || Math.abs(j - this.xRot) >= 1;
|
||||
|
||||
if (flag) {
|
||||
this.broadcast(new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, this.tracker.onGround));
|
||||
this.yRot = i;
|
||||
this.xRot = j;
|
||||
}
|
||||
|
||||
this.xLoc = EntityTracker.a(this.tracker.locX);
|
||||
this.yLoc = EntityTracker.a(this.tracker.locY);
|
||||
this.zLoc = EntityTracker.a(this.tracker.locZ);
|
||||
this.d();
|
||||
this.x = true;
|
||||
} else {
|
||||
++this.v;
|
||||
long k = EntityTracker.a(this.tracker.locX);
|
||||
long l = EntityTracker.a(this.tracker.locY);
|
||||
long i1 = EntityTracker.a(this.tracker.locZ);
|
||||
int j1 = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
|
||||
int k1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
|
||||
long l1 = k - this.xLoc;
|
||||
long i2 = l - this.yLoc;
|
||||
long j2 = i1 - this.zLoc;
|
||||
Object object = null;
|
||||
boolean flag1 = l1 * l1 + i2 * i2 + j2 * j2 >= 128L || this.a % 60 == 0;
|
||||
boolean flag2 = Math.abs(j1 - this.yRot) >= 1 || Math.abs(k1 - this.xRot) >= 1;
|
||||
|
||||
if (this.a > 0 || this.tracker instanceof EntityArrow) { // Paper - Moved up
|
||||
// CraftBukkit start - Code moved from below
|
||||
if (flag1) {
|
||||
this.xLoc = k;
|
||||
this.yLoc = l;
|
||||
this.zLoc = i1;
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
this.yRot = j1;
|
||||
this.xRot = k1;
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (l1 >= -32768L && l1 < 32768L && i2 >= -32768L && i2 < 32768L && j2 >= -32768L && j2 < 32768L && this.v <= 400 && !this.x && this.y == this.tracker.onGround) {
|
||||
if ((!flag1 || !flag2) && !(this.tracker instanceof EntityArrow)) {
|
||||
if (flag1) {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), l1, i2, j2, this.tracker.onGround);
|
||||
} else if (flag2) {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) j1, (byte) k1, this.tracker.onGround);
|
||||
}
|
||||
} else {
|
||||
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), l1, i2, j2, (byte) j1, (byte) k1, this.tracker.onGround);
|
||||
}
|
||||
} else {
|
||||
this.y = this.tracker.onGround;
|
||||
this.v = 0;
|
||||
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
this.scanPlayers(new java.util.ArrayList(this.trackedPlayers));
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.c();
|
||||
object = new PacketPlayOutEntityTeleport(this.tracker);
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag3 = this.u;
|
||||
|
||||
if (this.tracker instanceof EntityLiving && ((EntityLiving) this.tracker).cP()) {
|
||||
flag3 = true;
|
||||
}
|
||||
|
||||
if (flag3 && this.a > 0) {
|
||||
double d0 = this.tracker.motX - this.n;
|
||||
double d1 = this.tracker.motY - this.o;
|
||||
double d2 = this.tracker.motZ - this.p;
|
||||
double d3 = 0.02D;
|
||||
double d4 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||
|
||||
if (d4 > 4.0E-4D || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) {
|
||||
this.n = this.tracker.motX;
|
||||
this.o = this.tracker.motY;
|
||||
this.p = this.tracker.motZ;
|
||||
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.n, this.o, this.p));
|
||||
}
|
||||
}
|
||||
|
||||
if (object != null) {
|
||||
// Paper start - ensure fresh viewers get an absolute position on their first update,
|
||||
// since we can't be certain what position they received in the spawn packet.
|
||||
if (object instanceof PacketPlayOutEntityTeleport) {
|
||||
this.broadcast((Packet) object);
|
||||
} else {
|
||||
PacketPlayOutEntityTeleport teleportPacket = null;
|
||||
|
||||
for (java.util.Map.Entry<EntityPlayer, Boolean> viewer : trackedPlayerMap.entrySet()) {
|
||||
if (viewer.getValue()) {
|
||||
viewer.setValue(false);
|
||||
if (teleportPacket == null) {
|
||||
teleportPacket = new PacketPlayOutEntityTeleport(this.tracker);
|
||||
}
|
||||
viewer.getKey().playerConnection.sendPacket(teleportPacket);
|
||||
} else {
|
||||
viewer.getKey().playerConnection.sendPacket((Packet) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
|
||||
this.d();
|
||||
/* CraftBukkit start - Code moved up
|
||||
if (flag1) {
|
||||
this.xLoc = k;
|
||||
this.yLoc = l;
|
||||
this.zLoc = i1;
|
||||
}
|
||||
|
||||
if (flag2) {
|
||||
this.yRot = j1;
|
||||
this.xRot = k1;
|
||||
}
|
||||
// CraftBukkit end */
|
||||
|
||||
this.x = false;
|
||||
}
|
||||
|
||||
i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
if (Math.abs(i - this.headYaw) >= 1) {
|
||||
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
|
||||
this.headYaw = i;
|
||||
}
|
||||
|
||||
this.tracker.impulse = false;
|
||||
}
|
||||
|
||||
++this.a;
|
||||
if (this.tracker.velocityChanged) {
|
||||
// CraftBukkit start - Create PlayerVelocity event
|
||||
boolean cancelled = false;
|
||||
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
Player player = (Player) this.tracker.getBukkitEntity();
|
||||
org.bukkit.util.Vector velocity = player.getVelocity();
|
||||
|
||||
PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone());
|
||||
this.tracker.world.getServer().getPluginManager().callEvent(event);
|
||||
|
||||
if (event.isCancelled()) {
|
||||
cancelled = true;
|
||||
} else if (!velocity.equals(event.getVelocity())) {
|
||||
player.setVelocity(event.getVelocity());
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.tracker.velocityChanged = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void d() {
|
||||
DataWatcher datawatcher = this.tracker.getDataWatcher();
|
||||
|
||||
if (datawatcher.a()) {
|
||||
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
|
||||
Set set = attributemapserver.getAttributes();
|
||||
|
||||
if (!set.isEmpty()) {
|
||||
// CraftBukkit start - Send scaled max health
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false);
|
||||
}
|
||||
// CraftBukkit end
|
||||
this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
|
||||
}
|
||||
|
||||
set.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void broadcast(Packet<?> packet) {
|
||||
Iterator iterator = this.trackedPlayers.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void broadcastIncludingSelf(Packet<?> packet) {
|
||||
this.broadcast(packet);
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a() {
|
||||
Iterator iterator = this.trackedPlayers.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(EntityPlayer entityplayer) {
|
||||
if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void updatePlayer(EntityPlayer entityplayer) {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot
|
||||
if (entityplayer != this.tracker) {
|
||||
if (this.c(entityplayer)) {
|
||||
if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) {
|
||||
// CraftBukkit start - respect vanish API
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
Player player = ((EntityPlayer) this.tracker).getBukkitEntity();
|
||||
if (!entityplayer.getBukkitEntity().canSee(player)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId()));
|
||||
// CraftBukkit end
|
||||
this.trackedPlayerMap.put(entityplayer, true); // Paper
|
||||
Packet packet = this.e();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(packet);
|
||||
if (!this.tracker.getDataWatcher().d()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true));
|
||||
}
|
||||
|
||||
boolean flag = this.u;
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
|
||||
Collection collection = attributemapserver.c();
|
||||
|
||||
// CraftBukkit start - If sending own attributes send scaled health instead of current maximum health
|
||||
if (this.tracker.getId() == entityplayer.getId()) {
|
||||
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(collection, false);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (!collection.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection));
|
||||
}
|
||||
|
||||
if (((EntityLiving) this.tracker).cP()) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.n = this.tracker.motX;
|
||||
this.o = this.tracker.motY;
|
||||
this.p = this.tracker.motZ;
|
||||
if (flag && !(packet instanceof PacketPlayOutSpawnEntityLiving)) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ));
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
EnumItemSlot[] aenumitemslot = EnumItemSlot.values();
|
||||
int i = aenumitemslot.length;
|
||||
|
||||
for (int j = 0; j < i; ++j) {
|
||||
EnumItemSlot enumitemslot = aenumitemslot[j];
|
||||
ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(enumitemslot);
|
||||
|
||||
if (!itemstack.isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), enumitemslot, itemstack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) this.tracker;
|
||||
|
||||
if (entityhuman.isSleeping()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker)));
|
||||
}
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityLiving) {
|
||||
// CraftBukkit start - Fix for nonsensical head yaw
|
||||
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw));
|
||||
// CraftBukkit end
|
||||
EntityLiving entityliving = (EntityLiving) this.tracker;
|
||||
Iterator iterator = entityliving.getEffects().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
MobEffect mobeffect = (MobEffect) iterator.next();
|
||||
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect));
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.tracker.bF().isEmpty()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
|
||||
}
|
||||
|
||||
if (this.tracker.isPassenger()) {
|
||||
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker.bJ()));
|
||||
}
|
||||
|
||||
this.tracker.b(entityplayer);
|
||||
entityplayer.d(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
} else if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public boolean c(EntityPlayer entityplayer) {
|
||||
// Paper start
|
||||
if (tracker.isPassenger()) {
|
||||
return isTrackedBy(tracker.getVehicle(), entityplayer);
|
||||
} else if (hasPassengerInRange(tracker, entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isInRangeOfPlayer(entityplayer);
|
||||
}
|
||||
private static boolean hasPassengerInRange(Entity entity, EntityPlayer entityplayer) {
|
||||
if (!entity.isVehicle()) {
|
||||
return false;
|
||||
}
|
||||
for (Entity passenger : entity.passengers) {
|
||||
if (passenger.tracker != null && passenger.tracker.isInRangeOfPlayer(entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
if (passenger.isVehicle()) {
|
||||
if (hasPassengerInRange(passenger, entityplayer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) {
|
||||
return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer);
|
||||
}
|
||||
private void updatePassengers(EntityPlayer player) {
|
||||
if (tracker.isVehicle()) {
|
||||
tracker.passengers.forEach((e) -> {
|
||||
if (e.tracker != null) {
|
||||
e.tracker.updatePlayer(player);
|
||||
}
|
||||
});
|
||||
player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
|
||||
}
|
||||
}
|
||||
private boolean isInRangeOfPlayer(EntityPlayer entityplayer) {
|
||||
// Paper end
|
||||
double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D;
|
||||
double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D;
|
||||
int i = Math.min(this.e, this.f);
|
||||
|
||||
return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer);
|
||||
}
|
||||
|
||||
private boolean e(EntityPlayer entityplayer) {
|
||||
return entityplayer.x().getPlayerChunkMap().a(entityplayer, this.tracker.ab, this.tracker.ad);
|
||||
}
|
||||
|
||||
public void scanPlayers(List<EntityHuman> list) {
|
||||
for (int i = 0; i < list.size(); ++i) {
|
||||
this.updatePlayer((EntityPlayer) list.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Packet<?> e() {
|
||||
if (this.tracker.dead) {
|
||||
// CraftBukkit start - Remove useless error spam, just return
|
||||
// EntityTrackerEntry.d.warn("Fetching addPacket for removed entity");
|
||||
return null;
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
if (this.tracker instanceof EntityPlayer) {
|
||||
return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker);
|
||||
} else if (this.tracker instanceof IAnimal) {
|
||||
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
|
||||
return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker);
|
||||
} else if (this.tracker instanceof EntityPainting) {
|
||||
return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker);
|
||||
} else if (this.tracker instanceof EntityItem) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 2, 1);
|
||||
} else if (this.tracker instanceof EntityMinecartAbstract) {
|
||||
EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.v().a());
|
||||
} else if (this.tracker instanceof EntityBoat) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 1);
|
||||
} else if (this.tracker instanceof EntityExperienceOrb) {
|
||||
return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker);
|
||||
} else if (this.tracker instanceof EntityFishingHook) {
|
||||
EntityHuman entityhuman = ((EntityFishingHook) this.tracker).l();
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman == null ? this.tracker.getId() : entityhuman.getId());
|
||||
} else {
|
||||
Entity entity;
|
||||
|
||||
if (this.tracker instanceof EntitySpectralArrow) {
|
||||
entity = ((EntitySpectralArrow) this.tracker).shooter;
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 91, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
|
||||
} else if (this.tracker instanceof EntityTippedArrow) {
|
||||
entity = ((EntityArrow) this.tracker).shooter;
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 60, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
|
||||
} else if (this.tracker instanceof EntitySnowball) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 61);
|
||||
} else if (this.tracker instanceof EntityLlamaSpit) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 68);
|
||||
} else if (this.tracker instanceof EntityPotion) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 73);
|
||||
} else if (this.tracker instanceof EntityThrownExpBottle) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 75);
|
||||
} else if (this.tracker instanceof EntityEnderPearl) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 65);
|
||||
} else if (this.tracker instanceof EntityEnderSignal) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 72);
|
||||
} else if (this.tracker instanceof EntityFireworks) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 76);
|
||||
} else if (this.tracker instanceof EntityFireball) {
|
||||
EntityFireball entityfireball = (EntityFireball) this.tracker;
|
||||
PacketPlayOutSpawnEntity packetplayoutspawnentity = null;
|
||||
byte b0 = 63;
|
||||
|
||||
if (this.tracker instanceof EntitySmallFireball) {
|
||||
b0 = 64;
|
||||
} else if (this.tracker instanceof EntityDragonFireball) {
|
||||
b0 = 93;
|
||||
} else if (this.tracker instanceof EntityWitherSkull) {
|
||||
b0 = 66;
|
||||
}
|
||||
|
||||
if (entityfireball.shooter != null) {
|
||||
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId());
|
||||
} else {
|
||||
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0);
|
||||
}
|
||||
|
||||
packetplayoutspawnentity.a((int) (entityfireball.dirX * 8000.0D));
|
||||
packetplayoutspawnentity.b((int) (entityfireball.dirY * 8000.0D));
|
||||
packetplayoutspawnentity.c((int) (entityfireball.dirZ * 8000.0D));
|
||||
return packetplayoutspawnentity;
|
||||
} else if (this.tracker instanceof EntityShulkerBullet) {
|
||||
PacketPlayOutSpawnEntity packetplayoutspawnentity1 = new PacketPlayOutSpawnEntity(this.tracker, 67, 0);
|
||||
|
||||
packetplayoutspawnentity1.a((int) (this.tracker.motX * 8000.0D));
|
||||
packetplayoutspawnentity1.b((int) (this.tracker.motY * 8000.0D));
|
||||
packetplayoutspawnentity1.c((int) (this.tracker.motZ * 8000.0D));
|
||||
return packetplayoutspawnentity1;
|
||||
} else if (this.tracker instanceof EntityEgg) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 62);
|
||||
} else if (this.tracker instanceof EntityEvokerFangs) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 79);
|
||||
} else if (this.tracker instanceof EntityTNTPrimed) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 50);
|
||||
} else if (this.tracker instanceof EntityEnderCrystal) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 51);
|
||||
} else if (this.tracker instanceof EntityFallingBlock) {
|
||||
EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getCombinedId(entityfallingblock.getBlock()));
|
||||
} else if (this.tracker instanceof EntityArmorStand) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 78);
|
||||
} else if (this.tracker instanceof EntityItemFrame) {
|
||||
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction.get2DRotationValue(), entityitemframe.getBlockPosition());
|
||||
} else if (this.tracker instanceof EntityLeash) {
|
||||
EntityLeash entityleash = (EntityLeash) this.tracker;
|
||||
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 77, 0, entityleash.getBlockPosition());
|
||||
} else if (this.tracker instanceof EntityAreaEffectCloud) {
|
||||
return new PacketPlayOutSpawnEntity(this.tracker, 3);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Don\'t know how to add " + this.tracker.getClass() + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(EntityPlayer entityplayer) {
|
||||
org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot
|
||||
if (this.trackedPlayers.contains(entityplayer)) {
|
||||
this.trackedPlayers.remove(entityplayer);
|
||||
this.tracker.c(entityplayer);
|
||||
entityplayer.c(this.tracker);
|
||||
updatePassengers(entityplayer); // Paper
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Entity b() {
|
||||
return this.tracker;
|
||||
}
|
||||
|
||||
public void a(int i) {
|
||||
this.f = i;
|
||||
}
|
||||
|
||||
public void c() {
|
||||
this.isMoving = false;
|
||||
}
|
||||
}
|
||||
@@ -10,10 +10,8 @@ import java.util.Random;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add OBFHELPER<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 1) Add OBFHELPER (panda redstone)
|
||||
*/
|
||||
public enum EnumDirection implements INamable {
|
||||
|
||||
|
||||
504
sources/src/main/java/net/minecraft/server/Explosion.java
Normal file
504
sources/src/main/java/net/minecraft/server/Explosion.java
Normal file
@@ -0,0 +1,504 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.craftbukkit.event.CraftEventFactory;
|
||||
import org.bukkit.event.entity.EntityExplodeEvent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.event.block.BlockExplodeEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class Explosion {
|
||||
|
||||
private final boolean a;
|
||||
private final boolean b;
|
||||
private final Random c = new Random();
|
||||
private final World world;
|
||||
private final double posX;
|
||||
private final double posY;
|
||||
private final double posZ;
|
||||
public final Entity source;
|
||||
private final float size;
|
||||
private final ArrayList<BlockPosition> blocks = Lists.newArrayList();
|
||||
private final Map<EntityHuman, Vec3D> k = Maps.newHashMap();
|
||||
public boolean wasCanceled = false; // CraftBukkit - add field
|
||||
|
||||
// Dionysus start
|
||||
private final BlockPosition.MutableBlockPosition cachedPos = new BlockPosition.MutableBlockPosition();
|
||||
// The chunk coordinate of the most recently stepped through block.
|
||||
private int prevChunkX = Integer.MIN_VALUE;
|
||||
private int prevChunkZ = Integer.MIN_VALUE;
|
||||
|
||||
// The chunk belonging to prevChunkPos.
|
||||
private Chunk prevChunk;
|
||||
|
||||
private static final com.google.common.base.Predicate<Entity> hitPredicate = entity -> IEntitySelector.d.apply(entity) && !entity.dead; // Dionysus - Paper - don't hit dead entities
|
||||
// Dionysus end
|
||||
|
||||
public Explosion(World world, Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) {
|
||||
this.world = world;
|
||||
this.source = entity;
|
||||
this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values
|
||||
this.posX = d0;
|
||||
this.posY = d1;
|
||||
this.posZ = d2;
|
||||
this.a = flag;
|
||||
this.b = flag1;
|
||||
}
|
||||
|
||||
public void a() {
|
||||
// CraftBukkit start
|
||||
if (this.size < 0.1F) {
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
// Dionysus start - optimize memory usage from explosions
|
||||
// CaffeineMC optimized raytracing loop
|
||||
// @author JellySquid
|
||||
// @author nopjmp
|
||||
// https://github.com/CaffeineMC/lithium-fabric
|
||||
// Using integer encoding for the block positions provides a massive speedup and prevents us from needing to
|
||||
// allocate a block position for every step we make along each ray, eliminating essentially all the memory
|
||||
// allocations of this function. The overhead of packing block positions into integer format is negligible
|
||||
// compared to a memory allocation and associated overhead of hashing real objects in a set.
|
||||
final LongOpenHashSet touched = new LongOpenHashSet(0);
|
||||
final Random random = this.world.random;
|
||||
for (int rayX = 0; rayX < 16; ++rayX) {
|
||||
boolean xPlane = rayX == 0 || rayX == 15;
|
||||
double vecX = (((float) rayX / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
for (int rayY = 0; rayY < 16; ++rayY) {
|
||||
boolean yPlane = rayY == 0 || rayY == 15;
|
||||
double vecY = (((float) rayY / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
for (int rayZ = 0; rayZ < 16; ++rayZ) {
|
||||
boolean zPlane = rayZ == 0 || rayZ == 15;
|
||||
|
||||
// We only fire rays from the surface of our origin volume
|
||||
if (xPlane || yPlane || zPlane) {
|
||||
double vecZ = (((float) rayZ / 15.0F) * 2.0F) - 1.0F;
|
||||
|
||||
this.performRayCast(random, vecX, vecY, vecZ, touched);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We can now iterate back over the set of positions we modified and re-build BlockPos objects from them
|
||||
// This will only allocate as many objects as there are in the set, where otherwise we would allocate them
|
||||
// each step of a every ray.
|
||||
blocks.ensureCapacity(touched.size());
|
||||
for (Long longPos : touched) {
|
||||
blocks.add(BlockPosition.fromLong(longPos));
|
||||
}
|
||||
float f3 = this.size * 2.0F;
|
||||
|
||||
int i = MathHelper.floor(this.posX - (double) f3 - 1.0D);
|
||||
int j = MathHelper.floor(this.posX + (double) f3 + 1.0D);
|
||||
int l = MathHelper.floor(this.posY - (double) f3 - 1.0D);
|
||||
int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D);
|
||||
int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D);
|
||||
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D);
|
||||
// Paper start - Fix lag from explosions processing dead entities
|
||||
List<Entity> list = this.world.getEntities(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), hitPredicate);
|
||||
// Paper end
|
||||
Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ);
|
||||
|
||||
for (Entity entity : list) {
|
||||
|
||||
if (!entity.bB()) {
|
||||
double d7 = entity.e(this.posX, this.posY, this.posZ) / (double) f3;
|
||||
|
||||
if (d7 <= 1.0D) {
|
||||
double d8 = entity.locX - this.posX;
|
||||
double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY;
|
||||
double d10 = entity.locZ - this.posZ;
|
||||
double d11 = MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10);
|
||||
|
||||
if (d11 != 0.0D) {
|
||||
d8 /= d11;
|
||||
d9 /= d11;
|
||||
d10 /= d11;
|
||||
double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // Paper - Optimize explosions
|
||||
double d13 = (1.0D - d7) * d12;
|
||||
|
||||
// CraftBukkit start
|
||||
// entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
||||
CraftEventFactory.entityDamage = source;
|
||||
entity.forceExplosionKnockback = false;
|
||||
boolean wasDamaged = entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
|
||||
CraftEventFactory.entityDamage = null;
|
||||
if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) {
|
||||
continue;
|
||||
}
|
||||
// CraftBukkit end
|
||||
double d14 = d13;
|
||||
|
||||
if (entity instanceof EntityLiving) {
|
||||
d14 = entity instanceof EntityHuman && world.paperConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a((EntityLiving) entity, d13); // Paper - Disable explosion knockback
|
||||
}
|
||||
|
||||
entity.motX += d8 * d14;
|
||||
entity.motY += d9 * d14;
|
||||
entity.motZ += d10 * d14;
|
||||
if (entity instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) entity;
|
||||
|
||||
if (!entityhuman.isSpectator() && (!entityhuman.z() && !world.paperConfig.disableExplosionKnockback || !entityhuman.abilities.isFlying)) { // Paper - Disable explosion knockback
|
||||
this.k.put(entityhuman, new Vec3D(d8 * d13, d9 * d13, d10 * d13));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void performRayCast(Random random, double vecX, double vecY, double vecZ, LongOpenHashSet touched) {
|
||||
double dist = Math.sqrt((vecX * vecX) + (vecY * vecY) + (vecZ * vecZ));
|
||||
|
||||
double normX = (vecX / dist) * 0.3D;
|
||||
double normY = (vecY / dist) * 0.3D;
|
||||
double normZ = (vecZ / dist) * 0.3D;
|
||||
|
||||
float strength = this.size * (0.7F + (random.nextFloat() * 0.6F));
|
||||
|
||||
double stepX = this.posX;
|
||||
double stepY = this.posY;
|
||||
double stepZ = this.posZ;
|
||||
|
||||
int prevX = Integer.MIN_VALUE;
|
||||
int prevY = Integer.MIN_VALUE;
|
||||
int prevZ = Integer.MIN_VALUE;
|
||||
|
||||
float prevResistance = 0.0F;
|
||||
|
||||
// Step through the ray until it is finally stopped
|
||||
while (strength > 0.0F) {
|
||||
int blockX = MathHelper.floor(stepX);
|
||||
int blockY = MathHelper.floor(stepY);
|
||||
int blockZ = MathHelper.floor(stepZ);
|
||||
|
||||
float resistance;
|
||||
|
||||
// Check whether or not we have actually moved into a new block this step. Due to how rays are stepped through,
|
||||
// over-sampling of the same block positions will occur. Changing this behaviour would introduce differences in
|
||||
// aliasing and sampling, which is unacceptable for our purposes. As a band-aid, we can simply re-use the
|
||||
// previous result and get a decent boost.
|
||||
if (prevX != blockX || prevY != blockY || prevZ != blockZ) {
|
||||
resistance = this.traverseBlock(strength, blockX, blockY, blockZ, touched);
|
||||
|
||||
prevX = blockX;
|
||||
prevY = blockY;
|
||||
prevZ = blockZ;
|
||||
|
||||
prevResistance = resistance;
|
||||
} else {
|
||||
resistance = prevResistance;
|
||||
}
|
||||
|
||||
strength -= resistance;
|
||||
// Apply a constant fall-off
|
||||
strength -= 0.22500001F;
|
||||
|
||||
stepX += normX;
|
||||
stepY += normY;
|
||||
stepZ += normZ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for every step made by a ray being cast by an explosion.
|
||||
*
|
||||
* @param strength The strength of the ray during this step
|
||||
* @param blockX The x-coordinate of the block the ray is inside of
|
||||
* @param blockY The y-coordinate of the block the ray is inside of
|
||||
* @param blockZ The z-coordinate of the block the ray is inside of
|
||||
* @return The resistance of the current block space to the ray
|
||||
*/
|
||||
private float traverseBlock(float strength, int blockX, int blockY, int blockZ, LongOpenHashSet touched) {
|
||||
cachedPos.c(blockX, blockY, blockZ);
|
||||
IBlockData iblockdata = this.world.getType(cachedPos);
|
||||
|
||||
// Early-exit if the y-coordinate is out of bounds.
|
||||
if (cachedPos.isInvalidYLocation()) {
|
||||
if (iblockdata.getMaterial() != Material.AIR) {
|
||||
float blastResistance = this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null);
|
||||
return (blastResistance + 0.3F) * 0.3F;
|
||||
}
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
|
||||
int chunkX = blockX >> 4;
|
||||
int chunkZ = blockZ >> 4;
|
||||
|
||||
// Avoid calling into the chunk manager as much as possible through managing chunks locally
|
||||
if (this.prevChunkX != chunkX || this.prevChunkZ != chunkZ) {
|
||||
this.prevChunk = this.world.getChunkAt(chunkX, chunkZ);
|
||||
|
||||
this.prevChunkX = chunkX;
|
||||
this.prevChunkZ = chunkZ;
|
||||
}
|
||||
|
||||
final Chunk chunk = this.prevChunk;
|
||||
|
||||
float totalResistance = 0.0F;
|
||||
Optional<Float> blastResistance = Optional.empty();
|
||||
|
||||
// If the chunk is missing or out of bounds, assume that it is air
|
||||
if (chunk != null) {
|
||||
// We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much
|
||||
// performance as possible here
|
||||
ChunkSection section = chunk.getSections()[blockY >> 4];
|
||||
|
||||
// If the section doesn't exist or it's empty, assume that the block is air
|
||||
if (section != null && !section.a()) {
|
||||
// Retrieve the block state from the chunk section directly to avoid associated overhead
|
||||
IBlockData blockState = section.getType(blockX & 15, blockY & 15, blockZ & 15);
|
||||
|
||||
// If the block state is air, it cannot have fluid or any kind of resistance, so just leave
|
||||
if (blockState.getBlock() != Blocks.AIR) {
|
||||
// Get the explosion resistance like vanilla
|
||||
blastResistance = Optional.of(this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate how much this block will resist an explosion's ray
|
||||
if (blastResistance.isPresent()) {
|
||||
totalResistance = (blastResistance.get() + 0.3F) * 0.3F;
|
||||
}
|
||||
|
||||
// Check if this ray is still strong enough to break blocks, and if so, add this position to the set
|
||||
// of positions to destroy
|
||||
float reducedStrength = strength - totalResistance;
|
||||
if (reducedStrength > 0.0F && (this.a || iblockdata.getMaterial() != Material.AIR)) {
|
||||
if ((this.source == null || this.source.a(this, this.world, cachedPos, iblockdata, reducedStrength)) && cachedPos.getY() < 256 && cachedPos.getY() >= 0) {
|
||||
touched.add(cachedPos.asLong());
|
||||
}
|
||||
}
|
||||
|
||||
return totalResistance;
|
||||
}
|
||||
// Dionysus end
|
||||
|
||||
|
||||
public void a(boolean flag) {
|
||||
this.world.a((EntityHuman) null, this.posX, this.posY, this.posZ, SoundEffects.bV, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F);
|
||||
if (this.size >= 2.0F && this.b) {
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_HUGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
|
||||
} else {
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
|
||||
}
|
||||
|
||||
Iterator iterator;
|
||||
BlockPosition blockposition;
|
||||
|
||||
if (this.b) {
|
||||
// CraftBukkit start
|
||||
org.bukkit.World bworld = this.world.getWorld();
|
||||
org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity();
|
||||
Location location = new Location(bworld, this.posX, this.posY, this.posZ);
|
||||
|
||||
List<org.bukkit.block.Block> blockList = Lists.newArrayList();
|
||||
for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) {
|
||||
BlockPosition cpos = (BlockPosition) this.blocks.get(i1);
|
||||
org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
|
||||
if (bblock.getType() != org.bukkit.Material.AIR) {
|
||||
blockList.add(bblock);
|
||||
}
|
||||
}
|
||||
|
||||
boolean cancelled;
|
||||
List<org.bukkit.block.Block> bukkitBlocks;
|
||||
float yield;
|
||||
|
||||
if (explode != null) {
|
||||
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 1.0F / this.size);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
cancelled = event.isCancelled();
|
||||
bukkitBlocks = event.blockList();
|
||||
yield = event.getYield();
|
||||
} else {
|
||||
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 1.0F / this.size);
|
||||
this.world.getServer().getPluginManager().callEvent(event);
|
||||
cancelled = event.isCancelled();
|
||||
bukkitBlocks = event.blockList();
|
||||
yield = event.getYield();
|
||||
}
|
||||
|
||||
this.blocks.clear();
|
||||
|
||||
this.blocks.ensureCapacity(bukkitBlocks.size());
|
||||
for (org.bukkit.block.Block bblock : bukkitBlocks) {
|
||||
BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ());
|
||||
blocks.add(coords);
|
||||
}
|
||||
|
||||
if (cancelled) {
|
||||
this.wasCanceled = true;
|
||||
return;
|
||||
}
|
||||
// CraftBukkit end
|
||||
iterator = this.blocks.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
blockposition = (BlockPosition) iterator.next();
|
||||
IBlockData iblockdata = this.world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray
|
||||
|
||||
if (flag) {
|
||||
double d0 = (double) ((float) blockposition.getX() + this.world.random.nextFloat());
|
||||
double d1 = (double) ((float) blockposition.getY() + this.world.random.nextFloat());
|
||||
double d2 = (double) ((float) blockposition.getZ() + this.world.random.nextFloat());
|
||||
double d3 = d0 - this.posX;
|
||||
double d4 = d1 - this.posY;
|
||||
double d5 = d2 - this.posZ;
|
||||
double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5);
|
||||
|
||||
d3 /= d6;
|
||||
d4 /= d6;
|
||||
d5 /= d6;
|
||||
double d7 = 0.5D / (d6 / (double) this.size + 0.1D);
|
||||
|
||||
d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F);
|
||||
d3 *= d7;
|
||||
d4 *= d7;
|
||||
d5 *= d7;
|
||||
this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5);
|
||||
this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5);
|
||||
}
|
||||
|
||||
if (iblockdata.getMaterial() != Material.AIR) {
|
||||
if (block.a(this)) {
|
||||
// CraftBukkit - add yield
|
||||
block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), yield, 0);
|
||||
}
|
||||
|
||||
this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3);
|
||||
block.wasExploded(this.world, blockposition, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.a) {
|
||||
iterator = this.blocks.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
blockposition = (BlockPosition) iterator.next();
|
||||
if (this.world.getType(blockposition).getMaterial() == Material.AIR && this.world.getType(blockposition.down()).b() && this.c.nextInt(3) == 0) {
|
||||
// CraftBukkit start - Ignition by explosion
|
||||
if (!CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) {
|
||||
this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Map<EntityHuman, Vec3D> b() {
|
||||
return this.k;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public EntityLiving getSource() {
|
||||
// CraftBukkit start - obtain Fireball shooter for explosion tracking
|
||||
return this.source == null ? null : (this.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) this.source).getSource() : (this.source instanceof EntityLiving ? (EntityLiving) this.source : (this.source instanceof EntityFireball ? ((EntityFireball) this.source).shooter : null)));
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
public void clearBlocks() {
|
||||
this.blocks.clear();
|
||||
}
|
||||
|
||||
public List<BlockPosition> getBlocks() {
|
||||
return this.blocks;
|
||||
}
|
||||
|
||||
// Paper start - Optimize explosions
|
||||
private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) {
|
||||
if (!this.world.paperConfig.optimizeExplosions) {
|
||||
return this.world.a(vec3d, aabb);
|
||||
}
|
||||
CacheKey key = new CacheKey(this, aabb);
|
||||
return this.world.explosionDensityCache.computeIfAbsent(key, k1 -> this.world.a(vec3d, aabb));
|
||||
}
|
||||
|
||||
static class CacheKey {
|
||||
private final World world;
|
||||
private final double posX, posY, posZ;
|
||||
private final double minX, minY, minZ;
|
||||
private final double maxX, maxY, maxZ;
|
||||
|
||||
public CacheKey(Explosion explosion, AxisAlignedBB aabb) {
|
||||
this.world = explosion.world;
|
||||
this.posX = explosion.posX;
|
||||
this.posY = explosion.posY;
|
||||
this.posZ = explosion.posZ;
|
||||
this.minX = aabb.a;
|
||||
this.minY = aabb.b;
|
||||
this.minZ = aabb.c;
|
||||
this.maxX = aabb.d;
|
||||
this.maxY = aabb.e;
|
||||
this.maxZ = aabb.f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
CacheKey cacheKey = (CacheKey) o;
|
||||
|
||||
if (Double.compare(cacheKey.posX, posX) != 0) return false;
|
||||
if (Double.compare(cacheKey.posY, posY) != 0) return false;
|
||||
if (Double.compare(cacheKey.posZ, posZ) != 0) return false;
|
||||
if (Double.compare(cacheKey.minX, minX) != 0) return false;
|
||||
if (Double.compare(cacheKey.minY, minY) != 0) return false;
|
||||
if (Double.compare(cacheKey.minZ, minZ) != 0) return false;
|
||||
if (Double.compare(cacheKey.maxX, maxX) != 0) return false;
|
||||
if (Double.compare(cacheKey.maxY, maxY) != 0) return false;
|
||||
if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false;
|
||||
return world.equals(cacheKey.world);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = world.hashCode();
|
||||
temp = Double.doubleToLongBits(posX);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(posY);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(posZ);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(minX);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(minY);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(minZ);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(maxX);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(maxY);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(maxZ);
|
||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
81
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
81
sources/src/main/java/net/minecraft/server/FileIOThread.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Multi-threaded chunk saving (performance)
|
||||
*/
|
||||
public class FileIOThread implements Runnable {
|
||||
|
||||
private static final FileIOThread a = new FileIOThread();
|
||||
private final List<IAsyncChunkSaver> b = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this
|
||||
private volatile long c;
|
||||
private volatile long d;
|
||||
private volatile boolean e;
|
||||
|
||||
private FileIOThread() {
|
||||
// Thread thread = new Thread(this, "File IO Thread"); // Akarin
|
||||
|
||||
// thread.setPriority(1); // Akarin
|
||||
// thread.start(); // Akarin
|
||||
}
|
||||
|
||||
public static FileIOThread a() {
|
||||
return FileIOThread.a;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (true) {
|
||||
this.c();
|
||||
}
|
||||
}
|
||||
|
||||
private void c() {
|
||||
for (int i = 0; i < this.b.size(); ++i) {
|
||||
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i);
|
||||
boolean flag = iasyncchunksaver.a();
|
||||
|
||||
if (!flag) {
|
||||
this.b.remove(i--);
|
||||
++this.d;
|
||||
}
|
||||
|
||||
// Paper start - Add toggle
|
||||
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) {
|
||||
try {
|
||||
Thread.sleep(this.e ? 0L : 2L);
|
||||
} catch (InterruptedException interruptedexception) {
|
||||
interruptedexception.printStackTrace();
|
||||
}
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
|
||||
if (this.b.isEmpty()) {
|
||||
try {
|
||||
Thread.sleep(25L);
|
||||
} catch (InterruptedException interruptedexception1) {
|
||||
interruptedexception1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void a(IAsyncChunkSaver iasyncchunksaver) {
|
||||
if (!this.b.contains(iasyncchunksaver)) {
|
||||
++this.c;
|
||||
this.b.add(iasyncchunksaver);
|
||||
}
|
||||
}
|
||||
|
||||
public void b() throws InterruptedException {
|
||||
this.e = true;
|
||||
|
||||
while (this.c != this.d) {
|
||||
Thread.sleep(10L);
|
||||
}
|
||||
|
||||
this.e = false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
// CraftBukkit start
|
||||
import java.net.InetAddress;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
// CraftBukkit end
|
||||
|
||||
public class HandshakeListener implements PacketHandshakingInListener {
|
||||
|
||||
private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot
|
||||
// CraftBukkit start - add fields
|
||||
private static final Object2LongOpenHashMap<InetAddress> throttleTracker = new Object2LongOpenHashMap<>();
|
||||
private static int throttleCounter = 0;
|
||||
// CraftBukkit end
|
||||
|
||||
private final MinecraftServer a;
|
||||
private final NetworkManager b;
|
||||
private NetworkManager getNetworkManager() { return b; } // Paper - OBFHELPER
|
||||
|
||||
public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) {
|
||||
this.a = minecraftserver;
|
||||
this.b = networkmanager;
|
||||
}
|
||||
|
||||
public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) {
|
||||
switch (packethandshakinginsetprotocol.a()) {
|
||||
case LOGIN:
|
||||
this.b.setProtocol(EnumProtocol.LOGIN);
|
||||
ChatMessage chatmessage;
|
||||
|
||||
// CraftBukkit start - Connection throttle
|
||||
try {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle();
|
||||
InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress();
|
||||
|
||||
synchronized (throttleTracker) {
|
||||
if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.getLong(address) < connectionThrottle) {
|
||||
throttleTracker.put(address, currentTime);
|
||||
chatmessage = new ChatMessage("Connection throttled! Please wait before reconnecting.");
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
|
||||
throttleTracker.put(address, currentTime);
|
||||
throttleCounter++;
|
||||
if (throttleCounter > 200) {
|
||||
throttleCounter = 0;
|
||||
|
||||
// Cleanup stale entries
|
||||
throttleTracker.object2LongEntrySet().removeIf(entry -> entry.getLongValue() > connectionThrottle); // Dionysus
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t);
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
if (packethandshakinginsetprotocol.b() > 340) {
|
||||
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
} else if (packethandshakinginsetprotocol.b() < 340) {
|
||||
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
} else {
|
||||
this.b.setPacketListener(new LoginListener(this.a, this.b));
|
||||
// Paper start - handshake event
|
||||
boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee;
|
||||
boolean handledByEvent = false;
|
||||
// Try and handle the handshake through the event
|
||||
if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me?
|
||||
com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled);
|
||||
if (event.callEvent()) {
|
||||
// If we've failed somehow, let the client know so and go no further.
|
||||
if (event.isFailed()) {
|
||||
chatmessage = new ChatMessage(event.getFailMessage());
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
|
||||
packethandshakinginsetprotocol.hostname = event.getServerHostname();
|
||||
this.b.l = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort());
|
||||
this.b.spoofedUUID = event.getUniqueId();
|
||||
this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class);
|
||||
handledByEvent = true; // Hooray, we did it!
|
||||
}
|
||||
}
|
||||
// Don't try and handle default logic if it's been handled by the event.
|
||||
if (!handledByEvent && proxyLogicEnabled) {
|
||||
// Paper end
|
||||
// Spigot Start
|
||||
//if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above!
|
||||
String[] split = packethandshakinginsetprotocol.hostname.split("\00");
|
||||
if ( split.length == 3 || split.length == 4 ) {
|
||||
packethandshakinginsetprotocol.hostname = split[0];
|
||||
b.l = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort());
|
||||
b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] );
|
||||
} else
|
||||
{
|
||||
chatmessage = new ChatMessage("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
|
||||
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
|
||||
this.b.close(chatmessage);
|
||||
return;
|
||||
}
|
||||
if ( split.length == 4 )
|
||||
{
|
||||
b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class);
|
||||
}
|
||||
}
|
||||
// Spigot End
|
||||
((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname
|
||||
}
|
||||
break;
|
||||
|
||||
case STATUS:
|
||||
this.b.setProtocol(EnumProtocol.STATUS);
|
||||
this.b.setPacketListener(new PacketStatusListener(this.a, this.b));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.a());
|
||||
}
|
||||
|
||||
// Paper start - NetworkClient implementation
|
||||
this.getNetworkManager().protocolVersion = packethandshakinginsetprotocol.getProtocolVersion();
|
||||
this.getNetworkManager().virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packethandshakinginsetprotocol.hostname, packethandshakinginsetprotocol.port);
|
||||
// Paper end
|
||||
}
|
||||
|
||||
public void a(IChatBaseComponent ichatbasecomponent) {}
|
||||
}
|
||||
113
sources/src/main/java/net/minecraft/server/IntCache.java
Normal file
113
sources/src/main/java/net/minecraft/server/IntCache.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
// NeonPaper start
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
// NeonPaper end
|
||||
|
||||
public class IntCache {
|
||||
// NeonPaper start - Refactored IntCache to be thread local instead of static
|
||||
private static final ThreadLocal<IntCache> caches = new ThreadLocal<IntCache>() {
|
||||
@Override
|
||||
protected IntCache initialValue() {
|
||||
IntCache cache = new IntCache();
|
||||
synchronized (ALL_CACHES) {
|
||||
ALL_CACHES.add(new WeakReference<>(cache));
|
||||
}
|
||||
return new IntCache();
|
||||
}
|
||||
};
|
||||
|
||||
private static final List<WeakReference<IntCache>> ALL_CACHES = new ObjectArrayList<>();
|
||||
|
||||
private int a = 256;
|
||||
private final List<int[]> b = new ObjectArrayList<>();
|
||||
private final List<int[]> c = new ObjectArrayList<>();
|
||||
private final List<int[]> d = new ObjectArrayList<>();
|
||||
private final List<int[]> e = new ObjectArrayList<>();
|
||||
|
||||
private final int cacheLimit = org.spigotmc.SpigotConfig.intCacheLimit;
|
||||
|
||||
public static int[] a(int i) {
|
||||
return caches.get().aNonStatic(i);
|
||||
}
|
||||
|
||||
public int[] aNonStatic(int i) {
|
||||
int[] aint;
|
||||
|
||||
if (i <= 256) {
|
||||
if (this.b.isEmpty()) {
|
||||
aint = new int[256];
|
||||
if (c.size() < cacheLimit) this.c.add(aint);
|
||||
return aint;
|
||||
} else {
|
||||
aint = this.b.remove(this.b.size() - 1);
|
||||
if (c.size() < cacheLimit) this.c.add(aint);
|
||||
return aint;
|
||||
}
|
||||
} else if (i > this.a) {
|
||||
this.a = i;
|
||||
this.d.clear();
|
||||
this.e.clear();
|
||||
aint = new int[this.a];
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
} else if (this.d.isEmpty()) {
|
||||
aint = new int[this.a];
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
} else {
|
||||
aint = this.d.remove(this.d.size() - 1);
|
||||
if (e.size() < cacheLimit) this.e.add(aint);
|
||||
return aint;
|
||||
}
|
||||
}
|
||||
|
||||
public static void a() {
|
||||
caches.get().aNonStatic();
|
||||
}
|
||||
|
||||
public void aNonStatic() {
|
||||
if (!this.d.isEmpty()) {
|
||||
this.d.remove(this.d.size() - 1);
|
||||
}
|
||||
|
||||
if (!this.b.isEmpty()) {
|
||||
this.b.remove(this.b.size() - 1);
|
||||
}
|
||||
|
||||
this.d.addAll(this.e);
|
||||
this.b.addAll(this.c);
|
||||
this.e.clear();
|
||||
this.c.clear();
|
||||
}
|
||||
|
||||
public static String b() {
|
||||
int cache = 0;
|
||||
int tcache = 0;
|
||||
int allocated = 0;
|
||||
int tallocated = 0;
|
||||
int numberOfCaches;
|
||||
|
||||
synchronized (ALL_CACHES) {
|
||||
numberOfCaches = ALL_CACHES.size();
|
||||
Iterator<WeakReference<IntCache>> iter = ALL_CACHES.iterator();
|
||||
while (iter.hasNext()) {
|
||||
WeakReference<IntCache> reference = iter.next();
|
||||
IntCache intcache = reference.get();
|
||||
if (intcache != null) {
|
||||
cache += intcache.d.size();
|
||||
tcache += intcache.b.size();
|
||||
allocated += intcache.e.size();
|
||||
tallocated += intcache.c.size();
|
||||
} else {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return numberOfCaches + " IntCaches. In Total => cache: " + cache + ", tcache: " + tcache + ", allocated: " + allocated + ", tallocated: " + tallocated;
|
||||
}
|
||||
// NeonPaper end
|
||||
}
|
||||
@@ -3,10 +3,8 @@ package net.minecraft.server;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add end portal disable feature<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 1) Add end portal disable feature (feature)
|
||||
*/
|
||||
public class ItemEnderEye extends Item {
|
||||
|
||||
|
||||
243
sources/src/main/java/net/minecraft/server/ItemMonsterEgg.java
Normal file
243
sources/src/main/java/net/minecraft/server/ItemMonsterEgg.java
Normal file
@@ -0,0 +1,243 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
|
||||
/**
|
||||
* Akarin Changes Note
|
||||
* 1) Restricted spawner modify (feature)
|
||||
*/
|
||||
public class ItemMonsterEgg extends Item {
|
||||
|
||||
public ItemMonsterEgg() {
|
||||
this.b(CreativeModeTab.f);
|
||||
}
|
||||
|
||||
public String b(ItemStack itemstack) {
|
||||
String s = ("" + LocaleI18n.get(this.getName() + ".name")).trim();
|
||||
String s1 = EntityTypes.a(h(itemstack));
|
||||
|
||||
if (s1 != null) {
|
||||
s = s + " " + LocaleI18n.get("entity." + s1 + ".name");
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (world.isClientSide) {
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
} else if (!entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack)) {
|
||||
return EnumInteractionResult.FAIL;
|
||||
} else {
|
||||
IBlockData iblockdata = world.getType(blockposition);
|
||||
Block block = iblockdata.getBlock();
|
||||
|
||||
if (block == Blocks.MOB_SPAWNER && (AkarinGlobalConfig.allowSpawnerModify || entityhuman.isCreativeAndOp())) { // Akarin
|
||||
TileEntity tileentity = world.getTileEntity(blockposition);
|
||||
|
||||
if (tileentity instanceof TileEntityMobSpawner) {
|
||||
MobSpawnerAbstract mobspawnerabstract = ((TileEntityMobSpawner) tileentity).getSpawner();
|
||||
|
||||
mobspawnerabstract.setMobName(h(itemstack));
|
||||
tileentity.update();
|
||||
world.notify(blockposition, iblockdata, iblockdata, 3);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
BlockPosition blockposition1 = blockposition.shift(enumdirection);
|
||||
double d0 = this.a(world, blockposition1);
|
||||
Entity entity = a(world, h(itemstack), (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + d0, (double) blockposition1.getZ() + 0.5D);
|
||||
|
||||
if (entity != null) {
|
||||
if (entity instanceof EntityLiving && itemstack.hasName()) {
|
||||
entity.setCustomName(itemstack.getName());
|
||||
}
|
||||
|
||||
a(world, entityhuman, itemstack, entity);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
}
|
||||
|
||||
return EnumInteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
protected double a(World world, BlockPosition blockposition) {
|
||||
AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockposition)).b(0.0D, -1.0D, 0.0D);
|
||||
List list = world.getCubes((Entity) null, axisalignedbb);
|
||||
|
||||
if (list.isEmpty()) {
|
||||
return 0.0D;
|
||||
} else {
|
||||
double d0 = axisalignedbb.b;
|
||||
|
||||
AxisAlignedBB axisalignedbb1;
|
||||
|
||||
for (Iterator iterator = list.iterator(); iterator.hasNext(); d0 = Math.max(axisalignedbb1.e, d0)) {
|
||||
axisalignedbb1 = (AxisAlignedBB) iterator.next();
|
||||
}
|
||||
|
||||
return d0 - (double) blockposition.getY();
|
||||
}
|
||||
}
|
||||
|
||||
public static void a(World world, @Nullable EntityHuman entityhuman, ItemStack itemstack, @Nullable Entity entity) {
|
||||
MinecraftServer minecraftserver = world.getMinecraftServer();
|
||||
|
||||
if (minecraftserver != null && entity != null) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) {
|
||||
if (!world.isClientSide && entity.bC() && (entityhuman == null || !minecraftserver.getPlayerList().isOp(entityhuman.getProfile()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
NBTTagCompound nbttagcompound1 = entity.save(new NBTTagCompound());
|
||||
UUID uuid = entity.getUniqueID();
|
||||
|
||||
nbttagcompound1.a(nbttagcompound.getCompound("EntityTag"));
|
||||
entity.a(uuid);
|
||||
entity.f(nbttagcompound1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
|
||||
ItemStack itemstack = entityhuman.b(enumhand);
|
||||
|
||||
if (world.isClientSide) {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
} else {
|
||||
MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true);
|
||||
|
||||
if (movingobjectposition != null && movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) {
|
||||
BlockPosition blockposition = movingobjectposition.a();
|
||||
|
||||
if (!(world.getType(blockposition).getBlock() instanceof BlockFluids)) {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
} else if (world.a(entityhuman, blockposition) && entityhuman.a(blockposition, movingobjectposition.direction, itemstack)) {
|
||||
Entity entity = a(world, h(itemstack), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D);
|
||||
|
||||
if (entity == null) {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
} else {
|
||||
if (entity instanceof EntityLiving && itemstack.hasName()) {
|
||||
entity.setCustomName(itemstack.getName());
|
||||
}
|
||||
|
||||
a(world, entityhuman, itemstack, entity);
|
||||
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||
itemstack.subtract(1);
|
||||
}
|
||||
|
||||
entityhuman.b(StatisticList.b((Item) this));
|
||||
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
|
||||
}
|
||||
} else {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack);
|
||||
}
|
||||
} else {
|
||||
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Entity a(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2) {
|
||||
return spawnCreature(world, minecraftkey, d0, d1, d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Entity spawnCreature(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
|
||||
if (minecraftkey != null && EntityTypes.eggInfo.containsKey(minecraftkey)) {
|
||||
Entity entity = null;
|
||||
|
||||
for (int i = 0; i < 1; ++i) {
|
||||
entity = EntityTypes.a(minecraftkey, world);
|
||||
if (entity instanceof EntityInsentient) {
|
||||
EntityInsentient entityinsentient = (EntityInsentient) entity;
|
||||
|
||||
entity.setPositionRotation(d0, d1, d2, MathHelper.g(world.random.nextFloat() * 360.0F), 0.0F);
|
||||
entityinsentient.aP = entityinsentient.yaw;
|
||||
entityinsentient.aN = entityinsentient.yaw;
|
||||
entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), (GroupDataEntity) null);
|
||||
// CraftBukkit start - don't return an entity when CreatureSpawnEvent is canceled
|
||||
if (!world.addEntity(entity, spawnReason)) {
|
||||
entity = null;
|
||||
} else {
|
||||
entityinsentient.D();
|
||||
}
|
||||
// CraftBukkit end
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void a(CreativeModeTab creativemodetab, NonNullList<ItemStack> nonnulllist) {
|
||||
if (this.a(creativemodetab)) {
|
||||
Iterator iterator = EntityTypes.eggInfo.values().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = (EntityTypes.MonsterEggInfo) iterator.next();
|
||||
ItemStack itemstack = new ItemStack(this, 1);
|
||||
|
||||
a(itemstack, entitytypes_monsteregginfo.a);
|
||||
nonnulllist.add(itemstack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void a(ItemStack itemstack, MinecraftKey minecraftkey) {
|
||||
NBTTagCompound nbttagcompound = itemstack.hasTag() ? itemstack.getTag() : new NBTTagCompound();
|
||||
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
||||
|
||||
nbttagcompound1.setString("id", minecraftkey.toString());
|
||||
nbttagcompound.set("EntityTag", nbttagcompound1);
|
||||
itemstack.setTag(nbttagcompound);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MinecraftKey h(ItemStack itemstack) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound == null) {
|
||||
return null;
|
||||
} else if (!nbttagcompound.hasKeyOfType("EntityTag", 10)) {
|
||||
return null;
|
||||
} else {
|
||||
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("EntityTag");
|
||||
|
||||
if (!nbttagcompound1.hasKeyOfType("id", 8)) {
|
||||
return null;
|
||||
} else {
|
||||
String s = nbttagcompound1.getString("id");
|
||||
MinecraftKey minecraftkey = new MinecraftKey(s);
|
||||
|
||||
if (!s.contains(":")) {
|
||||
nbttagcompound1.setString("id", minecraftkey.toString());
|
||||
}
|
||||
|
||||
return minecraftkey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
367
sources/src/main/java/net/minecraft/server/ItemWorldMap.java
Normal file
367
sources/src/main/java/net/minecraft/server/ItemWorldMap.java
Normal file
@@ -0,0 +1,367 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.HashMultiset;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Multisets;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// CraftBukkit start
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.server.MapInitializeEvent;
|
||||
// CraftBukkit end
|
||||
|
||||
public class ItemWorldMap extends ItemWorldMapBase {
|
||||
|
||||
protected ItemWorldMap() {
|
||||
this.a(true);
|
||||
}
|
||||
|
||||
public static ItemStack a(World world, double d0, double d1, byte b0, boolean flag, boolean flag1) {
|
||||
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
|
||||
ItemStack itemstack = new ItemStack(Items.FILLED_MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps
|
||||
String s = "map_" + itemstack.getData();
|
||||
WorldMap worldmap = new WorldMap(s);
|
||||
|
||||
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit
|
||||
worldmap.scale = b0;
|
||||
worldmap.a(d0, d1, worldmap.scale);
|
||||
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension
|
||||
worldmap.track = flag;
|
||||
worldmap.unlimitedTracking = flag1;
|
||||
worldmap.c();
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit
|
||||
return itemstack;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public WorldMap getSavedMap(ItemStack itemstack, World world) {
|
||||
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
|
||||
String s = "map_" + itemstack.getData();
|
||||
WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps
|
||||
|
||||
if (worldmap == null && !world.isClientSide) {
|
||||
itemstack.setData(worldMain.b("map")); // CraftBukkit - use primary world for maps
|
||||
s = "map_" + itemstack.getData();
|
||||
worldmap = new WorldMap(s);
|
||||
worldmap.scale = 3;
|
||||
worldmap.a((double) world.getWorldData().b(), (double) world.getWorldData().d(), worldmap.scale);
|
||||
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps
|
||||
worldmap.c();
|
||||
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps
|
||||
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
return worldmap;
|
||||
}
|
||||
|
||||
public void a(World world, Entity entity, WorldMap worldmap) {
|
||||
// CraftBukkit - world.worldProvider -> ((WorldServer) world)
|
||||
if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) {
|
||||
int i = 1 << worldmap.scale;
|
||||
int j = worldmap.centerX;
|
||||
int k = worldmap.centerZ;
|
||||
int l = MathHelper.floor(entity.locX - (double) j) / i + 64;
|
||||
int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64;
|
||||
int j1 = 128 / i;
|
||||
|
||||
if (world.worldProvider.n()) {
|
||||
j1 /= 2;
|
||||
}
|
||||
|
||||
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = worldmap.a((EntityHuman) entity);
|
||||
|
||||
++worldmap_worldmaphumantracker.b;
|
||||
boolean flag = false;
|
||||
|
||||
for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) {
|
||||
if ((k1 & 15) == (worldmap_worldmaphumantracker.b & 15) || flag) {
|
||||
flag = false;
|
||||
double d0 = 0.0D;
|
||||
|
||||
for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) {
|
||||
if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) {
|
||||
int i2 = k1 - l;
|
||||
int j2 = l1 - i1;
|
||||
boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2);
|
||||
int k2 = (j / i + k1 - 64) * i;
|
||||
int l2 = (k / i + l1 - 64) * i;
|
||||
HashMultiset hashmultiset = HashMultiset.create();
|
||||
Chunk chunk = world.getChunkIfLoaded(new BlockPosition(k2, 0, l2)); // NeonPaper - Maps shouldn't load chunks
|
||||
|
||||
if (chunk != null && !chunk.isEmpty()) { // NeonPaper - Maps shouldn't load chunks
|
||||
int i3 = k2 & 15;
|
||||
int j3 = l2 & 15;
|
||||
int k3 = 0;
|
||||
double d1 = 0.0D;
|
||||
|
||||
if (world.worldProvider.n()) {
|
||||
int l3 = k2 + l2 * 231871;
|
||||
|
||||
l3 = l3 * l3 * 31287121 + l3 * 11;
|
||||
if ((l3 >> 20 & 1) == 0) {
|
||||
hashmultiset.add(Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, BlockDirt.EnumDirtVariant.DIRT).a((IBlockAccess) world, BlockPosition.ZERO), 10);
|
||||
} else {
|
||||
hashmultiset.add(Blocks.STONE.getBlockData().set(BlockStone.VARIANT, BlockStone.EnumStoneVariant.STONE).a((IBlockAccess) world, BlockPosition.ZERO), 100);
|
||||
}
|
||||
|
||||
d1 = 100.0D;
|
||||
} else {
|
||||
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
|
||||
|
||||
for (int i4 = 0; i4 < i; ++i4) {
|
||||
for (int j4 = 0; j4 < i; ++j4) {
|
||||
int k4 = chunk.b(i4 + i3, j4 + j3) + 1;
|
||||
IBlockData iblockdata = Blocks.AIR.getBlockData();
|
||||
|
||||
if (k4 > 1) {
|
||||
do {
|
||||
--k4;
|
||||
iblockdata = chunk.a(i4 + i3, k4, j4 + j3);
|
||||
blockposition_mutableblockposition.c((chunk.locX << 4) + i4 + i3, k4, (chunk.locZ << 4) + j4 + j3);
|
||||
} while (iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition) == MaterialMapColor.c && k4 > 0);
|
||||
|
||||
if (k4 > 0 && iblockdata.getMaterial().isLiquid()) {
|
||||
int l4 = k4 - 1;
|
||||
|
||||
IBlockData iblockdata1;
|
||||
|
||||
do {
|
||||
iblockdata1 = chunk.a(i4 + i3, l4--, j4 + j3);
|
||||
++k3;
|
||||
} while (l4 > 0 && iblockdata1.getMaterial().isLiquid());
|
||||
}
|
||||
} else {
|
||||
iblockdata = Blocks.BEDROCK.getBlockData();
|
||||
}
|
||||
|
||||
d1 += (double) k4 / (double) (i * i);
|
||||
hashmultiset.add(iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
k3 /= i * i;
|
||||
double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + l1 & 1) - 0.5D) * 0.4D;
|
||||
byte b0 = 1;
|
||||
|
||||
if (d2 > 0.6D) {
|
||||
b0 = 2;
|
||||
}
|
||||
|
||||
if (d2 < -0.6D) {
|
||||
b0 = 0;
|
||||
}
|
||||
|
||||
MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MaterialMapColor.c);
|
||||
|
||||
if (materialmapcolor == MaterialMapColor.o) {
|
||||
d2 = (double) k3 * 0.1D + (double) (k1 + l1 & 1) * 0.2D;
|
||||
b0 = 1;
|
||||
if (d2 < 0.5D) {
|
||||
b0 = 2;
|
||||
}
|
||||
|
||||
if (d2 > 0.9D) {
|
||||
b0 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
d0 = d1;
|
||||
if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) {
|
||||
byte b1 = worldmap.colors[k1 + l1 * 128];
|
||||
byte b2 = (byte) (materialmapcolor.ad * 4 + b0);
|
||||
|
||||
if (b1 != b2) {
|
||||
worldmap.colors[k1 + l1 * 128] = b2;
|
||||
worldmap.flagDirty(k1, l1);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void a(World world, ItemStack itemstack) {
|
||||
if (itemstack.getItem() == Items.FILLED_MAP) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
if (worldmap != null) {
|
||||
if (world.worldProvider.getDimensionManager().getDimensionID() == worldmap.map) {
|
||||
int i = 1 << worldmap.scale;
|
||||
int j = worldmap.centerX;
|
||||
int k = worldmap.centerZ;
|
||||
BiomeBase[] abiomebase = world.getWorldChunkManager().a((BiomeBase[]) null, (j / i - 64) * i, (k / i - 64) * i, 128 * i, 128 * i, false);
|
||||
|
||||
for (int l = 0; l < 128; ++l) {
|
||||
for (int i1 = 0; i1 < 128; ++i1) {
|
||||
int j1 = l * i;
|
||||
int k1 = i1 * i;
|
||||
BiomeBase biomebase = abiomebase[j1 + k1 * 128 * i];
|
||||
MaterialMapColor materialmapcolor = MaterialMapColor.c;
|
||||
int l1 = 3;
|
||||
int i2 = 8;
|
||||
|
||||
if (l > 0 && i1 > 0 && l < 127 && i1 < 127) {
|
||||
if (abiomebase[(l - 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l - 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l - 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[(l + 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[l * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (abiomebase[l * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
|
||||
--i2;
|
||||
}
|
||||
|
||||
if (biomebase.j() < 0.0F) {
|
||||
materialmapcolor = MaterialMapColor.r;
|
||||
if (i2 > 7 && i1 % 2 == 0) {
|
||||
l1 = (l + (int) (MathHelper.sin((float) i1 + 0.0F) * 7.0F)) / 8 % 5;
|
||||
if (l1 == 3) {
|
||||
l1 = 1;
|
||||
} else if (l1 == 4) {
|
||||
l1 = 0;
|
||||
}
|
||||
} else if (i2 > 7) {
|
||||
materialmapcolor = MaterialMapColor.c;
|
||||
} else if (i2 > 5) {
|
||||
l1 = 1;
|
||||
} else if (i2 > 3) {
|
||||
l1 = 0;
|
||||
} else if (i2 > 1) {
|
||||
l1 = 0;
|
||||
}
|
||||
} else if (i2 > 0) {
|
||||
materialmapcolor = MaterialMapColor.C;
|
||||
if (i2 > 3) {
|
||||
l1 = 1;
|
||||
} else {
|
||||
l1 = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (materialmapcolor != MaterialMapColor.c) {
|
||||
worldmap.colors[l + i1 * 128] = (byte) (materialmapcolor.ad * 4 + l1);
|
||||
worldmap.flagDirty(l, i1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) {
|
||||
if (!world.isClientSide) {
|
||||
WorldMap worldmap = this.getSavedMap(itemstack, world);
|
||||
|
||||
if (entity instanceof EntityHuman) {
|
||||
EntityHuman entityhuman = (EntityHuman) entity;
|
||||
|
||||
worldmap.a(entityhuman, itemstack);
|
||||
}
|
||||
|
||||
if (flag || entity instanceof EntityHuman && ((EntityHuman) entity).getItemInOffHand() == itemstack) {
|
||||
this.a(world, entity, worldmap);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Packet<?> a(ItemStack itemstack, World world, EntityHuman entityhuman) {
|
||||
return this.getSavedMap(itemstack, world).a(itemstack, world, entityhuman);
|
||||
}
|
||||
|
||||
public void b(ItemStack itemstack, World world, EntityHuman entityhuman) {
|
||||
NBTTagCompound nbttagcompound = itemstack.getTag();
|
||||
|
||||
if (nbttagcompound != null) {
|
||||
if (nbttagcompound.hasKeyOfType("map_scale_direction", 99)) {
|
||||
a(itemstack, world, nbttagcompound.getInt("map_scale_direction"));
|
||||
nbttagcompound.remove("map_scale_direction");
|
||||
} else if (nbttagcompound.getBoolean("map_tracking_position")) {
|
||||
b(itemstack, world);
|
||||
nbttagcompound.remove("map_tracking_position");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static void a(ItemStack itemstack, World world, int i) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
|
||||
itemstack.setData(world.b("map"));
|
||||
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
|
||||
|
||||
if (worldmap != null) {
|
||||
worldmap1.scale = (byte) MathHelper.clamp(worldmap.scale + i, 0, 4);
|
||||
worldmap1.track = worldmap.track;
|
||||
worldmap1.a((double) worldmap.centerX, (double) worldmap.centerZ, worldmap1.scale);
|
||||
worldmap1.map = worldmap.map;
|
||||
worldmap1.c();
|
||||
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static void b(ItemStack itemstack, World world) {
|
||||
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
|
||||
|
||||
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
|
||||
itemstack.setData(world.b("map"));
|
||||
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
|
||||
|
||||
worldmap1.track = true;
|
||||
if (worldmap != null) {
|
||||
worldmap1.centerX = worldmap.centerX;
|
||||
worldmap1.centerZ = worldmap.centerZ;
|
||||
worldmap1.scale = worldmap.scale;
|
||||
worldmap1.map = worldmap.map;
|
||||
worldmap1.c();
|
||||
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
|
||||
// CraftBukkit start
|
||||
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
|
||||
Bukkit.getServer().getPluginManager().callEvent(event);
|
||||
// CraftBukkit end
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
370
sources/src/main/java/net/minecraft/server/MathHelper.java
Normal file
370
sources/src/main/java/net/minecraft/server/MathHelper.java
Normal file
@@ -0,0 +1,370 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
public class MathHelper {
|
||||
|
||||
public static final float a = c(2.0F);
|
||||
private static final int[] SINE_TABLE_INT = new int[16384 + 1];
|
||||
private static final float SINE_TABLE_MIDPOINT;
|
||||
private static final Random c = new Random();
|
||||
private static final int[] d;
|
||||
private static final double e;
|
||||
private static final double[] f;
|
||||
private static final double[] g;
|
||||
|
||||
public static float sin(float f) {
|
||||
return lookup((int) (f * 10430.38) & 0xFFFF);
|
||||
}
|
||||
|
||||
public static float cos(float f) {
|
||||
return lookup((int) (f * 10430.38 + 16384.0) & 0xFFFF);
|
||||
}
|
||||
|
||||
private static float lookup(int index) {
|
||||
if (index == 32768) {
|
||||
return SINE_TABLE_MIDPOINT;
|
||||
}
|
||||
int neg = (index & 0x8000) << 16;
|
||||
int mask = (index << 17) >> 31;
|
||||
int pos = (0x8001 & mask) + (index ^ mask);
|
||||
pos &= 0x7fff;
|
||||
return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg);
|
||||
}
|
||||
|
||||
public static float c(float f) {
|
||||
return (float) Math.sqrt((double) f);
|
||||
}
|
||||
|
||||
public static float sqrt(double d0) {
|
||||
return (float) Math.sqrt(d0);
|
||||
}
|
||||
|
||||
public static int d(float f) {
|
||||
int i = (int) f;
|
||||
|
||||
return f < (float) i ? i - 1 : i;
|
||||
}
|
||||
|
||||
public static int floor(double d0) {
|
||||
int i = (int) d0;
|
||||
|
||||
return d0 < (double) i ? i - 1 : i;
|
||||
}
|
||||
|
||||
public static long d(double d0) {
|
||||
long i = (long) d0;
|
||||
|
||||
return d0 < (double) i ? i - 1L : i;
|
||||
}
|
||||
|
||||
public static float e(float f) {
|
||||
return f >= 0.0F ? f : -f;
|
||||
}
|
||||
|
||||
public static int a(int i) {
|
||||
return i >= 0 ? i : -i;
|
||||
}
|
||||
|
||||
public static int f(float f) {
|
||||
int i = (int) f;
|
||||
|
||||
return f > (float) i ? i + 1 : i;
|
||||
}
|
||||
|
||||
public static int f(double d0) {
|
||||
int i = (int) d0;
|
||||
|
||||
return d0 > (double) i ? i + 1 : i;
|
||||
}
|
||||
|
||||
public static int clamp(int i, int j, int k) {
|
||||
return i < j ? j : (i > k ? k : i);
|
||||
}
|
||||
|
||||
public static float a(float f, float f1, float f2) {
|
||||
return f < f1 ? f1 : (f > f2 ? f2 : f);
|
||||
}
|
||||
|
||||
public static double a(double d0, double d1, double d2) {
|
||||
return d0 < d1 ? d1 : (d0 > d2 ? d2 : d0);
|
||||
}
|
||||
|
||||
public static double b(double d0, double d1, double d2) {
|
||||
return d2 < 0.0D ? d0 : (d2 > 1.0D ? d1 : d0 + (d1 - d0) * d2);
|
||||
}
|
||||
|
||||
public static double a(double d0, double d1) {
|
||||
if (d0 < 0.0D) {
|
||||
d0 = -d0;
|
||||
}
|
||||
|
||||
if (d1 < 0.0D) {
|
||||
d1 = -d1;
|
||||
}
|
||||
|
||||
return d0 > d1 ? d0 : d1;
|
||||
}
|
||||
|
||||
public static int nextInt(Random random, int i, int j) {
|
||||
return i >= j ? i : random.nextInt(j - i + 1) + i;
|
||||
}
|
||||
|
||||
public static float a(Random random, float f, float f1) {
|
||||
return f >= f1 ? f : random.nextFloat() * (f1 - f) + f;
|
||||
}
|
||||
|
||||
public static double a(Random random, double d0, double d1) {
|
||||
return d0 >= d1 ? d0 : random.nextDouble() * (d1 - d0) + d0;
|
||||
}
|
||||
|
||||
public static double a(long[] along) {
|
||||
long i = 0L;
|
||||
long[] along1 = along;
|
||||
int j = along.length;
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
long l = along1[k];
|
||||
|
||||
i += l;
|
||||
}
|
||||
|
||||
return (double) i / (double) along.length;
|
||||
}
|
||||
|
||||
public static float g(float f) {
|
||||
f %= 360.0F;
|
||||
if (f >= 180.0F) {
|
||||
f -= 360.0F;
|
||||
}
|
||||
|
||||
if (f < -180.0F) {
|
||||
f += 360.0F;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
public static double g(double d0) {
|
||||
d0 %= 360.0D;
|
||||
if (d0 >= 180.0D) {
|
||||
d0 -= 360.0D;
|
||||
}
|
||||
|
||||
if (d0 < -180.0D) {
|
||||
d0 += 360.0D;
|
||||
}
|
||||
|
||||
return d0;
|
||||
}
|
||||
|
||||
public static int b(int i) {
|
||||
i %= 360;
|
||||
if (i >= 180) {
|
||||
i -= 360;
|
||||
}
|
||||
|
||||
if (i < -180) {
|
||||
i += 360;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
public static int a(String s, int i) {
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (Throwable throwable) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
public static int a(String s, int i, int j) {
|
||||
return Math.max(j, a(s, i));
|
||||
}
|
||||
|
||||
public static double a(String s, double d0) {
|
||||
try {
|
||||
return Double.parseDouble(s);
|
||||
} catch (Throwable throwable) {
|
||||
return d0;
|
||||
}
|
||||
}
|
||||
|
||||
public static double a(String s, double d0, double d1) {
|
||||
return Math.max(d1, a(s, d0));
|
||||
}
|
||||
|
||||
public static int c(int i) {
|
||||
int j = i - 1;
|
||||
|
||||
j |= j >> 1;
|
||||
j |= j >> 2;
|
||||
j |= j >> 4;
|
||||
j |= j >> 8;
|
||||
j |= j >> 16;
|
||||
return j + 1;
|
||||
}
|
||||
|
||||
private static boolean g(int i) {
|
||||
return i != 0 && (i & i - 1) == 0;
|
||||
}
|
||||
|
||||
public static int d(int i) {
|
||||
i = g(i) ? i : c(i);
|
||||
return MathHelper.d[(int) ((long) i * 125613361L >> 27) & 31];
|
||||
}
|
||||
|
||||
public static int e(int i) {
|
||||
return d(i) - (g(i) ? 0 : 1);
|
||||
}
|
||||
|
||||
public static int c(int i, int j) {
|
||||
if (j == 0) {
|
||||
return 0;
|
||||
} else if (i == 0) {
|
||||
return j;
|
||||
} else {
|
||||
if (i < 0) {
|
||||
j *= -1;
|
||||
}
|
||||
|
||||
int k = i % j;
|
||||
|
||||
return k == 0 ? i : i + j - k;
|
||||
}
|
||||
}
|
||||
|
||||
public static long c(int i, int j, int k) {
|
||||
long l = (long) (i * 3129871) ^ (long) k * 116129781L ^ (long) j;
|
||||
|
||||
l = l * l * 42317861L + l * 11L;
|
||||
return l;
|
||||
}
|
||||
|
||||
public static UUID a(Random random) {
|
||||
long i = random.nextLong() & -61441L | 16384L;
|
||||
long j = random.nextLong() & 4611686018427387903L | Long.MIN_VALUE;
|
||||
|
||||
return new UUID(i, j);
|
||||
}
|
||||
|
||||
public static UUID a() {
|
||||
return a(MathHelper.c);
|
||||
}
|
||||
|
||||
public static double c(double d0, double d1, double d2) {
|
||||
return (d0 - d1) / (d2 - d1);
|
||||
}
|
||||
|
||||
public static double c(double d0, double d1) {
|
||||
double d2 = d1 * d1 + d0 * d0;
|
||||
|
||||
if (Double.isNaN(d2)) {
|
||||
return Double.NaN;
|
||||
} else {
|
||||
boolean flag = d0 < 0.0D;
|
||||
|
||||
if (flag) {
|
||||
d0 = -d0;
|
||||
}
|
||||
|
||||
boolean flag1 = d1 < 0.0D;
|
||||
|
||||
if (flag1) {
|
||||
d1 = -d1;
|
||||
}
|
||||
|
||||
boolean flag2 = d0 > d1;
|
||||
double d3;
|
||||
|
||||
if (flag2) {
|
||||
d3 = d1;
|
||||
d1 = d0;
|
||||
d0 = d3;
|
||||
}
|
||||
|
||||
d3 = i(d2);
|
||||
d1 *= d3;
|
||||
d0 *= d3;
|
||||
double d4 = MathHelper.e + d0;
|
||||
int i = (int) Double.doubleToRawLongBits(d4);
|
||||
double d5 = MathHelper.f[i];
|
||||
double d6 = MathHelper.g[i];
|
||||
double d7 = d4 - MathHelper.e;
|
||||
double d8 = d0 * d6 - d1 * d7;
|
||||
double d9 = (6.0D + d8 * d8) * d8 * 0.16666666666666666D;
|
||||
double d10 = d5 + d9;
|
||||
|
||||
if (flag2) {
|
||||
d10 = 1.5707963267948966D - d10;
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
d10 = 3.141592653589793D - d10;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
d10 = -d10;
|
||||
}
|
||||
|
||||
return d10;
|
||||
}
|
||||
}
|
||||
|
||||
public static double i(double d0) {
|
||||
double d1 = 0.5D * d0;
|
||||
long i = Double.doubleToRawLongBits(d0);
|
||||
|
||||
i = 6910469410427058090L - (i >> 1);
|
||||
d0 = Double.longBitsToDouble(i);
|
||||
d0 *= 1.5D - d1 * d0 * d0;
|
||||
return d0;
|
||||
}
|
||||
|
||||
public static int f(int i) {
|
||||
i ^= i >>> 16;
|
||||
i *= -2048144789;
|
||||
i ^= i >>> 13;
|
||||
i *= -1028477387;
|
||||
i ^= i >>> 16;
|
||||
return i;
|
||||
}
|
||||
|
||||
static {
|
||||
int i;
|
||||
|
||||
final float[] SINE_TABLE = new float[65536];
|
||||
for (i = 0; i < 65536; ++i) {
|
||||
SINE_TABLE[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
|
||||
}
|
||||
for (i = 0; i < SINE_TABLE_INT.length; i++) {
|
||||
SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]);
|
||||
}
|
||||
|
||||
SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2];
|
||||
for (i = 0; i < SINE_TABLE.length; i++) {
|
||||
float expected = SINE_TABLE[i];
|
||||
float value = lookup(i);
|
||||
|
||||
if (expected != value) {
|
||||
throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value));
|
||||
}
|
||||
}
|
||||
|
||||
d = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
|
||||
e = Double.longBitsToDouble(4805340802404319232L);
|
||||
f = new double[257];
|
||||
g = new double[257];
|
||||
|
||||
for (i = 0; i < 257; ++i) {
|
||||
double d0 = (double) i / 256.0D;
|
||||
double d1 = Math.asin(d0);
|
||||
|
||||
MathHelper.g[i] = Math.cos(d1);
|
||||
MathHelper.f[i] = d1;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
164
sources/src/main/java/net/minecraft/server/MethodProfiler.java
Normal file
164
sources/src/main/java/net/minecraft/server/MethodProfiler.java
Normal file
@@ -0,0 +1,164 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class MethodProfiler {
|
||||
|
||||
public static final boolean ENABLED = Boolean.getBoolean("enableDebugMethodProfiler"); // CraftBukkit - disable unless specified in JVM arguments
|
||||
private static final Logger b = LogManager.getLogger();
|
||||
private final ObjectArrayList<String> c = new ObjectArrayList<>(); // Dionysus
|
||||
private final LongArrayList d = new LongArrayList(); // Dionysus
|
||||
public boolean a;
|
||||
private String e = "";
|
||||
private final Object2LongOpenHashMap<String> f = new Object2LongOpenHashMap<>();
|
||||
|
||||
public MethodProfiler() {}
|
||||
|
||||
public void a() {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
this.f.clear();
|
||||
this.e = "";
|
||||
this.c.clear();
|
||||
}
|
||||
|
||||
public void a(String s) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
if (!this.e.isEmpty()) {
|
||||
this.e = this.e + ".";
|
||||
}
|
||||
|
||||
this.e = this.e + s;
|
||||
this.c.add(this.e);
|
||||
this.d.add(Long.valueOf(System.nanoTime()));
|
||||
}
|
||||
}
|
||||
|
||||
public void a(Supplier<String> supplier) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
this.a((String) supplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
public void b() {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
if (this.a) {
|
||||
long i = System.nanoTime();
|
||||
long j = this.d.removeLong(this.d.size() - 1);
|
||||
|
||||
this.c.remove(this.c.size() - 1);
|
||||
long k = i - j;
|
||||
|
||||
if (this.f.containsKey(this.e)) {
|
||||
this.f.put(this.e, this.f.get(this.e) + k);
|
||||
} else {
|
||||
this.f.put(this.e, k);
|
||||
}
|
||||
|
||||
if (k > 100000000L) {
|
||||
MethodProfiler.b.warn("Something\'s taking too long! \'{}\' took aprox {} ms", this.e, Double.valueOf((double) k / 1000000.0D));
|
||||
}
|
||||
|
||||
this.e = this.c.isEmpty() ? "" : (String) this.c.get(this.c.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public List<MethodProfiler.ProfilerInfo> b(String s) {
|
||||
if (!ENABLED || !this.a) { // CraftBukkit
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
long i = this.f.getOrDefault("root", 0L);
|
||||
long j = this.f.getOrDefault(s, -1L);
|
||||
ArrayList<MethodProfiler.ProfilerInfo> arraylist = Lists.newArrayList();
|
||||
|
||||
if (!s.isEmpty()) {
|
||||
s = s + ".";
|
||||
}
|
||||
|
||||
long k = 0L;
|
||||
for (String s1 : this.f.keySet()) {
|
||||
if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) {
|
||||
k += this.f.getLong(s1);
|
||||
}
|
||||
}
|
||||
|
||||
float f = (float) k;
|
||||
|
||||
if (k < j) {
|
||||
k = j;
|
||||
}
|
||||
|
||||
if (i < k) {
|
||||
i = k;
|
||||
}
|
||||
|
||||
for (Object2LongMap.Entry<String> entry : this.f.object2LongEntrySet()) {
|
||||
String s2 = entry.getKey();
|
||||
if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) {
|
||||
long l = this.f.getLong(s2);
|
||||
double d0 = (double) l * 100.0D / (double) k;
|
||||
double d1 = (double) l * 100.0D / (double) i;
|
||||
String s3 = s2.substring(s.length());
|
||||
|
||||
arraylist.add(new MethodProfiler.ProfilerInfo(s3, d0, d1));
|
||||
}
|
||||
entry.setValue(entry.getLongValue() * 999L / 1000L);
|
||||
}
|
||||
|
||||
if ((float) k > f) {
|
||||
arraylist.add(new MethodProfiler.ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i));
|
||||
}
|
||||
|
||||
Collections.sort(arraylist);
|
||||
arraylist.add(0, new MethodProfiler.ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i));
|
||||
return arraylist;
|
||||
}
|
||||
}
|
||||
|
||||
public void c(String s) {
|
||||
if (!ENABLED) return; // CraftBukkit
|
||||
this.b();
|
||||
this.a(s);
|
||||
}
|
||||
|
||||
public String c() {
|
||||
if (!ENABLED) return "[DISABLED]"; // CraftBukkit
|
||||
return this.c.isEmpty() ? "[UNKNOWN]" : (String) this.c.get(this.c.size() - 1);
|
||||
}
|
||||
|
||||
public static final class ProfilerInfo implements Comparable<MethodProfiler.ProfilerInfo> {
|
||||
|
||||
public double a;
|
||||
public double b;
|
||||
public String c;
|
||||
|
||||
public ProfilerInfo(String s, double d0, double d1) {
|
||||
this.c = s;
|
||||
this.a = d0;
|
||||
this.b = d1;
|
||||
}
|
||||
|
||||
public int a(MethodProfiler.ProfilerInfo methodprofiler_profilerinfo) {
|
||||
return methodprofiler_profilerinfo.a < this.a ? -1 : (methodprofiler_profilerinfo.a > this.a ? 1 : methodprofiler_profilerinfo.c.compareTo(this.c));
|
||||
}
|
||||
|
||||
public int compareTo(MethodProfiler.ProfilerInfo object) { // CraftBukkit: decompile error
|
||||
return this.a((MethodProfiler.ProfilerInfo) object);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,9 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||
|
||||
import io.akarin.api.internal.collections.CheckedConcurrentLinkedQueue;
|
||||
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
@@ -20,7 +21,6 @@ import io.netty.util.concurrent.GenericFutureListener;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.SecretKey;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
@@ -31,11 +31,10 @@ import org.apache.logging.log4j.Marker;
|
||||
import org.apache.logging.log4j.MarkerManager;
|
||||
|
||||
/**
|
||||
* <b>Akarin Changes Note</b><br>
|
||||
* <br>
|
||||
* 1) Add volatile to fields<br>
|
||||
* 2) Expose private members<br>
|
||||
* @author cakoyo
|
||||
* Akarin Changes Note
|
||||
* 2) Expose private members (nsc)
|
||||
* 3) Changes lock type to updatable lock (compatibility)
|
||||
* 4) Removes unneed array creation (performance)
|
||||
*/
|
||||
public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
@@ -75,13 +74,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
};
|
||||
private final EnumProtocolDirection h;
|
||||
private final Queue<NetworkManager.QueuedPacket> i = new CheckedConcurrentLinkedQueue<NetworkManager.QueuedPacket>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - Anti-Xray - OBFHELPER // Akarin
|
||||
private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
|
||||
private final ReentrantReadWriteUpdateLock j = new ReentrantReadWriteUpdateLock(); // Akarin - use update lock
|
||||
public Channel channel;
|
||||
// Spigot Start // PAIL
|
||||
public SocketAddress l;
|
||||
public java.util.UUID spoofedUUID;
|
||||
public com.mojang.authlib.properties.Property[] spoofedProfile;
|
||||
public volatile boolean preparing = true; // Akarin - add volatile
|
||||
public boolean preparing = true;
|
||||
// Spigot End
|
||||
private PacketListener m;
|
||||
private IChatBaseComponent n;
|
||||
@@ -165,7 +164,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
this.j.writeLock().lock();
|
||||
|
||||
try {
|
||||
this.i.add(new NetworkManager.QueuedPacket(packet, new GenericFutureListener[0]));
|
||||
this.i.add(new NetworkManager.QueuedPacket(packet)); // Akarin - remove fake listener creation
|
||||
} finally {
|
||||
this.j.writeLock().unlock();
|
||||
}
|
||||
@@ -366,7 +365,10 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
|
||||
this.a(channelhandlercontext, object);
|
||||
// FlamePaper - Check if channel is opened before reading packet
|
||||
if (isConnected()) {
|
||||
this.a(channelhandlercontext, object);
|
||||
}
|
||||
}
|
||||
|
||||
public static class QueuedPacket { // Akarin - default -> public
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user