Compare commits
352 Commits
ver/1.13
...
1.12.2-R0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
15
README.md
15
README.md
@@ -1,8 +1,7 @@
|
|||||||
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
|
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
|
||||||
[](https://akarin.io)
|
[](https://akarin.io)
|
||||||
|
[](https://discord.gg/fw2pJAj)
|
||||||
[](https://bstats.org/plugin/bukkit/Torch)
|
[](https://bstats.org/plugin/bukkit/Torch)
|
||||||
[](http://ci.ilummc.com/job/Akarin/)
|
|
||||||
[](https://circleci.com/gh/Akarin-project/Akarin/tree/master)
|
|
||||||
|
|
||||||
Akarin is currently **under heavy development** and contributions are welcome!
|
Akarin is currently **under heavy development** and contributions are welcome!
|
||||||
|
|
||||||
@@ -24,8 +23,7 @@ Get Akarin
|
|||||||
---
|
---
|
||||||
### Download
|
### Download
|
||||||
#### Recommended
|
#### Recommended
|
||||||
+ [**Jenkins**](http://ci.ilummc.com/job/Akarin/) - Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)
|
+ [**Jenkins**](http://ci.josephworks.net/job/Akarin/job/ver%252F1.12.2/) - Kudos to [JosephWorks](https://github.com/josephworks)
|
||||||
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build
|
|
||||||
|
|
||||||
*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*
|
*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*
|
||||||
|
|
||||||
@@ -47,6 +45,7 @@ Get Akarin
|
|||||||
Demo Servers
|
Demo Servers
|
||||||
---
|
---
|
||||||
* `demo.akarin.io` (official)
|
* `demo.akarin.io` (official)
|
||||||
|
* `omc.hk` (auth required)
|
||||||
|
|
||||||
*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*
|
*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*
|
||||||
|
|
||||||
@@ -54,12 +53,6 @@ Contributing
|
|||||||
---
|
---
|
||||||
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
|
* 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).
|
* 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 us an email with your experience and necessary information.
|
* 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.
|
||||||
|
|
||||||
Contact
|
|
||||||
---
|
|
||||||
[Discord](https://discord.gg/D3Rsukh)
|
|
||||||
|
|
||||||
Email: `kira@kira.moe`
|
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
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-*.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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ public class PacketPlayOutMapChunkInfo {
|
|||||||
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||||
private final Chunk chunk;
|
private final Chunk chunk;
|
||||||
private final int chunkSectionSelector;
|
private final int chunkSectionSelector;
|
||||||
private ByteBuf data; // Akarin - byte[] -> ByteBuf
|
private ByteBuf data; // Akarin
|
||||||
private final int[] bitsPerValue = new int[16];
|
private final int[] bitsPerValue = new int[16];
|
||||||
private final DataPalette[] dataPalettes = new DataPalette[16];
|
private final DataPalette[] dataPalettes = new DataPalette[16];
|
||||||
private final int[] dataBitsIndexes = new int[16];
|
private final int[] dataBitsIndexes = new int[16];
|
||||||
@@ -39,11 +39,11 @@ public class PacketPlayOutMapChunkInfo {
|
|||||||
return chunkSectionSelector;
|
return chunkSectionSelector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getData() {
|
public ByteBuf getData() { // Akarin
|
||||||
return data.array(); // Akarin
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setData(ByteBuf data) { // Akarin - byte[] -> ByteBuf
|
public void setData(ByteBuf data) { // Akarin
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +105,8 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|||||||
packetdataserializer.writeInt(this.b);
|
packetdataserializer.writeInt(this.b);
|
||||||
packetdataserializer.writeBoolean(this.f);
|
packetdataserializer.writeBoolean(this.f);
|
||||||
packetdataserializer.d(this.c);
|
packetdataserializer.d(this.c);
|
||||||
packetdataserializer.d(this.d.array().length); // Akarin
|
packetdataserializer.d(this.d.capacity()); // Akarin
|
||||||
packetdataserializer.writeBytes(this.d.array());
|
packetdataserializer.writeBytes(this.d.array()); // Akarin
|
||||||
packetdataserializer.d(this.e.size());
|
packetdataserializer.d(this.e.size());
|
||||||
Iterator iterator = this.e.iterator();
|
Iterator iterator = this.e.iterator();
|
||||||
|
|
||||||
@@ -122,9 +122,9 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
|||||||
packetlistenerplayout.a(this);
|
packetlistenerplayout.a(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ByteBuf allocateBuffer(int expectedCapacity) { return g(expectedCapacity); } // Akarin - OBFHELPER
|
private ByteBuf g() { return allocateBuffer(-1); } // Akarin
|
||||||
private ByteBuf g(int expectedCapacity) { // Akarin - added argument
|
private ByteBuf allocateBuffer(int expectedCapacity) { // Akarin - added argument
|
||||||
ByteBuf bytebuf = Unpooled.buffer(expectedCapacity); // Akarin
|
ByteBuf bytebuf = expectedCapacity == -1 ? Unpooled.buffer() : Unpooled.buffer(expectedCapacity); // Akarin
|
||||||
|
|
||||||
bytebuf.writerIndex(0);
|
bytebuf.writerIndex(0);
|
||||||
return bytebuf;
|
return bytebuf;
|
||||||
@@ -15,7 +15,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
|
|||||||
if [ -d "Minecraft" ]; then
|
if [ -d "Minecraft" ]; then
|
||||||
rm Minecraft/ -r
|
rm Minecraft/ -r
|
||||||
fi
|
fi
|
||||||
git clone https://github.com/Akarin-project/Minecraft.git
|
git clone https://github.com/LegacyGamerHD/Minecraft.git
|
||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$paperbasedir"
|
cd "$paperbasedir"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
set -e
|
set -e
|
||||||
basedir="$pwd"
|
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"
|
echo "Failed to build Akarin"
|
||||||
exit 1
|
exit 1
|
||||||
) || exit 1
|
) || exit 1
|
||||||
|
|||||||
106
sources/pom.xml
106
sources/pom.xml
@@ -3,7 +3,7 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>akarin</artifactId>
|
<artifactId>akarin</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.12.2-R0.4-RELEASE</version>
|
<version>1.12.2-R0.4-SNAPSHOT</version>
|
||||||
<name>Akarin</name>
|
<name>Akarin</name>
|
||||||
<url>https://github.com/Akarin-project/Akarin</url>
|
<url>https://github.com/Akarin-project/Akarin</url>
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-all</artifactId>
|
<artifactId>netty-all</artifactId>
|
||||||
<version>4.1.24.Final</version>
|
<version>4.1.78.Final</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -45,6 +45,12 @@
|
|||||||
<version>${minecraft.version}-R0.1-SNAPSHOT</version>
|
<version>${minecraft.version}-R0.1-SNAPSHOT</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.spigotmc</groupId>
|
||||||
|
<artifactId>minecraft-server</artifactId>
|
||||||
|
<version>${minecraft.version}-SNAPSHOT</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.jopt-simple</groupId>
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
<artifactId>jopt-simple</artifactId>
|
<artifactId>jopt-simple</artifactId>
|
||||||
@@ -69,7 +75,6 @@
|
|||||||
<version>3.0.3</version>
|
<version>3.0.3</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.minecrell</groupId>
|
<groupId>net.minecrell</groupId>
|
||||||
<artifactId>terminalconsoleappender</artifactId>
|
<artifactId>terminalconsoleappender</artifactId>
|
||||||
@@ -91,7 +96,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.17.2</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -99,20 +104,20 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-slf4j-impl</artifactId>
|
<artifactId>log4j-slf4j-impl</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.17.2</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-iostreams</artifactId>
|
<artifactId>log4j-iostreams</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.17.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Paper - Async loggers -->
|
<!-- Paper - Async loggers -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.lmax</groupId>
|
<groupId>com.lmax</groupId>
|
||||||
<artifactId>disruptor</artifactId>
|
<artifactId>disruptor</artifactId>
|
||||||
<version>3.4.2</version>
|
<version>3.4.4</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -134,7 +139,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.akarin</groupId>
|
<groupId>io.akarin</groupId>
|
||||||
<artifactId>legacylauncher</artifactId>
|
<artifactId>legacylauncher</artifactId>
|
||||||
<version>1.25</version>
|
<version>1.26</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spongepowered</groupId>
|
<groupId>org.spongepowered</groupId>
|
||||||
@@ -149,18 +154,14 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
<artifactId>caffeine</artifactId>
|
<artifactId>caffeine</artifactId>
|
||||||
<version>2.6.3-SNAPSHOT</version>
|
<version>2.9.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>akarin-repo</id>
|
<id>elmakers-repo</id>
|
||||||
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
|
<url>http://maven.elmakers.com/repository/</url>
|
||||||
</repository>
|
|
||||||
<repository>
|
|
||||||
<id>spigotmc-public</id>
|
|
||||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spongepowered-repo</id>
|
<id>spongepowered-repo</id>
|
||||||
@@ -170,12 +171,36 @@
|
|||||||
<id>nallar-repo</id>
|
<id>nallar-repo</id>
|
||||||
<url>http://repo.nallar.me/</url>
|
<url>http://repo.nallar.me/</url>
|
||||||
</repository>
|
</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>
|
</repositories>
|
||||||
|
|
||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spigotmc-public</id>
|
<id>paper</id>
|
||||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
<url>https://papermc.io/repo/repository/maven-public/</url>
|
||||||
</pluginRepository>
|
</pluginRepository>
|
||||||
</pluginRepositories>
|
</pluginRepositories>
|
||||||
|
|
||||||
@@ -216,6 +241,7 @@
|
|||||||
<Specification-Title>Bukkit</Specification-Title>
|
<Specification-Title>Bukkit</Specification-Title>
|
||||||
<Specification-Version>${api.version}</Specification-Version>
|
<Specification-Version>${api.version}</Specification-Version>
|
||||||
<Specification-Vendor>Bukkit Team</Specification-Vendor>
|
<Specification-Vendor>Bukkit Team</Specification-Vendor>
|
||||||
|
<Multi-Release>true</Multi-Release> <!-- Paper start - update log4j -->
|
||||||
</manifestEntries>
|
</manifestEntries>
|
||||||
<manifestSections>
|
<manifestSections>
|
||||||
<manifestSection>
|
<manifestSection>
|
||||||
@@ -243,7 +269,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-shade-plugin</artifactId>
|
<artifactId>maven-shade-plugin</artifactId>
|
||||||
<version>3.1.0</version>
|
<version>3.2.4</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
@@ -252,6 +278,25 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
|
<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>
|
<relocations>
|
||||||
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
|
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
|
||||||
<!--<relocation>-->
|
<!--<relocation>-->
|
||||||
@@ -283,27 +328,16 @@
|
|||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
|
||||||
<resource>META-INF/services/java.sql.Driver</resource>
|
<resource>META-INF/services/java.sql.Driver</resource>
|
||||||
</transformer>
|
</transformer>
|
||||||
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
|
<transformer implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
|
||||||
</transformers>
|
</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>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.github.edwgiz</groupId>
|
<groupId>io.github.edwgiz</groupId>
|
||||||
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
|
<artifactId>log4j-maven-shade-plugin-extensions</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.17.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</plugin>
|
</plugin>
|
||||||
@@ -311,6 +345,12 @@
|
|||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.7.0</version>
|
<version>3.7.0</version>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<exclude>org/spigotmc/CaseInsensitiveHashingStrategy.java</exclude>
|
||||||
|
<exclude>org/spigotmc/CaseInsensitiveMap.java</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<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,21 +24,26 @@
|
|||||||
package co.aikar.timings;
|
package co.aikar.timings;
|
||||||
|
|
||||||
import co.aikar.util.LoadingIntMap;
|
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 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;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
/**
|
import org.bukkit.Bukkit;
|
||||||
* Akarin Changes Note
|
|
||||||
* 1) Add volatile to fields (safety issue)
|
|
||||||
*/
|
|
||||||
class TimingHandler implements Timing {
|
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 TimingIdentifier identifier;
|
||||||
final int id = idPool++;
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
private final boolean verbose;
|
private final boolean verbose;
|
||||||
|
|
||||||
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
||||||
@@ -46,22 +51,15 @@ class TimingHandler implements Timing {
|
|||||||
final TimingData record;
|
final TimingData record;
|
||||||
private final TimingHandler groupHandler;
|
private final TimingHandler groupHandler;
|
||||||
|
|
||||||
private volatile long start = 0; // Akarin - volatile
|
private long start = 0;
|
||||||
private volatile int timingDepth = 0; // Akarin - volatile
|
private int timingDepth = 0;
|
||||||
private boolean added;
|
private boolean added;
|
||||||
private boolean timed;
|
private boolean timed;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private TimingHandler parent;
|
|
||||||
|
|
||||||
TimingHandler(TimingIdentifier id) {
|
TimingHandler(TimingIdentifier id) {
|
||||||
if (id.name.startsWith("##")) {
|
this.identifier = id;
|
||||||
verbose = true;
|
this.verbose = id.name.startsWith("##");
|
||||||
this.name = id.name.substring(3);
|
|
||||||
} else {
|
|
||||||
this.name = id.name;
|
|
||||||
verbose = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.record = new TimingData(this.id);
|
this.record = new TimingData(this.id);
|
||||||
this.groupHandler = id.groupHandler;
|
this.groupHandler = id.groupHandler;
|
||||||
|
|
||||||
@@ -86,59 +84,61 @@ class TimingHandler implements Timing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timing startTimingIfSync() {
|
public Timing startTimingIfSync() {
|
||||||
if (Bukkit.isPrimaryThread()) {
|
startTiming();
|
||||||
startTiming();
|
return (Timing) this;
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopTimingIfSync() {
|
public void stopTimingIfSync() {
|
||||||
if (Bukkit.isPrimaryThread()) {
|
if (Akari.isPrimaryThread(false)) {
|
||||||
stopTiming();
|
stopTiming(true); // Avoid twice thread check
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timing startTiming() {
|
public Timing startTiming() {
|
||||||
if (enabled && ++timingDepth == 1) {
|
if (enabled && Bukkit.isPrimaryThread() && ++timingDepth == 1) {
|
||||||
start = System.nanoTime();
|
start = System.nanoTime();
|
||||||
parent = TimingsManager.CURRENT;
|
TIMING_STACK.addLast(this);
|
||||||
TimingsManager.CURRENT = this;
|
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopTiming() {
|
public void stopTiming() {
|
||||||
if (enabled && --timingDepth == 0 && start != 0) {
|
stopTiming(false);
|
||||||
if (!Bukkit.isPrimaryThread()) {
|
}
|
||||||
|
|
||||||
|
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);
|
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
||||||
new Throwable().printStackTrace();
|
Thread.dumpStack();
|
||||||
start = 0;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main thread ensured
|
||||||
|
if (--timingDepth == 0 && start != 0) {
|
||||||
addDiff(System.nanoTime() - start);
|
addDiff(System.nanoTime() - start);
|
||||||
start = 0;
|
start = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void abort() {
|
public final void abort() {
|
||||||
if (enabled && timingDepth > 0) {
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
void addDiff(long diff) {
|
void addDiff(long diff) {
|
||||||
if (TimingsManager.CURRENT == this) {
|
if (this != null) {
|
||||||
TimingsManager.CURRENT = parent;
|
this.children.get(id).add(diff);
|
||||||
if (parent != null) {
|
|
||||||
parent.children.get(id).add(diff);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
record.add(diff);
|
record.add(diff);
|
||||||
if (!added) {
|
if (!added) {
|
||||||
added = true;
|
added = true;
|
||||||
@@ -146,15 +146,29 @@ class TimingHandler implements Timing {
|
|||||||
TimingsManager.HANDLERS.add(this);
|
TimingsManager.HANDLERS.add(this);
|
||||||
}
|
}
|
||||||
if (groupHandler != null) {
|
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);
|
groupHandler.children.get(id).add(diff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset this timer, setting all values to zero.
|
* Reset this timer, setting all values to zero.
|
||||||
*
|
|
||||||
* @param full
|
|
||||||
*/
|
*/
|
||||||
void reset(boolean full) {
|
void reset(boolean full) {
|
||||||
record.reset();
|
record.reset();
|
||||||
@@ -184,8 +198,7 @@ class TimingHandler implements Timing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is simply for the Closeable interface so it can be used with
|
* This is simply for the Closeable interface so it can be used with try-with-resources ()
|
||||||
* try-with-resources ()
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
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,9 +4,12 @@ import java.lang.reflect.Field;
|
|||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.ThreadFactory;
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import com.google.common.collect.Queues;
|
import com.google.common.collect.Queues;
|
||||||
@@ -14,8 +17,15 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|||||||
|
|
||||||
import co.aikar.timings.Timing;
|
import co.aikar.timings.Timing;
|
||||||
import co.aikar.timings.Timings;
|
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 io.akarin.server.core.AkarinGlobalConfig;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
@SuppressWarnings("restriction")
|
@SuppressWarnings("restriction")
|
||||||
public abstract class Akari {
|
public abstract class Akari {
|
||||||
@@ -44,19 +54,59 @@ public abstract class Akari {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class AssignableFactory implements ThreadFactory {
|
public static class AssignableFactory implements ThreadFactory {
|
||||||
|
private final String threadName;
|
||||||
|
private int threadNumber;
|
||||||
|
|
||||||
|
public AssignableFactory(String name) {
|
||||||
|
threadName = name;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable run) {
|
public Thread newThread(Runnable run) {
|
||||||
Thread thread = new AssignableThread(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
|
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static class TimingSignal {
|
||||||
* A common tick pool
|
public final World tickedWorld;
|
||||||
*/
|
public final boolean isEntities;
|
||||||
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
|
|
||||||
|
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() {
|
public static boolean isPrimaryThread() {
|
||||||
return isPrimaryThread(true);
|
return isPrimaryThread(true);
|
||||||
@@ -64,7 +114,7 @@ public abstract class Akari {
|
|||||||
|
|
||||||
public static boolean isPrimaryThread(boolean assign) {
|
public static boolean isPrimaryThread(boolean assign) {
|
||||||
Thread current = Thread.currentThread();
|
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 = "";
|
public static final String EMPTY_STRING = "";
|
||||||
@@ -96,9 +146,7 @@ public abstract class Akari {
|
|||||||
*/
|
*/
|
||||||
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
|
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 Queue");
|
||||||
|
|
||||||
public final static Timing callbackTiming = getTiming("Akarin - Callback");
|
|
||||||
|
|
||||||
private static Timing getTiming(String name) {
|
private static Timing getTiming(String name) {
|
||||||
try {
|
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,16 +1,8 @@
|
|||||||
package io.akarin.api.internal.mixin;
|
package io.akarin.api.internal.mixin;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Random;
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
|
|
||||||
import net.minecraft.server.BlockPosition;
|
|
||||||
import net.minecraft.server.Chunk;
|
|
||||||
import net.minecraft.server.EnumSkyBlock;
|
|
||||||
|
|
||||||
public interface IMixinWorldServer {
|
public interface IMixinWorldServer {
|
||||||
boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk);
|
public Object lock();
|
||||||
|
public Random rand();
|
||||||
boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors);
|
|
||||||
|
|
||||||
ExecutorService getLightingExecutor();
|
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
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;
|
public static long timeUpdateInterval;
|
||||||
private static void timeUpdateInterval() {
|
private static void timeUpdateInterval() {
|
||||||
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
||||||
@@ -185,16 +180,6 @@ public class AkarinGlobalConfig {
|
|||||||
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
|
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int asyncLightingThreads;
|
|
||||||
private static void asyncLightingThreads() {
|
|
||||||
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean asyncLightingWorkStealing;
|
|
||||||
private static void asyncLightingWorkStealing() {
|
|
||||||
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean throwOnAsyncCaught;
|
public static boolean throwOnAsyncCaught;
|
||||||
private static void throwOnAsyncCaught() {
|
private static void throwOnAsyncCaught() {
|
||||||
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
|
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
|
||||||
@@ -211,52 +196,25 @@ public class AkarinGlobalConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static String messageKick;
|
public static String messageKick;
|
||||||
private static void messageKick() {
|
|
||||||
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String messageBan;
|
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;
|
public static String messageBanReason;
|
||||||
private static void messageBanReason() {
|
|
||||||
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String messageBanExpires;
|
public static String messageBanExpires;
|
||||||
private static void messageBanExpires() {
|
|
||||||
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String messageBanIp;
|
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;
|
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;
|
public static String messageJoin;
|
||||||
private static void messageJoin() {
|
|
||||||
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String messageJoinRenamed;
|
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;
|
public static String messageKickKeepAlive;
|
||||||
private static void messagekickKeepAlive() {
|
|
||||||
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String messagePlayerQuit;
|
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");
|
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,4 +252,14 @@ public class AkarinGlobalConfig {
|
|||||||
private static void forceHardcoreDifficulty() {
|
private static void forceHardcoreDifficulty() {
|
||||||
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class AkarinSlackScheduler extends Thread {
|
|||||||
MinecraftServer server = MinecraftServer.getServer();
|
MinecraftServer server = MinecraftServer.getServer();
|
||||||
|
|
||||||
while (server.isRunning()) {
|
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.
|
// Send time updates to everyone, it will get the right time from the world the player is in.
|
||||||
// Time update, from MinecraftServer#D
|
// Time update, from MinecraftServer#D
|
||||||
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
|
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
|
||||||
@@ -97,10 +98,10 @@ public class AkarinSlackScheduler extends Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Thread.sleep(100);
|
long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming);
|
||||||
} catch (InterruptedException ex) {
|
if (sleepFixed > 0) Thread.sleep(sleepFixed);
|
||||||
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
|
} catch (InterruptedException interrupted) {
|
||||||
ex.printStackTrace();
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.util.logging.Level;
|
|||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.craftbukkit.CraftServer;
|
|
||||||
import org.spigotmc.RestartCommand;
|
import org.spigotmc.RestartCommand;
|
||||||
import org.spigotmc.WatchdogThread;
|
import org.spigotmc.WatchdogThread;
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
|
|||||||
public abstract class Watchcat extends Thread {
|
public abstract class Watchcat extends Thread {
|
||||||
@Shadow private static WatchdogThread instance;
|
@Shadow private static WatchdogThread instance;
|
||||||
@Shadow private @Final long timeoutTime;
|
@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 @Final boolean restart;
|
||||||
@Shadow private volatile long lastTick;
|
@Shadow private volatile long lastTick;
|
||||||
@Shadow private volatile boolean stopping;
|
@Shadow private volatile boolean stopping;
|
||||||
@@ -38,48 +41,73 @@ public abstract class Watchcat extends Thread {
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!stopping) {
|
while (!stopping) {
|
||||||
//
|
// Paper start
|
||||||
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
|
long currentTime = System.currentTimeMillis();
|
||||||
Logger log = Bukkit.getServer().getLogger();
|
if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") )
|
||||||
log.log(Level.SEVERE, "Server has stopped responding!");
|
{
|
||||||
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues");
|
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
|
||||||
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
|
// Don't spam early warning dumps
|
||||||
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion());
|
if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
|
||||||
//
|
continue;
|
||||||
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
|
lastEarlyWarning = currentTime;
|
||||||
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 end
|
// 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, "------------------------------");
|
||||||
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Akarin!):");
|
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);
|
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
|
||||||
log.log(Level.SEVERE, "------------------------------");
|
log.log(Level.SEVERE, "------------------------------");
|
||||||
//
|
//
|
||||||
log.log(Level.SEVERE, "Entire Thread Dump:");
|
// Paper start - Only print full dump on long timeouts
|
||||||
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
if (isLongTimeout) {
|
||||||
for (ThreadInfo thread : threads) {
|
log.log(Level.SEVERE, "Entire Thread Dump:");
|
||||||
dumpThread(thread, log);
|
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, "------------------------------");
|
log.log(Level.SEVERE, "------------------------------");
|
||||||
|
|
||||||
if (restart) RestartCommand.restart(); // GC Inlined
|
if ( isLongTimeout )
|
||||||
break;
|
{
|
||||||
|
if (restart) {
|
||||||
|
RestartCommand.restart();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // Paper end
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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) {
|
} catch (InterruptedException ex) {
|
||||||
interrupt();
|
interrupt();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 MixinItemMonsterEgg {
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,10 +2,13 @@ package io.akarin.server.mixin.core;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.FutureTask;
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.WordUtils;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.craftbukkit.CraftServer;
|
import org.bukkit.craftbukkit.CraftServer;
|
||||||
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||||
@@ -23,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import co.aikar.timings.MinecraftTimings;
|
import co.aikar.timings.MinecraftTimings;
|
||||||
import io.akarin.api.internal.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.api.internal.Akari.AssignableFactory;
|
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.AkarinGlobalConfig;
|
||||||
import io.akarin.server.core.AkarinSlackScheduler;
|
import io.akarin.server.core.AkarinSlackScheduler;
|
||||||
import net.minecraft.server.BlockPosition;
|
import net.minecraft.server.BlockPosition;
|
||||||
@@ -42,6 +45,8 @@ import net.minecraft.server.WorldServer;
|
|||||||
@Mixin(value = MinecraftServer.class, remap = false)
|
@Mixin(value = MinecraftServer.class, remap = false)
|
||||||
public abstract class MixinMinecraftServer {
|
public abstract class MixinMinecraftServer {
|
||||||
@Shadow @Final public Thread primaryThread;
|
@Shadow @Final public Thread primaryThread;
|
||||||
|
private boolean tickedPrimaryEntities;
|
||||||
|
private int cachedWorldSize;
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public String getServerModName() {
|
public String getServerModName() {
|
||||||
@@ -56,10 +61,11 @@ public abstract class MixinMinecraftServer {
|
|||||||
private void prerun(CallbackInfo info) {
|
private void prerun(CallbackInfo info) {
|
||||||
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
|
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
|
||||||
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
|
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
|
||||||
|
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
|
||||||
|
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
for (int i = 0; i < worlds.size(); ++i) {
|
||||||
WorldServer world = worlds.get(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.get().boot();
|
AkarinSlackScheduler.get().boot();
|
||||||
}
|
}
|
||||||
@@ -109,7 +115,7 @@ public abstract class MixinMinecraftServer {
|
|||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
protected void l() throws InterruptedException {
|
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++) {
|
for (int index = 0; index < worlds.size(); index++) {
|
||||||
WorldServer world = this.worlds.get(index);
|
WorldServer world = this.worlds.get(index);
|
||||||
@@ -148,7 +154,11 @@ public abstract class MixinMinecraftServer {
|
|||||||
|
|
||||||
private boolean tickEntities(WorldServer world) {
|
private boolean tickEntities(WorldServer world) {
|
||||||
try {
|
try {
|
||||||
|
world.timings.tickEntities.startTiming();
|
||||||
world.tickEntities();
|
world.tickEntities();
|
||||||
|
world.timings.tickEntities.stopTiming();
|
||||||
|
world.getTracker().updatePlayers();
|
||||||
|
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
CrashReport crashreport;
|
CrashReport crashreport;
|
||||||
try {
|
try {
|
||||||
@@ -164,7 +174,9 @@ public abstract class MixinMinecraftServer {
|
|||||||
|
|
||||||
private void tickWorld(WorldServer world) {
|
private void tickWorld(WorldServer world) {
|
||||||
try {
|
try {
|
||||||
|
world.timings.doTick.startTiming();
|
||||||
world.doTick();
|
world.doTick();
|
||||||
|
world.timings.doTick.stopTiming();
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
CrashReport crashreport;
|
CrashReport crashreport;
|
||||||
try {
|
try {
|
||||||
@@ -178,8 +190,12 @@ public abstract class MixinMinecraftServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void D() throws InterruptedException {
|
public void D() throws InterruptedException, ExecutionException, CancellationException {
|
||||||
Runnable runnable;
|
Runnable runnable;
|
||||||
|
Akari.callbackTiming.startTiming();
|
||||||
|
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||||
|
Akari.callbackTiming.stopTiming();
|
||||||
|
|
||||||
MinecraftTimings.bukkitSchedulerTimer.startTiming();
|
MinecraftTimings.bukkitSchedulerTimer.startTiming();
|
||||||
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
|
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
|
||||||
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
|
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
|
||||||
@@ -200,54 +216,92 @@ public abstract class MixinMinecraftServer {
|
|||||||
ChunkIOExecutor.tick();
|
ChunkIOExecutor.tick();
|
||||||
MinecraftTimings.chunkIOTickTimer.stopTiming();
|
MinecraftTimings.chunkIOTickTimer.stopTiming();
|
||||||
|
|
||||||
Akari.worldTiming.startTiming();
|
if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
|
||||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
switch (AkarinGlobalConfig.parallelMode) {
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
case 1:
|
||||||
WorldServer world = worlds.get(i);
|
case 2:
|
||||||
world.timings.tickEntities.startTiming();
|
default:
|
||||||
world.timings.doTick.startTiming();
|
// Never tick one world concurrently!
|
||||||
}
|
for (int i = 0; i < cachedWorldSize; i++) {
|
||||||
}
|
// Impl Note:
|
||||||
Akari.STAGE_TICK.submit(() -> {
|
// Entities ticking: index 1 -> ... -> 0 (parallel)
|
||||||
// Never tick one world concurrently!
|
// World ticking: index 0 -> ... (parallel)
|
||||||
for (int i = 1; i <= worlds.size(); ++i) {
|
int interlace = i + 1;
|
||||||
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
|
WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
|
||||||
synchronized (((IMixinLockProvider) world).lock()) {
|
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);
|
tickEntities(world);
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
}, null);
|
|
||||||
|
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
|
||||||
WorldServer world = worlds.get(i);
|
|
||||||
synchronized (((IMixinLockProvider) world).lock()) {
|
|
||||||
tickWorld(world);
|
|
||||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Akari.entityCallbackTiming.startTiming();
|
|
||||||
Akari.STAGE_TICK.take();
|
|
||||||
Akari.entityCallbackTiming.stopTiming();
|
|
||||||
|
|
||||||
Akari.worldTiming.stopTiming();
|
|
||||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
|
||||||
WorldServer world = worlds.get(i);
|
|
||||||
world.timings.tickEntities.stopTiming();
|
|
||||||
world.timings.doTick.stopTiming();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Akari.callbackTiming.startTiming();
|
Akari.callbackTiming.startTiming();
|
||||||
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||||
Akari.callbackTiming.stopTiming();
|
Akari.callbackTiming.stopTiming();
|
||||||
|
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
|
||||||
WorldServer world = worlds.get(i);
|
|
||||||
tickUnsafeSync(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
MinecraftTimings.connectionTimer.startTiming();
|
MinecraftTimings.connectionTimer.startTiming();
|
||||||
serverConnection().c();
|
serverConnection().c();
|
||||||
MinecraftTimings.connectionTimer.stopTiming();
|
MinecraftTimings.connectionTimer.stopTiming();
|
||||||
@@ -266,12 +320,4 @@ public abstract class MixinMinecraftServer {
|
|||||||
}
|
}
|
||||||
MinecraftTimings.tickablesTimer.stopTiming();
|
MinecraftTimings.tickablesTimer.stopTiming();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tickUnsafeSync(WorldServer world) {
|
|
||||||
world.timings.doChunkMap.startTiming();
|
|
||||||
world.manager.flush();
|
|
||||||
world.timings.doChunkMap.stopTiming();
|
|
||||||
|
|
||||||
world.getTracker().updatePlayers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
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 java.util.Set;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
import org.bukkit.command.defaults.VersionCommand;
|
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.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
import io.akarin.api.internal.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
import net.minecraft.server.MCUtil;
|
import net.minecraft.server.MCUtil;
|
||||||
|
|
||||||
@Mixin(value = VersionCommand.class, remap = false)
|
@Mixin(value = VersionCommand.class, remap = false)
|
||||||
public abstract class MixinVersionCommand {
|
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
|
* Match current version with repository and calculate the distance
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public abstract class MixinWorldManager {
|
|||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void a(Entity entity) {
|
public void a(Entity entity) {
|
||||||
this.world.getTracker().entriesLock.lock(); // Akarin
|
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
|
||||||
this.world.getTracker().track(entity);
|
this.world.getTracker().track(entity);
|
||||||
this.world.getTracker().entriesLock.unlock(); // Akarin
|
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
|
||||||
if (entity instanceof EntityPlayer) {
|
if (entity instanceof EntityPlayer) {
|
||||||
this.world.worldProvider.a((EntityPlayer) entity);
|
this.world.worldProvider.a((EntityPlayer) entity);
|
||||||
|
|||||||
@@ -1,24 +1,37 @@
|
|||||||
package io.akarin.server.mixin.core;
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import java.util.Random;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
|
|
||||||
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;
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
@Mixin(value = WorldServer.class, remap = false)
|
@Mixin(value = WorldServer.class, remap = false)
|
||||||
public abstract class MixinWorldServer implements IMixinLockProvider {
|
public abstract class MixinWorldServer implements IMixinWorldServer {
|
||||||
@Redirect(method = "doTick()V", at = @At(
|
|
||||||
value = "INVOKE",
|
|
||||||
target = "net/minecraft/server/PlayerChunkMap.flush()V"
|
|
||||||
))
|
|
||||||
public void onFlush() {} // Migrated to main thread
|
|
||||||
|
|
||||||
private final Object tickLock = new Object();
|
private final Object tickLock = new Object();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object lock() {
|
public Object lock() {
|
||||||
return tickLock;
|
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,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;
|
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||||
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
|
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
|
||||||
activityAccountant.startActivity(0.5);
|
activityAccountant.startActivity(0.5);
|
||||||
|
|
||||||
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
|
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
|
||||||
int remainingChunks = chunks.size();
|
int remainingChunks = chunks.size();
|
||||||
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||||
@@ -47,23 +48,19 @@ public abstract class MixinChunkProviderServer {
|
|||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Entry<Chunk> entry = it.next();
|
Entry<Chunk> entry = it.next();
|
||||||
Chunk chunk = entry.getValue();
|
Chunk chunk = entry.getValue();
|
||||||
if (chunk == null) continue;
|
|
||||||
|
|
||||||
if (chunk.isUnloading()) {
|
if (chunk != null && chunk.isUnloading()) {
|
||||||
if (chunk.scheduledForUnload != null) {
|
if (chunk.scheduledForUnload != null) {
|
||||||
if (now - chunk.scheduledForUnload > unloadAfter) {
|
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
|
||||||
chunk.scheduledForUnload = null;
|
|
||||||
} else continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!unloadChunk(chunk, true)) { // Event cancelled
|
if (unloadChunk(chunk, true)) {
|
||||||
// If a plugin cancelled it, we shouldn't trying unload it for a while
|
it.remove();
|
||||||
chunk.setShouldUnload(false);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
chunk.setShouldUnload(false);
|
||||||
|
chunk.scheduledForUnload = null;
|
||||||
|
|
||||||
it.remove();
|
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
|
||||||
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activityAccountant.endActivity();
|
activityAccountant.endActivity();
|
||||||
|
|||||||
@@ -1,671 +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 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 static 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()) { // OBFHELPER: hasSkyLight
|
|
||||||
lightExecutorService.execute(() -> {
|
|
||||||
this.recheckGapsAsync(neighbors);
|
|
||||||
});
|
|
||||||
this.isGapLightingUpdated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.ticked = true;
|
|
||||||
|
|
||||||
if ((true || !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()) { // OBFHELPER: getTileEntity
|
|
||||||
TileEntity tileentity = this.createNewTileEntity(blockpos);
|
|
||||||
this.world.setTileEntity(blockpos, tileentity);
|
|
||||||
this.world.b(blockpos, blockpos); // OBFHELPER: 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()); // OBFHELPER: 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "h(Z)V", at = @At("HEAD"), cancellable = true)
|
|
||||||
private void onRecheckGaps(CallbackInfo ci) {
|
|
||||||
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isUnloading()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final List<Chunk> neighborChunks = this.getSurroundingChunks();
|
|
||||||
if (neighborChunks.isEmpty()) {
|
|
||||||
this.isGapLightingUpdated = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Akari.isPrimaryThread()) {
|
|
||||||
try {
|
|
||||||
lightExecutorService.execute(() -> {
|
|
||||||
this.recheckGapsAsync(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.recheckGapsAsync(neighborChunks);
|
|
||||||
}
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rechecks chunk gaps async.
|
|
||||||
*
|
|
||||||
* @param neighbors A thread-safe list of surrounding neighbor chunks
|
|
||||||
*/
|
|
||||||
private void recheckGapsAsync(List<Chunk> neighbors) {
|
|
||||||
this.isLightPopulated = false;
|
|
||||||
|
|
||||||
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()); // OBFHELPER: 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()) {
|
|
||||||
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()) { // OBFHELPER: hasSkyLight
|
|
||||||
CHECK_LIGHT:
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
|
||||||
for (int j = 0; j < 16; ++j) {
|
|
||||||
if (!this.checkLightAsync(i, j, neighbors)) {
|
|
||||||
this.isLightPopulated = false;
|
|
||||||
break CHECK_LIGHT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isLightPopulated) {
|
|
||||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
|
||||||
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // OBFHELPER: 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.a(enumfacing.opposite()); // OBFHELPER: checkLightSide
|
|
||||||
}
|
|
||||||
|
|
||||||
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(); // OBFHELPER: 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)) { // OBFHELPER: 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)) { // OBFHELPER: 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 = 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) { // OBFHELPER: 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()) { // OBFHELPER: 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); // OBFHELPER: setSkyLight
|
|
||||||
// this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // OBFHELPER: 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); // OBFHELPER: setSkyLight
|
|
||||||
// this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // OBFHELPER: 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); // OBFHELPER: 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()) { // OBFHELPER: hasSkyLight
|
|
||||||
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
|
||||||
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // OBFHELPER: 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()) { // OBFHELPER: 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); // OBFHELPER: 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()) { // OBFHELPER: 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,45 +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 = 1002)
|
|
||||||
public abstract class MixinWorld {
|
|
||||||
@Shadow protected IChunkProvider chunkProvider;
|
|
||||||
@Shadow int[] J; // OBFHELPER: 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,375 +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 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 io.akarin.server.core.AkarinGlobalConfig;
|
|
||||||
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, priority = 1002)
|
|
||||||
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 ExecutorService lightExecutorService = getExecutorService();
|
|
||||||
private static ExecutorService getExecutorService() {
|
|
||||||
return AkarinGlobalConfig.asyncLightingWorkStealing ?
|
|
||||||
Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build())
|
|
||||||
:
|
|
||||||
Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // OBFHELPER: checkLightFor
|
|
||||||
return updateLightAsync(lightType, pos, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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 recheckIndex = 0;
|
|
||||||
int blockIndex = 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[blockIndex++] = 133152; // OBFHELPER: lightUpdateBlockList
|
|
||||||
} else if (rawLight < current) {
|
|
||||||
this.J[blockIndex++] = 133152 | current << 18; // OBFHELPER: lightUpdateBlockList
|
|
||||||
|
|
||||||
while (recheckIndex < blockIndex) {
|
|
||||||
int blockData = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
|
|
||||||
int i2 = (blockData & 63) - 32 + x;
|
|
||||||
int j2 = (blockData >> 6 & 63) - 32 + y;
|
|
||||||
int k2 = (blockData >> 12 & 63) - 32 + z;
|
|
||||||
int l2 = blockData >> 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()); // OBFHELPER: getLightOpacity
|
|
||||||
lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors);
|
|
||||||
// Sponge end
|
|
||||||
|
|
||||||
if (lightLevel == l2 - opacity && blockIndex < this.J.length) { // OBFHELPER: lightUpdateBlockList
|
|
||||||
this.J[blockIndex++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mutableBlockpos.free();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
recheckIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (recheckIndex < blockIndex) {
|
|
||||||
int i5 = this.J[recheckIndex++]; // OBFHELPER: 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 = blockIndex < this.J.length - 6; // OBFHELPER: 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[blockIndex++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) {
|
|
||||||
this.J[blockIndex++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) {
|
|
||||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) {
|
|
||||||
this.J[blockIndex++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) {
|
|
||||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) {
|
|
||||||
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // OBFHELPER: 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() || 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
|
|
||||||
lightExecutorService.execute(() -> {
|
|
||||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.checkLightAsync(lightType, pos, chunk, neighbors);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExecutorService getLightingExecutor() {
|
|
||||||
return 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)) { // OBFHELPER: isAtLocation
|
|
||||||
if (currentChunk.isUnloading()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return currentChunk;
|
|
||||||
}
|
|
||||||
for (Chunk neighbor : neighbors) {
|
|
||||||
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: 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; // OBFHELPER: defaultLightValue
|
|
||||||
}
|
|
||||||
|
|
||||||
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
|
|
||||||
if (chunk == null || chunk.isUnloading()) {
|
|
||||||
return lightType.c; // OBFHELPER: 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; // OBFHELPER: defaultLightValue
|
|
||||||
}
|
|
||||||
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // OBFHELPER: 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(); // OBFHELPER: 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); // OBFHELPER: 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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -147,6 +147,7 @@ public abstract class PandaRedstoneWire extends Block {
|
|||||||
|
|
||||||
while (!turnOff.isEmpty()) {
|
while (!turnOff.isEmpty()) {
|
||||||
BlockPosition pos = turnOff.remove(0);
|
BlockPosition pos = turnOff.remove(0);
|
||||||
|
if (pos == null) continue; // Akarin
|
||||||
IBlockData state = worldIn.getType(pos);
|
IBlockData state = worldIn.getType(pos);
|
||||||
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
this.canProvidePower = false;
|
this.canProvidePower = false;
|
||||||
@@ -185,6 +186,7 @@ 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.
|
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
|
||||||
while (!this.turnOn.isEmpty()) {
|
while (!this.turnOn.isEmpty()) {
|
||||||
BlockPosition pos = this.turnOn.remove(0);
|
BlockPosition pos = this.turnOn.remove(0);
|
||||||
|
if (pos == null) continue; // Akarin
|
||||||
IBlockData state = worldIn.getType(pos);
|
IBlockData state = worldIn.getType(pos);
|
||||||
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
this.canProvidePower = false;
|
this.canProvidePower = false;
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
package net.minecraft.server;
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
// Paper start
|
||||||
import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode;
|
import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
// Paper end
|
||||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.collect.Queues;
|
import com.google.common.collect.Queues;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@@ -22,11 +24,6 @@ import com.google.common.collect.Lists; // CraftBukkit
|
|||||||
import org.bukkit.Server; // CraftBukkit
|
import org.bukkit.Server; // CraftBukkit
|
||||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers; // Paper
|
import org.bukkit.craftbukkit.util.CraftMagicNumbers; // Paper
|
||||||
|
|
||||||
/**
|
|
||||||
* Akarin Changes Note
|
|
||||||
* 1) Add volatile to fields (async lighting)
|
|
||||||
* 2) Expose private methods (async lighting)
|
|
||||||
*/
|
|
||||||
public class Chunk {
|
public class Chunk {
|
||||||
|
|
||||||
private static final Logger e = LogManager.getLogger();
|
private static final Logger e = LogManager.getLogger();
|
||||||
@@ -54,11 +51,11 @@ public class Chunk {
|
|||||||
TileEntity replaced = super.put(key, value);
|
TileEntity replaced = super.put(key, value);
|
||||||
if (replaced != null) {
|
if (replaced != null) {
|
||||||
replaced.setCurrentChunk(null);
|
replaced.setCurrentChunk(null);
|
||||||
tileEntityCounts.decrement(replaced.tileEntityKeyString);
|
tileEntityCounts.decrement(replaced.getMinecraftKeyString());
|
||||||
}
|
}
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
value.setCurrentChunk(Chunk.this);
|
value.setCurrentChunk(Chunk.this);
|
||||||
tileEntityCounts.increment(value.tileEntityKeyString);
|
tileEntityCounts.increment(value.getMinecraftKeyString());
|
||||||
}
|
}
|
||||||
return replaced;
|
return replaced;
|
||||||
}
|
}
|
||||||
@@ -68,16 +65,16 @@ public class Chunk {
|
|||||||
TileEntity removed = super.remove(key);
|
TileEntity removed = super.remove(key);
|
||||||
if (removed != null) {
|
if (removed != null) {
|
||||||
removed.setCurrentChunk(null);
|
removed.setCurrentChunk(null);
|
||||||
tileEntityCounts.decrement(removed.tileEntityKeyString);
|
tileEntityCounts.decrement(removed.getMinecraftKeyString());
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public
|
final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
|
||||||
// Paper end
|
// Paper end
|
||||||
private volatile boolean done; // Akarin - volatile
|
private boolean done;
|
||||||
private volatile boolean lit; // Akarin - volatile
|
private boolean lit;
|
||||||
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
|
private boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER
|
||||||
private boolean s;
|
private boolean s;
|
||||||
private boolean t;
|
private boolean t;
|
||||||
private long lastSaved;
|
private long lastSaved;
|
||||||
@@ -277,14 +274,7 @@ public class Chunk {
|
|||||||
|
|
||||||
private void h(boolean flag) {
|
private void h(boolean flag) {
|
||||||
this.world.methodProfiler.a("recheckGaps");
|
this.world.methodProfiler.a("recheckGaps");
|
||||||
if (this.world.areChunksLoaded(new BlockPosition(this.locX * 16 + 8, 0, this.locZ * 16 + 8), 16)) {
|
if (this.areNeighborsLoaded(1)) { // Paper
|
||||||
this.runOrQueueLightUpdate(() -> recheckGaps(flag)); // Paper - Queue light update
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recheckGaps(boolean flag) {
|
|
||||||
if (true) {
|
|
||||||
// Paper end
|
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
for (int j = 0; j < 16; ++j) {
|
for (int j = 0; j < 16; ++j) {
|
||||||
if (this.i[i + j * 16]) {
|
if (this.i[i + j * 16]) {
|
||||||
@@ -335,7 +325,7 @@ public class Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void a(int i, int j, int k, int l) {
|
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) {
|
for (int i1 = k; i1 < l; ++i1) {
|
||||||
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
|
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
|
||||||
}
|
}
|
||||||
@@ -667,6 +657,7 @@ public class Chunk {
|
|||||||
if (i != this.locX || j != this.locZ) {
|
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);
|
Chunk.e.warn("Wrong location! ({}, {}) should be ({}, {}), {}", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(this.locX), Integer.valueOf(this.locZ), entity);
|
||||||
entity.die();
|
entity.die();
|
||||||
|
return; // Paper
|
||||||
}
|
}
|
||||||
|
|
||||||
int k = MathHelper.floor(entity.locY / 16.0D);
|
int k = MathHelper.floor(entity.locY / 16.0D);
|
||||||
@@ -687,24 +678,18 @@ public class Chunk {
|
|||||||
// Paper start
|
// Paper start
|
||||||
List<Entity> entitySlice = this.entitySlices[k];
|
List<Entity> entitySlice = this.entitySlices[k];
|
||||||
boolean inThis = entitySlice.contains(entity);
|
boolean inThis = entitySlice.contains(entity);
|
||||||
if (entity.entitySlice != null || inThis) {
|
List<Entity> currentSlice = entity.entitySlice;
|
||||||
if (entity.entitySlice == entitySlice || inThis) {
|
if (inThis || (currentSlice != null && currentSlice.contains(entity))) {
|
||||||
LogManager.getLogger().warn(entity + " was already in this chunk section! Report this to https://github.com/PaperMC/Paper/issues/1223");
|
if (currentSlice == entitySlice || inThis) {
|
||||||
new Throwable().printStackTrace();
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
LogManager.getLogger().warn(entity + " is still in another ChunkSection! Report this to https://github.com/PaperMC/Paper/issues/1223");
|
|
||||||
|
|
||||||
Chunk chunk = entity.getCurrentChunk();
|
Chunk chunk = entity.getCurrentChunk();
|
||||||
if (chunk != null) {
|
if (chunk != null) {
|
||||||
if (chunk != this) {
|
|
||||||
LogManager.getLogger().warn(entity + " was in another chunk at that! " + chunk.locX + "," + chunk.locZ);
|
|
||||||
}
|
|
||||||
chunk.removeEntity(entity);
|
chunk.removeEntity(entity);
|
||||||
} else {
|
} else {
|
||||||
removeEntity(entity);
|
removeEntity(entity);
|
||||||
}
|
}
|
||||||
new Throwable().printStackTrace();
|
currentSlice.remove(entity); // Just incase the above did not remove from this target slice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entity.entitySlice = entitySlice;
|
entity.entitySlice = entitySlice;
|
||||||
@@ -712,7 +697,7 @@ public class Chunk {
|
|||||||
|
|
||||||
this.markDirty();
|
this.markDirty();
|
||||||
entity.setCurrentChunk(this);
|
entity.setCurrentChunk(this);
|
||||||
entityCounts.increment(entity.entityKeyString);
|
entityCounts.increment(entity.getMinecraftKeyString());
|
||||||
if (entity instanceof EntityItem) {
|
if (entity instanceof EntityItem) {
|
||||||
itemCounts[k]++;
|
itemCounts[k]++;
|
||||||
} else if (entity instanceof IInventory) {
|
} else if (entity instanceof IInventory) {
|
||||||
@@ -752,16 +737,13 @@ public class Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paper start
|
// Paper start
|
||||||
if (!this.entitySlices[i].remove(entity)) { return; }
|
if (entity.entitySlice == null || !entity.entitySlice.contains(entity) || entitySlices[i] == entity.entitySlice) {
|
||||||
if (entitySlices[i] == entity.entitySlice) {
|
|
||||||
entity.entitySlice = null;
|
entity.entitySlice = null;
|
||||||
} else {
|
|
||||||
LogManager.getLogger().warn(entity + " was removed from a entitySlice we did not expect. Report this to https://github.com/PaperMC/Paper/issues/1223");
|
|
||||||
new Throwable().printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
if (!this.entitySlices[i].remove(entity)) { return; }
|
||||||
this.markDirty();
|
this.markDirty();
|
||||||
entity.setCurrentChunk(null);
|
entity.setCurrentChunk(null);
|
||||||
entityCounts.decrement(entity.entityKeyString);
|
entityCounts.decrement(entity.getMinecraftKeyString());
|
||||||
if (entity instanceof EntityItem) {
|
if (entity instanceof EntityItem) {
|
||||||
itemCounts[i]--;
|
itemCounts[i]--;
|
||||||
} else if (entity instanceof IInventory) {
|
} else if (entity instanceof IInventory) {
|
||||||
@@ -890,23 +872,35 @@ public class Chunk {
|
|||||||
this.world.b(this.tileEntities.values());
|
this.world.b(this.tileEntities.values());
|
||||||
List[] aentityslice = this.entitySlices; // Spigot
|
List[] aentityslice = this.entitySlices; // Spigot
|
||||||
int i = aentityslice.length;
|
int i = aentityslice.length;
|
||||||
|
List<Entity> toAdd = new java.util.ArrayList<>(32); // Paper
|
||||||
|
|
||||||
for (int j = 0; j < i; ++j) {
|
for (int j = 0; j < i; ++j) {
|
||||||
List entityslice = aentityslice[j]; // Spigot
|
List entityslice = aentityslice[j]; // Spigot
|
||||||
// Paper start
|
// Paper start
|
||||||
DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode;
|
DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode;
|
||||||
if (mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.REGEN) {
|
if (mode == DuplicateUUIDMode.WARN || mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.SAFE_REGEN) {
|
||||||
Map<UUID, Entity> thisChunk = new HashMap<>();
|
Map<UUID, Entity> thisChunk = new HashMap<>();
|
||||||
for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) {
|
for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) {
|
||||||
Entity entity = iterator.next();
|
Entity entity = iterator.next();
|
||||||
if (entity.dead) continue;
|
if (entity.dead || entity.valid) continue;
|
||||||
Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID);
|
Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID);
|
||||||
if (other == null) {
|
if (other == null || other.dead || world.getEntityUnloadQueue().contains(other)) {
|
||||||
other = thisChunk.get(entity.uniqueID);
|
other = thisChunk.get(entity.uniqueID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
if (other != null && !other.dead) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case REGEN: {
|
case SAFE_REGEN: {
|
||||||
entity.setUUID(UUID.randomUUID());
|
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.");
|
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;
|
break;
|
||||||
@@ -917,15 +911,20 @@ public class Chunk {
|
|||||||
iterator.remove();
|
iterator.remove();
|
||||||
break;
|
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);
|
thisChunk.put(entity.uniqueID, entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Paper end
|
|
||||||
|
|
||||||
this.world.a((Collection) entityslice);
|
//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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -972,6 +971,8 @@ public class Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Spigot End
|
// 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.
|
// 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)
|
// (which for example disables inventory icon updates and prevents block breaking)
|
||||||
@@ -1218,7 +1219,7 @@ public class Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.r = true;
|
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();
|
this.o();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1405,7 +1406,7 @@ public class Chunk {
|
|||||||
this.h(false);
|
this.h(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a(EnumDirection enumdirection) { // Akarin - private -> public
|
private void a(EnumDirection enumdirection) {
|
||||||
if (this.done) {
|
if (this.done) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,9 @@ import org.bukkit.entity.LivingEntity;
|
|||||||
import org.bukkit.entity.Vehicle;
|
import org.bukkit.entity.Vehicle;
|
||||||
import co.aikar.timings.MinecraftTimings; // Paper
|
import co.aikar.timings.MinecraftTimings; // Paper
|
||||||
import co.aikar.timings.Timing; // Paper
|
import co.aikar.timings.Timing; // Paper
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||||
|
|
||||||
import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
||||||
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
||||||
import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
|
import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
|
||||||
@@ -51,7 +54,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
private static final int CURRENT_LEVEL = 2;
|
private static final int CURRENT_LEVEL = 2;
|
||||||
// Paper start
|
// Paper start
|
||||||
public static Random SHARED_RANDOM = new io.akarin.api.internal.utils.random.LightRandom() { // Akarin - LightRNG
|
public static Random SHARED_RANDOM = new java.util.Random() {
|
||||||
private boolean locked = false;
|
private boolean locked = false;
|
||||||
@Override
|
@Override
|
||||||
public synchronized void setSeed(long seed) {
|
public synchronized void setSeed(long seed) {
|
||||||
@@ -63,7 +66,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Object entitySlice = null;
|
List<Entity> entitySlice = null;
|
||||||
// Paper end
|
// Paper end
|
||||||
static boolean isLevelAtLeast(NBTTagCompound tag, int level) {
|
static boolean isLevelAtLeast(NBTTagCompound tag, int level) {
|
||||||
return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
|
return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
|
||||||
@@ -85,7 +88,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
private static final List<ItemStack> b = Collections.emptyList();
|
private static final List<ItemStack> b = Collections.emptyList();
|
||||||
private static final AxisAlignedBB c = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
|
private static final AxisAlignedBB c = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
|
||||||
private static double f = 1.0D;
|
private static double f = 1.0D;
|
||||||
private static int entityCount;
|
private static int entityCount = 1; // Paper - MC-111480 - ID 0 is treated as special for DataWatchers, start 1
|
||||||
private int id;
|
private int id;
|
||||||
public boolean i; public boolean blocksEntitySpawning() { return i; } // Paper - OBFHELPER
|
public boolean i; public boolean blocksEntitySpawning() { return i; } // Paper - OBFHELPER
|
||||||
public final List<Entity> passengers;
|
public final List<Entity> passengers;
|
||||||
@@ -128,6 +131,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
protected boolean E;
|
protected boolean E;
|
||||||
private boolean aw;
|
private boolean aw;
|
||||||
public boolean dead;
|
public boolean dead;
|
||||||
|
public boolean shouldBeRemoved; // Paper
|
||||||
public float width;
|
public float width;
|
||||||
public float length;
|
public float length;
|
||||||
public float I;
|
public float I;
|
||||||
@@ -207,7 +211,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
this.length = 1.8F;
|
this.length = 1.8F;
|
||||||
this.ax = 1;
|
this.ax = 1;
|
||||||
this.ay = 1.0F;
|
this.ay = 1.0F;
|
||||||
this.random = SHARED_RANDOM; // Paper
|
this.random = world == null ? SHARED_RANDOM : ((IMixinWorldServer) world).rand(); // Paper // Akarin
|
||||||
this.fireTicks = -this.getMaxFireTicks();
|
this.fireTicks = -this.getMaxFireTicks();
|
||||||
this.justCreated = true;
|
this.justCreated = true;
|
||||||
this.uniqueID = MathHelper.a(this.random);
|
this.uniqueID = MathHelper.a(this.random);
|
||||||
@@ -344,6 +348,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
this.locX = d0;
|
this.locX = d0;
|
||||||
this.locY = d1;
|
this.locY = d1;
|
||||||
this.locZ = d2;
|
this.locZ = d2;
|
||||||
|
if (valid) world.entityJoinedWorld(this, false); // Paper - ensure Entity is moved to its proper chunk
|
||||||
float f = this.width / 2.0F;
|
float f = this.width / 2.0F;
|
||||||
float f1 = this.length;
|
float f1 = this.length;
|
||||||
|
|
||||||
@@ -989,6 +994,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
this.locX = (axisalignedbb.a + axisalignedbb.d) / 2.0D;
|
this.locX = (axisalignedbb.a + axisalignedbb.d) / 2.0D;
|
||||||
this.locY = axisalignedbb.b;
|
this.locY = axisalignedbb.b;
|
||||||
this.locZ = (axisalignedbb.c + axisalignedbb.f) / 2.0D;
|
this.locZ = (axisalignedbb.c + axisalignedbb.f) / 2.0D;
|
||||||
|
if (valid) world.entityJoinedWorld(this, false); // Paper - ensure Entity is moved to its proper chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SoundEffect ae() {
|
protected SoundEffect ae() {
|
||||||
@@ -1310,6 +1316,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
this.lastYaw -= 360.0F;
|
this.lastYaw -= 360.0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
world.getChunkAt((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); // Paper - ensure chunk is always loaded
|
||||||
this.setPosition(this.locX, this.locY, this.locZ);
|
this.setPosition(this.locX, this.locY, this.locZ);
|
||||||
this.setYawPitch(f, f1);
|
this.setYawPitch(f, f1);
|
||||||
}
|
}
|
||||||
@@ -1471,6 +1478,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void runKillTrigger(Entity entity, int kills, DamageSource damageSource) { this.a(entity, kills, damageSource); } // Paper - OBFHELPER
|
||||||
public void a(Entity entity, int i, DamageSource damagesource) {
|
public void a(Entity entity, int i, DamageSource damagesource) {
|
||||||
if (entity instanceof EntityPlayer) {
|
if (entity instanceof EntityPlayer) {
|
||||||
CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource);
|
CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource);
|
||||||
@@ -1791,7 +1799,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
*/
|
*/
|
||||||
public Chunk getCurrentChunk() {
|
public Chunk getCurrentChunk() {
|
||||||
final Chunk chunk = currentChunk != null ? currentChunk.get() : null;
|
final Chunk chunk = currentChunk != null ? currentChunk.get() : null;
|
||||||
return chunk != null && chunk.isLoaded() ? chunk : null;
|
return chunk != null && chunk.isLoaded() ? chunk : (isAddedToChunk() ? world.getChunkIfLoaded(getChunkX(), getChunkZ()) : null);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns the chunk at the location, using the entities local cache if avail
|
* Returns the chunk at the location, using the entities local cache if avail
|
||||||
@@ -1813,21 +1821,26 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
public Chunk getChunkAtLocation() {
|
public Chunk getChunkAtLocation() {
|
||||||
return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4);
|
return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4);
|
||||||
}
|
}
|
||||||
public final MinecraftKey entityKey = EntityTypes.getKey(this);
|
private String entityKeyString = null;
|
||||||
public final String entityKeyString = entityKey != null ? entityKey.toString() : null;
|
private MinecraftKey entityKey = getMinecraftKey();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MinecraftKey getMinecraftKey() {
|
public MinecraftKey getMinecraftKey() {
|
||||||
|
if (entityKey == null) {
|
||||||
|
entityKey = EntityTypes.getKey(this);
|
||||||
|
entityKeyString = entityKey != null ? entityKey.toString() : null;
|
||||||
|
}
|
||||||
return entityKey;
|
return entityKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMinecraftKeyString() {
|
public String getMinecraftKeyString() {
|
||||||
|
getMinecraftKey(); // Try to load if it doesn't exists. see: https://github.com/PaperMC/Paper/issues/1280
|
||||||
return entityKeyString;
|
return entityKeyString;
|
||||||
}
|
}
|
||||||
@Nullable
|
@Nullable
|
||||||
public final String getSaveID() {
|
public final String getSaveID() {
|
||||||
return entityKeyString;
|
return getMinecraftKeyString();
|
||||||
// Paper end
|
// Paper end
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2262,6 +2275,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER
|
||||||
public void b(EntityLiving entityliving) {}
|
public void b(EntityLiving entityliving) {}
|
||||||
|
|
||||||
protected boolean i(double d0, double d1, double d2) {
|
protected boolean i(double d0, double d1, double d2) {
|
||||||
@@ -2960,6 +2974,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
|
|||||||
return EnumPistonReaction.NORMAL;
|
return EnumPistonReaction.NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SoundCategory getDeathSoundCategory() { return bK();} // Paper - OBFHELPER
|
||||||
public SoundCategory bK() {
|
public SoundCategory bK() {
|
||||||
return SoundCategory.NEUTRAL;
|
return SoundCategory.NEUTRAL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import org.bukkit.inventory.MainHand;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Akarin Changes Note
|
* Akarin Changes Note
|
||||||
* 1) Add volatile to fields (time update)
|
|
||||||
* 2) Add lock to player track (safety issue)
|
* 2) Add lock to player track (safety issue)
|
||||||
*/
|
*/
|
||||||
public class EntityPlayer extends EntityHuman implements ICrafting {
|
public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||||
@@ -85,6 +84,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
private int containerUpdateDelay; // Paper
|
private int containerUpdateDelay; // Paper
|
||||||
|
// Paper start - cancellable death event
|
||||||
|
public boolean queueHealthUpdatePacket = false;
|
||||||
|
public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
|
||||||
|
// Paper end
|
||||||
|
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
public String displayName;
|
public String displayName;
|
||||||
@@ -442,9 +445,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
public void die(DamageSource damagesource) {
|
public void die(DamageSource damagesource) {
|
||||||
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
|
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
|
// CraftBukkit start - fire PlayerDeathEvent
|
||||||
if (this.dead) {
|
if (this.dead) {
|
||||||
|
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.inventory.getSize());
|
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();
|
String deathmessage = chatmessage.toPlainText();
|
||||||
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
|
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();
|
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,9 +769,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
if (entity instanceof EntityPlayer) {
|
if (entity instanceof EntityPlayer) {
|
||||||
WorldServer worldServer = (WorldServer) entity.getWorld();
|
WorldServer worldServer = (WorldServer) entity.getWorld();
|
||||||
worldServer.tracker.untrackEntity(this);
|
worldServer.tracker.untrackEntity(this);
|
||||||
worldServer.tracker.entriesLock.lock(); // Akarin
|
worldServer.tracker.entriesLock.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
|
||||||
worldServer.tracker.track(this);
|
worldServer.tracker.track(this);
|
||||||
worldServer.tracker.entriesLock.unlock(); // Akarin
|
worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
|
||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
|
|
||||||
@@ -1422,8 +1446,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CraftBukkit start - Add per-player time and weather.
|
// CraftBukkit start - Add per-player time and weather.
|
||||||
public volatile long timeOffset = 0; // Akarin - volatile
|
public long timeOffset = 0;
|
||||||
public volatile boolean relativeTime = true; // Akarin - volatile
|
public boolean relativeTime = true;
|
||||||
|
|
||||||
public long getPlayerTime() {
|
public long getPlayerTime() {
|
||||||
if (this.relativeTime) {
|
if (this.relativeTime) {
|
||||||
|
|||||||
@@ -2,25 +2,28 @@ package net.minecraft.server;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
|
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Akarin Changes Note
|
* Akarin Changes Note
|
||||||
* 1) Add lock for entries set operations (safety issue)
|
* 1) Made collections and entry access thread-safe (safety issue)
|
||||||
*/
|
*/
|
||||||
|
@ThreadSafe // Akarin
|
||||||
public class EntityTracker {
|
public class EntityTracker {
|
||||||
|
|
||||||
private static final Logger a = LogManager.getLogger();
|
private static final Logger a = LogManager.getLogger();
|
||||||
private final WorldServer world;
|
private final WorldServer world;
|
||||||
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
|
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
|
||||||
public final ReentrantLock entriesLock = new ReentrantLock(); // Akarin - add lock
|
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
|
||||||
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
|
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
|
||||||
private int e;
|
private int e;
|
||||||
|
|
||||||
@@ -38,7 +41,7 @@ public class EntityTracker {
|
|||||||
this.addEntity(entity, 512, 2);
|
this.addEntity(entity, 512, 2);
|
||||||
EntityPlayer entityplayer = (EntityPlayer) entity;
|
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
// entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
@@ -47,7 +50,7 @@ public class EntityTracker {
|
|||||||
entitytrackerentry.updatePlayer(entityplayer);
|
entitytrackerentry.updatePlayer(entityplayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
// entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
|
||||||
} else if (entity instanceof EntityFishingHook) {
|
} else if (entity instanceof EntityFishingHook) {
|
||||||
this.addEntity(entity, 64, 5, true);
|
this.addEntity(entity, 64, 5, true);
|
||||||
} else if (entity instanceof EntityArrow) {
|
} else if (entity instanceof EntityArrow) {
|
||||||
@@ -118,18 +121,18 @@ public class EntityTracker {
|
|||||||
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
|
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
|
||||||
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
||||||
try {
|
try {
|
||||||
|
// entriesLock.writeLock().lock(); // Akarin - locked from track method
|
||||||
if (this.trackedEntities.b(entity.getId())) {
|
if (this.trackedEntities.b(entity.getId())) {
|
||||||
throw new IllegalStateException("Entity is already tracked!");
|
throw new IllegalStateException("Entity is already tracked!");
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
|
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
|
||||||
|
|
||||||
entriesLock.lock(); // Akarin
|
|
||||||
this.c.add(entitytrackerentry);
|
this.c.add(entitytrackerentry);
|
||||||
|
|
||||||
this.trackedEntities.a(entity.getId(), entitytrackerentry);
|
this.trackedEntities.a(entity.getId(), entitytrackerentry);
|
||||||
entitytrackerentry.scanPlayers(this.world.players);
|
entitytrackerentry.scanPlayers(this.world.players);
|
||||||
entriesLock.unlock(); // Akarin
|
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
|
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
|
||||||
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
|
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
|
||||||
@@ -166,34 +169,32 @@ public class EntityTracker {
|
|||||||
|
|
||||||
public void untrackEntity(Entity entity) {
|
public void untrackEntity(Entity entity) {
|
||||||
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
|
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
|
||||||
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
if (entity instanceof EntityPlayer) {
|
if (entity instanceof EntityPlayer) {
|
||||||
EntityPlayer entityplayer = (EntityPlayer) entity;
|
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
entitytrackerentry.a(entityplayer);
|
entitytrackerentry.a(entityplayer);
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
|
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
|
||||||
|
|
||||||
if (entitytrackerentry1 != null) {
|
if (entitytrackerentry1 != null) {
|
||||||
entriesLock.lock(); // Akarin
|
|
||||||
this.c.remove(entitytrackerentry1);
|
this.c.remove(entitytrackerentry1);
|
||||||
entitytrackerentry1.a();
|
entitytrackerentry1.a();
|
||||||
entriesLock.unlock(); // Akarin
|
|
||||||
}
|
}
|
||||||
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePlayers() {
|
public void updatePlayers() {
|
||||||
ArrayList arraylist = Lists.newArrayList();
|
ArrayList arraylist = Lists.newArrayList();
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
world.timings.tracker1.startTiming(); // Spigot
|
world.timings.tracker1.startTiming(); // Spigot
|
||||||
entriesLock.lock(); // Akarin
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
@@ -221,14 +222,14 @@ public class EntityTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
world.timings.tracker2.stopTiming(); // Spigot
|
world.timings.tracker2.stopTiming(); // Spigot
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a(EntityPlayer entityplayer) {
|
public void a(EntityPlayer entityplayer) {
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
@@ -239,11 +240,13 @@ public class EntityTracker {
|
|||||||
entitytrackerentry.updatePlayer(entityplayer);
|
entitytrackerentry.updatePlayer(entityplayer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a(Entity entity, Packet<?> packet) {
|
public void a(Entity entity, Packet<?> packet) {
|
||||||
|
entriesLock.readLock().lock(); // Akarin
|
||||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||||
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
|
|
||||||
if (entitytrackerentry != null) {
|
if (entitytrackerentry != null) {
|
||||||
entitytrackerentry.broadcast(packet);
|
entitytrackerentry.broadcast(packet);
|
||||||
@@ -252,7 +255,9 @@ public class EntityTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
|
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
|
||||||
|
entriesLock.readLock().lock(); // Akarin
|
||||||
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||||
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
|
|
||||||
if (entitytrackerentry != null) {
|
if (entitytrackerentry != null) {
|
||||||
entitytrackerentry.broadcastIncludingSelf(packet);
|
entitytrackerentry.broadcastIncludingSelf(packet);
|
||||||
@@ -262,21 +267,21 @@ public class EntityTracker {
|
|||||||
|
|
||||||
public void untrackPlayer(EntityPlayer entityplayer) {
|
public void untrackPlayer(EntityPlayer entityplayer) {
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
entriesLock.writeLock().lock();
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
entitytrackerentry.clear(entityplayer);
|
entitytrackerentry.clear(entityplayer);
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
entriesLock.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void a(EntityPlayer entityplayer, Chunk chunk) {
|
public void a(EntityPlayer entityplayer, Chunk chunk) {
|
||||||
ArrayList arraylist = Lists.newArrayList();
|
ArrayList arraylist = Lists.newArrayList();
|
||||||
ArrayList arraylist1 = Lists.newArrayList();
|
ArrayList arraylist1 = Lists.newArrayList();
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
@@ -293,7 +298,7 @@ public class EntityTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
|
||||||
Entity entity1;
|
Entity entity1;
|
||||||
|
|
||||||
@@ -320,13 +325,13 @@ public class EntityTracker {
|
|||||||
public void a(int i) {
|
public void a(int i) {
|
||||||
this.e = (i - 1) * 16;
|
this.e = (i - 1) * 16;
|
||||||
Iterator iterator = this.c.iterator();
|
Iterator iterator = this.c.iterator();
|
||||||
entriesLock.lock(); // Akarin
|
entriesLock.readLock().lock(); // Akarin
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
entitytrackerentry.a(this.e);
|
entitytrackerentry.a(this.e);
|
||||||
}
|
}
|
||||||
entriesLock.unlock(); // Akarin
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,658 +0,0 @@
|
|||||||
package net.minecraft.server;
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Akarin Changes Note
|
|
||||||
* 1) Make trackedPlayerMap thread-safe (safety issue)
|
|
||||||
*/
|
|
||||||
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.concurrent.ConcurrentHashMap<EntityPlayer, Boolean>(); // Akarin - make concurrent
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
if (this.tracker instanceof EntityLiving) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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) {}
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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
@@ -21,7 +21,6 @@ import io.netty.util.concurrent.GenericFutureListener;
|
|||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
@@ -33,7 +32,6 @@ import org.apache.logging.log4j.MarkerManager;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Akarin Changes Note
|
* Akarin Changes Note
|
||||||
* 1) Add volatile to fields (nsc)
|
|
||||||
* 2) Expose private members (nsc)
|
* 2) Expose private members (nsc)
|
||||||
* 3) Changes lock type to updatable lock (compatibility)
|
* 3) Changes lock type to updatable lock (compatibility)
|
||||||
* 4) Removes unneed array creation (performance)
|
* 4) Removes unneed array creation (performance)
|
||||||
@@ -82,7 +80,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|||||||
public SocketAddress l;
|
public SocketAddress l;
|
||||||
public java.util.UUID spoofedUUID;
|
public java.util.UUID spoofedUUID;
|
||||||
public com.mojang.authlib.properties.Property[] spoofedProfile;
|
public com.mojang.authlib.properties.Property[] spoofedProfile;
|
||||||
public volatile boolean preparing = true; // Akarin - add volatile
|
public boolean preparing = true;
|
||||||
// Spigot End
|
// Spigot End
|
||||||
private PacketListener m;
|
private PacketListener m;
|
||||||
private IChatBaseComponent n;
|
private IChatBaseComponent n;
|
||||||
|
|||||||
601
sources/src/main/java/net/minecraft/server/PlayerChunkMap.java
Normal file
601
sources/src/main/java/net/minecraft/server/PlayerChunkMap.java
Normal file
@@ -0,0 +1,601 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import co.aikar.timings.Timing;
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.AbstractIterator;
|
||||||
|
import com.google.common.collect.ComparisonChain;
|
||||||
|
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 java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
// CraftBukkit start
|
||||||
|
import java.util.LinkedList;
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Make whole class thread-safe (safety issue)
|
||||||
|
*/
|
||||||
|
@ThreadSafe // Akarin - idk why we need do so!!
|
||||||
|
public class PlayerChunkMap {
|
||||||
|
|
||||||
|
private static final Predicate<EntityPlayer> a = new Predicate() {
|
||||||
|
public boolean a(@Nullable EntityPlayer entityplayer) {
|
||||||
|
return entityplayer != null && !entityplayer.isSpectator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean apply(@Nullable Object object) {
|
||||||
|
return this.a((EntityPlayer) object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private static final Predicate<EntityPlayer> b = new Predicate() {
|
||||||
|
public boolean a(@Nullable EntityPlayer entityplayer) {
|
||||||
|
return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.x().getGameRules().getBoolean("spectatorsGenerateChunks"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean apply(@Nullable Object object) {
|
||||||
|
return this.a((EntityPlayer) object);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
private final WorldServer world;
|
||||||
|
private final List<EntityPlayer> managedPlayers = Lists.newArrayList();
|
||||||
|
private final ReentrantReadWriteLock managedPlayersLock = new ReentrantReadWriteLock(); // Akarin - add lock
|
||||||
|
private final Long2ObjectMap<PlayerChunk> e = new Long2ObjectOpenHashMap(4096);
|
||||||
|
private final Set<PlayerChunk> f = Sets.newHashSet();
|
||||||
|
private final List<PlayerChunk> g = Lists.newLinkedList();
|
||||||
|
private final List<PlayerChunk> h = Lists.newLinkedList();
|
||||||
|
private final List<PlayerChunk> i = Lists.newCopyOnWriteArrayList(); // Akarin - bad plugin will access this
|
||||||
|
private int j; public int getViewDistance() { return j; } // Paper OBFHELPER
|
||||||
|
private long k;
|
||||||
|
private boolean l = true;
|
||||||
|
private boolean m = true;
|
||||||
|
private boolean wasNotEmpty; // CraftBukkit - add field
|
||||||
|
|
||||||
|
public PlayerChunkMap(WorldServer worldserver) {
|
||||||
|
this.world = worldserver;
|
||||||
|
this.a(worldserver.spigotConfig.viewDistance); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldServer getWorld() {
|
||||||
|
return this.world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<Chunk> b() {
|
||||||
|
final Iterator iterator = this.i.iterator();
|
||||||
|
|
||||||
|
return new AbstractIterator<Chunk>() {
|
||||||
|
protected Chunk a() {
|
||||||
|
while (true) {
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
PlayerChunk playerchunk = (PlayerChunk) iterator.next();
|
||||||
|
Chunk chunk = playerchunk.f();
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunk.v() && chunk.isDone()) {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunk.j()) {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playerchunk.a(128.0D, PlayerChunkMap.a)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (Chunk) this.endOfData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Chunk computeNext() {
|
||||||
|
return this.a();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void flush() { // Akarin - synchronized
|
||||||
|
long i = this.world.getTime();
|
||||||
|
int j;
|
||||||
|
PlayerChunk playerchunk;
|
||||||
|
|
||||||
|
if (i - this.k > 8000L) {
|
||||||
|
try (Timing ignored = world.timings.doChunkMapUpdate.startTiming()) { // Paper
|
||||||
|
this.k = i;
|
||||||
|
|
||||||
|
for (j = 0; j < this.i.size(); ++j) {
|
||||||
|
playerchunk = (PlayerChunk) this.i.get(j);
|
||||||
|
playerchunk.d();
|
||||||
|
playerchunk.c();
|
||||||
|
}
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.f.isEmpty()) {
|
||||||
|
try (Timing ignored = world.timings.doChunkMapToUpdate.startTiming()) { // Paper
|
||||||
|
Iterator iterator = this.f.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
playerchunk = (PlayerChunk) iterator.next();
|
||||||
|
playerchunk.d();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.f.clear();
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.l && i % 4L == 0L) {
|
||||||
|
this.l = false;
|
||||||
|
try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper
|
||||||
|
Collections.sort(this.h, new Comparator() {
|
||||||
|
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
|
||||||
|
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(Object object, Object object1) {
|
||||||
|
return this.a((PlayerChunk) object, (PlayerChunk) object1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.m && i % 4L == 2L) {
|
||||||
|
this.m = false;
|
||||||
|
try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper
|
||||||
|
Collections.sort(this.g, new Comparator() {
|
||||||
|
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
|
||||||
|
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(Object object, Object object1) {
|
||||||
|
return this.a((PlayerChunk) object, (PlayerChunk) object1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.h.isEmpty()) {
|
||||||
|
try (Timing ignored = world.timings.doChunkMapPlayersNeedingChunks.startTiming()) { // Paper
|
||||||
|
// Spigot start
|
||||||
|
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
|
||||||
|
activityAccountant.startActivity(0.5);
|
||||||
|
int chunkGensAllowed = world.paperConfig.maxChunkGensPerTick; // Paper
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
Iterator iterator1 = this.h.iterator();
|
||||||
|
|
||||||
|
while (iterator1.hasNext()) {
|
||||||
|
PlayerChunk playerchunk1 = (PlayerChunk) iterator1.next();
|
||||||
|
|
||||||
|
if (playerchunk1.f() == null) {
|
||||||
|
boolean flag = playerchunk1.a(PlayerChunkMap.b);
|
||||||
|
// Paper start
|
||||||
|
if (flag && !playerchunk1.chunkExists && chunkGensAllowed-- <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
if (playerchunk1.a(flag)) {
|
||||||
|
iterator1.remove();
|
||||||
|
if (playerchunk1.b()) {
|
||||||
|
this.g.remove(playerchunk1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activityAccountant.activityTimeIsExhausted()) { // Spigot
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CraftBukkit start - SPIGOT-2891: remove once chunk has been provided
|
||||||
|
} else {
|
||||||
|
iterator1.remove();
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
}
|
||||||
|
|
||||||
|
activityAccountant.endActivity(); // Spigot
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.g.isEmpty()) {
|
||||||
|
j = world.paperConfig.maxChunkSendsPerTick; // Paper
|
||||||
|
try (Timing ignored = world.timings.doChunkMapPendingSendToPlayers.startTiming()) { // Paper
|
||||||
|
Iterator iterator2 = this.g.iterator();
|
||||||
|
|
||||||
|
while (iterator2.hasNext()) {
|
||||||
|
PlayerChunk playerchunk2 = (PlayerChunk) iterator2.next();
|
||||||
|
|
||||||
|
if (playerchunk2.b()) {
|
||||||
|
iterator2.remove();
|
||||||
|
--j;
|
||||||
|
if (j < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
|
||||||
|
managedPlayersLock.readLock().lock(); // Akarin
|
||||||
|
if (this.managedPlayers.isEmpty()) {
|
||||||
|
try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper
|
||||||
|
WorldProvider worldprovider = this.world.worldProvider;
|
||||||
|
|
||||||
|
if (!worldprovider.e() && !this.world.savingDisabled) { // Paper - respect saving disabled setting
|
||||||
|
this.world.getChunkProviderServer().b();
|
||||||
|
}
|
||||||
|
} // Paper timing
|
||||||
|
}
|
||||||
|
managedPlayersLock.readLock().unlock(); // Akarin
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean a(int i, int j) { // Akarin - synchronized
|
||||||
|
long k = d(i, j);
|
||||||
|
|
||||||
|
return this.e.get(k) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public synchronized PlayerChunk getChunk(int i, int j) { // Akarin - synchronized
|
||||||
|
return (PlayerChunk) this.e.get(d(i, j));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlayerChunk c(int i, int j) {
|
||||||
|
long k = d(i, j);
|
||||||
|
PlayerChunk playerchunk = (PlayerChunk) this.e.get(k);
|
||||||
|
|
||||||
|
if (playerchunk == null) {
|
||||||
|
playerchunk = new PlayerChunk(this, i, j);
|
||||||
|
this.e.put(k, playerchunk);
|
||||||
|
this.i.add(playerchunk);
|
||||||
|
if (playerchunk.f() == null) {
|
||||||
|
this.h.add(playerchunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!playerchunk.b()) {
|
||||||
|
this.g.add(playerchunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return playerchunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftBukkit start - add method
|
||||||
|
public final boolean isChunkInUse(int x, int z) {
|
||||||
|
PlayerChunk pi = getChunk(x, z);
|
||||||
|
if (pi != null) {
|
||||||
|
return (pi.c.size() > 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
public void flagDirty(BlockPosition blockposition) {
|
||||||
|
int i = blockposition.getX() >> 4;
|
||||||
|
int j = blockposition.getZ() >> 4;
|
||||||
|
PlayerChunk playerchunk = this.getChunk(i, j);
|
||||||
|
|
||||||
|
if (playerchunk != null) {
|
||||||
|
playerchunk.a(blockposition.getX() & 15, blockposition.getY(), blockposition.getZ() & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPlayer(EntityPlayer entityplayer) {
|
||||||
|
int i = (int) entityplayer.locX >> 4;
|
||||||
|
int j = (int) entityplayer.locZ >> 4;
|
||||||
|
|
||||||
|
entityplayer.d = entityplayer.locX;
|
||||||
|
entityplayer.e = entityplayer.locZ;
|
||||||
|
|
||||||
|
|
||||||
|
// CraftBukkit start - Load nearby chunks first
|
||||||
|
List<ChunkCoordIntPair> chunkList = new LinkedList<ChunkCoordIntPair>();
|
||||||
|
|
||||||
|
// Paper start - Player view distance API
|
||||||
|
int viewDistance = entityplayer.getViewDistance();
|
||||||
|
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
|
||||||
|
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
|
||||||
|
// Paper end
|
||||||
|
chunkList.add(new ChunkCoordIntPair(k, l));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(chunkList, new ChunkCoordComparator(entityplayer));
|
||||||
|
synchronized (this) { // Akarin - synchronized
|
||||||
|
for (ChunkCoordIntPair pair : chunkList) {
|
||||||
|
this.c(pair.x, pair.z).a(entityplayer);
|
||||||
|
}
|
||||||
|
} // Akarin
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
managedPlayersLock.writeLock().lock(); // Akarin
|
||||||
|
this.managedPlayers.add(entityplayer);
|
||||||
|
managedPlayersLock.writeLock().unlock(); // Akarin
|
||||||
|
this.e();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePlayer(EntityPlayer entityplayer) {
|
||||||
|
int i = (int) entityplayer.d >> 4;
|
||||||
|
int j = (int) entityplayer.e >> 4;
|
||||||
|
|
||||||
|
// Paper start - Player view distance API
|
||||||
|
int viewDistance = entityplayer.getViewDistance();
|
||||||
|
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
|
||||||
|
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
|
||||||
|
// Paper end
|
||||||
|
PlayerChunk playerchunk = this.getChunk(k, l);
|
||||||
|
|
||||||
|
if (playerchunk != null) {
|
||||||
|
playerchunk.b(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
managedPlayersLock.writeLock().lock(); // Akarin
|
||||||
|
this.managedPlayers.remove(entityplayer);
|
||||||
|
managedPlayersLock.writeLock().unlock(); // Akarin
|
||||||
|
this.e();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean a(int i, int j, int k, int l, int i1) {
|
||||||
|
int j1 = i - k;
|
||||||
|
int k1 = j - l;
|
||||||
|
|
||||||
|
return j1 >= -i1 && j1 <= i1 ? k1 >= -i1 && k1 <= i1 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void movePlayer(EntityPlayer entityplayer) {
|
||||||
|
int i = (int) entityplayer.locX >> 4;
|
||||||
|
int j = (int) entityplayer.locZ >> 4;
|
||||||
|
double d0 = entityplayer.d - entityplayer.locX;
|
||||||
|
double d1 = entityplayer.e - entityplayer.locZ;
|
||||||
|
double d2 = d0 * d0 + d1 * d1;
|
||||||
|
|
||||||
|
if (d2 >= 64.0D) {
|
||||||
|
int k = (int) entityplayer.d >> 4;
|
||||||
|
int l = (int) entityplayer.e >> 4;
|
||||||
|
final int viewDistance = entityplayer.getViewDistance(); // Paper - Player view distance API
|
||||||
|
int i1 = Math.max(getViewDistance(), viewDistance); // Paper - Player view distance API
|
||||||
|
|
||||||
|
int j1 = i - k;
|
||||||
|
int k1 = j - l;
|
||||||
|
|
||||||
|
List<ChunkCoordIntPair> chunksToLoad = new LinkedList<ChunkCoordIntPair>(); // CraftBukkit
|
||||||
|
|
||||||
|
if (j1 != 0 || k1 != 0) {
|
||||||
|
for (int l1 = i - i1; l1 <= i + i1; ++l1) {
|
||||||
|
for (int i2 = j - i1; i2 <= j + i1; ++i2) {
|
||||||
|
if (!this.a(l1, i2, k, l, viewDistance)) { // Paper - Player view distance API
|
||||||
|
// this.c(l1, i2).a(entityplayer);
|
||||||
|
chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.a(l1 - j1, i2 - k1, i, j, i1)) {
|
||||||
|
PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1);
|
||||||
|
|
||||||
|
if (playerchunk != null) {
|
||||||
|
playerchunk.b(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entityplayer.d = entityplayer.locX;
|
||||||
|
entityplayer.e = entityplayer.locZ;
|
||||||
|
this.e();
|
||||||
|
|
||||||
|
// CraftBukkit start - send nearest chunks first
|
||||||
|
Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer));
|
||||||
|
synchronized (this) { // Akarin - synchronized
|
||||||
|
for (ChunkCoordIntPair pair : chunksToLoad) {
|
||||||
|
this.c(pair.x, pair.z).a(entityplayer);
|
||||||
|
}
|
||||||
|
} // Akarin
|
||||||
|
// CraftBukkit end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(EntityPlayer entityplayer, int i, int j) {
|
||||||
|
PlayerChunk playerchunk = this.getChunk(i, j);
|
||||||
|
|
||||||
|
return playerchunk != null && playerchunk.d(entityplayer) && playerchunk.e();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setViewDistanceForAll(int viewDistance) { this.a(viewDistance); } // Paper - OBFHELPER
|
||||||
|
// Paper start - Separate into two methods
|
||||||
|
public void a(int i) {
|
||||||
|
i = MathHelper.clamp(i, 3, 32);
|
||||||
|
if (i != this.j) {
|
||||||
|
int j = i - this.j;
|
||||||
|
managedPlayersLock.readLock().lock(); // Akarin
|
||||||
|
ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
|
||||||
|
managedPlayersLock.readLock().unlock(); // Akarin
|
||||||
|
Iterator iterator = arraylist.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||||
|
this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after
|
||||||
|
}
|
||||||
|
|
||||||
|
this.j = i;
|
||||||
|
this.e();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewDistance(EntityPlayer entityplayer, int i) {
|
||||||
|
this.setViewDistance(entityplayer, i, true); // Mark sort pending by default so we don't have to remember to do so all the time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from above with minor changes
|
||||||
|
public void setViewDistance(EntityPlayer entityplayer, int i, boolean markSort) {
|
||||||
|
i = MathHelper.clamp(i, 3, 32);
|
||||||
|
int oldViewDistance = entityplayer.getViewDistance();
|
||||||
|
if (i != oldViewDistance) {
|
||||||
|
int j = i - oldViewDistance;
|
||||||
|
|
||||||
|
int k = (int) entityplayer.locX >> 4;
|
||||||
|
int l = (int) entityplayer.locZ >> 4;
|
||||||
|
int i1;
|
||||||
|
int j1;
|
||||||
|
|
||||||
|
if (j > 0) {
|
||||||
|
synchronized (this) { // Akarin - synchronized
|
||||||
|
for (i1 = k - i; i1 <= k + i; ++i1) {
|
||||||
|
for (j1 = l - i; j1 <= l + i; ++j1) {
|
||||||
|
PlayerChunk playerchunk = this.c(i1, j1);
|
||||||
|
|
||||||
|
if (!playerchunk.d(entityplayer)) {
|
||||||
|
playerchunk.a(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // Akarin
|
||||||
|
} else {
|
||||||
|
synchronized (this) { // Akarin - synchronized
|
||||||
|
for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) {
|
||||||
|
for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) {
|
||||||
|
if (!this.a(i1, j1, k, l, i)) {
|
||||||
|
this.c(i1, j1).b(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // Akarin
|
||||||
|
if (markSort) {
|
||||||
|
this.e();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
private void e() {
|
||||||
|
this.l = true;
|
||||||
|
this.m = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getFurthestViewableBlock(int i) {
|
||||||
|
return i * 16 - 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long d(int i, int j) {
|
||||||
|
return (long) i + 2147483647L | (long) j + 2147483647L << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void a(PlayerChunk playerchunk) { // Akarin - synchronized
|
||||||
|
// org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Add"); // Paper // Akarin
|
||||||
|
this.f.add(playerchunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void b(PlayerChunk playerchunk) { // Akarin - synchronized
|
||||||
|
org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Remove"); // Paper
|
||||||
|
ChunkCoordIntPair chunkcoordintpair = playerchunk.a();
|
||||||
|
long i = d(chunkcoordintpair.x, chunkcoordintpair.z);
|
||||||
|
|
||||||
|
playerchunk.c();
|
||||||
|
this.e.remove(i);
|
||||||
|
this.i.remove(playerchunk);
|
||||||
|
this.f.remove(playerchunk);
|
||||||
|
this.g.remove(playerchunk);
|
||||||
|
this.h.remove(playerchunk);
|
||||||
|
Chunk chunk = playerchunk.f();
|
||||||
|
|
||||||
|
if (chunk != null) {
|
||||||
|
// Paper start - delay chunk unloads
|
||||||
|
if (world.paperConfig.delayChunkUnloadsBy <= 0) {
|
||||||
|
this.getWorld().getChunkProviderServer().unload(chunk);
|
||||||
|
} else {
|
||||||
|
chunk.scheduledForUnload = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CraftBukkit start - Sorter to load nearby chunks first
|
||||||
|
private static class ChunkCoordComparator implements java.util.Comparator<ChunkCoordIntPair> {
|
||||||
|
private int x;
|
||||||
|
private int z;
|
||||||
|
|
||||||
|
public ChunkCoordComparator (EntityPlayer entityplayer) {
|
||||||
|
x = (int) entityplayer.locX >> 4;
|
||||||
|
z = (int) entityplayer.locZ >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) {
|
||||||
|
if (a.equals(b)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract current position to set center point
|
||||||
|
int ax = a.x - this.x;
|
||||||
|
int az = a.z - this.z;
|
||||||
|
int bx = b.x - this.x;
|
||||||
|
int bz = b.z - this.z;
|
||||||
|
|
||||||
|
int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz));
|
||||||
|
if (result != 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ax < 0) {
|
||||||
|
if (bx < 0) {
|
||||||
|
return bz - az;
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (bx < 0) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return az - bz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
// Paper start - Player view distance API
|
||||||
|
public void updateViewDistance(EntityPlayer player, int distanceIn) {
|
||||||
|
final int oldViewDistance = player.getViewDistance();
|
||||||
|
|
||||||
|
// This represents the view distance that we will set on the player
|
||||||
|
// It can exist as a negative value
|
||||||
|
int playerViewDistance = MathHelper.clamp(distanceIn, 3, 32);
|
||||||
|
|
||||||
|
// This value is the one we actually use to update the chunk map
|
||||||
|
// We don't ever want this to be a negative
|
||||||
|
int toSet = playerViewDistance;
|
||||||
|
|
||||||
|
if (distanceIn < 0) {
|
||||||
|
playerViewDistance = -1;
|
||||||
|
toSet = world.getPlayerChunkMap().getViewDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toSet != oldViewDistance) {
|
||||||
|
// Order matters
|
||||||
|
this.setViewDistance(player, toSet);
|
||||||
|
player.setViewDistance(playerViewDistance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package net.minecraft.server;
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.primitives.Doubles;
|
import com.google.common.primitives.Doubles;
|
||||||
import com.google.common.primitives.Floats;
|
import com.google.common.primitives.Floats;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
|
||||||
import io.akarin.api.internal.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
@@ -10,6 +12,8 @@ import io.netty.util.concurrent.GenericFutureListener;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@@ -75,12 +79,13 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
private final MinecraftServer minecraftServer;
|
private final MinecraftServer minecraftServer;
|
||||||
public EntityPlayer player;
|
public EntityPlayer player;
|
||||||
private int e;
|
private int e;
|
||||||
private volatile long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public - volatile
|
private long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public
|
||||||
private volatile boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public - volatile
|
private boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public
|
||||||
private volatile long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public - volatile
|
private long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public
|
||||||
// CraftBukkit start - multithreaded fields
|
// CraftBukkit start - multithreaded fields
|
||||||
private volatile int chatThrottle;
|
private volatile int chatThrottle;
|
||||||
private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");
|
private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");
|
||||||
|
private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
private int j;
|
private int j;
|
||||||
private final IntHashMap<Short> k = new IntHashMap();
|
private final IntHashMap<Short> k = new IntHashMap();
|
||||||
@@ -212,6 +217,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
this.minecraftServer.methodProfiler.b();
|
this.minecraftServer.methodProfiler.b();
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ;
|
for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ;
|
||||||
|
if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
|
||||||
/* Use thread-safe field access instead
|
/* Use thread-safe field access instead
|
||||||
if (this.chatThrottle > 0) {
|
if (this.chatThrottle > 0) {
|
||||||
--this.chatThrottle;
|
--this.chatThrottle;
|
||||||
@@ -380,10 +386,13 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
entity.setLocation(d3, d4, d5, f, f1);
|
entity.setLocation(d3, d4, d5, f, f1);
|
||||||
|
Location curPos = getPlayer().getLocation(); // Paper
|
||||||
|
player.setLocation(d3, d4, d5, f, f1); // Paper
|
||||||
boolean flag2 = worldserver.getCubes(entity, entity.getBoundingBox().shrink(0.0625D)).isEmpty();
|
boolean flag2 = worldserver.getCubes(entity, entity.getBoundingBox().shrink(0.0625D)).isEmpty();
|
||||||
|
|
||||||
if (flag && (flag1 || !flag2)) {
|
if (flag && (flag1 || !flag2)) {
|
||||||
entity.setLocation(d0, d1, d2, f, f1);
|
entity.setLocation(d0, d1, d2, f, f1);
|
||||||
|
player.setLocation(d0, d1, d2, f, f1); // Paper
|
||||||
this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
|
this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -393,7 +402,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
// Spigot Start
|
// Spigot Start
|
||||||
if ( !hasMoved )
|
if ( !hasMoved )
|
||||||
{
|
{
|
||||||
Location curPos = player.getLocation();
|
//Location curPos = player.getLocation(); // Paper - move up
|
||||||
lastPosX = curPos.getX();
|
lastPosX = curPos.getX();
|
||||||
lastPosY = curPos.getY();
|
lastPosY = curPos.getY();
|
||||||
lastPosZ = curPos.getZ();
|
lastPosZ = curPos.getZ();
|
||||||
@@ -2293,7 +2302,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
// Paper start - async tab completion
|
// Paper start - async tab completion
|
||||||
public void a(PacketPlayInTabComplete packet) {
|
public void a(PacketPlayInTabComplete packet) {
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
|
if (tabSpamLimiter.addAndGet(com.destroystokyo.paper.PaperConfig.tabSpamIncrement) > com.destroystokyo.paper.PaperConfig.tabSpamLimit && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { // Paper start - split and make configurable
|
||||||
minecraftServer.postToMainThread(() -> this.disconnect(new ChatMessage("disconnect.spam", new Object[0])));
|
minecraftServer.postToMainThread(() -> this.disconnect(new ChatMessage("disconnect.spam", new Object[0])));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2308,32 +2317,26 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
buffer, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
|
buffer, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
|
||||||
event.callEvent();
|
event.callEvent();
|
||||||
completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
|
||||||
if (event.isCancelled() || event.isHandled()) {
|
if (!event.isHandled()) {
|
||||||
// Still fire sync event with the provided completions, if someone is listening
|
// If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
|
||||||
if (!event.isCancelled() && org.bukkit.event.server.TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
||||||
java.util.List<String> finalCompletions = completions;
|
Waitable<java.util.List<String>> syncCompletions = new Waitable<java.util.List<String>>() {
|
||||||
Waitable<java.util.List<String>> syncCompletions = new Waitable<java.util.List<String>>() {
|
@Override
|
||||||
@Override
|
protected java.util.List<String> evaluate() {
|
||||||
protected java.util.List<String> evaluate() {
|
return minecraftServer.tabCompleteCommand(player, buffer, blockpos, isCommand);
|
||||||
org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(PlayerConnection.this.getPlayer(), buffer, finalCompletions, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
|
|
||||||
return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
server.getServer().processQueue.add(syncCompletions);
|
|
||||||
try {
|
|
||||||
completions = syncCompletions.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e1) {
|
|
||||||
e1.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
server.getServer().processQueue.add(syncCompletions);
|
||||||
|
try {
|
||||||
|
completions = syncCompletions.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e1) {
|
||||||
|
e1.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
|
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
|
||||||
return;
|
} else if (!event.isCancelled()) {
|
||||||
|
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
|
||||||
}
|
}
|
||||||
minecraftServer.postToMainThread(() -> {
|
|
||||||
java.util.List<String> syncCompletions = this.minecraftServer.tabCompleteCommand(this.player, buffer, blockpos, isCommand);
|
|
||||||
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(syncCompletions.toArray(new String[syncCompletions.size()])));
|
|
||||||
});
|
|
||||||
// Paper end
|
// Paper end
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2375,7 +2378,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) {
|
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) {
|
||||||
itemstack1 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
|
|
||||||
itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8));
|
itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8));
|
||||||
CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
|
CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,8 +79,20 @@ public abstract class PlayerList {
|
|||||||
private int v;
|
private int v;
|
||||||
|
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
private CraftServer cserver;
|
private final CraftServer cserver;
|
||||||
private final Map<String,EntityPlayer> playersByName = new org.spigotmc.CaseInsensitiveMap<EntityPlayer>();
|
private final Map<String,EntityPlayer> playersByName = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap<String, EntityPlayer>(
|
||||||
|
new it.unimi.dsi.fastutil.Hash.Strategy<String>() {
|
||||||
|
@Override
|
||||||
|
public int hashCode(String o) {
|
||||||
|
return o.toLowerCase().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(String a, String b) {
|
||||||
|
return a.equalsIgnoreCase(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
@Nullable String collideRuleTeamName; // Paper - Team name used for collideRule
|
@Nullable String collideRuleTeamName; // Paper - Team name used for collideRule
|
||||||
|
|
||||||
public PlayerList(MinecraftServer minecraftserver) {
|
public PlayerList(MinecraftServer minecraftserver) {
|
||||||
@@ -1252,8 +1264,25 @@ public abstract class PlayerList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet<?> packet) {
|
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet<?> packet) {
|
||||||
for (int j = 0; j < this.players.size(); ++j) {
|
// Paper start - Use world list instead of server list where preferable
|
||||||
EntityPlayer entityplayer = this.players.get(j);
|
sendPacketNearby(entityhuman, d0, d1, d2, d3, i, null, packet); // Retained for compatibility
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, WorldServer world, Packet<?> packet) {
|
||||||
|
sendPacketNearby(entityhuman, d0, d1, d2, d3, world.dimension, world, packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, @Nullable WorldServer world, Packet<?> packet) {
|
||||||
|
if (world == null && entityhuman != null && entityhuman.world instanceof WorldServer) {
|
||||||
|
world = (WorldServer) entityhuman.world;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<? extends EntityHuman> players1 = world == null ? players : world.players;
|
||||||
|
for (int j = 0; j < players1.size(); ++j) {
|
||||||
|
EntityHuman entity = players1.get(j);
|
||||||
|
if (!(entity instanceof EntityPlayer)) continue;
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) players1.get(j);
|
||||||
|
// Paper end
|
||||||
|
|
||||||
// CraftBukkit start - Test if player receiving packet can see the source of the packet
|
// CraftBukkit start - Test if player receiving packet can see the source of the packet
|
||||||
if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
|
if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
|
||||||
@@ -1261,7 +1290,7 @@ public abstract class PlayerList {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
if (entityplayer != entityhuman && entityplayer.dimension == i) {
|
if (entityplayer != entityhuman && (world != null || entityplayer.dimension == i)) { // Paper
|
||||||
double d4 = d0 - entityplayer.locX;
|
double d4 = d0 - entityplayer.locX;
|
||||||
double d5 = d1 - entityplayer.locY;
|
double d5 = d1 - entityplayer.locY;
|
||||||
double d6 = d2 - entityplayer.locZ;
|
double d6 = d2 - entityplayer.locZ;
|
||||||
|
|||||||
506
sources/src/main/java/net/minecraft/server/RegionFile.java
Normal file
506
sources/src/main/java/net/minecraft/server/RegionFile.java
Normal file
@@ -0,0 +1,506 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.Buffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.DeflaterOutputStream;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.InflaterInputStream;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class RegionFile {
|
||||||
|
|
||||||
|
// Spigot start
|
||||||
|
// Minecraft is limited to 256 sections per chunk. So 1MB. This can easily be overriden.
|
||||||
|
// So we extend this to use the REAL size when the count is maxed by seeking to that section and reading the length.
|
||||||
|
private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true"));
|
||||||
|
// Spigot end
|
||||||
|
private static final byte[] a = new byte[4096];
|
||||||
|
private final File b;private File getFile() { return b; } // Paper - OBFHELPER
|
||||||
|
private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER
|
||||||
|
private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER
|
||||||
|
private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER
|
||||||
|
private List<Boolean> f;
|
||||||
|
private int g;
|
||||||
|
private long h;
|
||||||
|
|
||||||
|
public RegionFile(File file) {
|
||||||
|
this.b = file;
|
||||||
|
this.g = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (file.exists()) {
|
||||||
|
this.h = file.lastModified();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.c = new RandomAccessFile(file, "rw");
|
||||||
|
if (this.c.length() < 8192L) { // Paper - headers should be 8192
|
||||||
|
this.c.write(a);
|
||||||
|
this.c.write(a);
|
||||||
|
this.g += 8192;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if ((this.c.length() & 4095L) != 0L) {
|
||||||
|
for (i = 0; (long) i < (this.c.length() & 4095L); ++i) {
|
||||||
|
this.c.write(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i = (int) this.c.length() / 4096;
|
||||||
|
this.f = Lists.newArrayListWithCapacity(i);
|
||||||
|
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < i; ++j) {
|
||||||
|
this.f.add(Boolean.valueOf(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.f.set(0, Boolean.valueOf(false));
|
||||||
|
this.f.set(1, Boolean.valueOf(false));
|
||||||
|
this.c.seek(0L);
|
||||||
|
|
||||||
|
int k;
|
||||||
|
|
||||||
|
// Paper Start
|
||||||
|
ByteBuffer header = ByteBuffer.allocate(8192);
|
||||||
|
while (header.hasRemaining()) {
|
||||||
|
if (this.c.getChannel().read(header) == -1) throw new EOFException();
|
||||||
|
}
|
||||||
|
((Buffer) header).clear();
|
||||||
|
IntBuffer headerAsInts = header.asIntBuffer();
|
||||||
|
initOversizedState();
|
||||||
|
// Paper End
|
||||||
|
for (j = 0; j < 1024; ++j) {
|
||||||
|
k = headerAsInts.get(); // Paper
|
||||||
|
this.d[j] = k;
|
||||||
|
// Spigot start
|
||||||
|
int length = k & 255;
|
||||||
|
if (length == 255) {
|
||||||
|
if ((k >> 8) <= this.f.size()) {
|
||||||
|
// We're maxed out, so we need to read the proper length from the section
|
||||||
|
this.c.seek((k >> 8) * 4096);
|
||||||
|
length = (this.c.readInt() + 4) / 4096 + 1;
|
||||||
|
this.c.seek(j * 4 + 4); // Go back to where we were
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k > 0 && (k >> 8) > 1 && (k >> 8) + (length) <= this.f.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid
|
||||||
|
for (int l = 0; l < (length); ++l) {
|
||||||
|
// Spigot end
|
||||||
|
this.f.set((k >> 8) + l, Boolean.valueOf(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Spigot start
|
||||||
|
else if (k != 0) { // Paper
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file});
|
||||||
|
deleteChunk(j); // Paper
|
||||||
|
}
|
||||||
|
// Spigot end
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 1024; ++j) {
|
||||||
|
k = headerAsInts.get(); // Paper
|
||||||
|
if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption
|
||||||
|
}
|
||||||
|
} catch (IOException ioexception) {
|
||||||
|
ioexception.printStackTrace();
|
||||||
|
ServerInternalException.reportInternalException(ioexception); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public synchronized DataInputStream getReadStream(int i, int j) { return a(i, j); } @Nullable public synchronized DataInputStream a(int i, int j) { // Paper - OBFHELPER
|
||||||
|
if (this.d(i, j)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
int k = this.getOffset(i, j);
|
||||||
|
|
||||||
|
if (k == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
int l = k >> 8;
|
||||||
|
int i1 = k & 255;
|
||||||
|
// Spigot start
|
||||||
|
if (i1 == 255) {
|
||||||
|
this.c.seek(l * 4096);
|
||||||
|
i1 = (this.c.readInt() + 4) / 4096 + 1;
|
||||||
|
}
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
if (l + i1 > this.f.size()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
this.c.seek((long) (l * 4096));
|
||||||
|
int j1 = this.c.readInt();
|
||||||
|
|
||||||
|
if (j1 > 4096 * i1) {
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot
|
||||||
|
return null;
|
||||||
|
} else if (j1 <= 0) {
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
byte b0 = this.c.readByte();
|
||||||
|
byte[] abyte;
|
||||||
|
|
||||||
|
if (b0 == 1) {
|
||||||
|
abyte = new byte[j1 - 1];
|
||||||
|
this.c.read(abyte);
|
||||||
|
return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte))));
|
||||||
|
} else if (b0 == 2) {
|
||||||
|
abyte = new byte[j1 - 1];
|
||||||
|
this.c.read(abyte);
|
||||||
|
return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte))));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioexception) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public DataOutputStream getWriteStream(int i, int j) { return b(i, j); } @Nullable public DataOutputStream b(int i, int j) { // Paper - OBFHELPER
|
||||||
|
return this.d(i, j) ? null : new DataOutputStream(new RegionFile.ChunkBuffer(i, j)); // Paper - remove middleware, move deflate to .close() for dynamic levels
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void a(int i, int j, byte[] abyte, int k) {
|
||||||
|
try {
|
||||||
|
int l = this.getOffset(i, j);
|
||||||
|
int i1 = l >> 8;
|
||||||
|
int j1 = l & 255;
|
||||||
|
// Spigot start
|
||||||
|
if (j1 == 255) {
|
||||||
|
this.c.seek(i1 * 4096);
|
||||||
|
j1 = (this.c.readInt() + 4) / 4096 + 1;
|
||||||
|
}
|
||||||
|
// Spigot end
|
||||||
|
int k1 = (k + 5) / 4096 + 1;
|
||||||
|
|
||||||
|
if (k1 >= 256) {
|
||||||
|
// Spigot start
|
||||||
|
if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b});
|
||||||
|
if (!ENABLE_EXTENDED_SAVE) return;
|
||||||
|
// Spigot end
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i1 != 0 && j1 == k1) {
|
||||||
|
this.a(i1, abyte, k);
|
||||||
|
} else {
|
||||||
|
int l1;
|
||||||
|
|
||||||
|
for (l1 = 0; l1 < j1; ++l1) {
|
||||||
|
this.f.set(i1 + l1, Boolean.valueOf(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
l1 = this.f.indexOf(Boolean.valueOf(true));
|
||||||
|
int i2 = 0;
|
||||||
|
int j2;
|
||||||
|
|
||||||
|
if (l1 != -1) {
|
||||||
|
for (j2 = l1; j2 < this.f.size(); ++j2) {
|
||||||
|
if (i2 != 0) {
|
||||||
|
if (((Boolean) this.f.get(j2)).booleanValue()) {
|
||||||
|
++i2;
|
||||||
|
} else {
|
||||||
|
i2 = 0;
|
||||||
|
}
|
||||||
|
} else if (((Boolean) this.f.get(j2)).booleanValue()) {
|
||||||
|
l1 = j2;
|
||||||
|
i2 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2 >= k1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i2 >= k1) {
|
||||||
|
i1 = l1;
|
||||||
|
this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
|
||||||
|
|
||||||
|
for (j2 = 0; j2 < k1; ++j2) {
|
||||||
|
this.f.set(i1 + j2, Boolean.valueOf(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.a(i1, abyte, k);
|
||||||
|
} else {
|
||||||
|
this.c.seek(this.c.length());
|
||||||
|
i1 = this.f.size();
|
||||||
|
|
||||||
|
for (j2 = 0; j2 < k1; ++j2) {
|
||||||
|
this.c.write(RegionFile.a);
|
||||||
|
this.f.add(Boolean.valueOf(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.g += 4096 * k1;
|
||||||
|
this.a(i1, abyte, k);
|
||||||
|
this.a(i, j, i1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.b(i, j, (int) (MinecraftServer.aw() / 1000L));
|
||||||
|
} catch (IOException ioexception) {
|
||||||
|
org.spigotmc.SneakyThrow.sneaky(ioexception); // Paper - we want the upper try/catch to retry this
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void a(int i, byte[] abyte, int j) throws IOException {
|
||||||
|
this.c.seek((long) (i * 4096));
|
||||||
|
this.c.writeInt(j + 1);
|
||||||
|
this.c.writeByte(2);
|
||||||
|
this.c.write(abyte, 0, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean d(int i, int j) {
|
||||||
|
return i < 0 || i >= 32 || j < 0 || j >= 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized int getOffset(int i, int j) {
|
||||||
|
return this.d[i + j * 32];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean c(int i, int j) {
|
||||||
|
return this.getOffset(i, j) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void a(int i, int j, int k) throws IOException {
|
||||||
|
this.d[i + j * 32] = k;
|
||||||
|
this.c.seek((long) ((i + j * 32) * 4));
|
||||||
|
this.c.writeInt(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void b(int i, int j, int k) throws IOException {
|
||||||
|
this.e[i + j * 32] = k;
|
||||||
|
this.c.seek((long) (4096 + (i + j * 32) * 4));
|
||||||
|
this.c.writeInt(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void c() throws IOException {
|
||||||
|
if (this.c != null) {
|
||||||
|
this.c.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start
|
||||||
|
public synchronized void deleteChunk(int j1) {
|
||||||
|
backup();
|
||||||
|
int k = offsets[j1];
|
||||||
|
int x = j1 & 1024;
|
||||||
|
int z = j1 >> 2;
|
||||||
|
int offset = (k >> 8);
|
||||||
|
int len = (k & 255);
|
||||||
|
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
|
||||||
|
String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len;
|
||||||
|
try {
|
||||||
|
RandomAccessFile file = getDataFile();
|
||||||
|
file.seek(j1 * 4);
|
||||||
|
file.writeInt(0);
|
||||||
|
// clear the timestamp
|
||||||
|
file.seek(4096 + j1 * 4);
|
||||||
|
file.writeInt(0);
|
||||||
|
timestamps[j1] = 0;
|
||||||
|
offsets[j1] = 0;
|
||||||
|
logger.error("Deleted corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
|
||||||
|
logger.error("Error deleting corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private boolean backedUp = false;
|
||||||
|
private synchronized void backup() {
|
||||||
|
if (backedUp) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
backedUp = true;
|
||||||
|
File file = this.getFile();
|
||||||
|
java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
|
||||||
|
java.util.Date today = new java.util.Date();
|
||||||
|
File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt");
|
||||||
|
if (corrupt.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
|
||||||
|
logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing");
|
||||||
|
try {
|
||||||
|
java.nio.file.Files.copy(file.toPath(), corrupt.toPath());
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final byte[] oversized = new byte[1024];
|
||||||
|
private int oversizedCount = 0;
|
||||||
|
|
||||||
|
private synchronized void initOversizedState() throws IOException {
|
||||||
|
File metaFile = getOversizedMetaFile();
|
||||||
|
if (metaFile.exists()) {
|
||||||
|
final byte[] read = java.nio.file.Files.readAllBytes(metaFile.toPath());
|
||||||
|
System.arraycopy(read, 0, oversized, 0, oversized.length);
|
||||||
|
for (byte temp : oversized) {
|
||||||
|
oversizedCount += temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getChunkIndex(int x, int z) {
|
||||||
|
return (x & 31) + (z & 31) * 32;
|
||||||
|
}
|
||||||
|
synchronized boolean isOversized(int x, int z) {
|
||||||
|
return this.oversized[getChunkIndex(x, z)] == 1;
|
||||||
|
}
|
||||||
|
synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
|
||||||
|
final int offset = getChunkIndex(x, z);
|
||||||
|
boolean previous = this.oversized[offset] == 1;
|
||||||
|
this.oversized[offset] = (byte) (oversized ? 1 : 0);
|
||||||
|
if (!previous && oversized) {
|
||||||
|
oversizedCount++;
|
||||||
|
} else if (!oversized && previous) {
|
||||||
|
oversizedCount--;
|
||||||
|
}
|
||||||
|
if (previous && !oversized) {
|
||||||
|
File oversizedFile = getOversizedFile(x, z);
|
||||||
|
if (oversizedFile.exists()) {
|
||||||
|
oversizedFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oversizedCount > 0) {
|
||||||
|
if (previous != oversized) {
|
||||||
|
writeOversizedMeta();
|
||||||
|
}
|
||||||
|
} else if (previous) {
|
||||||
|
File oversizedMetaFile = getOversizedMetaFile();
|
||||||
|
if (oversizedMetaFile.exists()) {
|
||||||
|
oversizedMetaFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeOversizedMeta() throws IOException {
|
||||||
|
java.nio.file.Files.write(getOversizedMetaFile().toPath(), oversized);
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getOversizedMetaFile() {
|
||||||
|
return new File(getFile().getParentFile(), getFile().getName().replaceAll("\\.mca$", "") + ".oversized.nbt");
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getOversizedFile(int x, int z) {
|
||||||
|
return new File(this.getFile().getParentFile(), this.getFile().getName().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeOversizedData(int x, int z, NBTTagCompound oversizedData) throws IOException {
|
||||||
|
File file = getOversizedFile(x, z);
|
||||||
|
try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new java.io.FileOutputStream(file), new java.util.zip.Deflater(java.util.zip.Deflater.BEST_COMPRESSION), 32 * 1024), 32 * 1024))) {
|
||||||
|
NBTCompressedStreamTools.writeNBT(oversizedData, out);
|
||||||
|
}
|
||||||
|
this.setOversized(x, z, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized NBTTagCompound getOversizedData(int x, int z) throws IOException {
|
||||||
|
File file = getOversizedFile(x, z);
|
||||||
|
try (DataInputStream out = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new java.io.FileInputStream(file))))) {
|
||||||
|
return NBTCompressedStreamTools.readNBT(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final boolean USE_SPIGOT_OVERSIZED_METHOD = Boolean.getBoolean("Paper.useSpigotExtendedSaveMethod"); // Paper
|
||||||
|
static {
|
||||||
|
if (USE_SPIGOT_OVERSIZED_METHOD) {
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Using Spigot Oversized Chunk save method. Warning this will result in extremely fragmented chunks, as well as making the entire region file unable to be to used in any other software but Forge or Spigot (not usable in Vanilla or CraftBukkit). Paper's method is highly recommended.");
|
||||||
|
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChunkTooLargeException extends RuntimeException {
|
||||||
|
public ChunkTooLargeException(int x, int z, int sectors) {
|
||||||
|
super("Chunk " + x + "," + z + " of " + getFile().toString() + " is too large (" + sectors + "/256)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static class DirectByteArrayOutputStream extends ByteArrayOutputStream {
|
||||||
|
public DirectByteArrayOutputStream() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectByteArrayOutputStream(int size) {
|
||||||
|
super(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuffer() {
|
||||||
|
return this.buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
class ChunkBuffer extends ByteArrayOutputStream {
|
||||||
|
|
||||||
|
private final int b;
|
||||||
|
private final int c;
|
||||||
|
|
||||||
|
public ChunkBuffer(int i, int j) {
|
||||||
|
super(8096);
|
||||||
|
this.b = i;
|
||||||
|
this.c = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
// Paper start - apply dynamic compression
|
||||||
|
int origLength = this.count;
|
||||||
|
byte[] buf = this.buf;
|
||||||
|
DirectByteArrayOutputStream out = compressData(buf, origLength);
|
||||||
|
byte[] bytes = out.getBuffer();
|
||||||
|
int length = out.size();
|
||||||
|
|
||||||
|
RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib
|
||||||
|
private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater();
|
||||||
|
// since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable.
|
||||||
|
private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException {
|
||||||
|
synchronized (deflater) {
|
||||||
|
deflater.setInput(buf, 0, length);
|
||||||
|
deflater.finish();
|
||||||
|
|
||||||
|
|
||||||
|
DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length);
|
||||||
|
while (!deflater.finished()) {
|
||||||
|
out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer));
|
||||||
|
}
|
||||||
|
out.close();
|
||||||
|
deflater.reset();
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
package net.minecraft.server;
|
|
||||||
|
|
||||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import com.destroystokyo.paper.PaperConfig; // Paper
|
|
||||||
import java.util.LinkedHashMap; // Paper
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Akarin Changes Note
|
|
||||||
* 1) Removes unneed synchronization (performance)
|
|
||||||
*/
|
|
||||||
public class RegionFileCache {
|
|
||||||
|
|
||||||
public static final Map<File, RegionFile> a = new LinkedHashMap(PaperConfig.regionFileCacheSize, 0.75f, true); // Spigot - private -> public, Paper - HashMap -> LinkedHashMap
|
|
||||||
|
|
||||||
public static synchronized RegionFile a(File file, int i, int j) {
|
|
||||||
File file1 = new File(file, "region");
|
|
||||||
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
|
|
||||||
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
|
|
||||||
|
|
||||||
if (regionfile != null) {
|
|
||||||
return regionfile;
|
|
||||||
} else {
|
|
||||||
if (!file1.exists()) {
|
|
||||||
file1.mkdirs();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RegionFileCache.a.size() >= PaperConfig.regionFileCacheSize) { // Paper
|
|
||||||
trimCache(); // Paper
|
|
||||||
}
|
|
||||||
|
|
||||||
RegionFile regionfile1 = new RegionFile(file2);
|
|
||||||
|
|
||||||
RegionFileCache.a.put(file2, regionfile1);
|
|
||||||
return regionfile1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static synchronized RegionFile b(File file, int i, int j) {
|
|
||||||
File file1 = new File(file, "region");
|
|
||||||
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
|
|
||||||
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
|
|
||||||
|
|
||||||
if (regionfile != null) {
|
|
||||||
return regionfile;
|
|
||||||
} else if (file1.exists() && file2.exists()) {
|
|
||||||
if (RegionFileCache.a.size() >= 256) {
|
|
||||||
a();
|
|
||||||
}
|
|
||||||
|
|
||||||
RegionFile regionfile1 = new RegionFile(file2);
|
|
||||||
|
|
||||||
RegionFileCache.a.put(file2, regionfile1);
|
|
||||||
return regionfile1;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Paper Start
|
|
||||||
private static synchronized void trimCache() {
|
|
||||||
Iterator<Map.Entry<File, RegionFile>> itr = RegionFileCache.a.entrySet().iterator();
|
|
||||||
int count = RegionFileCache.a.size() - PaperConfig.regionFileCacheSize;
|
|
||||||
while (count-- >= 0 && itr.hasNext()) {
|
|
||||||
try {
|
|
||||||
itr.next().getValue().c();
|
|
||||||
} catch (IOException ioexception) {
|
|
||||||
ioexception.printStackTrace();
|
|
||||||
ServerInternalException.reportInternalException(ioexception);
|
|
||||||
}
|
|
||||||
itr.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Paper End
|
|
||||||
|
|
||||||
public static synchronized void a() {
|
|
||||||
Iterator iterator = RegionFileCache.a.values().iterator();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
RegionFile regionfile = (RegionFile) iterator.next();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (regionfile != null) {
|
|
||||||
regionfile.c();
|
|
||||||
}
|
|
||||||
} catch (IOException ioexception) {
|
|
||||||
ioexception.printStackTrace();
|
|
||||||
ServerInternalException.reportInternalException(ioexception); // Paper
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RegionFileCache.a.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// CraftBukkit start - call sites hoisted for synchronization
|
|
||||||
public static /*synchronized*/ NBTTagCompound d(File file, int i, int j) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: read
|
|
||||||
RegionFile regionfile = a(file, i, j);
|
|
||||||
|
|
||||||
DataInputStream datainputstream = regionfile.a(i & 31, j & 31);
|
|
||||||
|
|
||||||
if (datainputstream == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NBTCompressedStreamTools.a(datainputstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static /*synchronized*/ void e(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: write
|
|
||||||
RegionFile regionfile = a(file, i, j);
|
|
||||||
|
|
||||||
DataOutputStream dataoutputstream = regionfile.b(i & 31, j & 31);
|
|
||||||
NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) dataoutputstream);
|
|
||||||
dataoutputstream.close();
|
|
||||||
}
|
|
||||||
// CraftBukkit end
|
|
||||||
|
|
||||||
public static /*synchronized*/ boolean chunkExists(File file, int i, int j) { // Akarin - 1.13 backport - remove synchronization
|
|
||||||
RegionFile regionfile = b(file, i, j);
|
|
||||||
|
|
||||||
return regionfile != null ? regionfile.c(i & 31, j & 31) : false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,8 @@ package net.minecraft.server;
|
|||||||
|
|
||||||
import com.google.common.base.Predicates;
|
import com.google.common.base.Predicates;
|
||||||
import com.google.common.collect.Iterators;
|
import com.google.common.collect.Iterators;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ public class RegistryID<K> implements Registry {
|
|||||||
this.b = (K[]) (new Object[i]);
|
this.b = (K[]) (new Object[i]);
|
||||||
this.c = new int[i];
|
this.c = new int[i];
|
||||||
this.d = (K[]) (new Object[i]);
|
this.d = (K[]) (new Object[i]);
|
||||||
this.usedIds = new java.util.BitSet(); // Akarin - 1.13 backport
|
this.usedIds = new BitSet(); // Akarin - 1.13 backport
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId(@Nullable K k0) {
|
public int getId(@Nullable K k0) {
|
||||||
|
|||||||
3324
sources/src/main/java/net/minecraft/server/World.java
Normal file
3324
sources/src/main/java/net/minecraft/server/World.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,773 +0,0 @@
|
|||||||
package net.minecraft.server;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
// CraftBukkit start
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.event.weather.ThunderChangeEvent;
|
|
||||||
import org.bukkit.event.weather.WeatherChangeEvent;
|
|
||||||
// CraftBukkit end
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Akarin Changes Note
|
|
||||||
* 1) Add volatile to fields (slack service)
|
|
||||||
*/
|
|
||||||
public class WorldData {
|
|
||||||
|
|
||||||
private String b;
|
|
||||||
private int c;
|
|
||||||
private boolean d;
|
|
||||||
public static final EnumDifficulty a = EnumDifficulty.NORMAL;
|
|
||||||
private long e;
|
|
||||||
private WorldType f;
|
|
||||||
private String g;
|
|
||||||
private int h;
|
|
||||||
private int i;
|
|
||||||
private int j;
|
|
||||||
private volatile long k; // Akarin - volatile - OBFHELPER: time
|
|
||||||
private volatile long l; // Akarin - volatile - OBFHELPER: dayTime
|
|
||||||
private long m;
|
|
||||||
private long n;
|
|
||||||
private NBTTagCompound o;
|
|
||||||
private int p;
|
|
||||||
private String levelName;
|
|
||||||
private int r;
|
|
||||||
private int s;
|
|
||||||
private boolean t;
|
|
||||||
private int u;
|
|
||||||
private boolean v;
|
|
||||||
private int w;
|
|
||||||
private EnumGamemode x;
|
|
||||||
private boolean y;
|
|
||||||
private boolean z;
|
|
||||||
private boolean A;
|
|
||||||
private boolean B;
|
|
||||||
private volatile EnumDifficulty C; // Akarin - volatile
|
|
||||||
private boolean D;
|
|
||||||
private double E;
|
|
||||||
private double F;
|
|
||||||
private double G;
|
|
||||||
private long H;
|
|
||||||
private double I;
|
|
||||||
private double J;
|
|
||||||
private double K;
|
|
||||||
private int L;
|
|
||||||
private int M;
|
|
||||||
private final Map<DimensionManager, NBTTagCompound> N;
|
|
||||||
private GameRules O;
|
|
||||||
public WorldServer world; // CraftBukkit
|
|
||||||
|
|
||||||
protected WorldData() {
|
|
||||||
this.f = WorldType.NORMAL;
|
|
||||||
this.g = "";
|
|
||||||
this.G = 6.0E7D;
|
|
||||||
this.J = 5.0D;
|
|
||||||
this.K = 0.2D;
|
|
||||||
this.L = 5;
|
|
||||||
this.M = 15;
|
|
||||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
|
||||||
this.O = new GameRules();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void a(DataConverterManager dataconvertermanager) {
|
|
||||||
dataconvertermanager.a(DataConverterTypes.LEVEL, new DataInspector() {
|
|
||||||
@Override
|
|
||||||
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
|
|
||||||
if (nbttagcompound.hasKeyOfType("Player", 10)) {
|
|
||||||
nbttagcompound.set("Player", dataconverter.a(DataConverterTypes.PLAYER, nbttagcompound.getCompound("Player"), i));
|
|
||||||
}
|
|
||||||
|
|
||||||
return nbttagcompound;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldData(NBTTagCompound nbttagcompound) {
|
|
||||||
this.f = WorldType.NORMAL;
|
|
||||||
this.g = "";
|
|
||||||
this.G = 6.0E7D;
|
|
||||||
this.J = 5.0D;
|
|
||||||
this.K = 0.2D;
|
|
||||||
this.L = 5;
|
|
||||||
this.M = 15;
|
|
||||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
|
||||||
this.O = new GameRules();
|
|
||||||
NBTTagCompound nbttagcompound1;
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("Version", 10)) {
|
|
||||||
nbttagcompound1 = nbttagcompound.getCompound("Version");
|
|
||||||
this.b = nbttagcompound1.getString("Name");
|
|
||||||
this.c = nbttagcompound1.getInt("Id");
|
|
||||||
this.d = nbttagcompound1.getBoolean("Snapshot");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.e = nbttagcompound.getLong("RandomSeed");
|
|
||||||
if (nbttagcompound.hasKeyOfType("generatorName", 8)) {
|
|
||||||
String s = nbttagcompound.getString("generatorName");
|
|
||||||
|
|
||||||
this.f = WorldType.getType(s);
|
|
||||||
if (this.f == null) {
|
|
||||||
this.f = WorldType.NORMAL;
|
|
||||||
} else if (this.f.f()) {
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) {
|
|
||||||
i = nbttagcompound.getInt("generatorVersion");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.f = this.f.a(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("generatorOptions", 8)) {
|
|
||||||
this.g = nbttagcompound.getString("generatorOptions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.x = EnumGamemode.getById(nbttagcompound.getInt("GameType"));
|
|
||||||
if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) {
|
|
||||||
this.y = nbttagcompound.getBoolean("MapFeatures");
|
|
||||||
} else {
|
|
||||||
this.y = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.h = nbttagcompound.getInt("SpawnX");
|
|
||||||
this.i = nbttagcompound.getInt("SpawnY");
|
|
||||||
this.j = nbttagcompound.getInt("SpawnZ");
|
|
||||||
this.k = nbttagcompound.getLong("Time");
|
|
||||||
if (nbttagcompound.hasKeyOfType("DayTime", 99)) {
|
|
||||||
this.l = nbttagcompound.getLong("DayTime");
|
|
||||||
} else {
|
|
||||||
this.l = this.k;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.m = nbttagcompound.getLong("LastPlayed");
|
|
||||||
this.n = nbttagcompound.getLong("SizeOnDisk");
|
|
||||||
this.levelName = nbttagcompound.getString("LevelName");
|
|
||||||
this.r = nbttagcompound.getInt("version");
|
|
||||||
this.s = nbttagcompound.getInt("clearWeatherTime");
|
|
||||||
this.u = nbttagcompound.getInt("rainTime");
|
|
||||||
this.t = nbttagcompound.getBoolean("raining");
|
|
||||||
this.w = nbttagcompound.getInt("thunderTime");
|
|
||||||
this.v = nbttagcompound.getBoolean("thundering");
|
|
||||||
this.z = nbttagcompound.getBoolean("hardcore");
|
|
||||||
if (nbttagcompound.hasKeyOfType("initialized", 99)) {
|
|
||||||
this.B = nbttagcompound.getBoolean("initialized");
|
|
||||||
} else {
|
|
||||||
this.B = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("allowCommands", 99)) {
|
|
||||||
this.A = nbttagcompound.getBoolean("allowCommands");
|
|
||||||
} else {
|
|
||||||
this.A = this.x == EnumGamemode.CREATIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("Player", 10)) {
|
|
||||||
this.o = nbttagcompound.getCompound("Player");
|
|
||||||
this.p = this.o.getInt("Dimension");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("GameRules", 10)) {
|
|
||||||
this.O.a(nbttagcompound.getCompound("GameRules"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("Difficulty", 99)) {
|
|
||||||
this.C = EnumDifficulty.getById(nbttagcompound.getByte("Difficulty"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("DifficultyLocked", 1)) {
|
|
||||||
this.D = nbttagcompound.getBoolean("DifficultyLocked");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderCenterX", 99)) {
|
|
||||||
this.E = nbttagcompound.getDouble("BorderCenterX");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderCenterZ", 99)) {
|
|
||||||
this.F = nbttagcompound.getDouble("BorderCenterZ");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderSize", 99)) {
|
|
||||||
this.G = nbttagcompound.getDouble("BorderSize");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTime", 99)) {
|
|
||||||
this.H = nbttagcompound.getLong("BorderSizeLerpTime");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTarget", 99)) {
|
|
||||||
this.I = nbttagcompound.getDouble("BorderSizeLerpTarget");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderSafeZone", 99)) {
|
|
||||||
this.J = nbttagcompound.getDouble("BorderSafeZone");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderDamagePerBlock", 99)) {
|
|
||||||
this.K = nbttagcompound.getDouble("BorderDamagePerBlock");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderWarningBlocks", 99)) {
|
|
||||||
this.L = nbttagcompound.getInt("BorderWarningBlocks");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("BorderWarningTime", 99)) {
|
|
||||||
this.M = nbttagcompound.getInt("BorderWarningTime");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbttagcompound.hasKeyOfType("DimensionData", 10)) {
|
|
||||||
nbttagcompound1 = nbttagcompound.getCompound("DimensionData");
|
|
||||||
Iterator iterator = nbttagcompound1.c().iterator();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
String s1 = (String) iterator.next();
|
|
||||||
|
|
||||||
this.N.put(DimensionManager.a(Integer.parseInt(s1)), nbttagcompound1.getCompound(s1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldData(WorldSettings worldsettings, String s) {
|
|
||||||
this.f = WorldType.NORMAL;
|
|
||||||
this.g = "";
|
|
||||||
this.G = 6.0E7D;
|
|
||||||
this.J = 5.0D;
|
|
||||||
this.K = 0.2D;
|
|
||||||
this.L = 5;
|
|
||||||
this.M = 15;
|
|
||||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
|
||||||
this.O = new GameRules();
|
|
||||||
this.a(worldsettings);
|
|
||||||
this.levelName = s;
|
|
||||||
this.C = WorldData.a;
|
|
||||||
this.B = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(WorldSettings worldsettings) {
|
|
||||||
this.e = worldsettings.d();
|
|
||||||
this.x = worldsettings.e();
|
|
||||||
this.y = worldsettings.g();
|
|
||||||
this.z = worldsettings.f();
|
|
||||||
this.f = worldsettings.h();
|
|
||||||
this.g = worldsettings.j();
|
|
||||||
this.A = worldsettings.i();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldData(WorldData worlddata) {
|
|
||||||
this.f = WorldType.NORMAL;
|
|
||||||
this.g = "";
|
|
||||||
this.G = 6.0E7D;
|
|
||||||
this.J = 5.0D;
|
|
||||||
this.K = 0.2D;
|
|
||||||
this.L = 5;
|
|
||||||
this.M = 15;
|
|
||||||
this.N = Maps.newEnumMap(DimensionManager.class);
|
|
||||||
this.O = new GameRules();
|
|
||||||
this.e = worlddata.e;
|
|
||||||
this.f = worlddata.f;
|
|
||||||
this.g = worlddata.g;
|
|
||||||
this.x = worlddata.x;
|
|
||||||
this.y = worlddata.y;
|
|
||||||
this.h = worlddata.h;
|
|
||||||
this.i = worlddata.i;
|
|
||||||
this.j = worlddata.j;
|
|
||||||
this.k = worlddata.k;
|
|
||||||
this.l = worlddata.l;
|
|
||||||
this.m = worlddata.m;
|
|
||||||
this.n = worlddata.n;
|
|
||||||
this.o = worlddata.o;
|
|
||||||
this.p = worlddata.p;
|
|
||||||
this.levelName = worlddata.levelName;
|
|
||||||
this.r = worlddata.r;
|
|
||||||
this.u = worlddata.u;
|
|
||||||
this.t = worlddata.t;
|
|
||||||
this.w = worlddata.w;
|
|
||||||
this.v = worlddata.v;
|
|
||||||
this.z = worlddata.z;
|
|
||||||
this.A = worlddata.A;
|
|
||||||
this.B = worlddata.B;
|
|
||||||
this.O = worlddata.O;
|
|
||||||
this.C = worlddata.C;
|
|
||||||
this.D = worlddata.D;
|
|
||||||
this.E = worlddata.E;
|
|
||||||
this.F = worlddata.F;
|
|
||||||
this.G = worlddata.G;
|
|
||||||
this.H = worlddata.H;
|
|
||||||
this.I = worlddata.I;
|
|
||||||
this.J = worlddata.J;
|
|
||||||
this.K = worlddata.K;
|
|
||||||
this.M = worlddata.M;
|
|
||||||
this.L = worlddata.L;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NBTTagCompound a(@Nullable NBTTagCompound nbttagcompound) {
|
|
||||||
if (nbttagcompound == null) {
|
|
||||||
nbttagcompound = this.o;
|
|
||||||
}
|
|
||||||
|
|
||||||
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
|
||||||
|
|
||||||
this.a(nbttagcompound1, nbttagcompound);
|
|
||||||
return nbttagcompound1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) {
|
|
||||||
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
|
|
||||||
|
|
||||||
nbttagcompound2.setString("Name", "1.12.2");
|
|
||||||
nbttagcompound2.setInt("Id", 1343);
|
|
||||||
nbttagcompound2.setBoolean("Snapshot", false);
|
|
||||||
nbttagcompound.set("Version", nbttagcompound2);
|
|
||||||
nbttagcompound.setInt("DataVersion", 1343);
|
|
||||||
nbttagcompound.setLong("RandomSeed", this.e);
|
|
||||||
nbttagcompound.setString("generatorName", this.f.name());
|
|
||||||
nbttagcompound.setInt("generatorVersion", this.f.getVersion());
|
|
||||||
nbttagcompound.setString("generatorOptions", this.g);
|
|
||||||
nbttagcompound.setInt("GameType", this.x.getId());
|
|
||||||
nbttagcompound.setBoolean("MapFeatures", this.y);
|
|
||||||
nbttagcompound.setInt("SpawnX", this.h);
|
|
||||||
nbttagcompound.setInt("SpawnY", this.i);
|
|
||||||
nbttagcompound.setInt("SpawnZ", this.j);
|
|
||||||
nbttagcompound.setLong("Time", this.k);
|
|
||||||
nbttagcompound.setLong("DayTime", this.l);
|
|
||||||
nbttagcompound.setLong("SizeOnDisk", this.n);
|
|
||||||
nbttagcompound.setLong("LastPlayed", MinecraftServer.aw());
|
|
||||||
nbttagcompound.setString("LevelName", this.levelName);
|
|
||||||
nbttagcompound.setInt("version", this.r);
|
|
||||||
nbttagcompound.setInt("clearWeatherTime", this.s);
|
|
||||||
nbttagcompound.setInt("rainTime", this.u);
|
|
||||||
nbttagcompound.setBoolean("raining", this.t);
|
|
||||||
nbttagcompound.setInt("thunderTime", this.w);
|
|
||||||
nbttagcompound.setBoolean("thundering", this.v);
|
|
||||||
nbttagcompound.setBoolean("hardcore", this.z);
|
|
||||||
nbttagcompound.setBoolean("allowCommands", this.A);
|
|
||||||
nbttagcompound.setBoolean("initialized", this.B);
|
|
||||||
nbttagcompound.setDouble("BorderCenterX", this.E);
|
|
||||||
nbttagcompound.setDouble("BorderCenterZ", this.F);
|
|
||||||
nbttagcompound.setDouble("BorderSize", this.G);
|
|
||||||
nbttagcompound.setLong("BorderSizeLerpTime", this.H);
|
|
||||||
nbttagcompound.setDouble("BorderSafeZone", this.J);
|
|
||||||
nbttagcompound.setDouble("BorderDamagePerBlock", this.K);
|
|
||||||
nbttagcompound.setDouble("BorderSizeLerpTarget", this.I);
|
|
||||||
nbttagcompound.setDouble("BorderWarningBlocks", this.L);
|
|
||||||
nbttagcompound.setDouble("BorderWarningTime", this.M);
|
|
||||||
if (this.C != null) {
|
|
||||||
nbttagcompound.setByte("Difficulty", (byte) this.C.a());
|
|
||||||
}
|
|
||||||
|
|
||||||
nbttagcompound.setBoolean("DifficultyLocked", this.D);
|
|
||||||
nbttagcompound.set("GameRules", this.O.a());
|
|
||||||
NBTTagCompound nbttagcompound3 = new NBTTagCompound();
|
|
||||||
Iterator iterator = this.N.entrySet().iterator();
|
|
||||||
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Entry entry = (Entry) iterator.next();
|
|
||||||
|
|
||||||
nbttagcompound3.set(String.valueOf(((DimensionManager) entry.getKey()).getDimensionID()), (NBTBase) entry.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
nbttagcompound.set("DimensionData", nbttagcompound3);
|
|
||||||
if (nbttagcompound1 != null) {
|
|
||||||
nbttagcompound.set("Player", nbttagcompound1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSeed() {
|
|
||||||
return this.e;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int b() {
|
|
||||||
return this.h;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int c() {
|
|
||||||
return this.i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int d() {
|
|
||||||
return this.j;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTime() {
|
|
||||||
return this.k;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDayTime() {
|
|
||||||
return this.l;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NBTTagCompound h() {
|
|
||||||
return this.o;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTime(long i) {
|
|
||||||
this.k = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDayTime(long i) {
|
|
||||||
this.l = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSpawn(BlockPosition blockposition) {
|
|
||||||
this.h = blockposition.getX();
|
|
||||||
this.i = blockposition.getY();
|
|
||||||
this.j = blockposition.getZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return this.levelName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(String s) {
|
|
||||||
this.levelName = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int k() {
|
|
||||||
return this.r;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void e(int i) {
|
|
||||||
this.r = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int z() {
|
|
||||||
return this.s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void i(int i) {
|
|
||||||
this.s = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isThundering() {
|
|
||||||
return this.v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setThundering(boolean flag) {
|
|
||||||
// CraftBukkit start
|
|
||||||
org.bukkit.World world = Bukkit.getWorld(getName());
|
|
||||||
if (world != null) {
|
|
||||||
ThunderChangeEvent thunder = new ThunderChangeEvent(world, flag);
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(thunder);
|
|
||||||
if (thunder.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// CraftBukkit end
|
|
||||||
this.v = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getThunderDuration() {
|
|
||||||
return this.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setThunderDuration(int i) {
|
|
||||||
this.w = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasStorm() {
|
|
||||||
return this.t;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStorm(boolean flag) {
|
|
||||||
// CraftBukkit start
|
|
||||||
org.bukkit.World world = Bukkit.getWorld(getName());
|
|
||||||
if (world != null) {
|
|
||||||
WeatherChangeEvent weather = new WeatherChangeEvent(world, flag);
|
|
||||||
Bukkit.getServer().getPluginManager().callEvent(weather);
|
|
||||||
if (weather.isCancelled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// CraftBukkit end
|
|
||||||
this.t = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWeatherDuration() {
|
|
||||||
return this.u;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWeatherDuration(int i) {
|
|
||||||
this.u = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnumGamemode getGameType() {
|
|
||||||
return this.x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean shouldGenerateMapFeatures() {
|
|
||||||
return this.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void f(boolean flag) {
|
|
||||||
this.y = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGameType(EnumGamemode enumgamemode) {
|
|
||||||
this.x = enumgamemode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHardcore() {
|
|
||||||
return this.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void g(boolean flag) {
|
|
||||||
this.z = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldType getType() {
|
|
||||||
return this.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(WorldType worldtype) {
|
|
||||||
this.f = worldtype;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGeneratorOptions() {
|
|
||||||
return this.g == null ? "" : this.g;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean u() {
|
|
||||||
return this.A;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void c(boolean flag) {
|
|
||||||
this.A = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean v() {
|
|
||||||
return this.B;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void d(boolean flag) {
|
|
||||||
this.B = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameRules w() {
|
|
||||||
return this.O;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double B() {
|
|
||||||
return this.E;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double C() {
|
|
||||||
return this.F;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double D() {
|
|
||||||
return this.G;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(double d0) {
|
|
||||||
this.G = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long E() {
|
|
||||||
return this.H;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void e(long i) {
|
|
||||||
this.H = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double F() {
|
|
||||||
return this.I;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void b(double d0) {
|
|
||||||
this.I = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void c(double d0) {
|
|
||||||
this.F = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void d(double d0) {
|
|
||||||
this.E = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double G() {
|
|
||||||
return this.J;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void e(double d0) {
|
|
||||||
this.J = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double H() {
|
|
||||||
return this.K;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void f(double d0) {
|
|
||||||
this.K = d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int I() {
|
|
||||||
return this.L;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int J() {
|
|
||||||
return this.M;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void j(int i) {
|
|
||||||
this.L = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void k(int i) {
|
|
||||||
this.M = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EnumDifficulty getDifficulty() {
|
|
||||||
return this.C;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDifficulty(EnumDifficulty enumdifficulty) {
|
|
||||||
this.C = enumdifficulty;
|
|
||||||
// CraftBukkit start
|
|
||||||
PacketPlayOutServerDifficulty packet = new PacketPlayOutServerDifficulty(this.getDifficulty(), this.isDifficultyLocked());
|
|
||||||
for (EntityPlayer player : (java.util.List<EntityPlayer>) (java.util.List) world.players) {
|
|
||||||
player.playerConnection.sendPacket(packet);
|
|
||||||
}
|
|
||||||
// CraftBukkit end
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDifficultyLocked() {
|
|
||||||
return this.D;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void e(boolean flag) {
|
|
||||||
this.D = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(CrashReportSystemDetails crashreportsystemdetails) {
|
|
||||||
crashreportsystemdetails.a("Level seed", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.valueOf(WorldData.this.getSeed());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level generator", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.format("ID %02d - %s, ver %d. Features enabled: %b", new Object[] { Integer.valueOf(WorldData.this.f.g()), WorldData.this.f.name(), Integer.valueOf(WorldData.this.f.getVersion()), Boolean.valueOf(WorldData.this.y)});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level generator options", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return WorldData.this.g;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level spawn location", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return CrashReportSystemDetails.a(WorldData.this.h, WorldData.this.i, WorldData.this.j);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level time", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.format("%d game time, %d day time", new Object[] { Long.valueOf(WorldData.this.k), Long.valueOf(WorldData.this.l)});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level dimension", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.valueOf(WorldData.this.p);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level storage version", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
String s = "Unknown?";
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (WorldData.this.r) {
|
|
||||||
case 19132:
|
|
||||||
s = "McRegion";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 19133:
|
|
||||||
s = "Anvil";
|
|
||||||
}
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.format("0x%05X - %s", new Object[] { Integer.valueOf(WorldData.this.r), s});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level weather", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", new Object[] { Integer.valueOf(WorldData.this.u), Boolean.valueOf(WorldData.this.t), Integer.valueOf(WorldData.this.w), Boolean.valueOf(WorldData.this.v)});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
crashreportsystemdetails.a("Level game mode", new CrashReportCallable() {
|
|
||||||
public String a() throws Exception {
|
|
||||||
return String.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", new Object[] { WorldData.this.x.b(), Integer.valueOf(WorldData.this.x.getId()), Boolean.valueOf(WorldData.this.z), Boolean.valueOf(WorldData.this.A)});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
|
||||||
return this.a();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public NBTTagCompound a(DimensionManager dimensionmanager) {
|
|
||||||
NBTTagCompound nbttagcompound = this.N.get(dimensionmanager);
|
|
||||||
|
|
||||||
return nbttagcompound == null ? new NBTTagCompound() : nbttagcompound;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void a(DimensionManager dimensionmanager, NBTTagCompound nbttagcompound) {
|
|
||||||
this.N.put(dimensionmanager, nbttagcompound);
|
|
||||||
}
|
|
||||||
|
|
||||||
// CraftBukkit start - Check if the name stored in NBT is the correct one
|
|
||||||
public void checkName( String name ) {
|
|
||||||
if ( !this.levelName.equals( name ) ) {
|
|
||||||
this.levelName = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// CraftBukkit end
|
|
||||||
}
|
|
||||||
@@ -424,33 +424,13 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
}
|
}
|
||||||
|
// Dionysus
|
||||||
|
// Fixes MC-47080 and simplifies the logic
|
||||||
public boolean everyoneDeeplySleeping() {
|
public boolean everyoneDeeplySleeping() {
|
||||||
if (this.Q && !this.isClientSide) {
|
if (this.players.size() == 0 || this.isClientSide || !this.Q) {
|
||||||
Iterator iterator = this.players.iterator();
|
|
||||||
|
|
||||||
// CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers
|
|
||||||
boolean foundActualSleepers = false;
|
|
||||||
|
|
||||||
EntityHuman entityhuman;
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (!iterator.hasNext()) {
|
|
||||||
return foundActualSleepers;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityhuman = (EntityHuman) iterator.next();
|
|
||||||
|
|
||||||
// CraftBukkit start
|
|
||||||
if (entityhuman.isDeeplySleeping()) {
|
|
||||||
foundActualSleepers = true;
|
|
||||||
}
|
|
||||||
} while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping);
|
|
||||||
// CraftBukkit end
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return this.players.stream().allMatch(p -> p.isSpectator() || p.isDeeplySleeping() || p.fauxSleeping);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1194,7 +1174,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
|||||||
if (this.entitiesByUUID.containsKey(uuid)) {
|
if (this.entitiesByUUID.containsKey(uuid)) {
|
||||||
Entity entity1 = this.entitiesByUUID.get(uuid);
|
Entity entity1 = this.entitiesByUUID.get(uuid);
|
||||||
|
|
||||||
if (this.f.contains(entity1) || entity1.dead) { // Paper - overwrite the current dead one
|
if (this.f.contains(entity1) || entity1.dead) { // Paper - if dupe is dead, overwrite
|
||||||
this.f.remove(entity1);
|
this.f.remove(entity1);
|
||||||
} else {
|
} else {
|
||||||
if (!(entity instanceof EntityHuman)) {
|
if (!(entity instanceof EntityHuman)) {
|
||||||
@@ -1291,7 +1271,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
if (super.strikeLightning(entity)) {
|
if (super.strikeLightning(entity)) {
|
||||||
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, dimension, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension
|
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, this, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension, // Paper - use world instead of dimension
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@@ -1369,8 +1349,8 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
|||||||
BlockActionData blockactiondata = (BlockActionData) iterator.next();
|
BlockActionData blockactiondata = (BlockActionData) iterator.next();
|
||||||
|
|
||||||
if (this.a(blockactiondata)) {
|
if (this.a(blockactiondata)) {
|
||||||
// CraftBukkit - this.worldProvider.dimension -> this.dimension
|
// CraftBukkit - this.worldProvider.dimension -> this.dimension, // Paper - dimension -> world
|
||||||
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, blockactiondata.a().getX(), blockactiondata.a().getY(), blockactiondata.a().getZ(), 64.0D, dimension, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c()));
|
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, (double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1922,17 +1922,20 @@ public final class CraftServer implements Server {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reloadPermissions() {
|
public void reloadPermissions() {
|
||||||
((SimplePluginManager) pluginManager).clearPermissions();
|
pluginManager.clearPermissions();
|
||||||
loadCustomPermissions();
|
if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
|
||||||
for (Plugin plugin : pluginManager.getPlugins()) {
|
for (Plugin plugin : pluginManager.getPlugins()) {
|
||||||
plugin.getDescription().getPermissions().forEach((perm) -> {
|
for (Permission perm : plugin.getDescription().getPermissions()) {
|
||||||
try {
|
try {
|
||||||
pluginManager.addPermission(perm);
|
pluginManager.addPermission(perm);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
|
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
|
||||||
|
DefaultPermissions.registerCorePermissions();
|
||||||
|
CraftDefaultPermissions.registerCorePermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1107,10 +1107,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|||||||
EntityTracker tracker = ((WorldServer) entity.world).tracker;
|
EntityTracker tracker = ((WorldServer) entity.world).tracker;
|
||||||
// Paper end
|
// Paper end
|
||||||
|
|
||||||
|
tracker.entriesLock.updateLock().lock(); // Akarin
|
||||||
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
|
tracker.entriesLock.writeLock().lock(); // Akarin
|
||||||
entry.clear(getHandle());
|
entry.clear(getHandle());
|
||||||
|
tracker.entriesLock.writeLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
|
tracker.entriesLock.updateLock().unlock(); // Akarin
|
||||||
|
|
||||||
// Remove the hidden player from this player user list, if they're on it
|
// Remove the hidden player from this player user list, if they're on it
|
||||||
if (other.sentListPacket) {
|
if (other.sentListPacket) {
|
||||||
@@ -1157,12 +1161,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|||||||
|
|
||||||
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
|
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
|
||||||
|
|
||||||
|
tracker.entriesLock.updateLock().lock(); // Akarin
|
||||||
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
|
||||||
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
|
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
|
||||||
tracker.entriesLock.lock(); // Akarin
|
tracker.entriesLock.writeLock().lock(); // Akarin
|
||||||
entry.updatePlayer(getHandle());
|
entry.updatePlayer(getHandle());
|
||||||
tracker.entriesLock.unlock(); // Akarin
|
tracker.entriesLock.writeLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
|
tracker.entriesLock.updateLock().unlock(); // Akarin
|
||||||
}
|
}
|
||||||
// Paper start
|
// Paper start
|
||||||
private void reregisterPlayer(EntityPlayer player) {
|
private void reregisterPlayer(EntityPlayer player) {
|
||||||
@@ -1616,7 +1622,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendHealthUpdate() {
|
public void sendHealthUpdate() {
|
||||||
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
|
// Paper start - cancellable death event
|
||||||
|
//getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
|
||||||
|
PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel());
|
||||||
|
if (this.getHandle().queueHealthUpdatePacket) {
|
||||||
|
this.getHandle().queuedHealthUpdatePacket = packet;
|
||||||
|
} else {
|
||||||
|
this.getHandle().playerConnection.sendPacket(packet);
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
}
|
}
|
||||||
|
|
||||||
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
|
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
791
sources/src/main/java/org/bukkit/plugin/SimplePluginManager.java
Normal file
791
sources/src/main/java/org/bukkit/plugin/SimplePluginManager.java
Normal file
@@ -0,0 +1,791 @@
|
|||||||
|
package org.bukkit.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
|
||||||
|
import com.destroystokyo.paper.exception.ServerEventException;
|
||||||
|
import com.destroystokyo.paper.exception.ServerPluginEnableDisableException;
|
||||||
|
import org.apache.commons.lang.Validate;
|
||||||
|
import org.bukkit.Server;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.command.PluginCommandYamlParser;
|
||||||
|
import org.bukkit.command.SimpleCommandMap;
|
||||||
|
import org.bukkit.event.Event;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.permissions.Permissible;
|
||||||
|
import org.bukkit.permissions.Permission;
|
||||||
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
|
import org.bukkit.util.FileUtil;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Reverts Paper's changes to ensure event order (safety issue)
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* Handles all plugin management from the Server
|
||||||
|
*/
|
||||||
|
public final class SimplePluginManager implements PluginManager {
|
||||||
|
private final Server server;
|
||||||
|
private final Map<Pattern, PluginLoader> fileAssociations = new HashMap<Pattern, PluginLoader>();
|
||||||
|
private final List<Plugin> plugins = new ArrayList<Plugin>();
|
||||||
|
private final Map<String, Plugin> lookupNames = new HashMap<String, Plugin>();
|
||||||
|
private File updateDirectory;
|
||||||
|
private final SimpleCommandMap commandMap;
|
||||||
|
private final Map<String, Permission> permissions = new HashMap<String, Permission>();
|
||||||
|
private final Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<Boolean, Set<Permission>>();
|
||||||
|
private final Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<String, Map<Permissible, Boolean>>();
|
||||||
|
private final Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<Boolean, Map<Permissible, Boolean>>();
|
||||||
|
private boolean useTimings = false;
|
||||||
|
|
||||||
|
public SimplePluginManager(Server instance, SimpleCommandMap commandMap) {
|
||||||
|
server = instance;
|
||||||
|
this.commandMap = commandMap;
|
||||||
|
|
||||||
|
defaultPerms.put(true, new HashSet<Permission>());
|
||||||
|
defaultPerms.put(false, new HashSet<Permission>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the specified plugin loader
|
||||||
|
*
|
||||||
|
* @param loader Class name of the PluginLoader to register
|
||||||
|
* @throws IllegalArgumentException Thrown when the given Class is not a
|
||||||
|
* valid PluginLoader
|
||||||
|
*/
|
||||||
|
public void registerInterface(Class<? extends PluginLoader> loader) throws IllegalArgumentException {
|
||||||
|
PluginLoader instance;
|
||||||
|
|
||||||
|
if (PluginLoader.class.isAssignableFrom(loader)) {
|
||||||
|
Constructor<? extends PluginLoader> constructor;
|
||||||
|
|
||||||
|
try {
|
||||||
|
constructor = loader.getConstructor(Server.class);
|
||||||
|
instance = constructor.newInstance(server);
|
||||||
|
} catch (NoSuchMethodException ex) {
|
||||||
|
String className = loader.getName();
|
||||||
|
|
||||||
|
throw new IllegalArgumentException(String.format("Class %s does not have a public %s(Server) constructor", className, className), ex);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException(String.format("Unexpected exception %s while attempting to construct a new instance of %s", ex.getClass().getName(), loader.getName()), ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(String.format("Class %s does not implement interface PluginLoader", loader.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Pattern[] patterns = instance.getPluginFileFilters();
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
for (Pattern pattern : patterns) {
|
||||||
|
fileAssociations.put(pattern, instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the plugins contained within the specified directory
|
||||||
|
*
|
||||||
|
* @param directory Directory to check for plugins
|
||||||
|
* @return A list of all plugins loaded
|
||||||
|
*/
|
||||||
|
public Plugin[] loadPlugins(File directory) {
|
||||||
|
Validate.notNull(directory, "Directory cannot be null");
|
||||||
|
Validate.isTrue(directory.isDirectory(), "Directory must be a directory");
|
||||||
|
|
||||||
|
List<Plugin> result = new ArrayList<Plugin>();
|
||||||
|
Set<Pattern> filters = fileAssociations.keySet();
|
||||||
|
|
||||||
|
if (!(server.getUpdateFolder().equals(""))) {
|
||||||
|
updateDirectory = new File(directory, server.getUpdateFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, File> plugins = new HashMap<String, File>();
|
||||||
|
Set<String> loadedPlugins = new HashSet<String>();
|
||||||
|
Map<String, Collection<String>> dependencies = new HashMap<String, Collection<String>>();
|
||||||
|
Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();
|
||||||
|
|
||||||
|
// This is where it figures out all possible plugins
|
||||||
|
for (File file : directory.listFiles()) {
|
||||||
|
PluginLoader loader = null;
|
||||||
|
for (Pattern filter : filters) {
|
||||||
|
Matcher match = filter.matcher(file.getName());
|
||||||
|
if (match.find()) {
|
||||||
|
loader = fileAssociations.get(filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loader == null) continue;
|
||||||
|
|
||||||
|
PluginDescriptionFile description = null;
|
||||||
|
try {
|
||||||
|
description = loader.getPluginDescription(file);
|
||||||
|
String name = description.getName();
|
||||||
|
if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) {
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name");
|
||||||
|
continue;
|
||||||
|
} else if (description.rawName.indexOf(' ') != -1) {
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': uses the space-character (0x20) in its name");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (InvalidDescriptionException ex) {
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
File replacedFile = plugins.put(description.getName(), file);
|
||||||
|
if (replacedFile != null) {
|
||||||
|
server.getLogger().severe(String.format(
|
||||||
|
"Ambiguous plugin name `%s' for files `%s' and `%s' in `%s'",
|
||||||
|
description.getName(),
|
||||||
|
file.getPath(),
|
||||||
|
replacedFile.getPath(),
|
||||||
|
directory.getPath()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> softDependencySet = description.getSoftDepend();
|
||||||
|
if (softDependencySet != null && !softDependencySet.isEmpty()) {
|
||||||
|
if (softDependencies.containsKey(description.getName())) {
|
||||||
|
// Duplicates do not matter, they will be removed together if applicable
|
||||||
|
softDependencies.get(description.getName()).addAll(softDependencySet);
|
||||||
|
} else {
|
||||||
|
softDependencies.put(description.getName(), new LinkedList<String>(softDependencySet));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> dependencySet = description.getDepend();
|
||||||
|
if (dependencySet != null && !dependencySet.isEmpty()) {
|
||||||
|
dependencies.put(description.getName(), new LinkedList<String>(dependencySet));
|
||||||
|
}
|
||||||
|
|
||||||
|
Collection<String> loadBeforeSet = description.getLoadBefore();
|
||||||
|
if (loadBeforeSet != null && !loadBeforeSet.isEmpty()) {
|
||||||
|
for (String loadBeforeTarget : loadBeforeSet) {
|
||||||
|
if (softDependencies.containsKey(loadBeforeTarget)) {
|
||||||
|
softDependencies.get(loadBeforeTarget).add(description.getName());
|
||||||
|
} else {
|
||||||
|
// softDependencies is never iterated, so 'ghost' plugins aren't an issue
|
||||||
|
Collection<String> shortSoftDependency = new LinkedList<String>();
|
||||||
|
shortSoftDependency.add(description.getName());
|
||||||
|
softDependencies.put(loadBeforeTarget, shortSoftDependency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!plugins.isEmpty()) {
|
||||||
|
boolean missingDependency = true;
|
||||||
|
Iterator<Map.Entry<String, File>> pluginIterator = plugins.entrySet().iterator();
|
||||||
|
|
||||||
|
while (pluginIterator.hasNext()) {
|
||||||
|
Map.Entry<String, File> entry = pluginIterator.next();
|
||||||
|
String plugin = entry.getKey();
|
||||||
|
|
||||||
|
if (dependencies.containsKey(plugin)) {
|
||||||
|
Iterator<String> dependencyIterator = dependencies.get(plugin).iterator();
|
||||||
|
|
||||||
|
while (dependencyIterator.hasNext()) {
|
||||||
|
String dependency = dependencyIterator.next();
|
||||||
|
|
||||||
|
// Dependency loaded
|
||||||
|
if (loadedPlugins.contains(dependency)) {
|
||||||
|
dependencyIterator.remove();
|
||||||
|
|
||||||
|
// We have a dependency not found
|
||||||
|
} else if (!plugins.containsKey(dependency)) {
|
||||||
|
missingDependency = false;
|
||||||
|
pluginIterator.remove();
|
||||||
|
softDependencies.remove(plugin);
|
||||||
|
dependencies.remove(plugin);
|
||||||
|
|
||||||
|
server.getLogger().log(
|
||||||
|
Level.SEVERE,
|
||||||
|
"Could not load '" + entry.getValue().getPath() + "' in folder '" + directory.getPath() + "'",
|
||||||
|
new UnknownDependencyException(dependency));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) {
|
||||||
|
dependencies.remove(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (softDependencies.containsKey(plugin)) {
|
||||||
|
Iterator<String> softDependencyIterator = softDependencies.get(plugin).iterator();
|
||||||
|
|
||||||
|
while (softDependencyIterator.hasNext()) {
|
||||||
|
String softDependency = softDependencyIterator.next();
|
||||||
|
|
||||||
|
// Soft depend is no longer around
|
||||||
|
if (!plugins.containsKey(softDependency)) {
|
||||||
|
softDependencyIterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (softDependencies.get(plugin).isEmpty()) {
|
||||||
|
softDependencies.remove(plugin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) {
|
||||||
|
// We're clear to load, no more soft or hard dependencies left
|
||||||
|
File file = plugins.get(plugin);
|
||||||
|
pluginIterator.remove();
|
||||||
|
missingDependency = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.add(loadPlugin(file));
|
||||||
|
loadedPlugins.add(plugin);
|
||||||
|
continue;
|
||||||
|
} catch (InvalidPluginException ex) {
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingDependency) {
|
||||||
|
// We now iterate over plugins until something loads
|
||||||
|
// This loop will ignore soft dependencies
|
||||||
|
pluginIterator = plugins.entrySet().iterator();
|
||||||
|
|
||||||
|
while (pluginIterator.hasNext()) {
|
||||||
|
Map.Entry<String, File> entry = pluginIterator.next();
|
||||||
|
String plugin = entry.getKey();
|
||||||
|
|
||||||
|
if (!dependencies.containsKey(plugin)) {
|
||||||
|
softDependencies.remove(plugin);
|
||||||
|
missingDependency = false;
|
||||||
|
File file = entry.getValue();
|
||||||
|
pluginIterator.remove();
|
||||||
|
|
||||||
|
try {
|
||||||
|
result.add(loadPlugin(file));
|
||||||
|
loadedPlugins.add(plugin);
|
||||||
|
break;
|
||||||
|
} catch (InvalidPluginException ex) {
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We have no plugins left without a depend
|
||||||
|
if (missingDependency) {
|
||||||
|
softDependencies.clear();
|
||||||
|
dependencies.clear();
|
||||||
|
Iterator<File> failedPluginIterator = plugins.values().iterator();
|
||||||
|
|
||||||
|
while (failedPluginIterator.hasNext()) {
|
||||||
|
File file = failedPluginIterator.next();
|
||||||
|
failedPluginIterator.remove();
|
||||||
|
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.toArray(new Plugin[result.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the plugin in the specified file
|
||||||
|
* <p>
|
||||||
|
* File must be valid according to the current enabled Plugin interfaces
|
||||||
|
*
|
||||||
|
* @param file File containing the plugin to load
|
||||||
|
* @return The Plugin loaded, or null if it was invalid
|
||||||
|
* @throws InvalidPluginException Thrown when the specified file is not a
|
||||||
|
* valid plugin
|
||||||
|
* @throws UnknownDependencyException If a required dependency could not
|
||||||
|
* be found
|
||||||
|
*/
|
||||||
|
public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException {
|
||||||
|
Validate.notNull(file, "File cannot be null");
|
||||||
|
|
||||||
|
checkUpdate(file);
|
||||||
|
|
||||||
|
Set<Pattern> filters = fileAssociations.keySet();
|
||||||
|
Plugin result = null;
|
||||||
|
|
||||||
|
for (Pattern filter : filters) {
|
||||||
|
String name = file.getName();
|
||||||
|
Matcher match = filter.matcher(name);
|
||||||
|
|
||||||
|
if (match.find()) {
|
||||||
|
PluginLoader loader = fileAssociations.get(filter);
|
||||||
|
|
||||||
|
result = loader.loadPlugin(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result != null) {
|
||||||
|
plugins.add(result);
|
||||||
|
lookupNames.put(result.getDescription().getName().toLowerCase(java.util.Locale.ENGLISH), result); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkUpdate(File file) {
|
||||||
|
if (updateDirectory == null || !updateDirectory.isDirectory()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File updateFile = new File(updateDirectory, file.getName());
|
||||||
|
if (updateFile.isFile() && FileUtil.copy(updateFile, file)) {
|
||||||
|
updateFile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given plugin is loaded and returns it when applicable
|
||||||
|
* <p>
|
||||||
|
* Please note that the name of the plugin is case-sensitive
|
||||||
|
*
|
||||||
|
* @param name Name of the plugin to check
|
||||||
|
* @return Plugin if it exists, otherwise null
|
||||||
|
*/
|
||||||
|
public synchronized Plugin getPlugin(String name) {
|
||||||
|
return lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Plugin[] getPlugins() {
|
||||||
|
return plugins.toArray(new Plugin[plugins.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given plugin is enabled or not
|
||||||
|
* <p>
|
||||||
|
* Please note that the name of the plugin is case-sensitive.
|
||||||
|
*
|
||||||
|
* @param name Name of the plugin to check
|
||||||
|
* @return true if the plugin is enabled, otherwise false
|
||||||
|
*/
|
||||||
|
public boolean isPluginEnabled(String name) {
|
||||||
|
Plugin plugin = getPlugin(name);
|
||||||
|
|
||||||
|
return isPluginEnabled(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given plugin is enabled or not
|
||||||
|
*
|
||||||
|
* @param plugin Plugin to check
|
||||||
|
* @return true if the plugin is enabled, otherwise false
|
||||||
|
*/
|
||||||
|
public synchronized boolean isPluginEnabled(Plugin plugin) { // Paper - synchronize
|
||||||
|
if ((plugin != null) && (plugins.contains(plugin))) {
|
||||||
|
return plugin.isEnabled();
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void enablePlugin(final Plugin plugin) { // Paper - synchronize
|
||||||
|
if (!plugin.isEnabled()) {
|
||||||
|
List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
|
||||||
|
|
||||||
|
if (!pluginCommands.isEmpty()) {
|
||||||
|
commandMap.registerAll(plugin.getDescription().getName(), pluginCommands);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
plugin.getPluginLoader().enablePlugin(plugin);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while enabling "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
HandlerList.bakeAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start - close Classloader on disable
|
||||||
|
public void disablePlugins() {
|
||||||
|
disablePlugins(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disablePlugins(boolean closeClassloaders) {
|
||||||
|
// Paper end - close Classloader on disable
|
||||||
|
Plugin[] plugins = getPlugins();
|
||||||
|
for (int i = plugins.length - 1; i >= 0; i--) {
|
||||||
|
disablePlugin(plugins[i], closeClassloaders); // Paper - close Classloader on disable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start - close Classloader on disable
|
||||||
|
public void disablePlugin(Plugin plugin) {
|
||||||
|
disablePlugin(plugin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void disablePlugin(final Plugin plugin, boolean closeClassloader) { // Paper - synchronize
|
||||||
|
// Paper end - close Classloader on disable
|
||||||
|
if (plugin.isEnabled()) {
|
||||||
|
try {
|
||||||
|
plugin.getPluginLoader().disablePlugin(plugin, closeClassloader); // Paper - close Classloader on disable
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while disabling "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.getScheduler().cancelTasks(plugin);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.getServicesManager().unregisterAll(plugin);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
HandlerList.unregisterAll(plugin);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.getMessenger().unregisterIncomingPluginChannel(plugin);
|
||||||
|
server.getMessenger().unregisterOutgoingPluginChannel(plugin);
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
|
||||||
|
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start
|
||||||
|
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
|
||||||
|
server.getLogger().log(Level.SEVERE, msg, ex);
|
||||||
|
callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
public void clearPlugins() {
|
||||||
|
synchronized (this) {
|
||||||
|
disablePlugins(true); // Paper - close Classloader on disable
|
||||||
|
plugins.clear();
|
||||||
|
lookupNames.clear();
|
||||||
|
HandlerList.unregisterAll();
|
||||||
|
fileAssociations.clear();
|
||||||
|
permissions.clear();
|
||||||
|
defaultPerms.get(true).clear();
|
||||||
|
defaultPerms.get(false).clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls an event with the given details.
|
||||||
|
* <p>
|
||||||
|
* This method only synchronizes when the event is not asynchronous.
|
||||||
|
*
|
||||||
|
* @param event Event details
|
||||||
|
*/
|
||||||
|
public void callEvent(Event event) {
|
||||||
|
if (event.isAsynchronous()) {
|
||||||
|
if (Thread.holdsLock(this)) {
|
||||||
|
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
|
||||||
|
}
|
||||||
|
if (server.isPrimaryThread()) {
|
||||||
|
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
|
||||||
|
}
|
||||||
|
fireEvent(event);
|
||||||
|
} else {
|
||||||
|
synchronized (this) {
|
||||||
|
fireEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fireEvent(Event event) {
|
||||||
|
HandlerList handlers = event.getHandlers();
|
||||||
|
RegisteredListener[] listeners = handlers.getRegisteredListeners();
|
||||||
|
|
||||||
|
for (RegisteredListener registration : listeners) {
|
||||||
|
if (!registration.getPlugin().isEnabled()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
registration.callEvent(event);
|
||||||
|
} catch (AuthorNagException ex) {
|
||||||
|
Plugin plugin = registration.getPlugin();
|
||||||
|
|
||||||
|
if (plugin.isNaggable()) {
|
||||||
|
plugin.setNaggable(false);
|
||||||
|
|
||||||
|
server.getLogger().log(Level.SEVERE, String.format(
|
||||||
|
"Nag author(s): '%s' of '%s' about the following: %s",
|
||||||
|
plugin.getDescription().getAuthors(),
|
||||||
|
plugin.getDescription().getFullName(),
|
||||||
|
ex.getMessage()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} catch (Throwable ex) {
|
||||||
|
// Paper start - error reporting
|
||||||
|
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
|
||||||
|
server.getLogger().log(Level.SEVERE, msg, ex);
|
||||||
|
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
|
||||||
|
callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerEvents(Listener listener, Plugin plugin) {
|
||||||
|
if (!plugin.isEnabled()) {
|
||||||
|
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) {
|
||||||
|
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin) {
|
||||||
|
registerEvent(event, listener, priority, executor, plugin, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the given event to the specified listener using a directly
|
||||||
|
* passed EventExecutor
|
||||||
|
*
|
||||||
|
* @param event Event class to register
|
||||||
|
* @param listener PlayerListener to register
|
||||||
|
* @param priority Priority of this event
|
||||||
|
* @param executor EventExecutor to register
|
||||||
|
* @param plugin Plugin to register
|
||||||
|
* @param ignoreCancelled Do not call executor if event was already
|
||||||
|
* cancelled
|
||||||
|
*/
|
||||||
|
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled) {
|
||||||
|
Validate.notNull(listener, "Listener cannot be null");
|
||||||
|
Validate.notNull(priority, "Priority cannot be null");
|
||||||
|
Validate.notNull(executor, "Executor cannot be null");
|
||||||
|
Validate.notNull(plugin, "Plugin cannot be null");
|
||||||
|
|
||||||
|
if (!plugin.isEnabled()) {
|
||||||
|
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Spigot
|
||||||
|
if (false) { // Spigot - RL handles useTimings check now
|
||||||
|
getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
|
||||||
|
} else {
|
||||||
|
getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HandlerList getEventListeners(Class<? extends Event> type) {
|
||||||
|
try {
|
||||||
|
Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList");
|
||||||
|
method.setAccessible(true);
|
||||||
|
return (HandlerList) method.invoke(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalPluginAccessException(e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<? extends Event> getRegistrationClass(Class<? extends Event> clazz) {
|
||||||
|
try {
|
||||||
|
clazz.getDeclaredMethod("getHandlerList");
|
||||||
|
return clazz;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
if (clazz.getSuperclass() != null
|
||||||
|
&& !clazz.getSuperclass().equals(Event.class)
|
||||||
|
&& Event.class.isAssignableFrom(clazz.getSuperclass())) {
|
||||||
|
return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
|
||||||
|
} else {
|
||||||
|
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Permission getPermission(String name) {
|
||||||
|
return permissions.get(name.toLowerCase(java.util.Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPermission(Permission perm) {
|
||||||
|
addPermission(perm, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void addPermission(Permission perm, boolean dirty) {
|
||||||
|
String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
|
||||||
|
|
||||||
|
if (permissions.containsKey(name)) {
|
||||||
|
throw new IllegalArgumentException("The permission " + name + " is already defined!");
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions.put(name, perm);
|
||||||
|
calculatePermissionDefault(perm, dirty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Permission> getDefaultPermissions(boolean op) {
|
||||||
|
return ImmutableSet.copyOf(defaultPerms.get(op));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePermission(Permission perm) {
|
||||||
|
removePermission(perm.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePermission(String name) {
|
||||||
|
permissions.remove(name.toLowerCase(java.util.Locale.ENGLISH));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recalculatePermissionDefaults(Permission perm) {
|
||||||
|
if (perm != null && permissions.containsKey(perm.getName().toLowerCase(java.util.Locale.ENGLISH))) {
|
||||||
|
defaultPerms.get(true).remove(perm);
|
||||||
|
defaultPerms.get(false).remove(perm);
|
||||||
|
|
||||||
|
calculatePermissionDefault(perm, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculatePermissionDefault(Permission perm, boolean dirty) {
|
||||||
|
if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
|
||||||
|
defaultPerms.get(true).add(perm);
|
||||||
|
if (dirty) {
|
||||||
|
dirtyPermissibles(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
|
||||||
|
defaultPerms.get(false).add(perm);
|
||||||
|
if (dirty) {
|
||||||
|
dirtyPermissibles(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void dirtyPermissibles() {
|
||||||
|
dirtyPermissibles(true);
|
||||||
|
dirtyPermissibles(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dirtyPermissibles(boolean op) {
|
||||||
|
Set<Permissible> permissibles = getDefaultPermSubscriptions(op);
|
||||||
|
|
||||||
|
for (Permissible p : permissibles) {
|
||||||
|
p.recalculatePermissions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subscribeToPermission(String permission, Permissible permissible) {
|
||||||
|
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||||
|
Map<Permissible, Boolean> map = permSubs.get(name);
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
map = new WeakHashMap<Permissible, Boolean>();
|
||||||
|
permSubs.put(name, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.put(permissible, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsubscribeFromPermission(String permission, Permissible permissible) {
|
||||||
|
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||||
|
Map<Permissible, Boolean> map = permSubs.get(name);
|
||||||
|
|
||||||
|
if (map != null) {
|
||||||
|
map.remove(permissible);
|
||||||
|
|
||||||
|
if (map.isEmpty()) {
|
||||||
|
permSubs.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Permissible> getPermissionSubscriptions(String permission) {
|
||||||
|
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
|
||||||
|
Map<Permissible, Boolean> map = permSubs.get(name);
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
return ImmutableSet.of();
|
||||||
|
} else {
|
||||||
|
return ImmutableSet.copyOf(map.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void subscribeToDefaultPerms(boolean op, Permissible permissible) {
|
||||||
|
Map<Permissible, Boolean> map = defSubs.get(op);
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
map = new WeakHashMap<Permissible, Boolean>();
|
||||||
|
defSubs.put(op, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.put(permissible, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible) {
|
||||||
|
Map<Permissible, Boolean> map = defSubs.get(op);
|
||||||
|
|
||||||
|
if (map != null) {
|
||||||
|
map.remove(permissible);
|
||||||
|
|
||||||
|
if (map.isEmpty()) {
|
||||||
|
defSubs.remove(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Permissible> getDefaultPermSubscriptions(boolean op) {
|
||||||
|
Map<Permissible, Boolean> map = defSubs.get(op);
|
||||||
|
|
||||||
|
if (map == null) {
|
||||||
|
return ImmutableSet.of();
|
||||||
|
} else {
|
||||||
|
return ImmutableSet.copyOf(map.keySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Permission> getPermissions() {
|
||||||
|
return new HashSet<Permission>(permissions.values());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean useTimings() {
|
||||||
|
return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not per event timing code should be used
|
||||||
|
*
|
||||||
|
* @param use True if per event timing code should be used
|
||||||
|
*/
|
||||||
|
public void useTimings(boolean use) {
|
||||||
|
co.aikar.timings.Timings.setTimingsEnabled(use); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start
|
||||||
|
public void clearPermissions() {
|
||||||
|
permissions.clear();
|
||||||
|
defaultPerms.get(true).clear();
|
||||||
|
defaultPerms.get(false).clear();
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
}
|
||||||
405
sources/src/main/java/org/spigotmc/SpigotConfig.java
Normal file
405
sources/src/main/java/org/spigotmc/SpigotConfig.java
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
package org.spigotmc;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import gnu.trove.map.hash.TObjectIntHashMap;
|
||||||
|
import net.minecraft.server.AttributeRanged;
|
||||||
|
import net.minecraft.server.GenericAttributes;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.StatisticList;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.core.LoggerContext;
|
||||||
|
import org.apache.logging.log4j.core.config.Configuration;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.Command;
|
||||||
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
|
||||||
|
public class SpigotConfig
|
||||||
|
{
|
||||||
|
|
||||||
|
private static File CONFIG_FILE;
|
||||||
|
private static final String HEADER = "This is the main configuration file for Spigot.\n"
|
||||||
|
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
|
||||||
|
+ "with caution, and make sure you know what each option does before configuring.\n"
|
||||||
|
+ "For a reference for any variable inside this file, check out the Spigot wiki at\n"
|
||||||
|
+ "http://www.spigotmc.org/wiki/spigot-configuration/\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "If you need help with the configuration or have any questions related to Spigot,\n"
|
||||||
|
+ "join us at the IRC or drop by our forums and leave a post.\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )\n"
|
||||||
|
+ "Forums: http://www.spigotmc.org/\n";
|
||||||
|
/*========================================================================*/
|
||||||
|
public static YamlConfiguration config;
|
||||||
|
static int version;
|
||||||
|
static Map<String, Command> commands;
|
||||||
|
/*========================================================================*/
|
||||||
|
private static Metrics metrics;
|
||||||
|
|
||||||
|
public static void init(File configFile)
|
||||||
|
{
|
||||||
|
CONFIG_FILE = configFile;
|
||||||
|
config = new YamlConfiguration();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config.load( CONFIG_FILE );
|
||||||
|
} catch ( IOException ex )
|
||||||
|
{
|
||||||
|
} catch ( InvalidConfigurationException ex )
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().log( Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex );
|
||||||
|
throw Throwables.propagate( ex );
|
||||||
|
}
|
||||||
|
|
||||||
|
config.options().header( HEADER );
|
||||||
|
config.options().copyDefaults( true );
|
||||||
|
|
||||||
|
commands = new HashMap<String, Command>();
|
||||||
|
commands.put( "spigot", new SpigotCommand( "spigot" ) );
|
||||||
|
|
||||||
|
version = getInt( "config-version", 11 );
|
||||||
|
set( "config-version", 11 );
|
||||||
|
readConfig( SpigotConfig.class, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerCommands()
|
||||||
|
{
|
||||||
|
for ( Map.Entry<String, Command> entry : commands.entrySet() )
|
||||||
|
{
|
||||||
|
MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", entry.getValue() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* // Paper - Replace with our own
|
||||||
|
if ( metrics == null )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
metrics = new Metrics();
|
||||||
|
metrics.start();
|
||||||
|
} catch ( IOException ex )
|
||||||
|
{
|
||||||
|
Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/ // Paper end
|
||||||
|
}
|
||||||
|
|
||||||
|
static void readConfig(Class<?> clazz, Object instance)
|
||||||
|
{
|
||||||
|
for ( Method method : clazz.getDeclaredMethods() )
|
||||||
|
{
|
||||||
|
if ( Modifier.isPrivate( method.getModifiers() ) )
|
||||||
|
{
|
||||||
|
if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE )
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
method.setAccessible( true );
|
||||||
|
method.invoke( instance );
|
||||||
|
} catch ( InvocationTargetException ex )
|
||||||
|
{
|
||||||
|
throw Throwables.propagate( ex.getCause() );
|
||||||
|
} catch ( Exception ex )
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().log( Level.SEVERE, "Error invoking " + method, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config.save( CONFIG_FILE );
|
||||||
|
} catch ( IOException ex )
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().log( Level.SEVERE, "Could not save " + CONFIG_FILE, ex );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void set(String path, Object val)
|
||||||
|
{
|
||||||
|
config.set( path, val );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean getBoolean(String path, boolean def)
|
||||||
|
{
|
||||||
|
config.addDefault( path, def );
|
||||||
|
return config.getBoolean( path, config.getBoolean( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getInt(String path, int def)
|
||||||
|
{
|
||||||
|
config.addDefault( path, def );
|
||||||
|
return config.getInt( path, config.getInt( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List getList(String path, T def)
|
||||||
|
{
|
||||||
|
config.addDefault( path, def );
|
||||||
|
return (List<T>) config.getList( path, config.getList( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getString(String path, String def)
|
||||||
|
{
|
||||||
|
config.addDefault( path, def );
|
||||||
|
return config.getString( path, config.getString( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double getDouble(String path, double def)
|
||||||
|
{
|
||||||
|
config.addDefault( path, def );
|
||||||
|
return config.getDouble( path, config.getDouble( path ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean logCommands;
|
||||||
|
private static void logCommands()
|
||||||
|
{
|
||||||
|
logCommands = getBoolean( "commands.log", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int tabComplete;
|
||||||
|
private static void tabComplete()
|
||||||
|
{
|
||||||
|
if ( version < 6 )
|
||||||
|
{
|
||||||
|
boolean oldValue = getBoolean( "commands.tab-complete", true );
|
||||||
|
if ( oldValue )
|
||||||
|
{
|
||||||
|
set( "commands.tab-complete", 0 );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
set( "commands.tab-complete", -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tabComplete = getInt( "commands.tab-complete", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String whitelistMessage;
|
||||||
|
public static String unknownCommandMessage;
|
||||||
|
public static String serverFullMessage;
|
||||||
|
public static String outdatedClientMessage = "Outdated client! Please use {0}";
|
||||||
|
public static String outdatedServerMessage = "Outdated server! I\'m still on {0}";
|
||||||
|
private static String transform(String s)
|
||||||
|
{
|
||||||
|
return ChatColor.translateAlternateColorCodes( '&', s ).replaceAll( "\\\\n", "\n" );
|
||||||
|
}
|
||||||
|
private static void messages()
|
||||||
|
{
|
||||||
|
if (version < 8)
|
||||||
|
{
|
||||||
|
set( "messages.outdated-client", outdatedClientMessage );
|
||||||
|
set( "messages.outdated-server", outdatedServerMessage );
|
||||||
|
}
|
||||||
|
|
||||||
|
whitelistMessage = transform( getString( "messages.whitelist", "You are not whitelisted on this server!" ) );
|
||||||
|
unknownCommandMessage = transform( getString( "messages.unknown-command", "Unknown command. Type \"/help\" for help." ) );
|
||||||
|
serverFullMessage = transform( getString( "messages.server-full", "The server is full!" ) );
|
||||||
|
outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) );
|
||||||
|
outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int timeoutTime = 60;
|
||||||
|
public static boolean restartOnCrash = true;
|
||||||
|
public static String restartScript = "./start.sh";
|
||||||
|
public static String restartMessage;
|
||||||
|
private static void watchdog()
|
||||||
|
{
|
||||||
|
timeoutTime = getInt( "settings.timeout-time", timeoutTime );
|
||||||
|
restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
|
||||||
|
restartScript = getString( "settings.restart-script", restartScript );
|
||||||
|
restartMessage = transform( getString( "messages.restart", "Server is restarting" ) );
|
||||||
|
commands.put( "restart", new RestartCommand( "restart" ) );
|
||||||
|
//WatchdogThread.doStart( timeoutTime, restartOnCrash ); // Paper - moved to PaperConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean bungee;
|
||||||
|
private static void bungee() {
|
||||||
|
if ( version < 4 )
|
||||||
|
{
|
||||||
|
set( "settings.bungeecord", false );
|
||||||
|
System.out.println( "Oudated config, disabling BungeeCord support!" );
|
||||||
|
}
|
||||||
|
bungee = getBoolean( "settings.bungeecord", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void nettyThreads()
|
||||||
|
{
|
||||||
|
int count = getInt( "settings.netty-threads", 4 );
|
||||||
|
System.setProperty( "io.netty.eventLoopThreads", Integer.toString( count ) );
|
||||||
|
Bukkit.getLogger().log( Level.INFO, "Using {0} threads for Netty based IO", count );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean lateBind;
|
||||||
|
private static void lateBind() {
|
||||||
|
lateBind = getBoolean( "settings.late-bind", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean disableStatSaving;
|
||||||
|
public static Object2IntOpenHashMap<String> forcedStats = new Object2IntOpenHashMap<String>();
|
||||||
|
private static void stats()
|
||||||
|
{
|
||||||
|
disableStatSaving = getBoolean( "stats.disable-saving", false );
|
||||||
|
|
||||||
|
if ( !config.contains( "stats.forced-stats" ) ) {
|
||||||
|
config.createSection( "stats.forced-stats" );
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigurationSection section = config.getConfigurationSection( "stats.forced-stats" );
|
||||||
|
for ( String name : section.getKeys( true ) )
|
||||||
|
{
|
||||||
|
if ( section.isInt( name ) )
|
||||||
|
{
|
||||||
|
if ( StatisticList.getStatistic(name) == null )
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().log(Level.WARNING, "Ignoring non existent stats.forced-stats " + name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
forcedStats.put( name, section.getInt( name ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void tpsCommand()
|
||||||
|
{
|
||||||
|
commands.put( "tps", new TicksPerSecondCommand( "tps" ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int playerSample;
|
||||||
|
private static void playerSample()
|
||||||
|
{
|
||||||
|
playerSample = Math.max(getInt( "settings.sample-count", 12 ), 0); // Paper - Avoid negative counts
|
||||||
|
Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int playerShuffle;
|
||||||
|
private static void playerShuffle()
|
||||||
|
{
|
||||||
|
playerShuffle = getInt( "settings.player-shuffle", 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> spamExclusions;
|
||||||
|
private static void spamExclusions()
|
||||||
|
{
|
||||||
|
spamExclusions = getList( "commands.spam-exclusions", Arrays.asList( new String[]
|
||||||
|
{
|
||||||
|
"/skill"
|
||||||
|
} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean silentCommandBlocks;
|
||||||
|
private static void silentCommandBlocks()
|
||||||
|
{
|
||||||
|
silentCommandBlocks = getBoolean( "commands.silent-commandblock-console", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean filterCreativeItems;
|
||||||
|
private static void filterCreativeItems()
|
||||||
|
{
|
||||||
|
filterCreativeItems = getBoolean( "settings.filter-creative-items", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> replaceCommands;
|
||||||
|
private static void replaceCommands()
|
||||||
|
{
|
||||||
|
if ( config.contains( "replace-commands" ) )
|
||||||
|
{
|
||||||
|
set( "commands.replace-commands", config.getStringList( "replace-commands" ) );
|
||||||
|
config.set( "replace-commands", null );
|
||||||
|
}
|
||||||
|
replaceCommands = new HashSet<String>( (List<String>) getList( "commands.replace-commands",
|
||||||
|
Arrays.asList( "setblock", "summon", "testforblock", "tellraw" ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int userCacheCap;
|
||||||
|
private static void userCacheCap()
|
||||||
|
{
|
||||||
|
userCacheCap = getInt( "settings.user-cache-size", 1000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean saveUserCacheOnStopOnly;
|
||||||
|
private static void saveUserCacheOnStopOnly()
|
||||||
|
{
|
||||||
|
saveUserCacheOnStopOnly = getBoolean( "settings.save-user-cache-on-stop-only", false );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int intCacheLimit;
|
||||||
|
private static void intCacheLimit()
|
||||||
|
{
|
||||||
|
intCacheLimit = getInt( "settings.int-cache-limit", 1024 );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double movedWronglyThreshold;
|
||||||
|
private static void movedWronglyThreshold()
|
||||||
|
{
|
||||||
|
movedWronglyThreshold = getDouble( "settings.moved-wrongly-threshold", 0.0625D );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double movedTooQuicklyMultiplier;
|
||||||
|
private static void movedTooQuicklyMultiplier()
|
||||||
|
{
|
||||||
|
movedTooQuicklyMultiplier = getDouble( "settings.moved-too-quickly-multiplier", 10.0D );
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double maxHealth = 2048;
|
||||||
|
public static double movementSpeed = 2048;
|
||||||
|
public static double attackDamage = 2048;
|
||||||
|
private static void attributeMaxes()
|
||||||
|
{
|
||||||
|
maxHealth = getDouble( "settings.attribute.maxHealth.max", maxHealth );
|
||||||
|
( (AttributeRanged) GenericAttributes.maxHealth ).b = maxHealth;
|
||||||
|
movementSpeed = getDouble( "settings.attribute.movementSpeed.max", movementSpeed );
|
||||||
|
( (AttributeRanged) GenericAttributes.MOVEMENT_SPEED ).b = movementSpeed;
|
||||||
|
attackDamage = getDouble( "settings.attribute.attackDamage.max", attackDamage );
|
||||||
|
( (AttributeRanged) GenericAttributes.ATTACK_DAMAGE ).b = attackDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean debug;
|
||||||
|
private static void debug()
|
||||||
|
{
|
||||||
|
debug = getBoolean( "settings.debug", false );
|
||||||
|
|
||||||
|
if ( debug && !LogManager.getRootLogger().isTraceEnabled() )
|
||||||
|
{
|
||||||
|
// Enable debug logging
|
||||||
|
LoggerContext ctx = (LoggerContext) LogManager.getContext( false );
|
||||||
|
Configuration conf = ctx.getConfiguration();
|
||||||
|
conf.getLoggerConfig( LogManager.ROOT_LOGGER_NAME ).setLevel( org.apache.logging.log4j.Level.ALL );
|
||||||
|
ctx.updateLoggers( conf );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( LogManager.getRootLogger().isTraceEnabled() )
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().info( "Debug logging is enabled" );
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
Bukkit.getLogger().info( "Debug logging is disabled" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int itemDirtyTicks;
|
||||||
|
private static void itemDirtyTicks() {
|
||||||
|
itemDirtyTicks = getInt("settings.item-dirty-ticks", 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean disableAdvancementSaving;
|
||||||
|
public static List<String> disabledAdvancements;
|
||||||
|
private static void disabledAdvancements() {
|
||||||
|
disableAdvancementSaving = getBoolean("advancements.disable-saving", false);
|
||||||
|
disabledAdvancements = getList("advancements.disabled", Arrays.asList(new String[]{"minecraft:story/disabled"}));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,13 +19,12 @@
|
|||||||
"core.MixinCommandKick",
|
"core.MixinCommandKick",
|
||||||
"core.MixinCraftServer",
|
"core.MixinCraftServer",
|
||||||
"core.MixinWorldServer",
|
"core.MixinWorldServer",
|
||||||
|
"core.MixinFileIOThread",
|
||||||
"core.MixinWorldManager",
|
"core.MixinWorldManager",
|
||||||
"core.MixinCommandBanIp",
|
"core.MixinCommandBanIp",
|
||||||
"core.MixinChunkSection",
|
"core.MixinChunkSection",
|
||||||
"core.MixinAsyncCatcher",
|
"core.MixinAsyncCatcher",
|
||||||
"core.MixinTimingHandler",
|
|
||||||
"core.MixinVersionCommand",
|
"core.MixinVersionCommand",
|
||||||
"core.MixinItemMonsterEgg",
|
|
||||||
"core.MixinMinecraftServer",
|
"core.MixinMinecraftServer",
|
||||||
"core.MixinChunkIOExecutor",
|
"core.MixinChunkIOExecutor",
|
||||||
"core.MixinPlayerConnectionUtils",
|
"core.MixinPlayerConnectionUtils",
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"required": true,
|
|
||||||
"minVersion": "0.7.10",
|
|
||||||
"package": "io.akarin.server.mixin",
|
|
||||||
"target": "@env(DEFAULT)",
|
|
||||||
"compatibilityLevel": "JAVA_8",
|
|
||||||
"server": [
|
|
||||||
"cps.MixinChunk",
|
|
||||||
|
|
||||||
"lighting.MixinChunk",
|
|
||||||
"lighting.MixinWorld",
|
|
||||||
"lighting.MixinWorldServer",
|
|
||||||
"lighting.MixinChunkProviderServer",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Submodule work/Paper updated: 8b96ee7ea8...e39995d213
Reference in New Issue
Block a user