Compare commits
208 Commits
1.12.2-R0.
...
parallel-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10d2d285d9 | ||
|
|
818f5559f7 | ||
|
|
bf7f6fe3bc | ||
|
|
7acc339704 | ||
|
|
f0d17e54e9 | ||
|
|
01c40ed0d3 | ||
|
|
4fdc9f0166 | ||
|
|
3b4926bed2 | ||
|
|
7e0f44f0af | ||
|
|
b41e4fd8c3 | ||
|
|
241a8ffefa | ||
|
|
c2748ea2df | ||
|
|
e563233ff3 | ||
|
|
9a5de6cf31 | ||
|
|
9f7b490f61 | ||
|
|
6dceb465c3 | ||
|
|
17ba164cbc | ||
|
|
bb19cf0f7a | ||
|
|
0a59c8ce5f | ||
|
|
5d5bb381c0 | ||
|
|
05cc09ff51 | ||
|
|
2ba4bc2755 | ||
|
|
9b5b40c002 | ||
|
|
a0545a756d | ||
|
|
6e62515d11 | ||
|
|
ff100c348e | ||
|
|
a25ff5dd93 | ||
|
|
c6dbae3c24 | ||
|
|
b2bafb826f | ||
|
|
fb20bb3113 | ||
|
|
927d946dba | ||
|
|
9ceec10f18 | ||
|
|
74353989e4 | ||
|
|
c2705d4722 | ||
|
|
b743d7dc4c | ||
|
|
601ec9e90d | ||
|
|
6dcd61f2c7 | ||
|
|
c42528f4f1 | ||
|
|
402d9d2536 | ||
|
|
6ba139a83e | ||
|
|
0690cb1c9f | ||
|
|
b8879db641 | ||
|
|
a3cc6062b6 | ||
|
|
dba9fad220 | ||
|
|
af7444df09 | ||
|
|
2def9e628a | ||
|
|
1641f2767f | ||
|
|
f2c15275e9 | ||
|
|
c58612f271 | ||
|
|
4d44c9f8cc | ||
|
|
3d34b1e011 | ||
|
|
032ffea438 | ||
|
|
2b6175edcb | ||
|
|
c06c7e2776 | ||
|
|
af678041f0 | ||
|
|
5c03cad4f2 | ||
|
|
6de709c4c6 | ||
|
|
bca8f20393 | ||
|
|
144c9e08f5 | ||
|
|
bd47cab2ff | ||
|
|
4c7da04d9f | ||
|
|
cf300713b4 | ||
|
|
4c93b2b13a | ||
|
|
78ddbbd7c7 | ||
|
|
ecb28a58a6 | ||
|
|
9e62b22fb8 | ||
|
|
2ace17105f | ||
|
|
528ab01026 | ||
|
|
87583d4e7c | ||
|
|
f77f039dbb | ||
|
|
3fbba5de34 | ||
|
|
79d3980be2 | ||
|
|
280db63b42 | ||
|
|
f93abbf076 | ||
|
|
03a5ef5149 | ||
|
|
58de950203 | ||
|
|
2887399202 | ||
|
|
cbfe35f062 | ||
|
|
1a330a4c07 | ||
|
|
c932a0d1b1 | ||
|
|
515e9d86b9 | ||
|
|
5f917f8253 | ||
|
|
cede9c99fa | ||
|
|
fd733df52a | ||
|
|
5f6671cd50 | ||
|
|
ff00b7556e | ||
|
|
3875266bb7 | ||
|
|
ca8e0d7c0f | ||
|
|
269e1c30f1 | ||
|
|
79d0207801 | ||
|
|
a2c09040b4 | ||
|
|
c50f6f2a68 | ||
|
|
9cfe108024 | ||
|
|
545ce61711 | ||
|
|
e881bb1c23 | ||
|
|
c926121dd6 | ||
|
|
b859bf6b4d | ||
|
|
5113e99853 | ||
|
|
68b4db992f | ||
|
|
2765bb8891 | ||
|
|
067b86b7cc | ||
|
|
08013d2b4a | ||
|
|
a6a51f9805 | ||
|
|
d223e4ad00 | ||
|
|
44d1491789 | ||
|
|
e82a99d4d8 | ||
|
|
5e912befc0 | ||
|
|
45d6b0c072 | ||
|
|
d596840e83 | ||
|
|
9e06a9fe5c | ||
|
|
452dd1f3b5 | ||
|
|
8dbfbd2650 | ||
|
|
fc17d8fdc7 | ||
|
|
bbd1057666 | ||
|
|
671c5fbc49 | ||
|
|
405a299b36 | ||
|
|
8b6de7576a | ||
|
|
f6f2d1121c | ||
|
|
8a6c36ab2c | ||
|
|
4c622207e1 | ||
|
|
8db031c856 | ||
|
|
7c6627edc8 | ||
|
|
343666756c | ||
|
|
99b9880f0e | ||
|
|
022a6468d5 | ||
|
|
9d5d638670 | ||
|
|
4e920243d1 | ||
|
|
239f83e1d3 | ||
|
|
93588656d1 | ||
|
|
8a9ad35a57 | ||
|
|
e4ed177d7b | ||
|
|
fb7bf16869 | ||
|
|
1e93929534 | ||
|
|
30f6e3f56f | ||
|
|
c55568a01c | ||
|
|
04ff47a598 | ||
|
|
fdec006644 | ||
|
|
3eb2067ffa | ||
|
|
b89d955eb5 | ||
|
|
54205afb71 | ||
|
|
711fc08eeb | ||
|
|
e8dfb64f42 | ||
|
|
a78ae42d9c | ||
|
|
f7792c2510 | ||
|
|
9fedd2d94e | ||
|
|
cf62349837 | ||
|
|
1d9f3a0584 | ||
|
|
8925ac73d4 | ||
|
|
5c39b7eabd | ||
|
|
d8f0674b89 | ||
|
|
cda8c79514 | ||
|
|
080b080c15 | ||
|
|
19e1d0d927 | ||
|
|
974cf681d0 | ||
|
|
3e38351ce7 | ||
|
|
dc30e8c8d5 | ||
|
|
427a4152f3 | ||
|
|
68816a0b5a | ||
|
|
f6e54678bb | ||
|
|
aed67213c2 | ||
|
|
9038d2d2b6 | ||
|
|
eb96ab4f5b | ||
|
|
89ca2f2bc5 | ||
|
|
97798e18ce | ||
|
|
db54c87abd | ||
|
|
f45908ae38 | ||
|
|
2b49aba77c | ||
|
|
bc2a639c52 | ||
|
|
3938713380 | ||
|
|
dc10b10979 | ||
|
|
876bf4d7cd | ||
|
|
3ee0111f24 | ||
|
|
42a90440b9 | ||
|
|
b58e98e0de | ||
|
|
dc9c0655de | ||
|
|
c3e9767dbc | ||
|
|
01ecfef964 | ||
|
|
8a7ebe839c | ||
|
|
c11a88d1ba | ||
|
|
9626867217 | ||
|
|
55248e4deb | ||
|
|
095a6e4d0b | ||
|
|
559896e14b | ||
|
|
6855fbe294 | ||
|
|
41d97284be | ||
|
|
b4f557ccb1 | ||
|
|
5f936eb1b7 | ||
|
|
f46e63958b | ||
|
|
68833e6726 | ||
|
|
3a1e04cfbb | ||
|
|
0f00949e6f | ||
|
|
24d0c6a541 | ||
|
|
474d9ecb6a | ||
|
|
2282ca02b7 | ||
|
|
ab9aaa6195 | ||
|
|
f89f26e2db | ||
|
|
7bdc57cb70 | ||
|
|
b4da072470 | ||
|
|
c43b8592c9 | ||
|
|
a17a194c6a | ||
|
|
7856043ed6 | ||
|
|
69e6cc1fea | ||
|
|
8a3ddb716e | ||
|
|
ccb3bb9865 | ||
|
|
f0df6a62c4 | ||
|
|
16f172a4d1 | ||
|
|
1bd0498f86 | ||
|
|
5a90c8d8fd |
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
|
||||||
@@ -4,16 +4,17 @@ As such, Akarin is licensed under the
|
|||||||
[GNU General Public License version 3](licenses/GPL.md); as it inherits it from Paper,
|
[GNU General Public License version 3](licenses/GPL.md); as it inherits it from Paper,
|
||||||
who in turn inherits it from the original Spigot projects.
|
who in turn inherits it from the original Spigot projects.
|
||||||
|
|
||||||
Any author who is _not_ listed below should be presumed to have released their work
|
Any author who is _not_ listed below should be presumed to have their work released
|
||||||
under the original [GPL](licenses/GPL.md) license.
|
under the original [GPL](licenses/GPL.md) license.
|
||||||
|
|
||||||
In the interest of promoting a better Minecraft platform for everyone, contributors
|
In the interest of promoting a better Minecraft platform for everyone, contributors
|
||||||
may choose to release their code under the more permissive [MIT License](licenses/MIT.md).
|
may choose to release their code under the more permissive [MIT License](licenses/MIT.md).
|
||||||
|
|
||||||
The authors listed below have chosen to release their code under that more permissive
|
The authors listed below have chosen to release their code under the more permissive
|
||||||
[MIT License](licenses/MIT.md). Any contributor who wants their name added below
|
[MIT License](licenses/MIT.md). Any contributor who wants their name added below
|
||||||
should submit a pull request to this project to add their name.
|
should submit a Pull Request to this project and add their name.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
Sotr <kira@kira.moe>
|
Sotr <kira@kira.moe>
|
||||||
```
|
MatrixTunnel <https://github.com/MatrixTunnel>
|
||||||
|
```
|
||||||
|
|||||||
53
README.md
53
README.md
@@ -1,29 +1,33 @@
|
|||||||
# <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
|
||||||
[](http://ci.ilummc.com/job/Akarin/)
|
|
||||||
[](https://bstats.org/plugin/bukkit/Torch)
|
|
||||||
[](https://akarin.io)
|
[](https://akarin.io)
|
||||||
|
[](https://discord.gg/fw2pJAj)
|
||||||
|
[](https://bstats.org/plugin/bukkit/Torch)
|
||||||
|
[](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.12.2)
|
||||||
|
|
||||||
|
Akarin is currently **under heavy development** and contributions are welcome!
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
---
|
---
|
||||||
> Akarin is a powerful server software form the 'new dimension', formerly known as [Torch](https://github.com/Akarin-project/Torch).
|
> Akarin is a powerful server software from the 'new dimension', formerly known as Torch.
|
||||||
|
|
||||||
As a [Paper](https://github.com/PaperMC/Paper) fork, it supports almost all plugins that [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse) can use.
|
As a [Paper](https://github.com/PaperMC/Paper) fork, it should support almost all plugins that work on [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse).
|
||||||
|
|
||||||
It has a few key goals:
|
Our project has a few key goals:
|
||||||
* **Open Access** - Make more game mechanism configurable.
|
|
||||||
* **Bedrock** - Safety and stable is important for a server.
|
|
||||||
* **Fast** - Simplify the logic and import the multi-thread compute.
|
|
||||||
|
|
||||||
Akarin is **under heavy development** yet, contribution is welcome and run a test before putting into production.
|
* **Open Access** - Make more game mechanics configurable.
|
||||||
|
* **Bedrock** - Make the server more safe and stable.
|
||||||
|
* **Fast** - Simplify the logic and implement multi-threaded computing.
|
||||||
|
|
||||||
|
*Issues and Pull Requests will be labeled accordingly*
|
||||||
|
|
||||||
Get Akarin
|
Get Akarin
|
||||||
---
|
---
|
||||||
### Download
|
### Download
|
||||||
#### Recommended Sites
|
#### Recommended
|
||||||
+ [ **Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build *Login required*
|
+ ~~[**Jenkins**](http://ci.ilummc.com/job/Akarin/)~~ - Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)
|
||||||
+ [ **Jenkins**](http://ci.ilummc.com/job/Akarin/) - *Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)*
|
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/ver/1.12.2) - Checkout the 'Artifacts' tab of the latest build
|
||||||
|
|
||||||
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your website here*
|
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
#### Requirements
|
#### Requirements
|
||||||
@@ -34,20 +38,23 @@ Get Akarin
|
|||||||
```sh
|
```sh
|
||||||
./scripts/inst.sh --setup --fast
|
./scripts/inst.sh --setup --fast
|
||||||
```
|
```
|
||||||
*For non-modification compile, add `--fast` option to skip the test is recommended.*
|
|
||||||
*Futhermore, if your machine have a insufficient memory, you may add `--remote` option to avoid decompile locally.*
|
|
||||||
|
|
||||||
Demonstration servers
|
**Notes**
|
||||||
|
* You must use `--setup` at least once to deploy necessary dependencies otherwise some imports cannot be organized.
|
||||||
|
* For non-modified projects, it is recommended to add the `--fast` option to skip any tests.
|
||||||
|
* If your machine has insufficient memory, you may want to add the `--remote` option to avoid decompiling locally.
|
||||||
|
|
||||||
|
Demo Servers
|
||||||
---
|
---
|
||||||
+ **demo.akarin.io**
|
* `demo.akarin.io` (official)
|
||||||
|
* `omc.hk` (auth required)
|
||||||
|
|
||||||
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your server here*
|
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
|
||||||
|
|
||||||
Contributing
|
Contributing
|
||||||
---
|
---
|
||||||
* Feel free to open an [Issue](https://github.com/Akarin-project/akarin/issues) if you have any problem with Akarin.
|
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
|
||||||
* [Pull Request](https://github.com/Akarin-project/akarin/pulls) is welcomed, Akarin use [Mixin](https://github.com/SpongePowered/Mixin) to modify the code, you can checkout `sources` folder to see them. Moreover, add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
|
* Add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
|
||||||
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send an email to `kira@kira.moe` with your experience and necessary information. Besides, welcome to join our [TIM Group](https://jq.qq.com/?_wv=1027&k=59q2kV4) to chat *(Chinese)*.
|
* 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.
|
||||||
* Note that you must `--setup` at least once to deploy necessary dependency otherwise some imports cannot be organized.
|
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
16
circle.yml
16
circle.yml
@@ -1,16 +0,0 @@
|
|||||||
machine:
|
|
||||||
java:
|
|
||||||
version: openjdk8
|
|
||||||
|
|
||||||
dependencies:
|
|
||||||
cache-directories:
|
|
||||||
- "/home/ubuntu/Akarin/work/Paper/work/Minecraft"
|
|
||||||
override:
|
|
||||||
- git config --global user.email "circle@circleci.com"
|
|
||||||
- git config --global user.name "CircleCI"
|
|
||||||
- chmod +x scripts/inst.sh
|
|
||||||
- ./scripts/inst.sh --setup --remote
|
|
||||||
|
|
||||||
test:
|
|
||||||
post:
|
|
||||||
- yes|cp -rf ./akarin-1.12.2.jar $CIRCLE_ARTIFACTS
|
|
||||||
@@ -19,21 +19,26 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd "$paperbasedir"
|
cd "$paperbasedir"
|
||||||
./paper patch
|
./paper jar
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[Akarin] Ready to build"
|
echo "[Akarin] Ready to build"
|
||||||
(
|
(
|
||||||
echo "[Akarin] Touch sources.."
|
echo "[Akarin] Touch sources.."
|
||||||
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
|
|
||||||
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
|
|
||||||
|
|
||||||
cd "$paperbasedir"
|
cd "$paperbasedir"
|
||||||
if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then
|
if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then
|
||||||
echo "[Akarin] Test has been skipped"
|
echo "[Akarin] Test has been skipped"
|
||||||
|
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
|
||||||
|
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
|
||||||
mvn clean install -DskipTests
|
mvn clean install -DskipTests
|
||||||
else
|
else
|
||||||
|
rm -rf Paper-API/src
|
||||||
|
rm -rf Paper-Server/src
|
||||||
|
./paper patch
|
||||||
|
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
|
||||||
|
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
|
||||||
mvn clean install
|
mvn clean install
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
0
scripts/inst.sh
Normal file → Executable file
0
scripts/inst.sh
Normal file → Executable file
@@ -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.1-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>
|
||||||
|
|
||||||
@@ -73,12 +73,12 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.minecrell</groupId>
|
<groupId>net.minecrell</groupId>
|
||||||
<artifactId>terminalconsoleappender</artifactId>
|
<artifactId>terminalconsoleappender</artifactId>
|
||||||
<version>1.0.0</version>
|
<version>1.1.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.java.dev.jna</groupId>
|
<groupId>net.java.dev.jna</groupId>
|
||||||
<artifactId>jna</artifactId>
|
<artifactId>jna</artifactId>
|
||||||
<version>4.4.0</version>
|
<version>4.5.2</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
@@ -107,6 +107,14 @@
|
|||||||
<artifactId>log4j-iostreams</artifactId>
|
<artifactId>log4j-iostreams</artifactId>
|
||||||
<version>2.8.1</version>
|
<version>2.8.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Paper - Async loggers -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.lmax</groupId>
|
||||||
|
<artifactId>disruptor</artifactId>
|
||||||
|
<version>3.4.2</version>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -126,28 +134,42 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.akarin</groupId>
|
<groupId>io.akarin</groupId>
|
||||||
<artifactId>legacylauncher</artifactId>
|
<artifactId>legacylauncher</artifactId>
|
||||||
<version>1.20</version>
|
<version>1.26</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.spongepowered</groupId>
|
<groupId>org.spongepowered</groupId>
|
||||||
<artifactId>mixin</artifactId>
|
<artifactId>mixin</artifactId>
|
||||||
<version>0.7.8-SNAPSHOT</version>
|
<version>0.7.11-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.googlecode.concurrent-locks</groupId>
|
||||||
|
<artifactId>concurrent-locks</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
<version>2.6.3-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
|
||||||
<id>spigotmc-public</id>
|
|
||||||
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
|
||||||
</repository>
|
|
||||||
<repository>
|
<repository>
|
||||||
<id>akarin-repo</id>
|
<id>akarin-repo</id>
|
||||||
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
|
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>spigotmc-public</id>
|
||||||
|
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
|
||||||
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>spongepowered-repo</id>
|
<id>spongepowered-repo</id>
|
||||||
<url>https://repo.spongepowered.org/maven/</url>
|
<url>https://repo.spongepowered.org/maven/</url>
|
||||||
</repository>
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<id>nallar-repo</id>
|
||||||
|
<url>http://repo.nallar.me/</url>
|
||||||
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<pluginRepositories>
|
<pluginRepositories>
|
||||||
@@ -168,7 +190,7 @@
|
|||||||
<version>1.3</version>
|
<version>1.3</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<outputPrefix>git-Akarin-</outputPrefix>
|
<outputPrefix>git-Akarin-</outputPrefix>
|
||||||
<scmDirectory>..</scmDirectory>
|
<scmDirectory>../..</scmDirectory> <!-- Akarin -->
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@@ -265,15 +287,15 @@
|
|||||||
</transformers>
|
</transformers>
|
||||||
<!-- Akarin - Avoid signature failure -->
|
<!-- Akarin - Avoid signature failure -->
|
||||||
<filters>
|
<filters>
|
||||||
<filter>
|
<filter>
|
||||||
<artifact>*:*</artifact>
|
<artifact>*:*</artifact>
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>META-INF/*.SF</exclude>
|
<exclude>META-INF/*.SF</exclude>
|
||||||
<exclude>META-INF/*.DSA</exclude>
|
<exclude>META-INF/*.DSA</exclude>
|
||||||
<exclude>META-INF/*.RSA</exclude>
|
<exclude>META-INF/*.RSA</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
</filter>
|
</filter>
|
||||||
</filters>
|
</filters>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|||||||
@@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is licensed under the MIT License (MIT).
|
|
||||||
*
|
|
||||||
* Copyright (c) 2014 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 co.aikar.util.LoadingIntMap;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
|
|
||||||
import java.util.logging.Level;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Akarin Changes Note</b><br>
|
|
||||||
* <br>
|
|
||||||
* 1) Add volatile to fields<br>
|
|
||||||
* @author cakoyo
|
|
||||||
*/
|
|
||||||
class TimingHandler implements Timing {
|
|
||||||
|
|
||||||
private static int idPool = 1;
|
|
||||||
final int id = idPool++;
|
|
||||||
|
|
||||||
final String name;
|
|
||||||
private final boolean verbose;
|
|
||||||
|
|
||||||
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
|
|
||||||
|
|
||||||
final TimingData record;
|
|
||||||
private final TimingHandler groupHandler;
|
|
||||||
|
|
||||||
private volatile long start = 0; // Akarin - volatile
|
|
||||||
private volatile int timingDepth = 0; // Akarin - volatile
|
|
||||||
private boolean added;
|
|
||||||
private boolean timed;
|
|
||||||
private boolean enabled;
|
|
||||||
private TimingHandler parent;
|
|
||||||
|
|
||||||
TimingHandler(TimingIdentifier id) {
|
|
||||||
if (id.name.startsWith("##")) {
|
|
||||||
verbose = true;
|
|
||||||
this.name = id.name.substring(3);
|
|
||||||
} else {
|
|
||||||
this.name = id.name;
|
|
||||||
verbose = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.record = new TimingData(this.id);
|
|
||||||
this.groupHandler = id.groupHandler;
|
|
||||||
|
|
||||||
TimingIdentifier.getGroup(id.group).handlers.add(this);
|
|
||||||
checkEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
final void checkEnabled() {
|
|
||||||
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void processTick(boolean violated) {
|
|
||||||
if (timingDepth != 0 || record.getCurTickCount() == 0) {
|
|
||||||
timingDepth = 0;
|
|
||||||
start = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
record.processTick(violated);
|
|
||||||
for (TimingData handler : children.values()) {
|
|
||||||
handler.processTick(violated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timing startTimingIfSync() {
|
|
||||||
if (Bukkit.isPrimaryThread()) {
|
|
||||||
startTiming();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopTimingIfSync() {
|
|
||||||
if (Bukkit.isPrimaryThread()) {
|
|
||||||
stopTiming();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Timing startTiming() {
|
|
||||||
if (enabled && ++timingDepth == 1) {
|
|
||||||
start = System.nanoTime();
|
|
||||||
parent = TimingsManager.CURRENT;
|
|
||||||
TimingsManager.CURRENT = this;
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopTiming() {
|
|
||||||
if (enabled && --timingDepth == 0 && start != 0) {
|
|
||||||
if (!Bukkit.isPrimaryThread()) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
|
||||||
new Throwable().printStackTrace();
|
|
||||||
start = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
addDiff(System.nanoTime() - start);
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void abort() {
|
|
||||||
if (enabled && timingDepth > 0) {
|
|
||||||
start = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void addDiff(long diff) {
|
|
||||||
if (TimingsManager.CURRENT == this) {
|
|
||||||
TimingsManager.CURRENT = 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);
|
|
||||||
groupHandler.children.get(id).add(diff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset this timer, setting all values to zero.
|
|
||||||
*
|
|
||||||
* @param full
|
|
||||||
*/
|
|
||||||
void reset(boolean full) {
|
|
||||||
record.reset();
|
|
||||||
if (full) {
|
|
||||||
timed = false;
|
|
||||||
}
|
|
||||||
start = 0;
|
|
||||||
timingDepth = 0;
|
|
||||||
added = false;
|
|
||||||
children.clear();
|
|
||||||
checkEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TimingHandler getTimingHandler() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
return (this == o);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is simply for the Closeable interface so it can be used with
|
|
||||||
* try-with-resources ()
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
stopTimingIfSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSpecial() {
|
|
||||||
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isTimed() {
|
|
||||||
return timed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
TimingData[] cloneChildren() {
|
|
||||||
final TimingData[] clonedChildren = new TimingData[children.size()];
|
|
||||||
int i = 0;
|
|
||||||
for (TimingData child : children.values()) {
|
|
||||||
clonedChildren[i++] = child.clone();
|
|
||||||
}
|
|
||||||
return clonedChildren;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package com.destroystokyo.paper.antixray;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import net.minecraft.server.Chunk;
|
||||||
|
import net.minecraft.server.DataPalette;
|
||||||
|
import net.minecraft.server.IBlockData;
|
||||||
|
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) byte[] -> ByteBuf (compatibility)
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutMapChunkInfo {
|
||||||
|
|
||||||
|
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
|
||||||
|
private final Chunk chunk;
|
||||||
|
private final int chunkSectionSelector;
|
||||||
|
private ByteBuf data; // Akarin - byte[] -> ByteBuf
|
||||||
|
private final int[] bitsPerValue = new int[16];
|
||||||
|
private final DataPalette[] dataPalettes = new DataPalette[16];
|
||||||
|
private final int[] dataBitsIndexes = new int[16];
|
||||||
|
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
|
||||||
|
|
||||||
|
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
|
||||||
|
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
|
||||||
|
this.chunk = chunk;
|
||||||
|
this.chunkSectionSelector = chunkSectionSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
|
||||||
|
return packetPlayOutMapChunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk getChunk() {
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkSectionSelector() {
|
||||||
|
return chunkSectionSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getData() {
|
||||||
|
return data.array(); // Akarin
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(ByteBuf data) { // Akarin - byte[] -> ByteBuf
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBitsPerValue(int chunkSectionIndex) {
|
||||||
|
return bitsPerValue[chunkSectionIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
|
||||||
|
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataPalette getDataPalette(int chunkSectionIndex) {
|
||||||
|
return dataPalettes[chunkSectionIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
|
||||||
|
dataPalettes[chunkSectionIndex] = dataPalette;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDataBitsIndex(int chunkSectionIndex) {
|
||||||
|
return dataBitsIndexes[chunkSectionIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
|
||||||
|
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
|
||||||
|
return predefinedBlockData[chunkSectionIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
|
||||||
|
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWritten(int chunkSectionIndex) {
|
||||||
|
return bitsPerValue[chunkSectionIndex] != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
package io.akarin.api;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import com.google.common.collect.Queues;
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
||||||
|
|
||||||
import co.aikar.timings.Timing;
|
|
||||||
import co.aikar.timings.Timings;
|
|
||||||
|
|
||||||
public abstract class Akari {
|
|
||||||
/**
|
|
||||||
* A common logger used by mixin classes
|
|
||||||
*/
|
|
||||||
public final static Logger logger = LogManager.getLogger("Akarin");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporarily disable desync timings error, moreover it's worthless to trace async operation
|
|
||||||
*/
|
|
||||||
public static volatile boolean silentTiming;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A common thread pool factory
|
|
||||||
*/
|
|
||||||
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Schedule Thread - %1$d").build();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main thread callback tasks
|
|
||||||
*/
|
|
||||||
public static final Queue<Runnable> callbackQueue = Queues.newConcurrentLinkedQueue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A common tick pool
|
|
||||||
*/
|
|
||||||
public static final ExecutorCompletionService<Void> STAGE_TICK = new ExecutorCompletionService<Void>(Executors.newFixedThreadPool(1, Akari.STAGE_FACTORY));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The unsafe
|
|
||||||
*/
|
|
||||||
public final static sun.misc.Unsafe UNSAFE = getUnsafe();
|
|
||||||
|
|
||||||
private static sun.misc.Unsafe getUnsafe() {
|
|
||||||
try {
|
|
||||||
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
|
||||||
theUnsafe.setAccessible(true);
|
|
||||||
return (sun.misc.Unsafe) theUnsafe.get(null);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Timings
|
|
||||||
*/
|
|
||||||
public final static Timing worldTiming = getTiming("Akarin - World");
|
|
||||||
|
|
||||||
public final static Timing callbackTiming = getTiming("Akarin - Callback");
|
|
||||||
|
|
||||||
private static Timing getTiming(String name) {
|
|
||||||
try {
|
|
||||||
Method ofSafe = Timings.class.getDeclaredMethod("ofSafe", String.class);
|
|
||||||
ofSafe.setAccessible(true);
|
|
||||||
return (Timing) ofSafe.invoke(null, name);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
161
sources/src/main/java/io/akarin/api/internal/Akari.java
Normal file
161
sources/src/main/java/io/akarin/api/internal/Akari.java
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package io.akarin.api.internal;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.ThreadFactory;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import com.google.common.collect.Queues;
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
|
||||||
|
import co.aikar.timings.Timing;
|
||||||
|
import co.aikar.timings.Timings;
|
||||||
|
import io.akarin.api.internal.Akari.AssignableFactory;
|
||||||
|
import io.akarin.api.internal.Akari.TimingSignal;
|
||||||
|
import io.akarin.api.internal.utils.ReentrantSpinningLock;
|
||||||
|
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
|
||||||
|
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
public abstract class Akari {
|
||||||
|
/**
|
||||||
|
* A common logger used by mixin classes
|
||||||
|
*/
|
||||||
|
public final static Logger logger = LogManager.getLogger("Akarin");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A common thread pool factory
|
||||||
|
*/
|
||||||
|
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Parallel Registry Thread - %1$d").build();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main thread callback tasks
|
||||||
|
*/
|
||||||
|
public static final Queue<Runnable> callbackQueue = Queues.newConcurrentLinkedQueue();
|
||||||
|
|
||||||
|
public static class AssignableThread extends Thread {
|
||||||
|
public AssignableThread(Runnable run) {
|
||||||
|
super(run);
|
||||||
|
}
|
||||||
|
public AssignableThread() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AssignableFactory implements ThreadFactory {
|
||||||
|
private final String threadName;
|
||||||
|
private int threadNumber;
|
||||||
|
|
||||||
|
public AssignableFactory(String name) {
|
||||||
|
threadName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Thread newThread(Runnable run) {
|
||||||
|
Thread thread = new AssignableThread(run);
|
||||||
|
thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
|
||||||
|
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class TimingSignal {
|
||||||
|
public final World tickedWorld;
|
||||||
|
public final boolean isEntities;
|
||||||
|
|
||||||
|
public TimingSignal(World world, boolean entities) {
|
||||||
|
tickedWorld = world;
|
||||||
|
isEntities = entities;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
|
||||||
|
|
||||||
|
static {
|
||||||
|
resizeTickExecutors(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void resizeTickExecutors(int worlds) {
|
||||||
|
int parallelism;
|
||||||
|
switch (AkarinGlobalConfig.parallelMode) {
|
||||||
|
case -1:
|
||||||
|
return;
|
||||||
|
case 0:
|
||||||
|
parallelism = 2;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
parallelism = worlds + 1;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
default:
|
||||||
|
parallelism = worlds * 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
|
||||||
|
0L, TimeUnit.MILLISECONDS,
|
||||||
|
new LinkedBlockingQueue<Runnable>(),
|
||||||
|
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPrimaryThread() {
|
||||||
|
return isPrimaryThread(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPrimaryThread(boolean assign) {
|
||||||
|
Thread current = Thread.currentThread();
|
||||||
|
return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The unsafe
|
||||||
|
*/
|
||||||
|
public final static sun.misc.Unsafe UNSAFE = getUnsafe();
|
||||||
|
|
||||||
|
private static sun.misc.Unsafe getUnsafe() {
|
||||||
|
try {
|
||||||
|
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
|
||||||
|
theUnsafe.setAccessible(true);
|
||||||
|
return (sun.misc.Unsafe) theUnsafe.get(null);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String serverVersion = Akari.class.getPackage().getImplementationVersion();
|
||||||
|
|
||||||
|
public static String getServerVersion() {
|
||||||
|
return serverVersion + " (MC: " + MinecraftServer.getServer().getVersion() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timings
|
||||||
|
*/
|
||||||
|
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
|
||||||
|
|
||||||
|
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
|
||||||
|
|
||||||
|
private static Timing getTiming(String name) {
|
||||||
|
try {
|
||||||
|
Method ofSafe = Timings.class.getDeclaredMethod("ofSafe", String.class);
|
||||||
|
ofSafe.setAccessible(true);
|
||||||
|
return (Timing) ofSafe.invoke(null, name);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
t.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package io.akarin.api;
|
package io.akarin.api.internal;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.api.internal.mixin;
|
||||||
|
|
||||||
|
public interface IMixinRealTimeTicking {
|
||||||
|
|
||||||
|
long getRealTimeTicks();
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package io.akarin.api.internal.mixin;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public interface IMixinWorldServer {
|
||||||
|
public Object lock();
|
||||||
|
public Random rand();
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
* at http://creativecommons.org/publicdomain/zero/1.0/
|
* at http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.akarin.api;
|
package io.akarin.api.internal.utils;
|
||||||
|
|
||||||
import java.util.AbstractQueue;
|
import java.util.AbstractQueue;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -45,6 +45,8 @@ import java.util.Spliterator;
|
|||||||
import java.util.Spliterators;
|
import java.util.Spliterators;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
|
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
|
||||||
* This queue orders elements FIFO (first-in-first-out).
|
* This queue orders elements FIFO (first-in-first-out).
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package io.akarin.api.internal.utils;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public class ReentrantSpinningLock {
|
||||||
|
/*
|
||||||
|
* Impl Note:
|
||||||
|
* A write lock can reentrant as a read lock, while a
|
||||||
|
* read lock is not allowed to reentrant as a write lock.
|
||||||
|
* READ LOCK IS UNTESTED, USE WITH CATION.
|
||||||
|
*/
|
||||||
|
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
// --------- Thread local restricted fields ---------
|
||||||
|
private long heldThreadId = 0;
|
||||||
|
private int reentrantLocks = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock as a typical reentrant write lock
|
||||||
|
*/
|
||||||
|
public void lock() {
|
||||||
|
long currentThreadId = Thread.currentThread().getId();
|
||||||
|
if (heldThreadId == currentThreadId) {
|
||||||
|
reentrantLocks++;
|
||||||
|
} else {
|
||||||
|
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
|
||||||
|
heldThreadId = currentThreadId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unlock() {
|
||||||
|
if (reentrantLocks == 0) {
|
||||||
|
heldThreadId = 0;
|
||||||
|
//if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
|
||||||
|
writeLocked.set(false);
|
||||||
|
//}
|
||||||
|
} else {
|
||||||
|
--reentrantLocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AtomicInteger readerThreads = new AtomicInteger(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lock as a typical reentrant read lock
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public void lockWeak() {
|
||||||
|
long currentThreadId = Thread.currentThread().getId();
|
||||||
|
if (heldThreadId == currentThreadId) {
|
||||||
|
reentrantLocks++;
|
||||||
|
} else {
|
||||||
|
if (readerThreads.get() == 0) {
|
||||||
|
while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock
|
||||||
|
}
|
||||||
|
heldThreadId = currentThreadId;
|
||||||
|
readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void unlockWeak() {
|
||||||
|
if (reentrantLocks == 0) {
|
||||||
|
heldThreadId = 0;
|
||||||
|
writeLocked.set(false);
|
||||||
|
} else {
|
||||||
|
--reentrantLocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------- Wrappers to allow typical usages ---------
|
||||||
|
private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock();
|
||||||
|
private SpinningReadLock wrappedReadLock = new SpinningReadLock();
|
||||||
|
|
||||||
|
public class SpinningWriteLock {
|
||||||
|
public void lock() {
|
||||||
|
lock();
|
||||||
|
}
|
||||||
|
public void unlock() {
|
||||||
|
unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public class SpinningReadLock {
|
||||||
|
public void lock() {
|
||||||
|
lockWeak();
|
||||||
|
}
|
||||||
|
public void unlock() {
|
||||||
|
unlockWeak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpinningWriteLock writeLock() {
|
||||||
|
return wrappedWriteLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SpinningReadLock readLock() {
|
||||||
|
return wrappedReadLock;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package io.akarin.api;
|
package io.akarin.api.internal.utils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
Written in 2015 by Sebastiano Vigna (vigna@acm.org)
|
||||||
|
|
||||||
|
To the extent possible under law, the author has dedicated all copyright
|
||||||
|
and related and neighboring rights to this software to the public domain
|
||||||
|
worldwide. This software is distributed without any warranty.
|
||||||
|
|
||||||
|
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
|
||||||
|
package io.akarin.api.internal.utils.random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a SplittableRandom-style generator, meant to have a tiny state
|
||||||
|
* that permits storing many different generators with low overhead.
|
||||||
|
* It should be rather fast, though no guarantees can be made on all hardware.
|
||||||
|
* <br>
|
||||||
|
* Benchmarking on a Windows laptop with an i7-4700MQ processor running OpenJDK 8
|
||||||
|
* reports generation of 64-bit random long output as 17.8x faster than generating
|
||||||
|
* an equivalent number of random longs with java.util.Random, and generation of
|
||||||
|
* 32-bit random int output as 9.8x faster. Specifically, generating 1 billion longs
|
||||||
|
* took about 1.28 nanoseconds per long (1.277 seconds for the whole group) with
|
||||||
|
* LightRNG, while java.util.Random (which is meant to produce int, to be fair) took
|
||||||
|
* about 22.8 nanoseconds per long (22.797 seconds for the whole group). XorRNG
|
||||||
|
* appears to be occasionally faster on int output than LightRNG, but it isn't clear
|
||||||
|
* why or what causes that (JIT or GC internals, possibly). XorRNG is slightly
|
||||||
|
* slower at generating 64-bit random data, including long and double, but not by
|
||||||
|
* a significant degree (a multiplier between 0.9 and 1.2 times). The only deciding
|
||||||
|
* factor then is state size, where LightRNG is as small as possible for any JVM
|
||||||
|
* object with even a single field: 16 bytes (on a 64-bit JVM; 8-byte objects with
|
||||||
|
* 4 bytes or less of non-static members may be possible on 32-bit JVMs but I can't
|
||||||
|
* find anything confirming that guess).
|
||||||
|
* <br>
|
||||||
|
* So yes, this should be very fast, and with only a single long used per LightRNG,
|
||||||
|
* it is about as memory-efficient as these generators get.
|
||||||
|
* <br>
|
||||||
|
* Written in 2015 by Sebastiano Vigna (vigna@acm.org)
|
||||||
|
* @author Sebastiano Vigna
|
||||||
|
* @author Tommy Ettinger
|
||||||
|
*/
|
||||||
|
public class LightRNG implements RandomnessSource, StatefulRandomness
|
||||||
|
{
|
||||||
|
/** 2 raised to the 53, - 1. */
|
||||||
|
private static final long DOUBLE_MASK = ( 1L << 53 ) - 1;
|
||||||
|
/** 2 raised to the -53. */
|
||||||
|
private static final double NORM_53 = 1. / ( 1L << 53 );
|
||||||
|
/** 2 raised to the 24, -1. */
|
||||||
|
private static final long FLOAT_MASK = ( 1L << 24 ) - 1;
|
||||||
|
/** 2 raised to the -24. */
|
||||||
|
private static final double NORM_24 = 1. / ( 1L << 24 );
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -374415589203474497L;
|
||||||
|
|
||||||
|
public long state; /* The state can be seeded with any value. */
|
||||||
|
|
||||||
|
/** Creates a new generator seeded using Math.random. */
|
||||||
|
public LightRNG() {
|
||||||
|
this((long) Math.floor(Math.random() * Long.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public LightRNG( final long seed ) {
|
||||||
|
setSeed(seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int next( int bits ) {
|
||||||
|
return (int)( nextLong() & ( 1L << bits ) - 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.
|
||||||
|
* @return any long, all 64 bits are random
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long nextLong() {
|
||||||
|
long z = ( state += 0x9E3779B97F4A7C15L );
|
||||||
|
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
|
||||||
|
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
|
||||||
|
return z ^ (z >>> 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
|
||||||
|
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
|
||||||
|
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
|
||||||
|
*
|
||||||
|
* @return a copy of this RandomnessSource
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public RandomnessSource copy() {
|
||||||
|
return new LightRNG(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
|
||||||
|
* @return any int, all 32 bits are random
|
||||||
|
*/
|
||||||
|
public int nextInt() {
|
||||||
|
return (int)nextLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclusive on the upper bound. The lower bound is 0.
|
||||||
|
* @param bound the upper bound; should be positive
|
||||||
|
* @return a random int less than n and at least equal to 0
|
||||||
|
*/
|
||||||
|
public int nextInt( final int bound ) {
|
||||||
|
if ( bound <= 0 ) return 0;
|
||||||
|
int threshold = (0x7fffffff - bound + 1) % bound;
|
||||||
|
for (;;) {
|
||||||
|
int bits = (int)(nextLong() & 0x7fffffff);
|
||||||
|
if (bits >= threshold)
|
||||||
|
return bits % bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Inclusive lower, exclusive upper.
|
||||||
|
* @param lower the lower bound, inclusive, can be positive or negative
|
||||||
|
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
|
||||||
|
* @return a random int at least equal to lower and less than upper
|
||||||
|
*/
|
||||||
|
public int nextInt( final int lower, final int upper ) {
|
||||||
|
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
|
||||||
|
return lower + nextInt(upper - lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exclusive on the upper bound. The lower bound is 0.
|
||||||
|
* @param bound the upper bound; should be positive
|
||||||
|
* @return a random long less than n
|
||||||
|
*/
|
||||||
|
public long nextLong( final long bound ) {
|
||||||
|
if ( bound <= 0 ) return 0;
|
||||||
|
long threshold = (0x7fffffffffffffffL - bound + 1) % bound;
|
||||||
|
for (;;) {
|
||||||
|
long bits = nextLong() & 0x7fffffffffffffffL;
|
||||||
|
if (bits >= threshold)
|
||||||
|
return bits % bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inclusive lower, exclusive upper.
|
||||||
|
* @param lower the lower bound, inclusive, can be positive or negative
|
||||||
|
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
|
||||||
|
* @return a random long at least equal to lower and less than upper
|
||||||
|
*/
|
||||||
|
public long nextLong( final long lower, final long upper ) {
|
||||||
|
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
|
||||||
|
return lower + nextLong(upper - lower);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets a uniform random double in the range [0.0,1.0)
|
||||||
|
* @return a random double at least equal to 0.0 and less than 1.0
|
||||||
|
*/
|
||||||
|
public double nextDouble() {
|
||||||
|
return ( nextLong() & DOUBLE_MASK ) * NORM_53;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer
|
||||||
|
* is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound.
|
||||||
|
* @param outer the exclusive outer bound, can be negative
|
||||||
|
* @return a random double between 0.0 (inclusive) and outer (exclusive)
|
||||||
|
*/
|
||||||
|
public double nextDouble(final double outer) {
|
||||||
|
return nextDouble() * outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a uniform random float in the range [0.0,1.0)
|
||||||
|
* @return a random float at least equal to 0.0 and less than 1.0
|
||||||
|
*/
|
||||||
|
public float nextFloat() {
|
||||||
|
return (float)( ( nextLong() & FLOAT_MASK ) * NORM_24 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a random value, true or false.
|
||||||
|
* Calls nextLong() once.
|
||||||
|
* @return a random true or false value.
|
||||||
|
*/
|
||||||
|
public boolean nextBoolean() {
|
||||||
|
return ( nextLong() & 1 ) != 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a byte array as a parameter, this will fill the array with random bytes (modifying it
|
||||||
|
* in-place). Calls nextLong() {@code Math.ceil(bytes.length / 8.0)} times.
|
||||||
|
* @param bytes a byte array that will have its contents overwritten with random bytes.
|
||||||
|
*/
|
||||||
|
public void nextBytes( final byte[] bytes ) {
|
||||||
|
int i = bytes.length, n = 0;
|
||||||
|
while( i != 0 ) {
|
||||||
|
n = Math.min( i, 8 );
|
||||||
|
for ( long bits = nextLong(); n-- != 0; bits >>= 8 ) bytes[ --i ] = (byte)bits;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the seed of this generator (which is also the current state).
|
||||||
|
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
|
||||||
|
*/
|
||||||
|
public void setSeed( final long seed ) {
|
||||||
|
state = seed;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sets the seed (also the current state) of this generator.
|
||||||
|
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setState( final long seed ) {
|
||||||
|
state = seed;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets the current state of this generator.
|
||||||
|
* @return the current seed of this LightRNG, changed once per call to nextLong()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advances or rolls back the LightRNG's state without actually generating numbers. Skip forward
|
||||||
|
* or backward a number of steps specified by advance, where a step is equal to one call to nextInt().
|
||||||
|
* @param advance Number of future generations to skip past. Can be negative to backtrack.
|
||||||
|
* @return the state after skipping.
|
||||||
|
*/
|
||||||
|
public long skip(long advance)
|
||||||
|
{
|
||||||
|
return state += 0x9E3779B97F4A7C15L * advance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "LightRNG (" + state + ")"; // Akarin
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package io.akarin.api.internal.utils.random;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a "fake" LightRandom, backed by the LightRNG.
|
||||||
|
*
|
||||||
|
* This is useful if you want to quickly replace a Random variable with
|
||||||
|
* LightRNG without breaking every code.
|
||||||
|
*/
|
||||||
|
public class LightRandom extends Random {
|
||||||
|
public static LightRNG light = new LightRNG(); // LightRNG, static.
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int next(int bits) {
|
||||||
|
return light.next(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void nextBytes(byte[] bytes) {
|
||||||
|
light.nextBytes(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextInt() {
|
||||||
|
return light.nextInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int nextInt(int n) {
|
||||||
|
return light.nextInt(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long nextLong() {
|
||||||
|
return light.nextLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean nextBoolean() {
|
||||||
|
return light.nextBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float nextFloat() {
|
||||||
|
return light.nextFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double nextDouble() {
|
||||||
|
return light.nextDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Akarin start
|
||||||
|
@Override
|
||||||
|
public void setSeed(long seed) {
|
||||||
|
light.setSeed(seed);
|
||||||
|
}
|
||||||
|
// Akarin end
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package io.akarin.api.internal.utils.random;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface defines the interactions required of a random number
|
||||||
|
* generator. It is a replacement for Java's built-in Random because for
|
||||||
|
* improved performance.
|
||||||
|
*
|
||||||
|
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
|
||||||
|
*/
|
||||||
|
public interface RandomnessSource extends Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Using this method, any algorithm that might use the built-in Java Random
|
||||||
|
* can interface with this randomness source.
|
||||||
|
*
|
||||||
|
* @param bits the number of bits to be returned
|
||||||
|
* @return the integer containing the appropriate number of bits
|
||||||
|
*/
|
||||||
|
int next(int bits);
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Using this method, any algorithm that needs to efficiently generate more
|
||||||
|
* than 32 bits of random data can interface with this randomness source.
|
||||||
|
*
|
||||||
|
* Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
|
||||||
|
* @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
|
||||||
|
*/
|
||||||
|
long nextLong();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
|
||||||
|
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
|
||||||
|
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
|
||||||
|
* @return a copy of this RandomnessSource
|
||||||
|
*/
|
||||||
|
RandomnessSource copy();
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package io.akarin.api.internal.utils.random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple interface for RandomnessSources that have the additional property of a state that can be re-set.
|
||||||
|
* Created by Tommy Ettinger on 9/15/2015.
|
||||||
|
*/
|
||||||
|
public interface StatefulRandomness extends RandomnessSource {
|
||||||
|
/**
|
||||||
|
* Get the current internal state of the StatefulRandomness as a long.
|
||||||
|
* @return the current internal state of this object.
|
||||||
|
*/
|
||||||
|
long getState();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current internal state of this StatefulRandomness with a long.
|
||||||
|
*
|
||||||
|
* @param state a 64-bit long. You should avoid passing 0, even though some implementations can handle that.
|
||||||
|
*/
|
||||||
|
void setState(long state);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package io.akarin.api.internal.utils.thread;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class OpenExecutionException extends ExecutionException {
|
||||||
|
private static final long serialVersionUID = 7830266012832686185L;
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
/*
|
||||||
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Written by Doug Lea with assistance from members of JCP JSR-166
|
||||||
|
* Expert Group and released to the public domain, as explained at
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.akarin.api.internal.utils.thread;
|
||||||
|
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.CompletionService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
import java.util.concurrent.RunnableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class SuspendableExecutorCompletionService<V> implements CompletionService<V> {
|
||||||
|
private final SuspendableThreadPoolExecutor executor;
|
||||||
|
private final BlockingQueue<Future<V>> completionQueue;
|
||||||
|
|
||||||
|
public void suspend() {
|
||||||
|
executor.suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resume() {
|
||||||
|
executor.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FutureTask extension to enqueue upon completion
|
||||||
|
*/
|
||||||
|
private class QueueingFuture extends FutureTask<Void> {
|
||||||
|
QueueingFuture(RunnableFuture<V> task) {
|
||||||
|
super(task, null);
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
protected void done() { completionQueue.add(task); }
|
||||||
|
private final Future<V> task;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RunnableFuture<V> newTaskFor(Callable<V> task) {
|
||||||
|
return new FutureTask<V>(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
|
||||||
|
return new FutureTask<V>(task, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an ExecutorCompletionService using the supplied
|
||||||
|
* executor for base task execution and a
|
||||||
|
* {@link LinkedBlockingQueue} as a completion queue.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use
|
||||||
|
* @throws NullPointerException if executor is {@code null}
|
||||||
|
*/
|
||||||
|
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) {
|
||||||
|
if (executor == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
this.executor = executor;
|
||||||
|
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an ExecutorCompletionService using the supplied
|
||||||
|
* executor for base task execution and the supplied queue as its
|
||||||
|
* completion queue.
|
||||||
|
*
|
||||||
|
* @param executor the executor to use
|
||||||
|
* @param completionQueue the queue to use as the completion queue
|
||||||
|
* normally one dedicated for use by this service. This
|
||||||
|
* queue is treated as unbounded -- failed attempted
|
||||||
|
* {@code Queue.add} operations for completed tasks cause
|
||||||
|
* them not to be retrievable.
|
||||||
|
* @throws NullPointerException if executor or completionQueue are {@code null}
|
||||||
|
*/
|
||||||
|
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor,
|
||||||
|
BlockingQueue<Future<V>> completionQueue) {
|
||||||
|
if (executor == null || completionQueue == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
this.executor = executor;
|
||||||
|
this.completionQueue = completionQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<V> submit(Callable<V> task) {
|
||||||
|
if (task == null) throw new NullPointerException();
|
||||||
|
RunnableFuture<V> f = newTaskFor(task);
|
||||||
|
executor.execute(new QueueingFuture(f));
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<V> submit(Runnable task, V result) {
|
||||||
|
if (task == null) throw new NullPointerException();
|
||||||
|
RunnableFuture<V> f = newTaskFor(task, result);
|
||||||
|
executor.execute(new QueueingFuture(f));
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<V> take() throws InterruptedException {
|
||||||
|
return completionQueue.take();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<V> poll() {
|
||||||
|
return completionQueue.poll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Future<V> poll(long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException {
|
||||||
|
return completionQueue.poll(timeout, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@ package io.akarin.server.core;
|
|||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@@ -14,8 +16,8 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
import org.bukkit.configuration.InvalidConfigurationException;
|
import org.bukkit.configuration.InvalidConfigurationException;
|
||||||
import org.bukkit.configuration.file.YamlConfiguration;
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
import io.akarin.api.Akari;
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class AkarinGlobalConfig {
|
public class AkarinGlobalConfig {
|
||||||
|
|
||||||
private static File CONFIG_FILE;
|
private static File CONFIG_FILE;
|
||||||
@@ -153,43 +155,106 @@ public class AkarinGlobalConfig {
|
|||||||
legacyVersioningCompat = getBoolean("alternative.legacy-versioning-compat", false);
|
legacyVersioningCompat = getBoolean("alternative.legacy-versioning-compat", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int registryTerminationSeconds;
|
|
||||||
private static void registryTerminationSeconds() {
|
|
||||||
registryTerminationSeconds = getSeconds(getString("bootstrap.parallel-registry-termination", "9s"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int playersPerIOThread;
|
public static int playersPerIOThread;
|
||||||
private static void playersPerIOThread() {
|
private static void playersPerIOThread() {
|
||||||
playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50);
|
playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean silentAsyncTimings;
|
public static long timeUpdateInterval;
|
||||||
private static void silentAsyncTimings() {
|
|
||||||
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 int timeUpdateInterval;
|
|
||||||
private static void timeUpdateInterval() {
|
private static void timeUpdateInterval() {
|
||||||
timeUpdateInterval = getSeconds(getString("core.world-time-update-interval", "1s"));
|
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int keepAliveSendInterval;
|
public static long keepAliveSendInterval;
|
||||||
private static void keepAliveSendInterval() {
|
private static void keepAliveSendInterval() {
|
||||||
keepAliveSendInterval = getSeconds(getString("core.keep-alive-packet-send-interval", "15s"));
|
keepAliveSendInterval = getSeconds(getString("core.tick-rate.keep-alive-packet-send-interval", "15s")) * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int keepAliveTimeout;
|
public static long keepAliveTimeout;
|
||||||
private static void keepAliveTimeout() {
|
private static void keepAliveTimeout() {
|
||||||
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s"));
|
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean throwOnAsyncCaught;
|
||||||
|
private static void throwOnAsyncCaught() {
|
||||||
|
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean allowSpawnerModify;
|
public static boolean allowSpawnerModify;
|
||||||
private static void allowSpawnerModify() {
|
private static void allowSpawnerModify() {
|
||||||
allowSpawnerModify = getBoolean("alternative.allow-spawner-modify", true);
|
allowSpawnerModify = getBoolean("alternative.allow-spawner-modify", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean noResponseDoGC;
|
||||||
|
private static void noResponseDoGC() {
|
||||||
|
noResponseDoGC = getBoolean("alternative.gc-before-stuck-restart", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String messageKick;
|
||||||
|
public static String messageBan;
|
||||||
|
public static String messageBanReason;
|
||||||
|
public static String messageBanExpires;
|
||||||
|
public static String messageBanIp;
|
||||||
|
public static String messageDupLogin;
|
||||||
|
public static String messageJoin;
|
||||||
|
public static String messageJoinRenamed;
|
||||||
|
public static String messageKickKeepAlive;
|
||||||
|
public static String 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String serverBrandName;
|
||||||
|
private static void serverBrandName() {
|
||||||
|
serverBrandName = getString("alternative.modified-server-brand-name", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean disableEndPortalCreate;
|
||||||
|
private static void disableEndPortalCreate() {
|
||||||
|
disableEndPortalCreate = getBoolean("alternative.disable-end-portal-create", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int primaryThreadPriority;
|
||||||
|
private static void primaryThreadPriority() {
|
||||||
|
primaryThreadPriority = getInt("core.primary-thread-priority", 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long playersInfoUpdateInterval;
|
||||||
|
private static void playersInfoUpdateInterval() {
|
||||||
|
playersInfoUpdateInterval = getSeconds(getString("core.tick-rate.players-info-update-interval", "30s")) * 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long versionUpdateInterval;
|
||||||
|
private static void versionUpdateInterval() {
|
||||||
|
versionUpdateInterval = getSeconds(getString("alternative.version-update-interval", "3600s")) * 1000; // 1 hour
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean sendLightOnlyChunkSection;
|
||||||
|
private static void sendLightOnlyChunkSection() {
|
||||||
|
sendLightOnlyChunkSection = getBoolean("core.send-light-only-chunk-sections", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean forceHardcoreDifficulty;
|
||||||
|
private static void forceHardcoreDifficulty() {
|
||||||
|
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int fileIOThreads;
|
||||||
|
private static void fileIOThreads() {
|
||||||
|
fileIOThreads = getInt("core.chunk-save-threads", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int parallelMode;
|
||||||
|
private static void parallelMode() {
|
||||||
|
parallelMode = getInt("core.parallel-mode", 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
package io.akarin.server.core;
|
package io.akarin.server.core;
|
||||||
|
|
||||||
import io.akarin.api.Akari;
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
import net.minecraft.server.EntityPlayer;
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.EnumDifficulty;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.PacketPlayOutKeepAlive;
|
import net.minecraft.server.PacketPlayOutKeepAlive;
|
||||||
|
import net.minecraft.server.PacketPlayOutPlayerInfo;
|
||||||
import net.minecraft.server.PacketPlayOutUpdateTime;
|
import net.minecraft.server.PacketPlayOutUpdateTime;
|
||||||
import net.minecraft.server.PlayerConnection;
|
import net.minecraft.server.PlayerConnection;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
public class AkarinSlackScheduler extends Thread {
|
public class AkarinSlackScheduler extends Thread {
|
||||||
public static AkarinSlackScheduler get() {
|
public static AkarinSlackScheduler get() {
|
||||||
return Singleton.instance;
|
return Singleton.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void boot() {
|
public void boot() {
|
||||||
Singleton.instance.setName("Akarin Slack Scheduler Thread");
|
setName("Akarin Slack Scheduler Thread");
|
||||||
Singleton.instance.setPriority(MIN_PRIORITY);
|
setDaemon(true);
|
||||||
Singleton.instance.setDaemon(true);
|
start();
|
||||||
Singleton.instance.start();
|
|
||||||
Akari.logger.info("Slack scheduler service started");
|
Akari.logger.info("Slack scheduler service started");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,49 +29,79 @@ public class AkarinSlackScheduler extends Thread {
|
|||||||
private static final AkarinSlackScheduler instance = new AkarinSlackScheduler();
|
private static final AkarinSlackScheduler instance = new AkarinSlackScheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Timers
|
||||||
|
*/
|
||||||
private long updateTime;
|
private long updateTime;
|
||||||
|
private long resendPlayersInfo;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
MinecraftServer server = MinecraftServer.getServer();
|
MinecraftServer server = MinecraftServer.getServer();
|
||||||
|
|
||||||
// Send time updates to everyone, it will get the right time from the world the player is in.
|
while (server.isRunning()) {
|
||||||
if (++updateTime == AkarinGlobalConfig.timeUpdateInterval * 10) {
|
long startProcessTiming = System.currentTimeMillis();
|
||||||
|
// Send time updates to everyone, it will get the right time from the world the player is in.
|
||||||
|
// Time update, from MinecraftServer#D
|
||||||
|
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
|
||||||
|
for (EntityPlayer player : server.getPlayerList().players) {
|
||||||
|
// Add support for per player time
|
||||||
|
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle")));
|
||||||
|
}
|
||||||
|
updateTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep alive, from PlayerConnection#e
|
||||||
for (EntityPlayer player : server.getPlayerList().players) {
|
for (EntityPlayer player : server.getPlayerList().players) {
|
||||||
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time
|
PlayerConnection conn = player.playerConnection;
|
||||||
}
|
// Paper - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
||||||
updateTime = 0;
|
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
||||||
}
|
long currentTime = System.nanoTime() / 1000000L;
|
||||||
|
long elapsedTime = currentTime - conn.getLastPing();
|
||||||
for (EntityPlayer player : server.getPlayerList().players) {
|
if (conn.isPendingPing()) {
|
||||||
PlayerConnection conn = player.playerConnection;
|
// We're pending a ping from the client
|
||||||
// Paper - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout) { // check keepalive limit, don't fire if already disconnected
|
||||||
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
Akari.callbackQueue.add(() -> {
|
||||||
long currentTime = System.currentTimeMillis();
|
Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info
|
||||||
long elapsedTime = currentTime - conn.getLastPing();
|
conn.disconnect("disconnect.timeout");
|
||||||
if (conn.isPendingPing()) {
|
});
|
||||||
// We're pending a ping from the client
|
}
|
||||||
if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout * 1000L) { // check keepalive limit, don't fire if already disconnected
|
} else {
|
||||||
Akari.callbackQueue.add(() -> {
|
if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval) { // 15 seconds default
|
||||||
Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info
|
conn.setPendingPing(true);
|
||||||
conn.disconnect("disconnect.timeout");
|
conn.setLastPing(currentTime);
|
||||||
});
|
conn.setKeepAliveID(currentTime);
|
||||||
}
|
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); // 15s lagg you should stop your server
|
||||||
} else {
|
}
|
||||||
if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval * 1000L) { // 15 seconds default
|
|
||||||
conn.setPendingPing(true);
|
|
||||||
conn.setLastPing(currentTime);
|
|
||||||
conn.setKeepAliveID(currentTime);
|
|
||||||
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Force hardcore difficulty, from WorldServer#doTick
|
||||||
try {
|
if (AkarinGlobalConfig.forceHardcoreDifficulty)
|
||||||
Thread.sleep(100);
|
for (WorldServer world : server.worlds) {
|
||||||
} catch (InterruptedException ex) {
|
if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) {
|
||||||
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
|
world.getWorldData().setDifficulty(EnumDifficulty.HARD);
|
||||||
ex.printStackTrace();
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update player info, from PlayerList#tick
|
||||||
|
if (++resendPlayersInfo > AkarinGlobalConfig.playersInfoUpdateInterval) {
|
||||||
|
for (EntityPlayer player : server.getPlayerList().players) {
|
||||||
|
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(server.getPlayerList().players, new Predicate<EntityPlayer>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(EntityPlayer each) {
|
||||||
|
return player.getBukkitEntity().canSee(each.getBukkitEntity());
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
resendPlayersInfo = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(100 - (System.currentTimeMillis() - startProcessTiming));
|
||||||
|
} catch (InterruptedException interrupted) {
|
||||||
|
;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package io.akarin.server.core;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelException;
|
|
||||||
import io.netty.channel.ChannelInitializer;
|
|
||||||
import io.netty.channel.ChannelOption;
|
|
||||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
|
||||||
import net.minecraft.server.EnumProtocolDirection;
|
|
||||||
import net.minecraft.server.HandshakeListener;
|
|
||||||
import net.minecraft.server.LegacyPingHandler;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.NetworkManager;
|
|
||||||
import net.minecraft.server.PacketDecoder;
|
|
||||||
import net.minecraft.server.PacketEncoder;
|
|
||||||
import net.minecraft.server.PacketPrepender;
|
|
||||||
import net.minecraft.server.PacketSplitter;
|
|
||||||
|
|
||||||
public class ChannelAdapter extends ChannelInitializer<Channel> {
|
|
||||||
private final List<NetworkManager> managers;
|
|
||||||
|
|
||||||
public ChannelAdapter(List<NetworkManager> list) {
|
|
||||||
managers = list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ChannelAdapter create(List<NetworkManager> managers) {
|
|
||||||
return new ChannelAdapter(managers);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initChannel(Channel channel) {
|
|
||||||
try {
|
|
||||||
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
|
|
||||||
} catch (ChannelException ex) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30))
|
|
||||||
.addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection()))
|
|
||||||
.addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
|
|
||||||
.addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
|
|
||||||
|
|
||||||
NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
|
|
||||||
managers.add(manager);
|
|
||||||
|
|
||||||
channel.pipeline().addLast("packet_handler", manager);
|
|
||||||
manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,11 +10,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import io.akarin.api.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
|
||||||
@Mixin(value = Main.class, remap = false)
|
@Mixin(value = Main.class, remap = false)
|
||||||
public class Bootstrap {
|
public abstract class Bootstrap {
|
||||||
@Inject(method = "main([Ljava/lang/String;)V", at = @At("HEAD"))
|
@Inject(method = "main([Ljava/lang/String;)V", at = @At("HEAD"))
|
||||||
private static void premain(CallbackInfo info) {
|
private static void premain(CallbackInfo info) {
|
||||||
AkarinGlobalConfig.init(new File("akarin.yml"));
|
AkarinGlobalConfig.init(new File("akarin.yml"));
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.Overwrite;
|
|||||||
import net.minecraft.server.EULA;
|
import net.minecraft.server.EULA;
|
||||||
|
|
||||||
@Mixin(value = EULA.class, remap = false)
|
@Mixin(value = EULA.class, remap = false)
|
||||||
public class DummyEula {
|
public abstract class DummyEula {
|
||||||
/**
|
/**
|
||||||
* Read then check the EULA file <i>formerly</i>
|
* Read then check the EULA file <i>formerly</i>
|
||||||
* @param file
|
* @param file
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import com.destroystokyo.paper.Metrics;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
@Mixin(targets = "com.destroystokyo.paper.Metrics$PaperMetrics", remap = false)
|
@Mixin(targets = "com.destroystokyo.paper.Metrics$PaperMetrics", remap = false)
|
||||||
public class MetricsBootstrap {
|
public abstract class MetricsBootstrap {
|
||||||
@Overwrite
|
@Overwrite
|
||||||
static void startMetrics() {
|
static void startMetrics() {
|
||||||
// Get the config file
|
// Get the config file
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import com.destroystokyo.paper.Metrics;
|
|||||||
import com.destroystokyo.paper.Metrics.CustomChart;
|
import com.destroystokyo.paper.Metrics.CustomChart;
|
||||||
|
|
||||||
@Mixin(value = Metrics.class, remap = false)
|
@Mixin(value = Metrics.class, remap = false)
|
||||||
public class MixinMetrics {
|
public abstract class MixinMetrics {
|
||||||
// The url to which the data is sent - bukkit/Torch (keep our old name)
|
// The url to which the data is sent - bukkit/Torch (keep our old name)
|
||||||
private final static String URL = "https://bStats.org/submitData/bukkit";
|
private final static String URL = "https://bStats.org/submitData/bukkit";
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package io.akarin.server.mixin.bootstrap;
|
||||||
|
|
||||||
|
import org.spigotmc.RestartCommand;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
|
||||||
|
@Mixin(value = RestartCommand.class, remap = false)
|
||||||
|
public abstract class MixinRestartCommand {
|
||||||
|
@Inject(method = "restart()V", at = @At("HEAD"))
|
||||||
|
private static void beforeRestart(CallbackInfo ci) {
|
||||||
|
if (AkarinGlobalConfig.noResponseDoGC) {
|
||||||
|
Akari.logger.warn("Attempting to garbage collect, may takes a few seconds");
|
||||||
|
System.runFinalization();
|
||||||
|
System.gc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import io.akarin.api.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
|
||||||
import net.minecraft.server.BiomeBase;
|
import net.minecraft.server.BiomeBase;
|
||||||
import net.minecraft.server.Block;
|
import net.minecraft.server.Block;
|
||||||
import net.minecraft.server.BlockFire;
|
import net.minecraft.server.BlockFire;
|
||||||
@@ -25,7 +24,7 @@ import net.minecraft.server.PotionRegistry;
|
|||||||
import net.minecraft.server.SoundEffect;
|
import net.minecraft.server.SoundEffect;
|
||||||
|
|
||||||
@Mixin(value = DispenserRegistry.class, remap = false)
|
@Mixin(value = DispenserRegistry.class, remap = false)
|
||||||
public class ParallelRegistry {
|
public abstract class ParallelRegistry {
|
||||||
/**
|
/**
|
||||||
* Registry order: SoundEffect -> Block
|
* Registry order: SoundEffect -> Block
|
||||||
*/
|
*/
|
||||||
@@ -40,8 +39,6 @@ public class ParallelRegistry {
|
|||||||
*/
|
*/
|
||||||
private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY);
|
private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY);
|
||||||
|
|
||||||
private static final int TERMINATION_IN_SEC = AkarinGlobalConfig.registryTerminationSeconds;
|
|
||||||
|
|
||||||
// We should keep the original order in codes thought orderless in runtime
|
// We should keep the original order in codes thought orderless in runtime
|
||||||
@Redirect(method = "c()V", at = @At(
|
@Redirect(method = "c()V", at = @At(
|
||||||
value = "INVOKE",
|
value = "INVOKE",
|
||||||
@@ -131,11 +128,12 @@ public class ParallelRegistry {
|
|||||||
// Shutdown BLOCK and STANDALONE stage
|
// Shutdown BLOCK and STANDALONE stage
|
||||||
STAGE_STANDALONE.shutdown();
|
STAGE_STANDALONE.shutdown();
|
||||||
STAGE_BLOCK.shutdown();
|
STAGE_BLOCK.shutdown();
|
||||||
STAGE_BLOCK.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS);
|
STAGE_BLOCK.awaitTermination(10, TimeUnit.MINUTES);
|
||||||
|
|
||||||
STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated
|
STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated
|
||||||
STAGE_BLOCK_BASE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS);
|
STAGE_BLOCK_BASE.awaitTermination(20, TimeUnit.MINUTES);
|
||||||
|
|
||||||
STAGE_STANDALONE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); // Behind the shutdown of BLOCK_BASE should faster
|
STAGE_STANDALONE.awaitTermination(30, TimeUnit.MINUTES); // Behind the shutdown of BLOCK_BASE should faster
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -20,9 +19,13 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
@Mixin(value = WatchdogThread.class, remap = false)
|
@Mixin(value = WatchdogThread.class, remap = false)
|
||||||
public 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 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();
|
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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
package io.akarin.server.mixin.core;
|
|
||||||
|
|
||||||
import org.spigotmc.AsyncCatcher;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
|
|
||||||
@Mixin(value = AsyncCatcher.class, remap = false)
|
|
||||||
public class DesyncCatcher {
|
|
||||||
@Shadow public static boolean enabled;
|
|
||||||
|
|
||||||
@Overwrite
|
|
||||||
public static void catchOp(String reason) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import org.spigotmc.AsyncCatcher;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
|
||||||
|
@Mixin(value = AsyncCatcher.class, remap = false)
|
||||||
|
public abstract class MixinAsyncCatcher {
|
||||||
|
@Shadow public static boolean enabled;
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public static void catchOp(String reason) {
|
||||||
|
if (enabled) {
|
||||||
|
if (Akari.isPrimaryThread()) return;
|
||||||
|
|
||||||
|
if (AkarinGlobalConfig.throwOnAsyncCaught) {
|
||||||
|
throw new IllegalStateException("Asynchronous " + reason + "!");
|
||||||
|
} else {
|
||||||
|
Akari.logger.warn("Asynchronous " + reason + "!");
|
||||||
|
Thread.dumpStack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import io.akarin.server.core.AkarinGlobalConfig;
|
|||||||
import net.minecraft.server.Chunk;
|
import net.minecraft.server.Chunk;
|
||||||
|
|
||||||
@Mixin(value = ChunkIOExecutor.class, remap = false)
|
@Mixin(value = ChunkIOExecutor.class, remap = false)
|
||||||
public class MixinChunkIOExecutor {
|
public abstract class MixinChunkIOExecutor {
|
||||||
@Shadow @Final static int BASE_THREADS;
|
@Shadow @Final static int BASE_THREADS;
|
||||||
@Shadow @Mutable @Final static int PLAYERS_PER_THREAD;
|
@Shadow @Mutable @Final static int PLAYERS_PER_THREAD;
|
||||||
@Shadow @Final private static AsynchronousExecutor<?, Chunk, Runnable, RuntimeException> instance;
|
@Shadow @Final private static AsynchronousExecutor<?, Chunk, Runnable, RuntimeException> instance;
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.ChunkSection;
|
||||||
|
|
||||||
|
@Mixin(value = ChunkSection.class, remap = false)
|
||||||
|
public abstract class MixinChunkSection {
|
||||||
|
@Shadow private int nonEmptyBlockCount;
|
||||||
|
|
||||||
|
@Overwrite // OBFHELPER: isEmpty
|
||||||
|
public boolean a() {
|
||||||
|
return AkarinGlobalConfig.sendLightOnlyChunkSection ? false : nonEmptyBlockCount == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.CommandAbstract;
|
||||||
|
import net.minecraft.server.CommandBan;
|
||||||
|
import net.minecraft.server.CommandException;
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.ExceptionUsage;
|
||||||
|
import net.minecraft.server.GameProfileBanEntry;
|
||||||
|
import net.minecraft.server.ICommand;
|
||||||
|
import net.minecraft.server.ICommandListener;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
@Mixin(value = CommandBan.class, remap = false)
|
||||||
|
public abstract class MixinCommandBan {
|
||||||
|
@Overwrite
|
||||||
|
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
|
||||||
|
if (args.length >= 1 && args[0].length() > 1) {
|
||||||
|
GameProfile profile = server.getUserCache().getProfile(args[0]);
|
||||||
|
|
||||||
|
if (profile == null) {
|
||||||
|
throw new CommandException("commands.ban.failed", new Object[] {args[0]});
|
||||||
|
} else {
|
||||||
|
// Akarin start - use string
|
||||||
|
boolean hasReason = true; // Akarin
|
||||||
|
String message = null;
|
||||||
|
if (args.length >= 2) {
|
||||||
|
message = "";
|
||||||
|
for (int i = 2; i < args.length; i++) {
|
||||||
|
message = message + args[i];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasReason = false; // Akarin
|
||||||
|
message = Akari.EMPTY_STRING; // Akarin - modify message
|
||||||
|
}
|
||||||
|
// Akarin end
|
||||||
|
|
||||||
|
GameProfileBanEntry entry = new GameProfileBanEntry(profile, (Date) null, sender.getName(), (Date) null, message);
|
||||||
|
|
||||||
|
server.getPlayerList().getProfileBans().add(entry);
|
||||||
|
EntityPlayer entityplayer = server.getPlayerList().getPlayer(args[0]);
|
||||||
|
|
||||||
|
if (entityplayer != null) {
|
||||||
|
entityplayer.playerConnection.disconnect(hasReason ? message : AkarinGlobalConfig.messageBan);
|
||||||
|
}
|
||||||
|
|
||||||
|
CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // OBFHELPER: notifyCommandListener
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ExceptionUsage("commands.ban.usage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.CommandAbstract;
|
||||||
|
import net.minecraft.server.CommandBanIp;
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.ICommand;
|
||||||
|
import net.minecraft.server.ICommandListener;
|
||||||
|
import net.minecraft.server.IpBanEntry;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
@Mixin(value = CommandBanIp.class, remap = false)
|
||||||
|
public abstract class MixinCommandBanIp {
|
||||||
|
@Overwrite // OBFHELPER: banIp
|
||||||
|
protected void a(MinecraftServer server, ICommandListener sender, String args, @Nullable String banReason) {
|
||||||
|
// Akarin start - modify message
|
||||||
|
boolean hasReason = true;
|
||||||
|
if (banReason == null) {
|
||||||
|
banReason = Akari.EMPTY_STRING;
|
||||||
|
hasReason = false;
|
||||||
|
}
|
||||||
|
// Akarin end
|
||||||
|
IpBanEntry ipbanentry = new IpBanEntry(args, (Date) null, sender.getName(), (Date) null, banReason);
|
||||||
|
|
||||||
|
server.getPlayerList().getIPBans().add(ipbanentry);
|
||||||
|
List<EntityPlayer> withIpPlayers = server.getPlayerList().b(args); // OBFHELPER: getPlayersMatchingAddress
|
||||||
|
String[] banPlayerNames = new String[withIpPlayers.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < banPlayerNames.length; i++) {
|
||||||
|
EntityPlayer each = withIpPlayers.get(i);
|
||||||
|
banPlayerNames[i] = each.getName();
|
||||||
|
each.playerConnection.disconnect(hasReason ? banReason : AkarinGlobalConfig.messageBanIp); // Akarin
|
||||||
|
}
|
||||||
|
|
||||||
|
if (withIpPlayers.isEmpty()) {
|
||||||
|
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // OBFHELPER: notifyCommandListener
|
||||||
|
} else {
|
||||||
|
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // OBFHELPER: notifyCommandListener - joinNiceString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.CommandAbstract;
|
||||||
|
import net.minecraft.server.CommandException;
|
||||||
|
import net.minecraft.server.CommandKick;
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.ExceptionPlayerNotFound;
|
||||||
|
import net.minecraft.server.ExceptionUsage;
|
||||||
|
import net.minecraft.server.ICommand;
|
||||||
|
import net.minecraft.server.ICommandListener;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
@Mixin(value = CommandKick.class, remap = false)
|
||||||
|
public abstract class MixinCommandKick {
|
||||||
|
@Overwrite
|
||||||
|
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
|
||||||
|
if (args.length > 0 && args[0].length() > 1) {
|
||||||
|
EntityPlayer target = server.getPlayerList().getPlayer(args[0]);
|
||||||
|
|
||||||
|
if (target == null) {
|
||||||
|
throw new ExceptionPlayerNotFound("commands.generic.player.notFound", args[0]);
|
||||||
|
} else {
|
||||||
|
if (args.length >= 2) {
|
||||||
|
// Akarin start - use string
|
||||||
|
String message = "";
|
||||||
|
for (int i = 2; i < args.length; i++) {
|
||||||
|
message = message + args[i];
|
||||||
|
}
|
||||||
|
target.playerConnection.disconnect(message);
|
||||||
|
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // OBFHELPER: notifyCommandListener
|
||||||
|
// Akarin end
|
||||||
|
} else {
|
||||||
|
target.playerConnection.disconnect(AkarinGlobalConfig.messageKick); // Akarin
|
||||||
|
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // OBFHELPER: notifyCommandListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ExceptionUsage("commands.kick.usage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,19 +7,40 @@ import org.spongepowered.asm.mixin.Mutable;
|
|||||||
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 io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
@Mixin(value = CraftServer.class, remap = false)
|
@Mixin(value = CraftServer.class, remap = false)
|
||||||
public class MixinCraftServer {
|
public abstract class MixinCraftServer {
|
||||||
@Shadow @Final @Mutable private String serverName;
|
@Shadow @Final @Mutable private String serverName;
|
||||||
|
@Shadow @Final @Mutable private String serverVersion;
|
||||||
|
@Shadow @Final protected MinecraftServer console;
|
||||||
private boolean needApplyServerName = true;
|
private boolean needApplyServerName = true;
|
||||||
|
private boolean needApplyServerVersion = true;
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public String getName() {
|
public String getName() {
|
||||||
// We cannot apply the name modification in <init> method,
|
// We cannot apply the name modification in <init> method,
|
||||||
// cause the initializer will be added to the tail
|
// cause the initializer will be added to the tail
|
||||||
if (needApplyServerName) {
|
if (needApplyServerName) {
|
||||||
serverName = "Akarin";
|
serverName = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? "Akarin" : AkarinGlobalConfig.serverBrandName;
|
||||||
needApplyServerName = false;
|
needApplyServerName = false;
|
||||||
}
|
}
|
||||||
return serverName;
|
return serverName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public String getVersion() {
|
||||||
|
if (needApplyServerVersion) {
|
||||||
|
serverVersion = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? serverVersion : serverVersion.replace("Akarin", AkarinGlobalConfig.serverBrandName);
|
||||||
|
needApplyServerVersion = false;
|
||||||
|
}
|
||||||
|
return serverVersion + " (MC: " + console.getVersion() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public boolean isPrimaryThread() {
|
||||||
|
return Akari.isPrimaryThread();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,16 +1,38 @@
|
|||||||
package io.akarin.server.mixin.core;
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.bukkit.craftbukkit.util.Waitable;
|
||||||
|
import org.spigotmc.AsyncCatcher;
|
||||||
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 io.akarin.api.internal.Akari;
|
||||||
import net.minecraft.server.MCUtil;
|
import net.minecraft.server.MCUtil;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
@Mixin(value = MCUtil.class, remap = false)
|
@Mixin(value = MCUtil.class, remap = false)
|
||||||
public class MixinMCUtil {
|
public abstract class MixinMCUtil {
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public static <T> T ensureMain(String reason, Supplier<T> run) {
|
public static <T> T ensureMain(String reason, Supplier<T> run) {
|
||||||
|
if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) {
|
||||||
|
new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
|
||||||
|
Waitable<T> wait = new Waitable<T>() {
|
||||||
|
@Override
|
||||||
|
protected T evaluate() {
|
||||||
|
return run.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MinecraftServer.getServer().processQueue.add(wait);
|
||||||
|
try {
|
||||||
|
return wait.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return run.get();
|
return run.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,18 @@ 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.Executors;
|
||||||
import java.util.concurrent.FutureTask;
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.WordUtils;
|
||||||
|
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;
|
||||||
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
import org.bukkit.event.inventory.InventoryMoveItemEvent;
|
||||||
|
import org.bukkit.event.world.WorldLoadEvent;
|
||||||
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.Mutable;
|
import org.spongepowered.asm.mixin.Mutable;
|
||||||
@@ -16,9 +24,12 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import co.aikar.timings.MinecraftTimings;
|
import co.aikar.timings.MinecraftTimings;
|
||||||
import io.akarin.api.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
|
import io.akarin.api.internal.Akari.AssignableFactory;
|
||||||
|
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.CrashReport;
|
import net.minecraft.server.CrashReport;
|
||||||
import net.minecraft.server.CustomFunctionData;
|
import net.minecraft.server.CustomFunctionData;
|
||||||
import net.minecraft.server.ITickable;
|
import net.minecraft.server.ITickable;
|
||||||
@@ -32,12 +43,38 @@ import net.minecraft.server.TileEntityHopper;
|
|||||||
import net.minecraft.server.WorldServer;
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
@Mixin(value = MinecraftServer.class, remap = false)
|
@Mixin(value = MinecraftServer.class, remap = false)
|
||||||
public class MixinMinecraftServer {
|
public abstract class MixinMinecraftServer {
|
||||||
|
@Shadow @Final public Thread primaryThread;
|
||||||
|
private boolean tickedPrimaryEntities;
|
||||||
|
private int cachedWorldSize;
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public String getServerModName() {
|
public String getServerModName() {
|
||||||
return "Akarin";
|
return "Akarin";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Inject(method = "run()V", at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "net/minecraft/server/MinecraftServer.aw()J",
|
||||||
|
shift = At.Shift.BEFORE
|
||||||
|
))
|
||||||
|
private void prerun(CallbackInfo info) {
|
||||||
|
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
|
||||||
|
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
|
||||||
|
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
|
||||||
|
|
||||||
|
for (int i = 0; i < worlds.size(); ++i) {
|
||||||
|
WorldServer world = worlds.get(i);
|
||||||
|
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
|
||||||
|
}
|
||||||
|
AkarinSlackScheduler.get().boot();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public boolean isMainThread() {
|
||||||
|
return Akari.isPrimaryThread();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Forcely disable snooper
|
* Forcely disable snooper
|
||||||
*/
|
*/
|
||||||
@@ -47,35 +84,81 @@ public class MixinMinecraftServer {
|
|||||||
@Overwrite
|
@Overwrite
|
||||||
public void b(MojangStatisticsGenerator generator) {}
|
public void b(MojangStatisticsGenerator generator) {}
|
||||||
|
|
||||||
@Inject(method = "run()V", at = @At(
|
/*
|
||||||
value = "INVOKE",
|
* Parallel spawn chunks generation
|
||||||
target = "net/minecraft/server/MinecraftServer.aw()J",
|
*/
|
||||||
shift = At.Shift.BEFORE
|
@Shadow public abstract boolean isRunning();
|
||||||
))
|
@Shadow(aliases = "a_") protected abstract void output(String s, int i);
|
||||||
private void prerun(CallbackInfo info) {
|
@Shadow(aliases = "t") protected abstract void enablePluginsPostWorld();
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
|
||||||
WorldServer world = worlds.get(i);
|
private void prepareChunks(WorldServer world, int index) {
|
||||||
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
|
MinecraftServer.LOGGER.info("Preparing start region for level " + index + " (Seed: " + world.getSeed() + ")");
|
||||||
}
|
BlockPosition spawnPos = world.getSpawn();
|
||||||
|
long lastRecord = System.currentTimeMillis();
|
||||||
|
|
||||||
AkarinSlackScheduler.boot();
|
int preparedChunks = 0;
|
||||||
|
short radius = world.paperConfig.keepLoadedRange;
|
||||||
|
for (int skipX = -radius; skipX <= radius && isRunning(); skipX += 16) {
|
||||||
|
for (int skipZ = -radius; skipZ <= radius && isRunning(); skipZ += 16) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if (now - lastRecord > 1000L) {
|
||||||
|
output("Preparing spawn area (level " + index + ") ", preparedChunks * 100 / 625);
|
||||||
|
lastRecord = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
preparedChunks++;
|
||||||
|
world.getChunkProviderServer().getChunkAt(spawnPos.getX() + skipX >> 4, spawnPos.getZ() + skipZ >> 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
protected void l() throws InterruptedException {
|
||||||
|
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
|
||||||
|
|
||||||
|
for (int index = 0; index < worlds.size(); index++) {
|
||||||
|
WorldServer world = this.worlds.get(index);
|
||||||
|
if (!world.getWorld().getKeepSpawnInMemory()) continue;
|
||||||
|
|
||||||
|
int fIndex = index;
|
||||||
|
executor.submit(() -> prepareChunks(world, fIndex), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (WorldServer world : this.worlds) {
|
||||||
|
if (world.getWorld().getKeepSpawnInMemory()) executor.take();
|
||||||
|
}
|
||||||
|
if (WorldLoadEvent.getHandlerList().getRegisteredListeners().length != 0) {
|
||||||
|
for (WorldServer world : this.worlds) {
|
||||||
|
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enablePluginsPostWorld();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parallel world ticking
|
||||||
|
*/
|
||||||
@Shadow public CraftServer server;
|
@Shadow public CraftServer server;
|
||||||
@Shadow @Mutable protected Queue<FutureTask<?>> j;
|
@Shadow @Mutable protected Queue<FutureTask<?>> j;
|
||||||
@Shadow public Queue<Runnable> processQueue;
|
@Shadow public Queue<Runnable> processQueue;
|
||||||
@Shadow private int ticks;
|
@Shadow private int ticks;
|
||||||
@Shadow public List<WorldServer> worlds;
|
@Shadow public List<WorldServer> worlds;
|
||||||
@Shadow private PlayerList v;
|
@Shadow(aliases = "v") private PlayerList playerList;
|
||||||
@Shadow @Final private List<ITickable> o;
|
@Shadow(aliases = "o") @Final private List<ITickable> tickables;
|
||||||
|
|
||||||
@Shadow public PlayerList getPlayerList() { return null; }
|
@Shadow public abstract PlayerList getPlayerList();
|
||||||
@Shadow public ServerConnection an() { return null; }
|
@Shadow(aliases = "an") public abstract ServerConnection serverConnection();
|
||||||
@Shadow public CustomFunctionData aL() { return null; }
|
@Shadow(aliases = "aL") public abstract CustomFunctionData functionManager();
|
||||||
|
|
||||||
private void 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 {
|
||||||
@@ -86,11 +169,14 @@ public class MixinMinecraftServer {
|
|||||||
world.a(crashreport);
|
world.a(crashreport);
|
||||||
throw new ReportedException(crashreport);
|
throw new ReportedException(crashreport);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@@ -104,7 +190,7 @@ public class MixinMinecraftServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void D() throws InterruptedException {
|
public void D() throws InterruptedException, ExecutionException, CancellationException {
|
||||||
Runnable runnable;
|
Runnable runnable;
|
||||||
Akari.callbackTiming.startTiming();
|
Akari.callbackTiming.startTiming();
|
||||||
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||||
@@ -130,75 +216,108 @@ public 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:
|
||||||
worlds.get(i).timings.tickEntities.startTiming();
|
case 2:
|
||||||
worlds.get(i).timings.doTick.startTiming();
|
default:
|
||||||
}
|
// Never tick one world concurrently!
|
||||||
}
|
for (int i = 0; i < cachedWorldSize; i++) {
|
||||||
Akari.silentTiming = true; // Disable timings
|
// Impl Note:
|
||||||
Akari.STAGE_TICK.submit(() -> {
|
// Entities ticking: index 1 -> ... -> 0 (parallel)
|
||||||
// Never tick one world concurrently!
|
// World ticking: index 0 -> ... (parallel)
|
||||||
// TODO better treat world index
|
int interlace = i + 1;
|
||||||
for (int i = 1; i <= worlds.size(); ++i) {
|
WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
|
||||||
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
|
Akari.STAGE_TICK.submit(() -> {
|
||||||
synchronized (world.tickLock) {
|
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 (world.tickLock) {
|
|
||||||
tickWorld(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Akari.STAGE_TICK.take();
|
|
||||||
Akari.silentTiming = false; // Enable timings
|
|
||||||
Akari.worldTiming.stopTiming();
|
|
||||||
if (AkarinGlobalConfig.legacyWorldTimings) {
|
|
||||||
for (int i = 0; i < worlds.size(); ++i) {
|
|
||||||
worlds.get(i).timings.tickEntities.stopTiming();
|
|
||||||
worlds.get(i).timings.doTick.startTiming();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
tickConflictSync(world);
|
|
||||||
|
|
||||||
world.getTracker().updatePlayers();
|
|
||||||
world.explosionDensityCache.clear(); // Paper - Optimize explosions
|
|
||||||
}
|
|
||||||
|
|
||||||
MinecraftTimings.connectionTimer.startTiming();
|
MinecraftTimings.connectionTimer.startTiming();
|
||||||
this.an().c();
|
serverConnection().c();
|
||||||
MinecraftTimings.connectionTimer.stopTiming();
|
MinecraftTimings.connectionTimer.stopTiming();
|
||||||
|
|
||||||
MinecraftTimings.playerListTimer.startTiming();
|
Akari.callbackTiming.startTiming();
|
||||||
this.v.tick();
|
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
|
||||||
MinecraftTimings.playerListTimer.stopTiming();
|
Akari.callbackTiming.stopTiming();
|
||||||
|
|
||||||
MinecraftTimings.commandFunctionsTimer.startTiming();
|
MinecraftTimings.commandFunctionsTimer.startTiming();
|
||||||
this.aL().e();
|
functionManager().e();
|
||||||
MinecraftTimings.commandFunctionsTimer.stopTiming();
|
MinecraftTimings.commandFunctionsTimer.stopTiming();
|
||||||
|
|
||||||
MinecraftTimings.tickablesTimer.startTiming();
|
MinecraftTimings.tickablesTimer.startTiming();
|
||||||
for (int i = 0; i < this.o.size(); ++i) {
|
for (int i = 0; i < this.tickables.size(); ++i) {
|
||||||
this.o.get(i).e();
|
tickables.get(i).e();
|
||||||
}
|
}
|
||||||
MinecraftTimings.tickablesTimer.stopTiming();
|
MinecraftTimings.tickablesTimer.stopTiming();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tickConflictSync(WorldServer world) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import co.aikar.timings.MinecraftTimings;
|
||||||
|
import co.aikar.timings.Timing;
|
||||||
|
import io.akarin.api.internal.Akari;
|
||||||
|
import net.minecraft.server.CancelledPacketHandleException;
|
||||||
|
import net.minecraft.server.IAsyncTaskHandler;
|
||||||
|
import net.minecraft.server.Packet;
|
||||||
|
import net.minecraft.server.PacketListener;
|
||||||
|
import net.minecraft.server.PlayerConnectionUtils;
|
||||||
|
|
||||||
|
@Mixin(value = PlayerConnectionUtils.class, remap = false)
|
||||||
|
public abstract class MixinPlayerConnectionUtils {
|
||||||
|
@Overwrite
|
||||||
|
public static <T extends PacketListener> void ensureMainThread(final Packet<T> packet, final T listener, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException {
|
||||||
|
if (!iasynctaskhandler.isMainThread()) {
|
||||||
|
Timing timing = MinecraftTimings.getPacketTiming(packet);
|
||||||
|
// MinecraftServer#postToMainThread inlined thread check, no twice
|
||||||
|
Akari.callbackQueue.add(() -> {
|
||||||
|
try (Timing ignored = timing.startTiming()) {
|
||||||
|
packet.a(listener);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
throw CancelledPacketHandleException.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package io.akarin.server.mixin.core;
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -7,49 +9,55 @@ 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 co.aikar.timings.Timing;
|
||||||
import io.akarin.api.Akari;
|
import io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
|
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
|
||||||
public class MixinTimingHandler {
|
public abstract class MixinTimingHandler {
|
||||||
@Shadow @Final String name;
|
@Shadow @Final String name;
|
||||||
@Shadow private boolean enabled;
|
@Shadow private boolean enabled;
|
||||||
@Shadow private volatile long start;
|
@Shadow private long start;
|
||||||
@Shadow private volatile int timingDepth;
|
@Shadow private int timingDepth;
|
||||||
|
|
||||||
|
@Shadow abstract void addDiff(long diff);
|
||||||
|
@Shadow public abstract Timing startTiming();
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public Timing startTimingIfSync() {
|
||||||
|
startTiming();
|
||||||
|
return (Timing) this;
|
||||||
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void stopTimingIfSync() {
|
public void stopTimingIfSync() {
|
||||||
if (Bukkit.isPrimaryThread()) {
|
if (Akari.isPrimaryThread(false)) {
|
||||||
stopTiming(true); // Avoid twice thread check
|
stopTiming(true); // Avoid twice thread check
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void stopTiming() {
|
public void stopTiming() {
|
||||||
// Akarin start - avoid twice thread check
|
|
||||||
stopTiming(false);
|
stopTiming(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Shadow void addDiff(long diff) {}
|
public void stopTiming(long start) {
|
||||||
|
if (enabled) addDiff(System.nanoTime() - start);
|
||||||
|
}
|
||||||
|
|
||||||
public void stopTiming(boolean sync) {
|
public void stopTiming(boolean alreadySync) {
|
||||||
if (enabled && --timingDepth == 0 && start != 0) {
|
if (!enabled || --timingDepth != 0 || start == 0) return;
|
||||||
if (Akari.silentTiming) { // It must be off-main thread now
|
if (!alreadySync) {
|
||||||
|
Thread curThread = Thread.currentThread();
|
||||||
|
if (curThread != MinecraftServer.getServer().primaryThread) {
|
||||||
start = 0;
|
start = 0;
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
if (!sync && !Bukkit.isPrimaryThread()) {
|
|
||||||
if (AkarinGlobalConfig.silentAsyncTimings) {
|
|
||||||
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
|
|
||||||
new Throwable().printStackTrace();
|
|
||||||
}
|
|
||||||
start = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
addDiff(System.nanoTime() - start);
|
|
||||||
start = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Safety ensured
|
||||||
|
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 io.akarin.api.Akari;
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
|
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 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
|
||||||
@@ -59,6 +96,9 @@ public class MixinVersionCommand {
|
|||||||
// This should be lying in 'obtainVersion' method, but bump for faster returning
|
// This should be lying in 'obtainVersion' method, but bump for faster returning
|
||||||
if (customVersion) return;
|
if (customVersion) return;
|
||||||
|
|
||||||
|
synchronized (versionWaiters) {
|
||||||
|
versionWaiters.add(sender);
|
||||||
|
}
|
||||||
if (versionObtaining) return;
|
if (versionObtaining) return;
|
||||||
// The volatile guarantees the safety between different threads.
|
// The volatile guarantees the safety between different threads.
|
||||||
// Remembers that we are still on main thread now,
|
// Remembers that we are still on main thread now,
|
||||||
@@ -70,7 +110,7 @@ public class MixinVersionCommand {
|
|||||||
|
|
||||||
if (hasVersion) {
|
if (hasVersion) {
|
||||||
long current = System.currentTimeMillis();
|
long current = System.currentTimeMillis();
|
||||||
if (current - lastCheckMillis > 7200000 /* 2 hours */) {
|
if (current - lastCheckMillis > AkarinGlobalConfig.versionUpdateInterval) {
|
||||||
lastCheckMillis = current;
|
lastCheckMillis = current;
|
||||||
hasVersion = false;
|
hasVersion = false;
|
||||||
} else {
|
} else {
|
||||||
@@ -100,11 +140,9 @@ public class MixinVersionCommand {
|
|||||||
// We post all things because a custom version is rare (expiring is not rare),
|
// We post all things because a custom version is rare (expiring is not rare),
|
||||||
// and we'd better post this task as early as we can, since it's a will (horrible destiny).
|
// and we'd better post this task as early as we can, since it's a will (horrible destiny).
|
||||||
MCUtil.scheduleAsyncTask(() -> {
|
MCUtil.scheduleAsyncTask(() -> {
|
||||||
// This should be lying in 'sendVersion' method, but comes here for relax main thread
|
|
||||||
versionWaiters.add(sender);
|
|
||||||
sender.sendMessage("Checking version, please wait...");
|
sender.sendMessage("Checking version, please wait...");
|
||||||
|
|
||||||
String version = Bukkit.getVersion();
|
String version = Akari.getServerVersion();
|
||||||
if (version == null) {
|
if (version == null) {
|
||||||
version = "Unique"; // Custom - > Unique
|
version = "Unique"; // Custom - > Unique
|
||||||
customVersion = true;
|
customVersion = true;
|
||||||
@@ -140,9 +178,11 @@ public class MixinVersionCommand {
|
|||||||
versionMessage = message;
|
versionMessage = message;
|
||||||
hasVersion = true;
|
hasVersion = true;
|
||||||
|
|
||||||
for (CommandSender sender : versionWaiters) {
|
synchronized (versionWaiters) {
|
||||||
sender.sendMessage(versionMessage);
|
for (CommandSender sender : versionWaiters) {
|
||||||
|
sender.sendMessage(versionMessage);
|
||||||
|
}
|
||||||
|
versionWaiters.clear();
|
||||||
}
|
}
|
||||||
versionWaiters.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import net.minecraft.server.AxisAlignedBB;
|
||||||
|
import net.minecraft.server.Entity;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fixes MC-103516(https://bugs.mojang.com/browse/MC-103516)
|
||||||
|
*/
|
||||||
|
@Mixin(value = World.class, remap = false)
|
||||||
|
public abstract class MixinWorld {
|
||||||
|
@Shadow public abstract List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB box);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity
|
||||||
|
*/
|
||||||
|
@Overwrite
|
||||||
|
public boolean a(AxisAlignedBB box, @Nullable Entity target) { // OBFHELPER: checkNoEntityCollision
|
||||||
|
List<Entity> list = this.getEntities(null, box);
|
||||||
|
|
||||||
|
for (Entity each : list) {
|
||||||
|
if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // OBFHELPER: preventEntitySpawning - isRidingSameEntity
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import net.minecraft.server.Entity;
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.WorldManager;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@Mixin(value = WorldManager.class, remap = false)
|
||||||
|
public abstract class MixinWorldManager {
|
||||||
|
@Shadow @Final private WorldServer world;
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public void a(Entity entity) {
|
||||||
|
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
|
||||||
|
this.world.getTracker().track(entity);
|
||||||
|
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
|
||||||
|
if (entity instanceof EntityPlayer) {
|
||||||
|
this.world.worldProvider.a((EntityPlayer) entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package io.akarin.server.mixin.core;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import io.akarin.api.internal.mixin.IMixinWorldServer;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@Mixin(value = WorldServer.class, remap = false)
|
||||||
|
public abstract class MixinWorldServer implements IMixinWorldServer {
|
||||||
|
private final Object tickLock = new Object();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object lock() {
|
||||||
|
return tickLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private boolean locked = false;
|
||||||
|
@Override
|
||||||
|
public synchronized void setSeed(long seed) {
|
||||||
|
if (locked) {
|
||||||
|
LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
|
||||||
|
} else {
|
||||||
|
super.setSeed(seed);
|
||||||
|
locked = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Random rand() {
|
||||||
|
return sharedRandom;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +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 class MonsterEggGuardian {
|
|
||||||
|
|
||||||
@Redirect(method = "a", at = @At(
|
|
||||||
value = "FIELD",
|
|
||||||
target = "net/minecraft/server/Blocks.MOB_SPAWNER:Lnet/minecraft/server/Block;",
|
|
||||||
opcode = Opcodes.GETSTATIC
|
|
||||||
))
|
|
||||||
private boolean configurable(Block target) {
|
|
||||||
return target == Blocks.MOB_SPAWNER && AkarinGlobalConfig.allowSpawnerModify;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package io.akarin.server.mixin.cps;
|
||||||
|
|
||||||
|
import org.spigotmc.SlackActivityAccountant;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
|
||||||
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectIterator;
|
||||||
|
import net.minecraft.server.Chunk;
|
||||||
|
import net.minecraft.server.ChunkProviderServer;
|
||||||
|
import net.minecraft.server.IChunkLoader;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@Mixin(value = ChunkProviderServer.class, remap = false)
|
||||||
|
public abstract class MixinChunkProviderServer {
|
||||||
|
@Shadow @Final public WorldServer world;
|
||||||
|
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
|
||||||
|
|
||||||
|
public void unload(Chunk chunk) {
|
||||||
|
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
|
||||||
|
// Akarin - avoid using the queue and simply check the unloaded flag during unloads
|
||||||
|
// this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
|
||||||
|
chunk.setShouldUnload(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Shadow public abstract boolean unloadChunk(Chunk chunk, boolean save);
|
||||||
|
@Shadow @Final private IChunkLoader chunkLoader;
|
||||||
|
@Shadow @Final private static double UNLOAD_QUEUE_RESIZE_FACTOR;
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public boolean unloadChunks() {
|
||||||
|
if (!this.world.savingDisabled) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
|
||||||
|
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
|
||||||
|
activityAccountant.startActivity(0.5);
|
||||||
|
|
||||||
|
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
|
||||||
|
int remainingChunks = chunks.size();
|
||||||
|
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
|
||||||
|
|
||||||
|
while (it.hasNext()) {
|
||||||
|
Entry<Chunk> entry = it.next();
|
||||||
|
Chunk chunk = entry.getValue();
|
||||||
|
|
||||||
|
if (chunk != null && chunk.isUnloading()) {
|
||||||
|
if (chunk.scheduledForUnload != null) {
|
||||||
|
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unloadChunk(chunk, true)) {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
chunk.setShouldUnload(false);
|
||||||
|
chunk.scheduledForUnload = null;
|
||||||
|
|
||||||
|
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activityAccountant.endActivity();
|
||||||
|
this.chunkLoader.b(); // OBFHELPER: chunkTick
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "unloadChunk", at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "it/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap.remove(J)Ljava/lang/Object;"
|
||||||
|
))
|
||||||
|
private Object remove(Long2ObjectOpenHashMap<Chunk> chunks, long chunkHash) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
public String getName() {
|
||||||
|
return "ServerChunkCache: " + chunks.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package io.akarin.server.mixin.cps;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.bukkit.craftbukkit.CraftWorld;
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import net.minecraft.server.Chunk;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@Mixin(value = CraftWorld.class, remap = false)
|
||||||
|
public abstract class MixinCraftWorld {
|
||||||
|
@Shadow @Final private WorldServer world;
|
||||||
|
|
||||||
|
@Redirect(method = "processChunkGC()V", at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "java/util/Set.contains(Ljava/lang/Object;)Z",
|
||||||
|
opcode = Opcodes.INVOKEINTERFACE
|
||||||
|
))
|
||||||
|
public boolean checkUnloading(Set<Long> set, Object chunkHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "regenerateChunk", at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "java/util/Set.remove(Ljava/lang/Object;)Z",
|
||||||
|
opcode = Opcodes.INVOKEINTERFACE
|
||||||
|
))
|
||||||
|
public boolean regenChunk(Set<Long> set, Object chunkHash) {
|
||||||
|
Chunk chunk = world.getChunkProviderServer().chunks.get(chunkHash);
|
||||||
|
if (chunk != null) chunk.setShouldUnload(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package io.akarin.server.mixin.nsc;
|
|
||||||
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Overwrite;
|
|
||||||
|
|
||||||
import net.minecraft.server.PlayerConnection;
|
|
||||||
|
|
||||||
@Mixin(value = PlayerConnection.class, remap = false)
|
|
||||||
public class MixinPlayerConnection {
|
|
||||||
@Overwrite
|
|
||||||
private long d() {
|
|
||||||
return System.currentTimeMillis(); // nanoTime() / 1000000L
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,6 +7,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
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 org.spigotmc.SpigotConfig;
|
import org.spigotmc.SpigotConfig;
|
||||||
@@ -17,41 +18,52 @@ import org.spongepowered.asm.mixin.Overwrite;
|
|||||||
import org.spongepowered.asm.mixin.Shadow;
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
import io.akarin.api.LocalAddress;
|
import io.akarin.api.internal.LocalAddress;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
import io.akarin.server.core.ChannelAdapter;
|
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.ChannelOption;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
import io.netty.channel.ServerChannel;
|
import io.netty.channel.ServerChannel;
|
||||||
import io.netty.channel.epoll.Epoll;
|
import io.netty.channel.epoll.Epoll;
|
||||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
|
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.GenericFutureListener;
|
||||||
import net.minecraft.server.ChatComponentText;
|
import net.minecraft.server.ChatComponentText;
|
||||||
|
import net.minecraft.server.EnumProtocolDirection;
|
||||||
|
import net.minecraft.server.HandshakeListener;
|
||||||
|
import net.minecraft.server.LegacyPingHandler;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.NetworkManager;
|
import net.minecraft.server.NetworkManager;
|
||||||
|
import net.minecraft.server.PacketDecoder;
|
||||||
|
import net.minecraft.server.PacketEncoder;
|
||||||
import net.minecraft.server.PacketPlayOutKickDisconnect;
|
import net.minecraft.server.PacketPlayOutKickDisconnect;
|
||||||
|
import net.minecraft.server.PacketPrepender;
|
||||||
|
import net.minecraft.server.PacketSplitter;
|
||||||
import net.minecraft.server.ServerConnection;
|
import net.minecraft.server.ServerConnection;
|
||||||
|
|
||||||
@Mixin(value = ServerConnection.class, remap = false)
|
@Mixin(value = ServerConnection.class, remap = false)
|
||||||
public class NonblockingServerConnection {
|
public abstract class NonblockingServerConnection {
|
||||||
private final static Logger logger = LogManager.getLogger("NSC");
|
private final static Logger logger = LogManager.getLogger("NSC");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all endpoints added to this NetworkSystem
|
* Contains all endpoints added to this NetworkSystem
|
||||||
*/
|
*/
|
||||||
@Shadow @Mutable @Final private List<ChannelFuture> g;
|
@Shadow(aliases = "g") @Mutable @Final private List<ChannelFuture> endPoints;
|
||||||
/**
|
/**
|
||||||
* A list containing all NetworkManager instances of all endpoints
|
* A list containing all NetworkManager instances of all endpoints
|
||||||
*/
|
*/
|
||||||
@Shadow @Mutable @Final private List<NetworkManager> h;
|
@Shadow(aliases = "h") @Mutable @Final private List<NetworkManager> networkManagers;
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
private void addPending() {} // just keep compatibility
|
private void addPending() {} // just keep compatibility
|
||||||
|
|
||||||
@Shadow @Final private MinecraftServer f;
|
@Shadow(aliases = "f") @Final private MinecraftServer server;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds channels (endpoint) that listens on publicly accessible network ports
|
* Adds channels (endpoint) that listens on publicly accessible network ports
|
||||||
@@ -65,7 +77,7 @@ public class NonblockingServerConnection {
|
|||||||
Class<? extends ServerChannel> channelClass;
|
Class<? extends ServerChannel> channelClass;
|
||||||
EventLoopGroup loopGroup;
|
EventLoopGroup loopGroup;
|
||||||
|
|
||||||
if (Epoll.isAvailable() && this.f.af()) { // PAIL: MinecraftServer::useNativeTransport
|
if (Epoll.isAvailable() && this.server.af()) { // OBFHELPER: MinecraftServer::useNativeTransport
|
||||||
channelClass = EpollServerSocketChannel.class;
|
channelClass = EpollServerSocketChannel.class;
|
||||||
loopGroup = ServerConnection.b.c();
|
loopGroup = ServerConnection.b.c();
|
||||||
logger.info("Using epoll channel type");
|
logger.info("Using epoll channel type");
|
||||||
@@ -75,8 +87,27 @@ public class NonblockingServerConnection {
|
|||||||
logger.info("Using nio channel type");
|
logger.info("Using nio channel type");
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(ChannelAdapter.create(h)).group(loopGroup);
|
ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(new ChannelInitializer<Channel>() {
|
||||||
synchronized (g) {
|
@Override
|
||||||
|
protected void initChannel(Channel channel) throws Exception {
|
||||||
|
try {
|
||||||
|
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
|
||||||
|
} catch (ChannelException ex) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30))
|
||||||
|
.addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection()))
|
||||||
|
.addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
|
||||||
|
.addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
|
||||||
|
|
||||||
|
NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
|
||||||
|
networkManagers.add(manager);
|
||||||
|
|
||||||
|
channel.pipeline().addLast("packet_handler", manager);
|
||||||
|
manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager));
|
||||||
|
}
|
||||||
|
}).group(loopGroup);
|
||||||
|
synchronized (endPoints) {
|
||||||
data.addAll(Lists.transform(AkarinGlobalConfig.extraAddress, s -> {
|
data.addAll(Lists.transform(AkarinGlobalConfig.extraAddress, s -> {
|
||||||
String[] info = s.split(":");
|
String[] info = s.split(":");
|
||||||
try {
|
try {
|
||||||
@@ -87,19 +118,19 @@ public class NonblockingServerConnection {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
data.forEach(address -> g.add(bootstrap.localAddress(address.host(), address.port()).bind().syncUninterruptibly())); // supports multi-port bind
|
data.forEach(address -> endPoints.add(bootstrap.localAddress(address.host(), address.port()).bind().syncUninterruptibly())); // supports multi-port bind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Shadow public volatile boolean d; // PAIL: neverTerminate
|
@Shadow public volatile boolean d; // OBFHELPER: neverTerminate
|
||||||
/**
|
/**
|
||||||
* Shuts down all open endpoints
|
* Shuts down all open endpoints
|
||||||
*/
|
*/
|
||||||
public void b() {
|
public void b() {
|
||||||
this.d = false;
|
this.d = false;
|
||||||
try {
|
try {
|
||||||
synchronized (g) { // safe fixes
|
synchronized (endPoints) { // safe fixes
|
||||||
for (ChannelFuture channel : g) channel.channel().close().sync();
|
for (ChannelFuture channel : endPoints) channel.channel().close().sync();
|
||||||
}
|
}
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
logger.error("Interrupted whilst closing channel");
|
logger.error("Interrupted whilst closing channel");
|
||||||
@@ -108,15 +139,15 @@ public class NonblockingServerConnection {
|
|||||||
|
|
||||||
public void processPackets(NetworkManager manager) {
|
public void processPackets(NetworkManager manager) {
|
||||||
try {
|
try {
|
||||||
manager.a(); // PAIL: NetworkManager::processReceivedPackets
|
manager.a(); // OBFHELPER: NetworkManager::processReceivedPackets
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.warn("Failed to handle packet for {}", new Object[] { manager.getSocketAddress(), ex });
|
logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex);
|
||||||
final ChatComponentText kick = new ChatComponentText("Internal server error");
|
final ChatComponentText message = new ChatComponentText("Internal server error");
|
||||||
|
|
||||||
manager.sendPacket(new PacketPlayOutKickDisconnect(kick), new GenericFutureListener<Future<? super Void>>() {
|
manager.sendPacket(new PacketPlayOutKickDisconnect(message), new GenericFutureListener<Future<? super Void>>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<? super Void> future) throws Exception {
|
public void operationComplete(Future<? super Void> future) throws Exception {
|
||||||
manager.close(kick);
|
manager.close(message);
|
||||||
}
|
}
|
||||||
}, new GenericFutureListener[0]);
|
}, new GenericFutureListener[0]);
|
||||||
manager.stopReading();
|
manager.stopReading();
|
||||||
@@ -128,16 +159,16 @@ public class NonblockingServerConnection {
|
|||||||
*/
|
*/
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void c() throws InterruptedException {
|
public void c() throws InterruptedException {
|
||||||
synchronized (h) {
|
synchronized (networkManagers) {
|
||||||
// Spigot - This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
|
// Spigot - This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
|
||||||
if (SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % SpigotConfig.playerShuffle == 0) {
|
if (SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % SpigotConfig.playerShuffle == 0) {
|
||||||
Collections.shuffle(h);
|
Collections.shuffle(networkManagers);
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator<NetworkManager> it = h.iterator();
|
Iterator<NetworkManager> it = networkManagers.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
NetworkManager manager = it.next();
|
NetworkManager manager = it.next();
|
||||||
if (manager.h()) continue; // PAIL: NetworkManager::hasNoChannel
|
if (manager.h()) continue; // OBFHELPER: NetworkManager::hasNoChannel
|
||||||
|
|
||||||
if (manager.isConnected()) {
|
if (manager.isConnected()) {
|
||||||
processPackets(manager);
|
processPackets(manager);
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package io.akarin.server.mixin.nsc;
|
package io.akarin.server.mixin.nsc;
|
||||||
|
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
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 io.akarin.api.CheckedConcurrentLinkedQueue;
|
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.GenericFutureListener;
|
||||||
@@ -18,26 +18,27 @@ import net.minecraft.server.Packet;
|
|||||||
import net.minecraft.server.PacketPlayOutMapChunk;
|
import net.minecraft.server.PacketPlayOutMapChunk;
|
||||||
|
|
||||||
@Mixin(value = NetworkManager.class, remap = false)
|
@Mixin(value = NetworkManager.class, remap = false)
|
||||||
public class OptimisticNetworkManager {
|
public abstract class OptimisticNetworkManager {
|
||||||
@Shadow public Channel channel;
|
@Shadow public Channel channel;
|
||||||
@Shadow @Final private Queue<NetworkManager.QueuedPacket> i;
|
@Shadow(aliases = "i") @Final private Queue<NetworkManager.QueuedPacket> packets;
|
||||||
@Shadow @Final private ReentrantReadWriteLock j;
|
@Shadow(aliases = "j") @Final private ReentrantReadWriteUpdateLock queueLock;
|
||||||
|
|
||||||
@Shadow private Queue<NetworkManager.QueuedPacket> getPacketQueue() { return null; }
|
@Shadow public abstract Queue<NetworkManager.QueuedPacket> getPacketQueue();
|
||||||
@Shadow private void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners) {}
|
@Shadow public abstract void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners);
|
||||||
|
|
||||||
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null, null);
|
@SuppressWarnings("unchecked")
|
||||||
|
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null);
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite // OBFHELPER: trySendQueue
|
||||||
private boolean m() {
|
private boolean m() {
|
||||||
if (this.channel != null && this.channel.isOpen()) {
|
if (this.channel != null && this.channel.isOpen()) {
|
||||||
if (this.i.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
|
if (this.packets.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.j.readLock().lock();
|
this.queueLock.updateLock().lock();
|
||||||
try {
|
try {
|
||||||
while (!this.i.isEmpty()) {
|
while (!this.packets.isEmpty()) {
|
||||||
NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue<QueuedPacket>) getPacketQueue()).poll(item -> {
|
NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue<QueuedPacket>) getPacketQueue()).poll(item -> {
|
||||||
return item.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) item.getPacket()).isReady();
|
return item.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) item.getPacket()).isReady();
|
||||||
}, SIGNAL_PACKET);
|
}, SIGNAL_PACKET);
|
||||||
@@ -51,7 +52,7 @@ public class OptimisticNetworkManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this.j.readLock().unlock();
|
this.queueLock.updateLock().unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package io.akarin.server.mixin.optimization;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import net.minecraft.server.AxisAlignedBB;
|
||||||
|
import net.minecraft.server.Entity;
|
||||||
|
import net.minecraft.server.Material;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = Entity.class, remap = false)
|
||||||
|
public abstract class MixinEntity {
|
||||||
|
@Shadow public World world;
|
||||||
|
@Shadow public abstract AxisAlignedBB getBoundingBox();
|
||||||
|
|
||||||
|
private boolean isInLava;
|
||||||
|
private int lastLavaCheck = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
@Overwrite // OBFHELPER: isInLava
|
||||||
|
public boolean au() {
|
||||||
|
/*
|
||||||
|
* This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285)
|
||||||
|
* Thanks @Poweruser
|
||||||
|
*/
|
||||||
|
int currentTick = MinecraftServer.currentTick;
|
||||||
|
if (this.lastLavaCheck != currentTick) {
|
||||||
|
this.lastLavaCheck = currentTick;
|
||||||
|
this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
|
||||||
|
}
|
||||||
|
return this.isInLava;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,20 +42,21 @@ import net.minecraft.server.World;
|
|||||||
|
|
||||||
@Mixin(value = EntityHorseAbstract.class, remap = false)
|
@Mixin(value = EntityHorseAbstract.class, remap = false)
|
||||||
public abstract class MixinEntityHorseAbstract extends Entity {
|
public abstract class MixinEntityHorseAbstract extends Entity {
|
||||||
@Shadow @Final private static DataWatcherObject<Optional<UUID>> bJ;
|
@Shadow(aliases = "bJ") @Final private static DataWatcherObject<Optional<UUID>> OWNER_UNIQUE_ID;
|
||||||
|
|
||||||
@Nullable private Optional<UUID> cachedOwnerId;
|
@Nullable private Optional<UUID> cachedOwnerId;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Overwrite
|
@Overwrite
|
||||||
@Nullable public UUID getOwnerUUID() {
|
public UUID getOwnerUUID() {
|
||||||
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(bJ);
|
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(OWNER_UNIQUE_ID);
|
||||||
return cachedOwnerId.orNull();
|
return cachedOwnerId.orNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void setOwnerUUID(@Nullable UUID uuid) {
|
public void setOwnerUUID(@Nullable UUID uuid) {
|
||||||
cachedOwnerId = Optional.fromNullable(uuid);
|
cachedOwnerId = Optional.fromNullable(uuid);
|
||||||
datawatcher.set(bJ, cachedOwnerId);
|
datawatcher.set(OWNER_UNIQUE_ID, cachedOwnerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,8 +46,9 @@ public abstract class MixinEntityTameableAnimal extends Entity {
|
|||||||
|
|
||||||
@Nullable private Optional<UUID> cachedOwnerId;
|
@Nullable private Optional<UUID> cachedOwnerId;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
@Overwrite
|
@Overwrite
|
||||||
@Nullable public UUID getOwnerUUID() {
|
public UUID getOwnerUUID() {
|
||||||
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by);
|
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by);
|
||||||
return cachedOwnerId.orNull();
|
return cachedOwnerId.orNull();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package io.akarin.server.mixin.optimization;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Overwrite;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
|
||||||
|
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||||
|
|
||||||
|
import net.minecraft.server.IDataManager;
|
||||||
|
import net.minecraft.server.MCUtil;
|
||||||
|
import net.minecraft.server.NBTCompressedStreamTools;
|
||||||
|
import net.minecraft.server.NBTTagCompound;
|
||||||
|
import net.minecraft.server.PersistentBase;
|
||||||
|
import net.minecraft.server.PersistentCollection;
|
||||||
|
|
||||||
|
@Mixin(value = PersistentCollection.class, remap = false)
|
||||||
|
public abstract class MixinPersistentCollection {
|
||||||
|
@Shadow(aliases = "b") @Final private IDataManager dataManager;
|
||||||
|
|
||||||
|
@Overwrite
|
||||||
|
private void a(PersistentBase persistentbase) {
|
||||||
|
if (this.dataManager == null) return;
|
||||||
|
|
||||||
|
File file = this.dataManager.getDataFile(persistentbase.id);
|
||||||
|
if (file == null) return;
|
||||||
|
|
||||||
|
NBTTagCompound nbttagcompound = new NBTTagCompound();
|
||||||
|
nbttagcompound.set("data", persistentbase.b(new NBTTagCompound()));
|
||||||
|
|
||||||
|
// Akarin start
|
||||||
|
MCUtil.scheduleAsyncTask(() -> {
|
||||||
|
try {
|
||||||
|
FileOutputStream fileoutputstream = new FileOutputStream(file);
|
||||||
|
|
||||||
|
NBTCompressedStreamTools.a(nbttagcompound, fileoutputstream);
|
||||||
|
fileoutputstream.close();
|
||||||
|
} catch (Exception exception) {
|
||||||
|
exception.printStackTrace();
|
||||||
|
ServerInternalException.reportInternalException(exception); // Paper
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Akarin end
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.Overwrite;
|
|||||||
import net.minecraft.server.TileEntityEnchantTable;
|
import net.minecraft.server.TileEntityEnchantTable;
|
||||||
|
|
||||||
@Mixin(value = TileEntityEnchantTable.class, remap = false)
|
@Mixin(value = TileEntityEnchantTable.class, remap = false)
|
||||||
public class MixinTileEntityEnchantTable {
|
public abstract class MixinTileEntityEnchantTable {
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public void e() {} // No tickable
|
public void e() {} // No tickable
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,510 @@
|
|||||||
|
package io.akarin.server.mixin.optimization;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
import org.bukkit.event.block.BlockRedstoneEvent;
|
||||||
|
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 com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
|
||||||
|
import net.minecraft.server.BaseBlockPosition;
|
||||||
|
import net.minecraft.server.Block;
|
||||||
|
import net.minecraft.server.BlockDiodeAbstract;
|
||||||
|
import net.minecraft.server.BlockObserver;
|
||||||
|
import net.minecraft.server.BlockPiston;
|
||||||
|
import net.minecraft.server.BlockPosition;
|
||||||
|
import net.minecraft.server.BlockRedstoneComparator;
|
||||||
|
import net.minecraft.server.BlockRedstoneTorch;
|
||||||
|
import net.minecraft.server.BlockRedstoneWire;
|
||||||
|
import net.minecraft.server.BlockRepeater;
|
||||||
|
import net.minecraft.server.Blocks;
|
||||||
|
import net.minecraft.server.EnumDirection;
|
||||||
|
import net.minecraft.server.IBlockAccess;
|
||||||
|
import net.minecraft.server.IBlockData;
|
||||||
|
import net.minecraft.server.Material;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = BlockRedstoneWire.class, remap = false)
|
||||||
|
public abstract class PandaRedstoneWire extends Block {
|
||||||
|
|
||||||
|
protected PandaRedstoneWire(Material material) {
|
||||||
|
super(material);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Positions that need to be turned off **/
|
||||||
|
private List<BlockPosition> turnOff = Lists.newArrayList();
|
||||||
|
/** Positions that need to be checked to be turned on **/
|
||||||
|
private List<BlockPosition> turnOn = Lists.newArrayList();
|
||||||
|
/** Positions of wire that was updated already (Ordering determines update order and is therefore required!) **/
|
||||||
|
private final Set<BlockPosition> updatedRedstoneWire = Sets.newLinkedHashSet();
|
||||||
|
|
||||||
|
/** Ordered arrays of the facings; Needed for the update order.
|
||||||
|
* I went with a vertical-first order here, but vertical last would work to.
|
||||||
|
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior. **/
|
||||||
|
private static final EnumDirection[] facingsHorizontal = {EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH};
|
||||||
|
private static final EnumDirection[] facingsVertical = {EnumDirection.DOWN, EnumDirection.UP};
|
||||||
|
private static final EnumDirection[] facings = ArrayUtils.addAll(facingsVertical, facingsHorizontal);
|
||||||
|
|
||||||
|
/** Offsets for all surrounding blocks that need to receive updates **/
|
||||||
|
private static final BaseBlockPosition[] surroundingBlocksOffset;
|
||||||
|
static {
|
||||||
|
Set<BaseBlockPosition> set = Sets.newLinkedHashSet();
|
||||||
|
for (EnumDirection facing : facings) {
|
||||||
|
set.add(facing.getDirectionPosition());
|
||||||
|
}
|
||||||
|
for (EnumDirection facing1 : facings) {
|
||||||
|
BaseBlockPosition v1 = facing1.getDirectionPosition();
|
||||||
|
for (EnumDirection facing2 : facings) {
|
||||||
|
BaseBlockPosition v2 = facing2.getDirectionPosition();
|
||||||
|
set.add(new BaseBlockPosition(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set.remove(BaseBlockPosition.ZERO);
|
||||||
|
surroundingBlocksOffset = set.toArray(new BaseBlockPosition[set.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Shadow(aliases = "g") private boolean canProvidePower;
|
||||||
|
@Shadow public abstract int getPower(World world, BlockPosition pos, int strength);
|
||||||
|
@Shadow(aliases = "b") public abstract boolean isPowerSourceAt(IBlockAccess worldIn, BlockPosition pos, EnumDirection side);
|
||||||
|
|
||||||
|
@Inject(method = "e", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onUpdateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
|
||||||
|
this.updateSurroundingRedstone(worldIn, pos);
|
||||||
|
cir.setReturnValue(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "a*", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void onCalculateCurrentChanges(World worldIn, BlockPosition pos1, BlockPosition pos2, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
|
||||||
|
this.calculateCurrentChanges(worldIn, pos1);
|
||||||
|
cir.setReturnValue(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recalculates all surrounding wires and causes all needed updates
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param world World
|
||||||
|
* @param pos Position that needs updating
|
||||||
|
*/
|
||||||
|
private void updateSurroundingRedstone(World world, BlockPosition pos) {
|
||||||
|
// Recalculate the connected wires
|
||||||
|
this.calculateCurrentChanges(world, pos);
|
||||||
|
|
||||||
|
// Set to collect all the updates, to only execute them once. Ordering required.
|
||||||
|
Set<BlockPosition> blocksNeedingUpdate = Sets.newLinkedHashSet();
|
||||||
|
|
||||||
|
// Add the needed updates
|
||||||
|
for (BlockPosition posi : this.updatedRedstoneWire) {
|
||||||
|
this.addBlocksNeedingUpdate(world, posi, blocksNeedingUpdate);
|
||||||
|
}
|
||||||
|
// Add all other updates to keep known behaviors
|
||||||
|
// They are added in a backwards order because it preserves a commonly used behavior with the update order
|
||||||
|
Iterator<BlockPosition> it = Lists.newLinkedList(this.updatedRedstoneWire).descendingIterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
this.addAllSurroundingBlocks(it.next(), blocksNeedingUpdate);
|
||||||
|
}
|
||||||
|
// Remove updates on the wires as they just were updated
|
||||||
|
blocksNeedingUpdate.removeAll(this.updatedRedstoneWire);
|
||||||
|
/*
|
||||||
|
* Avoid unnecessary updates on the just updated wires A huge scale test
|
||||||
|
* showed about 40% more ticks per second It's probably less in normal
|
||||||
|
* usage but likely still worth it
|
||||||
|
*/
|
||||||
|
this.updatedRedstoneWire.clear();
|
||||||
|
|
||||||
|
// Execute updates
|
||||||
|
for (BlockPosition posi : blocksNeedingUpdate) {
|
||||||
|
world.applyPhysics(posi, (BlockRedstoneWire) (Object) this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on or off all connected wires
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param position Position of the wire that received the update
|
||||||
|
*/
|
||||||
|
private void calculateCurrentChanges(World worldIn, BlockPosition position) {
|
||||||
|
// Turn off all connected wires first if needed
|
||||||
|
if (worldIn.getType(position).getBlock() == (BlockRedstoneWire) (Object) this) {
|
||||||
|
turnOff.add(position);
|
||||||
|
} else {
|
||||||
|
// In case this wire was removed, check the surrounding wires
|
||||||
|
this.checkSurroundingWires(worldIn, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!turnOff.isEmpty()) {
|
||||||
|
BlockPosition pos = turnOff.remove(0);
|
||||||
|
if (pos == null) continue; // Akarin
|
||||||
|
IBlockData state = worldIn.getType(pos);
|
||||||
|
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
|
this.canProvidePower = false;
|
||||||
|
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
|
||||||
|
this.canProvidePower = true;
|
||||||
|
int wirePower = this.getSurroundingWirePower(worldIn, pos);
|
||||||
|
|
||||||
|
// Lower the strength as it moved a block
|
||||||
|
wirePower--;
|
||||||
|
int newPower = Math.max(blockPower, wirePower);
|
||||||
|
|
||||||
|
// Akarin start - BlockRedstoneEvent
|
||||||
|
if (oldPower != newPower) {
|
||||||
|
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
|
||||||
|
worldIn.getServer().getPluginManager().callEvent(event);
|
||||||
|
newPower = event.getNewCurrent();
|
||||||
|
}
|
||||||
|
// Akarin end
|
||||||
|
|
||||||
|
// Power lowered?
|
||||||
|
if (newPower < oldPower) {
|
||||||
|
// If it's still powered by a direct source (but weaker) mark for turn on
|
||||||
|
if (blockPower > 0 && !this.turnOn.contains(pos)) {
|
||||||
|
this.turnOn.add(pos);
|
||||||
|
}
|
||||||
|
// Set all the way to off for now, because wires that were powered by this need to update first
|
||||||
|
setWireState(worldIn, pos, state, 0);
|
||||||
|
// Power rose?
|
||||||
|
} else if (newPower > oldPower) {
|
||||||
|
// Set new Power
|
||||||
|
this.setWireState(worldIn, pos, state, newPower);
|
||||||
|
}
|
||||||
|
// Check if surrounding wires need to change based on the current/new state and add them to the lists
|
||||||
|
this.checkSurroundingWires(worldIn, pos);
|
||||||
|
}
|
||||||
|
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
|
||||||
|
while (!this.turnOn.isEmpty()) {
|
||||||
|
BlockPosition pos = this.turnOn.remove(0);
|
||||||
|
if (pos == null) continue; // Akarin
|
||||||
|
IBlockData state = worldIn.getType(pos);
|
||||||
|
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
|
this.canProvidePower = false;
|
||||||
|
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
|
||||||
|
this.canProvidePower = true;
|
||||||
|
int wirePower = this.getSurroundingWirePower(worldIn, pos);
|
||||||
|
// Lower the strength as it moved a block
|
||||||
|
wirePower--;
|
||||||
|
int newPower = Math.max(blockPower, wirePower);
|
||||||
|
|
||||||
|
// Akarin start - BlockRedstoneEvent
|
||||||
|
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
|
||||||
|
worldIn.getServer().getPluginManager().callEvent(event);
|
||||||
|
newPower = event.getNewCurrent();
|
||||||
|
// Akarin end
|
||||||
|
|
||||||
|
if (newPower > oldPower) {
|
||||||
|
setWireState(worldIn, pos, state, newPower);
|
||||||
|
} else if (newPower < oldPower) {
|
||||||
|
// Add warning
|
||||||
|
}
|
||||||
|
// Check if surrounding wires need to change based on the current/new state and add them to the lists
|
||||||
|
this.checkSurroundingWires(worldIn, pos);
|
||||||
|
}
|
||||||
|
this.turnOff.clear();
|
||||||
|
this.turnOn.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an wire needs to be marked for update depending on the power next to it
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position of the wire that might need to change
|
||||||
|
* @param otherPower Power of the wire next to it
|
||||||
|
*/
|
||||||
|
private void addWireToList(World worldIn, BlockPosition pos, int otherPower) {
|
||||||
|
IBlockData state = worldIn.getType(pos);
|
||||||
|
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
|
||||||
|
int power = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
|
// Could get powered stronger by the neighbor?
|
||||||
|
if (power < (otherPower - 1) && !this.turnOn.contains(pos)) {
|
||||||
|
// Mark for turn on check.
|
||||||
|
this.turnOn.add(pos);
|
||||||
|
}
|
||||||
|
// Should have powered the neighbor? Probably was powered by it and is in turn off phase.
|
||||||
|
if (power > otherPower && !this.turnOff.contains(pos)) {
|
||||||
|
// Mark for turn off check.
|
||||||
|
this.turnOff.add(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the wires around need to get updated depending on this wires state.
|
||||||
|
* Checks all wires below before the same layer before on top to keep
|
||||||
|
* some more rotational symmetry around the y-axis.
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position of the wire
|
||||||
|
*/
|
||||||
|
private void checkSurroundingWires(World worldIn, BlockPosition pos) {
|
||||||
|
IBlockData state = worldIn.getType(pos);
|
||||||
|
int ownPower = 0;
|
||||||
|
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
|
||||||
|
ownPower = state.get(BlockRedstoneWire.POWER).intValue();
|
||||||
|
}
|
||||||
|
// Check wires on the same layer first as they appear closer to the wire
|
||||||
|
for (EnumDirection facing : facingsHorizontal) {
|
||||||
|
BlockPosition offsetPos = pos.shift(facing);
|
||||||
|
if (facing.getAxis().isHorizontal()) {
|
||||||
|
this.addWireToList(worldIn, offsetPos, ownPower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (EnumDirection facingVertical : facingsVertical) {
|
||||||
|
BlockPosition offsetPos = pos.shift(facingVertical);
|
||||||
|
boolean solidBlock = worldIn.getType(offsetPos).k(); // OBFHELPER: isBlockNormalCube
|
||||||
|
for (EnumDirection facingHorizontal : facingsHorizontal) {
|
||||||
|
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
|
||||||
|
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
|
||||||
|
if ((facingVertical == EnumDirection.UP && !solidBlock) || (facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).k())) { // OBFHELPER: isBlockNormalCube
|
||||||
|
this.addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the maximum power of the surrounding wires
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position of the asking wire
|
||||||
|
* @return The maximum power of the wires that could power the wire at pos
|
||||||
|
*/
|
||||||
|
private int getSurroundingWirePower(World worldIn, BlockPosition pos) {
|
||||||
|
int wirePower = 0;
|
||||||
|
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
|
||||||
|
BlockPosition offsetPos = pos.shift(enumfacing);
|
||||||
|
// Wires on the same layer
|
||||||
|
wirePower = this.getPower(worldIn, offsetPos, wirePower);
|
||||||
|
|
||||||
|
// Block below the wire need to be solid (Upwards diode of slabs/stairs/glowstone) and no block should cut the wire
|
||||||
|
if(worldIn.getType(offsetPos).l() && !worldIn.getType(pos.up()).l()) { // OBFHELPER: isNormalCube
|
||||||
|
wirePower = this.getPower(worldIn, offsetPos.up(), wirePower);
|
||||||
|
// Only get from power below if no block is cutting the wire
|
||||||
|
} else if (!worldIn.getType(offsetPos).l()) { // OBFHELPER: isNormalCube
|
||||||
|
wirePower = this.getPower(worldIn, offsetPos.down(), wirePower);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return wirePower;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all blocks that need to receive an update from a redstone change in this position.
|
||||||
|
* This means only blocks that actually could change.
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position of the wire
|
||||||
|
* @param set Set to add the update positions too
|
||||||
|
*/
|
||||||
|
private void addBlocksNeedingUpdate(World worldIn, BlockPosition pos, Set<BlockPosition> set) {
|
||||||
|
List<EnumDirection> connectedSides = this.getSidesToPower(worldIn, pos);
|
||||||
|
// Add the blocks next to the wire first (closest first order)
|
||||||
|
for (EnumDirection facing : facings) {
|
||||||
|
BlockPosition offsetPos = pos.shift(facing);
|
||||||
|
// canConnectTo() is not the nicest solution here as it returns true for e.g. the front of a repeater
|
||||||
|
// canBlockBePowereFromSide catches these cases
|
||||||
|
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN
|
||||||
|
&& (!facing.getAxis().isHorizontal() || canConnectToBlock(worldIn.getType(offsetPos), facing))) continue;
|
||||||
|
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos), facing, true))
|
||||||
|
set.add(offsetPos);
|
||||||
|
}
|
||||||
|
// Later add blocks around the surrounding blocks that get powered
|
||||||
|
for (EnumDirection facing : facings) {
|
||||||
|
BlockPosition offsetPos = pos.shift(facing);
|
||||||
|
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN || !worldIn.getType(offsetPos).l()) continue; // OBFHELPER: isNormalCube
|
||||||
|
for (EnumDirection facing1 : facings) {
|
||||||
|
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false))
|
||||||
|
set.add(offsetPos.shift(facing1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a block can get powered from a side.
|
||||||
|
* This behavior would better be implemented per block type as follows:
|
||||||
|
* - return false as default. (blocks that are not affected by redstone don't need to be updated, it doesn't really hurt if they are either)
|
||||||
|
* - return true for all blocks that can get powered from all side and change based on it (doors, fence gates, trap doors, note blocks, lamps, dropper, hopper, TNT, rails, possibly more)
|
||||||
|
* - implement own logic for pistons, repeaters, comparators and redstone torches
|
||||||
|
* The current implementation was chosen to keep everything in one class.
|
||||||
|
*
|
||||||
|
* Why is this extra check needed?
|
||||||
|
* 1. It makes sure that many old behaviors still work (QC + Pistons).
|
||||||
|
* 2. It prevents updates from "jumping".
|
||||||
|
* Or rather it prevents this wire to update a block that would get powered by the next one of the same line.
|
||||||
|
* This is to prefer as it makes understanding the update order of the wire really easy. The signal "travels" from the power source.
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param state State of the block
|
||||||
|
* @param side Side from which it gets powered
|
||||||
|
* @param isWire True if it's powered by a wire directly, False if through a block
|
||||||
|
* @return True if the block can change based on the power level it gets on the given side, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean canBlockBePoweredFromSide(IBlockData state, EnumDirection side, boolean isWire) {
|
||||||
|
if (state.getBlock() instanceof BlockPiston && state.get(BlockPiston.FACING) == side.opposite()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.getBlock() instanceof BlockDiodeAbstract && state.get(BlockDiodeAbstract.FACING) != side.opposite()) {
|
||||||
|
if (isWire && state.getBlock() instanceof BlockRedstoneComparator
|
||||||
|
&& state.get(BlockRedstoneComparator.FACING).k() != side.getAxis() && side.getAxis().isHorizontal()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (state.getBlock() instanceof BlockRedstoneTorch) {
|
||||||
|
if (isWire || state.get(BlockRedstoneTorch.FACING) != side) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a list of all horizontal sides that can get powered by a wire.
|
||||||
|
* The list is ordered the same as the facingsHorizontal.
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position of the wire
|
||||||
|
* @return List of all facings that can get powered by this wire
|
||||||
|
*/
|
||||||
|
private List<EnumDirection> getSidesToPower(World worldIn, BlockPosition pos) {
|
||||||
|
List<EnumDirection> retval = Lists.newArrayList();
|
||||||
|
for (EnumDirection facing : facingsHorizontal) {
|
||||||
|
if (isPowerSourceAt(worldIn, pos, facing))
|
||||||
|
retval.add(facing);
|
||||||
|
}
|
||||||
|
if (retval.isEmpty()) return Lists.newArrayList(facingsHorizontal);
|
||||||
|
boolean northsouth = retval.contains(EnumDirection.NORTH) || retval.contains(EnumDirection.SOUTH);
|
||||||
|
boolean eastwest = retval.contains(EnumDirection.EAST) || retval.contains(EnumDirection.WEST);
|
||||||
|
if (northsouth) {
|
||||||
|
retval.remove(EnumDirection.EAST);
|
||||||
|
retval.remove(EnumDirection.WEST);
|
||||||
|
}
|
||||||
|
if (eastwest) {
|
||||||
|
retval.remove(EnumDirection.NORTH);
|
||||||
|
retval.remove(EnumDirection.SOUTH);
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds all surrounding positions to a set.
|
||||||
|
* This is the neighbor blocks, as well as their neighbors
|
||||||
|
*
|
||||||
|
* @param pos
|
||||||
|
* @param set
|
||||||
|
*/
|
||||||
|
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
|
||||||
|
for (BaseBlockPosition vect : surroundingBlocksOffset) {
|
||||||
|
set.add(pos.a(vect)); // OBFHELPER: add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the block state of a wire with a new power level and marks for updates
|
||||||
|
*
|
||||||
|
* @author panda
|
||||||
|
*
|
||||||
|
* @param worldIn World
|
||||||
|
* @param pos Position at which the state needs to be set
|
||||||
|
* @param state Old state
|
||||||
|
* @param power Power it should get set to
|
||||||
|
*/
|
||||||
|
private void setWireState(World worldIn, BlockPosition pos, IBlockData state, int power) {
|
||||||
|
state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(power));
|
||||||
|
worldIn.setTypeAndData(pos, state, 2);
|
||||||
|
updatedRedstoneWire.add(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author panda
|
||||||
|
* @reason Uses local surrounding block offset list for notifications.
|
||||||
|
*
|
||||||
|
* @param world The world
|
||||||
|
* @param pos The position
|
||||||
|
* @param state The block state
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Overwrite
|
||||||
|
public void onPlace(World world, BlockPosition pos, IBlockData state) {
|
||||||
|
this.updateSurroundingRedstone(world, pos);
|
||||||
|
for (BaseBlockPosition vec : surroundingBlocksOffset) {
|
||||||
|
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author panda
|
||||||
|
* @reason Uses local surrounding block offset list for notifications.
|
||||||
|
*
|
||||||
|
* @param world The world
|
||||||
|
* @param pos The position
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Overwrite
|
||||||
|
public void remove(World world, BlockPosition pos, IBlockData state) {
|
||||||
|
super.remove(world, pos, state);
|
||||||
|
this.updateSurroundingRedstone(world, pos);
|
||||||
|
for (BaseBlockPosition vec : surroundingBlocksOffset) {
|
||||||
|
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author panda
|
||||||
|
* @reason Changed to use getSidesToPower() to avoid duplicate implementation.
|
||||||
|
*
|
||||||
|
* @param blockState The block state
|
||||||
|
* @param blockAccess The block access
|
||||||
|
* @param pos The position
|
||||||
|
* @param side The side
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Overwrite
|
||||||
|
public int b(IBlockData blockState, IBlockAccess blockAccess, BlockPosition pos, EnumDirection side) { // OBFHELPER: getWeakPower
|
||||||
|
if (!this.canProvidePower) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
if (side == EnumDirection.UP || this.getSidesToPower((World) blockAccess, pos).contains(side)) {
|
||||||
|
return blockState.get(BlockRedstoneWire.POWER).intValue();
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canConnectToBlock(IBlockData blockState, @Nullable EnumDirection side) {
|
||||||
|
Block block = blockState.getBlock();
|
||||||
|
|
||||||
|
if (block == Blocks.REDSTONE_WIRE) {
|
||||||
|
return true;
|
||||||
|
} else if (Blocks.UNPOWERED_REPEATER.D(blockState)) { // OBFHELPER: isSameDiode
|
||||||
|
EnumDirection enumdirection1 = blockState.get(BlockRepeater.FACING);
|
||||||
|
|
||||||
|
return enumdirection1 == side || enumdirection1.opposite() == side;
|
||||||
|
} else if (Blocks.dk == blockState.getBlock()) {
|
||||||
|
return side == blockState.get(BlockObserver.FACING); // OBFHELPER: OBSERVER
|
||||||
|
} else {
|
||||||
|
return blockState.m() && side != null; // OBFHELPER: canProvidePower
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -16,11 +16,11 @@ import net.minecraft.server.WorldGenBigTree;
|
|||||||
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
|
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
|
||||||
*/
|
*/
|
||||||
@Mixin(value = WorldGenBigTree.class, remap = false)
|
@Mixin(value = WorldGenBigTree.class, remap = false)
|
||||||
public class WeakBigTree {
|
public abstract class WeakBigTree {
|
||||||
@Shadow private World l;
|
@Shadow(aliases = "l") private World worldReference;
|
||||||
|
|
||||||
@Inject(method = "generate", at = @At("RETURN"))
|
@Inject(method = "generate", at = @At("RETURN"))
|
||||||
private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable<?> info) {
|
private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable<?> info) {
|
||||||
l = null; // Akarin - remove references to world objects to avoid memory leaks
|
world = null; // Akarin - remove references to world objects to avoid memory leaks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,56 +40,56 @@ import net.minecraft.server.ItemStack;
|
|||||||
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
|
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
|
||||||
*/
|
*/
|
||||||
@Mixin(value = EnchantmentManager.class, remap = false)
|
@Mixin(value = EnchantmentManager.class, remap = false)
|
||||||
public class WeakEnchantmentManager {
|
public abstract class WeakEnchantmentManager {
|
||||||
@Shadow @Final private static EnchantmentManager.EnchantmentModifierProtection a;
|
@Shadow(aliases = "a") @Final private static EnchantmentManager.EnchantmentModifierProtection protection;
|
||||||
@Shadow @Final private static EnchantmentManager.EnchantmentModifierThorns c;
|
@Shadow(aliases = "c") @Final private static EnchantmentManager.EnchantmentModifierThorns thorns;
|
||||||
@Shadow @Final private static EnchantmentManager.EnchantmentModifierArthropods d;
|
@Shadow(aliases = "d") @Final private static EnchantmentManager.EnchantmentModifierArthropods arthropods;
|
||||||
|
|
||||||
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, Iterable<ItemStack> iterable) {}
|
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, Iterable<ItemStack> iterable) {}
|
||||||
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, ItemStack itemstack) {}
|
@Shadow private static void a(EnchantmentManager.EnchantmentModifier modifier, ItemStack itemstack) {}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public static int a(Iterable<ItemStack> iterable, DamageSource damageSource) {
|
public static int a(Iterable<ItemStack> iterable, DamageSource damageSource) {
|
||||||
EnchantmentManager.a.a = 0; // PAIL: damageModifier
|
protection.a = 0; // OBFHELPER: damageModifier
|
||||||
EnchantmentManager.a.b = damageSource;
|
protection.b = damageSource;
|
||||||
a(EnchantmentManager.a, iterable);
|
a(protection, iterable); // OBFHELPER: applyEnchantmentModifierArray
|
||||||
a.b = null; // Akarin - Remove reference to Damagesource
|
protection.b = null; // Akarin - Remove reference to Damagesource
|
||||||
return EnchantmentManager.a.a;
|
return protection.a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public static void a(EntityLiving user, Entity attacker) { // PAIL: applyThornEnchantments
|
public static void a(EntityLiving user, Entity attacker) { // OBFHELPER: applyThornEnchantments
|
||||||
EnchantmentManager.c.b = attacker;
|
thorns.b = attacker;
|
||||||
EnchantmentManager.c.a = user;
|
thorns.a = user;
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
a(EnchantmentManager.c, user.aQ()); // PAIL: applyEnchantmentModifierArray, getEquipmentAndArmor
|
a(thorns, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker instanceof EntityHuman) {
|
if (attacker instanceof EntityHuman) {
|
||||||
a(EnchantmentManager.c, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
|
a(thorns, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// Akarin Start - remove references to entity objects to avoid memory leaks
|
// Akarin Start - remove references to entity objects to avoid memory leaks
|
||||||
c.b = null;
|
thorns.b = null;
|
||||||
c.a = null;
|
thorns.a = null;
|
||||||
// SAkarin end
|
// Akarin end
|
||||||
}
|
}
|
||||||
|
|
||||||
@Overwrite
|
@Overwrite
|
||||||
public static void b(EntityLiving user, Entity target) { // PAIL: applyArthropodEnchantments
|
public static void b(EntityLiving user, Entity target) { // OBFHELPER: applyArthropodEnchantments
|
||||||
EnchantmentManager.d.a = user;
|
arthropods.a = user;
|
||||||
EnchantmentManager.d.b = target;
|
arthropods.b = target;
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
a(EnchantmentManager.d, user.aQ()); // PAIL: applyEnchantmentModifierArray, getEquipmentAndArmor
|
a(arthropods, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user instanceof EntityHuman) {
|
if (user instanceof EntityHuman) {
|
||||||
a(EnchantmentManager.d, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
|
a(arthropods, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
|
||||||
}
|
}
|
||||||
|
|
||||||
// Akarin Start - remove references to entity objects to avoid memory leaks
|
// Akarin Start - remove references to entity objects to avoid memory leaks
|
||||||
d.a = null;
|
arthropods.a = null;
|
||||||
d.b = null;
|
arthropods.b = null;
|
||||||
// Akarin end
|
// Akarin end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.Entity;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = Entity.class, remap = false, priority = 1001)
|
||||||
|
public abstract class MixinEntity {
|
||||||
|
private static final String ENTITY_RIDABLE_COOLDOWN_FIELD = "Lnet/minecraft/entity/Entity;j:I"; // PUTFIELD: rideCooldown
|
||||||
|
private static final String ENTITY_PORTAL_COUNTER_FIELD = "Lnet/minecraft/entity/Entity;al:I"; // PUTFIELD: portalCounter
|
||||||
|
@Shadow protected int j;
|
||||||
|
@Shadow protected int al;
|
||||||
|
@Shadow public World world;
|
||||||
|
|
||||||
|
// OBFHELPER: onEntityUpdate
|
||||||
|
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_RIDABLE_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupEntityCooldown(Entity self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
|
||||||
|
this.j = Math.max(0, this.j - ticks); // OBFHELPER: rideCooldown
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_PORTAL_COUNTER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupPortalCounter(Entity self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
|
||||||
|
this.al += ticks; // OBFHELPER: portalCounter
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityAgeable;
|
||||||
|
|
||||||
|
@Mixin(value = EntityAgeable.class, remap = false)
|
||||||
|
public abstract class MixinEntityAgeable {
|
||||||
|
private static final String ENTITY_AGEABLE_SET_GROWING_AGE_METHOD = "Lnet/minecraft/entity/EntityAgeable;setAgeRaw(I)V";
|
||||||
|
|
||||||
|
// OBFHELPER: onLivingUpdate
|
||||||
|
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 0))
|
||||||
|
public void fixupGrowingUp(EntityAgeable self, int age) {
|
||||||
|
// Subtract the one the original update method added
|
||||||
|
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
|
||||||
|
self.setAgeRaw(Math.min(0, age + diff));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 1))
|
||||||
|
public void fixupBreedingCooldown(EntityAgeable self, int age) {
|
||||||
|
// Subtract the one the original update method added
|
||||||
|
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
|
||||||
|
self.setAgeRaw(Math.max(0, age - diff));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityExperienceOrb;
|
||||||
|
|
||||||
|
@Mixin(value = EntityExperienceOrb.class, remap = false)
|
||||||
|
public abstract class MixinEntityExperienceOrb {
|
||||||
|
private static final String ENTITY_XP_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;c:I"; // PUTFIELD: delayBeforeCanPickup
|
||||||
|
private static final String ENTITY_XP_AGE_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;b:I"; // PUTFIELD: xpOrbAge
|
||||||
|
@Shadow public int c; // OBFHELPER: delayBeforeCanPickup
|
||||||
|
@Shadow public int b; // OBFHELPER: xpOrbAge
|
||||||
|
|
||||||
|
// OBFHELPER: onUpdate
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupPickupDelay(EntityExperienceOrb self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.c = Math.max(0, this.c - ticks); // OBFHELPER: delayBeforeCanPickup
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupAge(EntityExperienceOrb self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.b += ticks; // OBFHELPER: xpOrbAge
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityHuman;
|
||||||
|
|
||||||
|
@Mixin(value = EntityHuman.class, remap = false)
|
||||||
|
public abstract class MixinEntityHuman {
|
||||||
|
private static final String ENTITY_PLAYER_XP_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityHuman;bD:I"; // PUTFIELD: xpCooldown
|
||||||
|
private static final String ENTITY_PLAYER_SLEEP_TIMER_FIELD = "Lnet/minecraft/entity/player/EntityHuman;sleepTicks:I";
|
||||||
|
@Shadow public int bD;
|
||||||
|
@Shadow private int sleepTicks;
|
||||||
|
|
||||||
|
// OBFHELPER: onUpdate
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_XP_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupXpCooldown(EntityHuman self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.bD = Math.max(0, this.bD - ticks); // OBFHELPER: xpCooldown
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupSleepTimer(EntityHuman self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.sleepTicks += ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 2))
|
||||||
|
public void fixupWakeTimer(EntityHuman self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.sleepTicks += ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
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.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityInsentient;
|
||||||
|
import net.minecraft.server.EntityLiving;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = EntityInsentient.class, remap = false)
|
||||||
|
public abstract class MixinEntityInsentient extends EntityLiving {
|
||||||
|
private static final String ENTITY_LIVING_AGE_FIELD = "Lnet/minecraft/entity/EntityInsentient;ticksFarFromPlayer:I";
|
||||||
|
|
||||||
|
public MixinEntityInsentient(World world) {
|
||||||
|
super(world);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "doTick()V", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupEntityDespawnAge(EntityInsentient self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.ticksFarFromPlayer += ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityItem;
|
||||||
|
|
||||||
|
@Mixin(value = EntityItem.class, remap = false)
|
||||||
|
public abstract class MixinEntityItem {
|
||||||
|
private static final String ENTITY_ITEM_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityItem;pickupDelay:I";
|
||||||
|
private static final String ENTITY_ITEM_AGE_FIELD = "Lnet/minecraft/entity/item/EntityItem;age:I";
|
||||||
|
@Shadow public int age;
|
||||||
|
@Shadow private int pickupDelay;
|
||||||
|
|
||||||
|
// OBFHELPER: onUpdate
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupPickupDelay(EntityItem self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.pickupDelay = Math.max(0, this.pickupDelay - ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupAge(EntityItem self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.age += ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
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.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.Entity;
|
||||||
|
import net.minecraft.server.EntityPlayer;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = EntityPlayer.class, remap = false)
|
||||||
|
public abstract class MixinEntityPlayer extends Entity {
|
||||||
|
private static final String ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityPlayer;portalCooldown:I";
|
||||||
|
|
||||||
|
public MixinEntityPlayer(World worldIn) {
|
||||||
|
super(worldIn);
|
||||||
|
}
|
||||||
|
|
||||||
|
// OBFHELPER: decrementTimeUntilPortal
|
||||||
|
@Redirect(method = "I()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupPortalCooldown(EntityPlayer self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
this.portalCooldown = Math.max(0, this.portalCooldown - ticks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.EntityZombieVillager;
|
||||||
|
|
||||||
|
@Mixin(value = EntityZombieVillager.class, remap = false)
|
||||||
|
public abstract class MixinEntityZombieVillager {
|
||||||
|
private static final String ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD = "Lnet/minecraft/entity/monster/EntityZombieVillager;du()I"; // INVOKE: getConversionProgress
|
||||||
|
|
||||||
|
@Shadow(aliases = "du") protected abstract int getConversionProgress();
|
||||||
|
|
||||||
|
// OBFHELPER: onUpdate
|
||||||
|
@Redirect(method = "B_()V", at = @At(value = "INVOKE", target = ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD, ordinal = 0))
|
||||||
|
public int fixupConversionTimeBoost(EntityZombieVillager self) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
|
||||||
|
return this.getConversionProgress() * ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package io.akarin.server.mixin.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
@Mixin(value = MinecraftServer.class, remap = false, priority = 1001)
|
||||||
|
public abstract class MixinMinecraftServer implements IMixinRealTimeTicking {
|
||||||
|
private static long lastTickNanos = System.nanoTime();
|
||||||
|
private static long realTimeTicks = 1;
|
||||||
|
|
||||||
|
@Inject(method = "C()V", at = @At("HEAD")) // OBFHELPER: fullTick
|
||||||
|
public void onTickUpdateRealTimeTicks(CallbackInfo ci) {
|
||||||
|
long currentNanos = System.nanoTime();
|
||||||
|
realTimeTicks = (currentNanos - lastTickNanos) / 50000000;
|
||||||
|
if (realTimeTicks < 1) {
|
||||||
|
realTimeTicks = 1;
|
||||||
|
}
|
||||||
|
lastTickNanos = currentNanos;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRealTimeTicks() {
|
||||||
|
return realTimeTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.PlayerConnection;
|
||||||
|
|
||||||
|
@Mixin(value = PlayerConnection.class, remap = false)
|
||||||
|
public abstract class MixinPlayerConnection {
|
||||||
|
private static final String NET_HANDLER_PLAY_CHAT_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;chatThrottle:I";
|
||||||
|
private static final String NET_HANDLER_PLAY_DROP_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;itemDropThreshold:I";
|
||||||
|
@Shadow private volatile int chatThrottle;
|
||||||
|
@Shadow(aliases = "j") private int itemDropThreshold;
|
||||||
|
@Shadow @Final private MinecraftServer minecraftServer;
|
||||||
|
|
||||||
|
// OBFHELPER: update
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_CHAT_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupChatSpamCheck(PlayerConnection self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
|
||||||
|
this.chatThrottle = Math.max(0, this.chatThrottle - ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_DROP_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupDropSpamCheck(PlayerConnection self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
|
||||||
|
this.itemDropThreshold = Math.max(0, this.itemDropThreshold - ticks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.PlayerInteractManager;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = PlayerInteractManager.class, remap = false)
|
||||||
|
public abstract class MixinPlayerInteractManager {
|
||||||
|
private static final String PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD = "Lnet/minecraft/server/management/PlayerInteractManager;currentTick:I";
|
||||||
|
@Shadow public World world;
|
||||||
|
@Shadow private int currentTick;
|
||||||
|
|
||||||
|
// OBFHELPER: updateBlockRemoving
|
||||||
|
@Redirect(method = "a()V", at = @At(value = "FIELD", target = PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupDiggingTime(PlayerInteractManager self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.world.getMinecraftServer()).getRealTimeTicks();
|
||||||
|
this.currentTick += ticks;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.TileEntity;
|
||||||
|
import net.minecraft.server.TileEntityBrewingStand;
|
||||||
|
|
||||||
|
@Mixin(value = TileEntityBrewingStand.class, remap = false)
|
||||||
|
public abstract class MixinTileEntityBrewingStand extends TileEntity {
|
||||||
|
private static final String BREWING_STAND_BREW_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityBrewingStand;brewTime:I";
|
||||||
|
@Shadow private int brewTime;
|
||||||
|
|
||||||
|
// OBFHELPER: update
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = BREWING_STAND_BREW_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupBrewTime(TileEntityBrewingStand self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||||
|
this.brewTime = Math.max(0, this.brewTime - ticks);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.lib.Opcodes;
|
||||||
|
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.Redirect;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.MathHelper;
|
||||||
|
import net.minecraft.server.TileEntity;
|
||||||
|
import net.minecraft.server.TileEntityFurnace;
|
||||||
|
|
||||||
|
@Mixin(value = TileEntityFurnace.class, remap = false)
|
||||||
|
public abstract class MixinTileEntityFurnace extends TileEntity {
|
||||||
|
private static final String FURNACE_BURN_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;burnTime:I";
|
||||||
|
private static final String FURNACE_COOK_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;cookTime:I";
|
||||||
|
@Shadow private int burnTime;
|
||||||
|
@Shadow private int cookTime;
|
||||||
|
@Shadow private int cookTimeTotal;
|
||||||
|
|
||||||
|
// OBFHELPER: update
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_BURN_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupBurnTime(TileEntityFurnace self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||||
|
this.burnTime = Math.max(0, this.burnTime - ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
|
||||||
|
public void fixupCookTime(TileEntityFurnace self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||||
|
this.cookTime = Math.min(this.cookTimeTotal, this.cookTime + ticks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 3))
|
||||||
|
public void fixupCookTimeCooldown(TileEntityFurnace self, int modifier) {
|
||||||
|
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
|
||||||
|
this.cookTime = MathHelper.clamp(this.cookTime - (2 * ticks), 0, this.cookTimeTotal);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.Shadow;
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
|
||||||
|
@Mixin(value = World.class, remap = false, priority = 1001)
|
||||||
|
public abstract class MixinWorld implements IMixinRealTimeTicking {
|
||||||
|
@Shadow @Nullable public abstract MinecraftServer getMinecraftServer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getRealTimeTicks() {
|
||||||
|
if (this.getMinecraftServer() != null) {
|
||||||
|
return ((IMixinRealTimeTicking) this.getMinecraftServer()).getRealTimeTicks();
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.realtime;
|
||||||
|
|
||||||
|
import org.bukkit.World.Environment;
|
||||||
|
import org.bukkit.generator.ChunkGenerator;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
|
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
|
||||||
|
import net.minecraft.server.IDataManager;
|
||||||
|
import net.minecraft.server.MethodProfiler;
|
||||||
|
import net.minecraft.server.World;
|
||||||
|
import net.minecraft.server.WorldData;
|
||||||
|
import net.minecraft.server.WorldProvider;
|
||||||
|
import net.minecraft.server.WorldServer;
|
||||||
|
|
||||||
|
@Mixin(value = WorldServer.class, remap = false, priority = 1001)
|
||||||
|
public abstract class MixinWorldServer extends World implements IMixinRealTimeTicking {
|
||||||
|
|
||||||
|
protected MixinWorldServer(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, Environment env) {
|
||||||
|
super(idatamanager, worlddata, worldprovider, methodprofiler, flag, gen, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "doTick()V", at = @At("HEAD"))
|
||||||
|
public void fixTimeOfDay(CallbackInfo ci) {
|
||||||
|
if (this.getGameRules().getBoolean("doDaylightCycle")) {
|
||||||
|
// Subtract the one the original tick method is going to add
|
||||||
|
long diff = this.getRealTimeTicks() - 1;
|
||||||
|
// Don't set if we're not changing it as other mods might be listening for changes
|
||||||
|
if (diff > 0) {
|
||||||
|
this.worldData.setDayTime(this.worldData.getDayTime() + diff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -11,17 +11,15 @@ import java.util.Random;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Akarin Changes Note</b><br>
|
* Akarin Changes Note
|
||||||
* <br>
|
* 1) Expose private members (cause mixin errors)
|
||||||
* 1) Expose private members<br>
|
|
||||||
* @author cakoyo
|
|
||||||
*/
|
*/
|
||||||
public class EnchantmentManager {
|
public class EnchantmentManager {
|
||||||
|
|
||||||
public static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null); // Akarin - private -> public
|
private static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null);
|
||||||
private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null);
|
private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null);
|
||||||
public static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null); // Akarin - private -> public
|
private static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null);
|
||||||
public static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null); // Akarin - private -> public
|
private static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null);
|
||||||
|
|
||||||
public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) {
|
public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) {
|
||||||
if (itemstack.isEmpty()) {
|
if (itemstack.isEmpty()) {
|
||||||
|
|||||||
2981
sources/src/main/java/net/minecraft/server/Entity.java
Normal file
2981
sources/src/main/java/net/minecraft/server/Entity.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@ package net.minecraft.server;
|
|||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import java.util.ArrayDeque; // Paper
|
import java.util.ArrayDeque; // Paper
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -31,10 +32,8 @@ import org.bukkit.inventory.MainHand;
|
|||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Akarin Changes Note</b><br>
|
* Akarin Changes Note
|
||||||
* <br>
|
* 2) Add lock to player track (safety issue)
|
||||||
* 1) Add volatile to fields<br>
|
|
||||||
* @author cakoyo
|
|
||||||
*/
|
*/
|
||||||
public class EntityPlayer extends EntityHuman implements ICrafting {
|
public class EntityPlayer extends EntityHuman implements ICrafting {
|
||||||
|
|
||||||
@@ -129,7 +128,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
if (worldserver.worldProvider.m() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) {
|
if (worldserver.worldProvider.m() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) {
|
||||||
int i = Math.max(0, minecraftserver.a(worldserver));
|
int i = Math.max(0, minecraftserver.a(worldserver));
|
||||||
int j = MathHelper.floor(worldserver.getWorldBorder().b(blockposition.getX(), blockposition.getZ()));
|
int j = MathHelper.floor(worldserver.getWorldBorder().b((double) blockposition.getX(), (double) blockposition.getZ()));
|
||||||
|
|
||||||
if (j < i) {
|
if (j < i) {
|
||||||
i = j;
|
i = j;
|
||||||
@@ -146,7 +145,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(NBTTagCompound nbttagcompound) {
|
public void a(NBTTagCompound nbttagcompound) {
|
||||||
super.a(nbttagcompound);
|
super.a(nbttagcompound);
|
||||||
if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world
|
if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world
|
||||||
@@ -174,7 +172,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
public static void a(DataConverterManager dataconvertermanager) {
|
public static void a(DataConverterManager dataconvertermanager) {
|
||||||
dataconvertermanager.a(DataConverterTypes.PLAYER, new DataInspector() {
|
dataconvertermanager.a(DataConverterTypes.PLAYER, new DataInspector() {
|
||||||
@Override
|
|
||||||
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
|
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
|
||||||
if (nbttagcompound.hasKeyOfType("RootVehicle", 10)) {
|
if (nbttagcompound.hasKeyOfType("RootVehicle", 10)) {
|
||||||
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
|
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
|
||||||
@@ -189,7 +186,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(NBTTagCompound nbttagcompound) {
|
public void b(NBTTagCompound nbttagcompound) {
|
||||||
super.b(nbttagcompound);
|
super.b(nbttagcompound);
|
||||||
nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId());
|
nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId());
|
||||||
@@ -221,7 +217,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CraftBukkit start - World fallback code, either respawn location or global spawn
|
// CraftBukkit start - World fallback code, either respawn location or global spawn
|
||||||
@Override
|
|
||||||
public void spawnIn(World world) {
|
public void spawnIn(World world) {
|
||||||
super.spawnIn(world);
|
super.spawnIn(world);
|
||||||
if (world == null) {
|
if (world == null) {
|
||||||
@@ -246,13 +241,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void levelDown(int i) {
|
public void levelDown(int i) {
|
||||||
super.levelDown(i);
|
super.levelDown(i);
|
||||||
this.lastSentExp = -1;
|
this.lastSentExp = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enchantDone(ItemStack itemstack, int i) {
|
public void enchantDone(ItemStack itemstack, int i) {
|
||||||
super.enchantDone(itemstack, i);
|
super.enchantDone(itemstack, i);
|
||||||
this.lastSentExp = -1;
|
this.lastSentExp = -1;
|
||||||
@@ -262,29 +255,24 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.activeContainer.addSlotListener(this);
|
this.activeContainer.addSlotListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enterCombat() {
|
public void enterCombat() {
|
||||||
super.enterCombat();
|
super.enterCombat();
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT));
|
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exitCombat() {
|
public void exitCombat() {
|
||||||
super.exitCombat();
|
super.exitCombat();
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT));
|
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void a(IBlockData iblockdata) {
|
protected void a(IBlockData iblockdata) {
|
||||||
CriterionTriggers.d.a(this, iblockdata);
|
CriterionTriggers.d.a(this, iblockdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ItemCooldown l() {
|
protected ItemCooldown l() {
|
||||||
return new ItemCooldownPlayer(this);
|
return new ItemCooldownPlayer(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void B_() {
|
public void B_() {
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
if (this.joining) {
|
if (this.joining) {
|
||||||
@@ -304,7 +292,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
if (!this.world.isClientSide && !this.activeContainer.canUse(this)) {
|
if (!this.world.isClientSide && !this.activeContainer.canUse(this)) {
|
||||||
this.closeInventory();
|
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
|
||||||
this.activeContainer = this.defaultContainer;
|
this.activeContainer = this.defaultContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,7 +347,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
ItemStack itemstack = this.inventory.getItem(i);
|
ItemStack itemstack = this.inventory.getItem(i);
|
||||||
|
|
||||||
if (!itemstack.isEmpty() && itemstack.getItem().f()) {
|
if (!itemstack.isEmpty() && itemstack.getItem().f()) {
|
||||||
Packet packet = ((ItemWorldMapBase) itemstack.getItem()).a(itemstack, this.world, this);
|
Packet packet = ((ItemWorldMapBase) itemstack.getItem()).a(itemstack, this.world, (EntityHuman) this);
|
||||||
|
|
||||||
if (packet != null) {
|
if (packet != null) {
|
||||||
this.playerConnection.sendPacket(packet);
|
this.playerConnection.sendPacket(packet);
|
||||||
@@ -425,7 +413,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.oldLevel != this.expLevel) {
|
if (this.oldLevel != this.expLevel) {
|
||||||
CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer(this), this.oldLevel, this.expLevel);
|
CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer((EntityPlayer) this), this.oldLevel, this.expLevel);
|
||||||
this.oldLevel = this.expLevel;
|
this.oldLevel = this.expLevel;
|
||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
@@ -450,7 +438,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void die(DamageSource damagesource) {
|
public void die(DamageSource damagesource) {
|
||||||
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
|
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
|
||||||
|
|
||||||
@@ -483,9 +470,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) {
|
if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) {
|
||||||
if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) {
|
if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) {
|
||||||
this.server.getPlayerList().a(this, chatmessage);
|
this.server.getPlayerList().a((EntityHuman) this, chatmessage);
|
||||||
} else if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) {
|
} else if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) {
|
||||||
this.server.getPlayerList().b(this, chatmessage);
|
this.server.getPlayerList().b((EntityHuman) this, chatmessage);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.server.getPlayerList().sendMessage(chatmessage);
|
this.server.getPlayerList().sendMessage(chatmessage);
|
||||||
@@ -501,7 +488,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.inventory.clear();
|
this.inventory.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeInventory();
|
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DEATH); // Paper
|
||||||
this.setSpectatorTarget(this); // Remove spectated target
|
this.setSpectatorTarget(this); // Remove spectated target
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@@ -518,7 +505,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
EntityLiving entityliving = this.ci();
|
EntityLiving entityliving = this.ci();
|
||||||
|
|
||||||
if (entityliving != null) {
|
if (entityliving != null) {
|
||||||
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = EntityTypes.eggInfo.get(EntityTypes.a(entityliving));
|
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = (EntityTypes.MonsterEggInfo) EntityTypes.eggInfo.get(EntityTypes.a((Entity) entityliving));
|
||||||
|
|
||||||
if (entitytypes_monsteregginfo != null) {
|
if (entitytypes_monsteregginfo != null) {
|
||||||
this.b(entitytypes_monsteregginfo.killedByEntityStatistic);
|
this.b(entitytypes_monsteregginfo.killedByEntityStatistic);
|
||||||
@@ -534,7 +521,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.getCombatTracker().g();
|
this.getCombatTracker().g();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Entity entity, int i, DamageSource damagesource) {
|
public void a(Entity entity, int i, DamageSource damagesource) {
|
||||||
if (entity != this) {
|
if (entity != this) {
|
||||||
super.a(entity, i, damagesource);
|
super.a(entity, i, damagesource);
|
||||||
@@ -603,7 +589,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return Lists.newArrayList();
|
return Lists.newArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean damageEntity(DamageSource damagesource, float f) {
|
public boolean damageEntity(DamageSource damagesource, float f) {
|
||||||
if (this.isInvulnerable(damagesource)) {
|
if (this.isInvulnerable(damagesource)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -634,7 +619,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean a(EntityHuman entityhuman) {
|
public boolean a(EntityHuman entityhuman) {
|
||||||
return !this.canPvP() ? false : super.a(entityhuman);
|
return !this.canPvP() ? false : super.a(entityhuman);
|
||||||
}
|
}
|
||||||
@@ -644,7 +628,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return this.world.pvpMode;
|
return this.world.pvpMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Entity b(int i) {
|
public Entity b(int i) {
|
||||||
if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154
|
if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154
|
||||||
@@ -683,7 +666,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean a(EntityPlayer entityplayer) {
|
public boolean a(EntityPlayer entityplayer) {
|
||||||
return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer));
|
return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer));
|
||||||
}
|
}
|
||||||
@@ -699,13 +681,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receive(Entity entity, int i) {
|
public void receive(Entity entity, int i) {
|
||||||
super.receive(entity, i);
|
super.receive(entity, i);
|
||||||
this.activeContainer.b();
|
this.activeContainer.b();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EntityHuman.EnumBedResult a(BlockPosition blockposition) {
|
public EntityHuman.EnumBedResult a(BlockPosition blockposition) {
|
||||||
EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition);
|
EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition);
|
||||||
|
|
||||||
@@ -713,7 +693,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.b(StatisticList.ab);
|
this.b(StatisticList.ab);
|
||||||
PacketPlayOutBed packetplayoutbed = new PacketPlayOutBed(this, blockposition);
|
PacketPlayOutBed packetplayoutbed = new PacketPlayOutBed(this, blockposition);
|
||||||
|
|
||||||
this.x().getTracker().a(this, packetplayoutbed);
|
this.x().getTracker().a((Entity) this, (Packet) packetplayoutbed);
|
||||||
this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
|
this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
|
||||||
this.playerConnection.sendPacket(packetplayoutbed);
|
this.playerConnection.sendPacket(packetplayoutbed);
|
||||||
CriterionTriggers.p.a(this);
|
CriterionTriggers.p.a(this);
|
||||||
@@ -722,7 +702,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return entityhuman_enumbedresult;
|
return entityhuman_enumbedresult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(boolean flag, boolean flag1, boolean flag2) {
|
public void a(boolean flag, boolean flag1, boolean flag2) {
|
||||||
if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one!
|
if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one!
|
||||||
if (this.isSleeping()) {
|
if (this.isSleeping()) {
|
||||||
@@ -736,7 +715,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean a(Entity entity, boolean flag) {
|
public boolean a(Entity entity, boolean flag) {
|
||||||
Entity entity1 = this.bJ();
|
Entity entity1 = this.bJ();
|
||||||
|
|
||||||
@@ -753,7 +731,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopRiding() {
|
public void stopRiding() {
|
||||||
Entity entity = this.bJ();
|
Entity entity = this.bJ();
|
||||||
|
|
||||||
@@ -767,21 +744,20 @@ 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.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
|
||||||
worldServer.tracker.track(this);
|
worldServer.tracker.track(this);
|
||||||
|
worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
|
||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvulnerable(DamageSource damagesource) {
|
public boolean isInvulnerable(DamageSource damagesource) {
|
||||||
return super.isInvulnerable(damagesource) || this.L();
|
return super.isInvulnerable(damagesource) || this.L();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
|
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void b(BlockPosition blockposition) {
|
protected void b(BlockPosition blockposition) {
|
||||||
if (!this.isSpectator()) {
|
if (!this.isSpectator()) {
|
||||||
super.b(blockposition);
|
super.b(blockposition);
|
||||||
@@ -810,9 +786,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
super.a(d0, flag, iblockdata, blockposition);
|
super.a(d0, flag, iblockdata, blockposition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openSign(TileEntitySign tileentitysign) {
|
public void openSign(TileEntitySign tileentitysign) {
|
||||||
tileentitysign.a(this);
|
tileentitysign.a((EntityHuman) this);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition()));
|
this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -821,7 +796,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return containerCounter; // CraftBukkit
|
return containerCounter; // CraftBukkit
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTileEntity(ITileEntityContainer itileentitycontainer) {
|
public void openTileEntity(ITileEntityContainer itileentitycontainer) {
|
||||||
// CraftBukkit start - Inventory open hook
|
// CraftBukkit start - Inventory open hook
|
||||||
if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).b() != null && this.isSpectator()) {
|
if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).b() != null && this.isSpectator()) {
|
||||||
@@ -841,7 +815,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openContainer(IInventory iinventory) {
|
public void openContainer(IInventory iinventory) {
|
||||||
// CraftBukkit start - Inventory open hook
|
// CraftBukkit start - Inventory open hook
|
||||||
// Copied from below
|
// Copied from below
|
||||||
@@ -871,7 +844,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).setChatModifier((new ChatModifier()).setColor(EnumChatFormat.RED)), true);
|
this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).setChatModifier((new ChatModifier()).setColor(EnumChatFormat.RED)), true);
|
||||||
} else {
|
} else {
|
||||||
if (this.activeContainer != this.defaultContainer) {
|
if (this.activeContainer != this.defaultContainer) {
|
||||||
this.closeInventory();
|
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iinventory instanceof ITileInventory) {
|
if (iinventory instanceof ITileInventory) {
|
||||||
@@ -901,7 +874,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openTrade(IMerchant imerchant) {
|
public void openTrade(IMerchant imerchant) {
|
||||||
// CraftBukkit start - Inventory open hook
|
// CraftBukkit start - Inventory open hook
|
||||||
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world));
|
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world));
|
||||||
@@ -929,7 +901,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) {
|
public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) {
|
||||||
// CraftBukkit start - Inventory open hook
|
// CraftBukkit start - Inventory open hook
|
||||||
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this));
|
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this));
|
||||||
@@ -939,7 +910,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
if (this.activeContainer != this.defaultContainer) {
|
if (this.activeContainer != this.defaultContainer) {
|
||||||
this.closeInventory();
|
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nextContainerCounter();
|
this.nextContainerCounter();
|
||||||
@@ -949,26 +920,23 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.activeContainer.addSlotListener(this);
|
this.activeContainer.addSlotListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(ItemStack itemstack, EnumHand enumhand) {
|
public void a(ItemStack itemstack, EnumHand enumhand) {
|
||||||
Item item = itemstack.getItem();
|
Item item = itemstack.getItem();
|
||||||
|
|
||||||
if (item == Items.WRITTEN_BOOK) {
|
if (item == Items.WRITTEN_BOOK) {
|
||||||
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
|
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
|
||||||
|
|
||||||
packetdataserializer.a(enumhand);
|
packetdataserializer.a((Enum) enumhand);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", packetdataserializer));
|
this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", packetdataserializer));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(TileEntityCommand tileentitycommand) {
|
public void a(TileEntityCommand tileentitycommand) {
|
||||||
tileentitycommand.c(true);
|
tileentitycommand.c(true);
|
||||||
this.a((TileEntity) tileentitycommand);
|
this.a((TileEntity) tileentitycommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Container container, int i, ItemStack itemstack) {
|
public void a(Container container, int i, ItemStack itemstack) {
|
||||||
if (!(container.getSlot(i) instanceof SlotResult)) {
|
if (!(container.getSlot(i) instanceof SlotResult)) {
|
||||||
if (container == this.defaultContainer) {
|
if (container == this.defaultContainer) {
|
||||||
@@ -985,7 +953,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.a(container, container.a());
|
this.a(container, container.a());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Container container, NonNullList<ItemStack> nonnulllist) {
|
public void a(Container container, NonNullList<ItemStack> nonnulllist) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist));
|
this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist));
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried()));
|
this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried()));
|
||||||
@@ -996,12 +963,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContainerData(Container container, int i, int j) {
|
public void setContainerData(Container container, int i, int j) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j));
|
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setContainerData(Container container, IInventory iinventory) {
|
public void setContainerData(Container container, IInventory iinventory) {
|
||||||
for (int i = 0; i < iinventory.h(); ++i) {
|
for (int i = 0; i < iinventory.h(); ++i) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i)));
|
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i)));
|
||||||
@@ -1009,9 +974,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void closeInventory() {
|
public void closeInventory() {
|
||||||
CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit
|
// Paper start
|
||||||
|
closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNKNOWN);
|
||||||
|
}
|
||||||
|
public void closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
|
||||||
|
CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
|
||||||
|
// Paper end
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId));
|
this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId));
|
||||||
this.r();
|
this.r();
|
||||||
}
|
}
|
||||||
@@ -1023,7 +992,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void r() {
|
public void r() {
|
||||||
this.activeContainer.b(this);
|
this.activeContainer.b((EntityHuman) this);
|
||||||
this.activeContainer = this.defaultContainer;
|
this.activeContainer = this.defaultContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1043,7 +1012,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Statistic statistic, int i) {
|
public void a(Statistic statistic, int i) {
|
||||||
if (statistic != null) {
|
if (statistic != null) {
|
||||||
this.bZ.b(this, statistic, i);
|
this.bZ.b(this, statistic, i);
|
||||||
@@ -1058,7 +1026,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Statistic statistic) {
|
public void a(Statistic statistic) {
|
||||||
if (statistic != null) {
|
if (statistic != null) {
|
||||||
this.bZ.setStatistic(this, statistic, 0);
|
this.bZ.setStatistic(this, statistic, 0);
|
||||||
@@ -1073,12 +1040,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(List<IRecipe> list) {
|
public void a(List<IRecipe> list) {
|
||||||
this.cr.a(list, this);
|
this.cr.a(list, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(MinecraftKey[] aminecraftkey) {
|
public void a(MinecraftKey[] aminecraftkey) {
|
||||||
ArrayList arraylist = Lists.newArrayList();
|
ArrayList arraylist = Lists.newArrayList();
|
||||||
MinecraftKey[] aminecraftkey1 = aminecraftkey;
|
MinecraftKey[] aminecraftkey1 = aminecraftkey;
|
||||||
@@ -1099,7 +1064,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.a((List<IRecipe>) arraylist); // CraftBukkit - decompile error
|
this.a((List<IRecipe>) arraylist); // CraftBukkit - decompile error
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(List<IRecipe> list) {
|
public void b(List<IRecipe> list) {
|
||||||
this.cr.b(list, this);
|
this.cr.b(list, this);
|
||||||
}
|
}
|
||||||
@@ -1130,12 +1094,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(IChatBaseComponent ichatbasecomponent, boolean flag) {
|
public void a(IChatBaseComponent ichatbasecomponent, boolean flag) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT));
|
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void v() {
|
protected void v() {
|
||||||
if (!this.activeItem.isEmpty() && this.isHandRaised()) {
|
if (!this.activeItem.isEmpty() && this.isHandRaised()) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9));
|
this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9));
|
||||||
@@ -1182,7 +1144,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.setShoulderEntityRight(entityplayer.getShoulderEntityRight());
|
this.setShoulderEntityRight(entityplayer.getShoulderEntityRight());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void a(MobEffect mobeffect) {
|
protected void a(MobEffect mobeffect) {
|
||||||
super.a(mobeffect);
|
super.a(mobeffect);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
|
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
|
||||||
@@ -1194,14 +1155,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
CriterionTriggers.z.a(this);
|
CriterionTriggers.z.a(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void a(MobEffect mobeffect, boolean flag) {
|
protected void a(MobEffect mobeffect, boolean flag) {
|
||||||
super.a(mobeffect, flag);
|
super.a(mobeffect, flag);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
|
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
|
||||||
CriterionTriggers.z.a(this);
|
CriterionTriggers.z.a(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void b(MobEffect mobeffect) {
|
protected void b(MobEffect mobeffect) {
|
||||||
super.b(mobeffect);
|
super.b(mobeffect);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect()));
|
this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect()));
|
||||||
@@ -1212,22 +1171,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
CriterionTriggers.z.a(this);
|
CriterionTriggers.z.a(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void enderTeleportTo(double d0, double d1, double d2) {
|
public void enderTeleportTo(double d0, double d1, double d2) {
|
||||||
this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch);
|
this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(Entity entity) {
|
public void a(Entity entity) {
|
||||||
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4));
|
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void b(Entity entity) {
|
public void b(Entity entity) {
|
||||||
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5));
|
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateAbilities() {
|
public void updateAbilities() {
|
||||||
if (this.playerConnection != null) {
|
if (this.playerConnection != null) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities));
|
this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities));
|
||||||
@@ -1239,7 +1194,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return (WorldServer) this.world;
|
return (WorldServer) this.world;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(EnumGamemode enumgamemode) {
|
public void a(EnumGamemode enumgamemode) {
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
if (enumgamemode == this.playerInteractManager.getGameMode()) {
|
if (enumgamemode == this.playerInteractManager.getGameMode()) {
|
||||||
@@ -1254,7 +1208,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
this.playerInteractManager.setGameMode(enumgamemode);
|
this.playerInteractManager.setGameMode(enumgamemode);
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, enumgamemode.getId()));
|
this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, (float) enumgamemode.getId()));
|
||||||
if (enumgamemode == EnumGamemode.SPECTATOR) {
|
if (enumgamemode == EnumGamemode.SPECTATOR) {
|
||||||
this.releaseShoulderEntities();
|
this.releaseShoulderEntities();
|
||||||
this.stopRiding();
|
this.stopRiding();
|
||||||
@@ -1266,22 +1220,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.cE();
|
this.cE();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSpectator() {
|
public boolean isSpectator() {
|
||||||
return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR;
|
return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean z() {
|
public boolean z() {
|
||||||
return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE;
|
return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendMessage(IChatBaseComponent ichatbasecomponent) {
|
public void sendMessage(IChatBaseComponent ichatbasecomponent) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent));
|
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean a(int i, String s) {
|
public boolean a(int i, String s) {
|
||||||
/* CraftBukkit start
|
/* CraftBukkit start
|
||||||
if ("seed".equals(s) && !this.server.aa()) {
|
if ("seed".equals(s) && !this.server.aa()) {
|
||||||
@@ -1354,7 +1304,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1));
|
this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPosition getChunkCoordinates() {
|
public BlockPosition getChunkCoordinates() {
|
||||||
return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ);
|
return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ);
|
||||||
}
|
}
|
||||||
@@ -1384,7 +1333,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
this.removeQueue.remove(Integer.valueOf(entity.getId()));
|
this.removeQueue.remove(Integer.valueOf(entity.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void G() {
|
protected void G() {
|
||||||
if (this.isSpectator()) {
|
if (this.isSpectator()) {
|
||||||
this.bY();
|
this.bY();
|
||||||
@@ -1397,13 +1345,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Entity getSpecatorTarget() {
|
public Entity getSpecatorTarget() {
|
||||||
return this.co == null ? this : this.co;
|
return (Entity) (this.co == null ? this : this.co);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSpectatorTarget(Entity entity) {
|
public void setSpectatorTarget(Entity entity) {
|
||||||
Entity entity1 = this.getSpecatorTarget();
|
Entity entity1 = this.getSpecatorTarget();
|
||||||
|
|
||||||
this.co = entity == null ? this : entity;
|
this.co = (Entity) (entity == null ? this : entity);
|
||||||
if (entity1 != this.co) {
|
if (entity1 != this.co) {
|
||||||
this.playerConnection.sendPacket(new PacketPlayOutCamera(this.co));
|
this.playerConnection.sendPacket(new PacketPlayOutCamera(this.co));
|
||||||
this.playerConnection.a(this.co.locX, this.co.locY, this.co.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
|
this.playerConnection.a(this.co.locX, this.co.locY, this.co.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
|
||||||
@@ -1411,7 +1359,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void I() {
|
protected void I() {
|
||||||
if (this.portalCooldown > 0 && !this.worldChangeInvuln) {
|
if (this.portalCooldown > 0 && !this.worldChangeInvuln) {
|
||||||
--this.portalCooldown;
|
--this.portalCooldown;
|
||||||
@@ -1419,7 +1366,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attack(Entity entity) {
|
public void attack(Entity entity) {
|
||||||
if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
|
if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
|
||||||
this.setSpectatorTarget(entity);
|
this.setSpectatorTarget(entity);
|
||||||
@@ -1438,7 +1384,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
|
|||||||
return listName; // CraftBukkit
|
return listName; // CraftBukkit
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(EnumHand enumhand) {
|
public void a(EnumHand enumhand) {
|
||||||
super.a(enumhand);
|
super.a(enumhand);
|
||||||
this.ds();
|
this.ds();
|
||||||
@@ -1476,8 +1421,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) {
|
||||||
|
|||||||
337
sources/src/main/java/net/minecraft/server/EntityTracker.java
Normal file
337
sources/src/main/java/net/minecraft/server/EntityTracker.java
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Made collections and entry access thread-safe (safety issue)
|
||||||
|
*/
|
||||||
|
@ThreadSafe // Akarin
|
||||||
|
public class EntityTracker {
|
||||||
|
|
||||||
|
private static final Logger a = LogManager.getLogger();
|
||||||
|
private final WorldServer world;
|
||||||
|
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
|
||||||
|
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
|
||||||
|
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
|
||||||
|
private int e;
|
||||||
|
|
||||||
|
public EntityTracker(WorldServer worldserver) {
|
||||||
|
this.world = worldserver;
|
||||||
|
this.e = PlayerChunkMap.getFurthestViewableBlock(worldserver.spigotConfig.viewDistance); // Spigot
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long a(double d0) {
|
||||||
|
return MathHelper.d(d0 * 4096.0D);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void track(Entity entity) {
|
||||||
|
if (entity instanceof EntityPlayer) {
|
||||||
|
this.addEntity(entity, 512, 2);
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
// entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
if (entitytrackerentry.b() != entityplayer) {
|
||||||
|
entitytrackerentry.updatePlayer(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
|
||||||
|
} else if (entity instanceof EntityFishingHook) {
|
||||||
|
this.addEntity(entity, 64, 5, true);
|
||||||
|
} else if (entity instanceof EntityArrow) {
|
||||||
|
this.addEntity(entity, 64, 20, false);
|
||||||
|
} else if (entity instanceof EntitySmallFireball) {
|
||||||
|
this.addEntity(entity, 64, 10, false);
|
||||||
|
} else if (entity instanceof EntityFireball) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntitySnowball) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityLlamaSpit) {
|
||||||
|
this.addEntity(entity, 64, 10, false);
|
||||||
|
} else if (entity instanceof EntityEnderPearl) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityEnderSignal) {
|
||||||
|
this.addEntity(entity, 64, 4, true);
|
||||||
|
} else if (entity instanceof EntityEgg) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityPotion) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityThrownExpBottle) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityFireworks) {
|
||||||
|
this.addEntity(entity, 64, 10, true);
|
||||||
|
} else if (entity instanceof EntityItem) {
|
||||||
|
this.addEntity(entity, 64, 20, true);
|
||||||
|
} else if (entity instanceof EntityMinecartAbstract) {
|
||||||
|
this.addEntity(entity, 80, 3, true);
|
||||||
|
} else if (entity instanceof EntityBoat) {
|
||||||
|
this.addEntity(entity, 80, 3, true);
|
||||||
|
} else if (entity instanceof EntitySquid) {
|
||||||
|
this.addEntity(entity, 64, 3, true);
|
||||||
|
} else if (entity instanceof EntityWither) {
|
||||||
|
this.addEntity(entity, 80, 3, false);
|
||||||
|
} else if (entity instanceof EntityShulkerBullet) {
|
||||||
|
this.addEntity(entity, 80, 3, true);
|
||||||
|
} else if (entity instanceof EntityBat) {
|
||||||
|
this.addEntity(entity, 80, 3, false);
|
||||||
|
} else if (entity instanceof EntityEnderDragon) {
|
||||||
|
this.addEntity(entity, 160, 3, true);
|
||||||
|
} else if (entity instanceof IAnimal) {
|
||||||
|
this.addEntity(entity, 80, 3, true);
|
||||||
|
} else if (entity instanceof EntityTNTPrimed) {
|
||||||
|
this.addEntity(entity, 160, 10, true);
|
||||||
|
} else if (entity instanceof EntityFallingBlock) {
|
||||||
|
this.addEntity(entity, 160, 20, true);
|
||||||
|
} else if (entity instanceof EntityHanging) {
|
||||||
|
this.addEntity(entity, 160, Integer.MAX_VALUE, false);
|
||||||
|
} else if (entity instanceof EntityArmorStand) {
|
||||||
|
this.addEntity(entity, 160, 3, true);
|
||||||
|
} else if (entity instanceof EntityExperienceOrb) {
|
||||||
|
this.addEntity(entity, 160, 20, true);
|
||||||
|
} else if (entity instanceof EntityAreaEffectCloud) {
|
||||||
|
this.addEntity(entity, 160, 10, true); // CraftBukkit
|
||||||
|
} else if (entity instanceof EntityEnderCrystal) {
|
||||||
|
this.addEntity(entity, 256, Integer.MAX_VALUE, false);
|
||||||
|
} else if (entity instanceof EntityEvokerFangs) {
|
||||||
|
this.addEntity(entity, 160, 2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(Entity entity, int i, int j) {
|
||||||
|
this.addEntity(entity, i, j, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(Entity entity, int i, final int j, boolean flag) {
|
||||||
|
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
|
||||||
|
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
|
||||||
|
try {
|
||||||
|
// entriesLock.writeLock().lock(); // Akarin - locked from track method
|
||||||
|
if (this.trackedEntities.b(entity.getId())) {
|
||||||
|
throw new IllegalStateException("Entity is already tracked!");
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
|
||||||
|
|
||||||
|
this.c.add(entitytrackerentry);
|
||||||
|
|
||||||
|
this.trackedEntities.a(entity.getId(), entitytrackerentry);
|
||||||
|
entitytrackerentry.scanPlayers(this.world.players);
|
||||||
|
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
|
||||||
|
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
|
||||||
|
|
||||||
|
crashreportsystemdetails.a("Tracking range", i + " blocks");
|
||||||
|
final int finalI = i; // CraftBukkit - fix decompile error
|
||||||
|
crashreportsystemdetails.a("Update interval", new CrashReportCallable() {
|
||||||
|
public String a() throws Exception {
|
||||||
|
String s = "Once per " + finalI + " ticks"; // CraftBukkit
|
||||||
|
|
||||||
|
if (finalI == Integer.MAX_VALUE) { // CraftBukkit
|
||||||
|
s = "Maximum (" + s + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object call() throws Exception {
|
||||||
|
return this.a();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
entity.appendEntityCrashDetails(crashreportsystemdetails);
|
||||||
|
this.trackedEntities.get(entity.getId()).b().appendEntityCrashDetails(crashreport.a("Entity That Is Already Tracked"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
throw new ReportedException(crashreport);
|
||||||
|
} catch (ReportedException reportedexception) {
|
||||||
|
EntityTracker.a.error("\"Silently\" catching entity tracking error.", reportedexception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void untrackEntity(Entity entity) {
|
||||||
|
org.spigotmc.AsyncCatcher.catchOp( "entity untrack"); // Spigot
|
||||||
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
if (entity instanceof EntityPlayer) {
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) entity;
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
entitytrackerentry.a(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
|
||||||
|
|
||||||
|
if (entitytrackerentry1 != null) {
|
||||||
|
this.c.remove(entitytrackerentry1);
|
||||||
|
entitytrackerentry1.a();
|
||||||
|
}
|
||||||
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePlayers() {
|
||||||
|
ArrayList arraylist = Lists.newArrayList();
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
world.timings.tracker1.startTiming(); // Spigot
|
||||||
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
entitytrackerentry.track(this.world.players);
|
||||||
|
if (entitytrackerentry.b) {
|
||||||
|
Entity entity = entitytrackerentry.b();
|
||||||
|
|
||||||
|
if (entity instanceof EntityPlayer) {
|
||||||
|
arraylist.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
world.timings.tracker1.stopTiming(); // Spigot
|
||||||
|
|
||||||
|
world.timings.tracker2.startTiming(); // Spigot
|
||||||
|
for (int i = 0; i < arraylist.size(); ++i) {
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) arraylist.get(i);
|
||||||
|
Iterator iterator1 = this.c.iterator();
|
||||||
|
|
||||||
|
while (iterator1.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry1 = (EntityTrackerEntry) iterator1.next();
|
||||||
|
|
||||||
|
if (entitytrackerentry1.b() != entityplayer) {
|
||||||
|
entitytrackerentry1.updatePlayer(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
world.timings.tracker2.stopTiming(); // Spigot
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(EntityPlayer entityplayer) {
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
if (entitytrackerentry.b() == entityplayer) {
|
||||||
|
entitytrackerentry.scanPlayers(this.world.players);
|
||||||
|
} else {
|
||||||
|
entitytrackerentry.updatePlayer(entityplayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(Entity entity, Packet<?> packet) {
|
||||||
|
entriesLock.readLock().lock(); // Akarin
|
||||||
|
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||||
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
|
|
||||||
|
if (entitytrackerentry != null) {
|
||||||
|
entitytrackerentry.broadcast(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
|
||||||
|
entriesLock.readLock().lock(); // Akarin
|
||||||
|
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
|
||||||
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
|
|
||||||
|
if (entitytrackerentry != null) {
|
||||||
|
entitytrackerentry.broadcastIncludingSelf(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void untrackPlayer(EntityPlayer entityplayer) {
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
entriesLock.writeLock().lock();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
entitytrackerentry.clear(entityplayer);
|
||||||
|
}
|
||||||
|
entriesLock.writeLock().unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(EntityPlayer entityplayer, Chunk chunk) {
|
||||||
|
ArrayList arraylist = Lists.newArrayList();
|
||||||
|
ArrayList arraylist1 = Lists.newArrayList();
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
entriesLock.writeLock().lock(); // Akarin
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
Entity entity = entitytrackerentry.b();
|
||||||
|
|
||||||
|
if (entity != entityplayer && entity.ab == chunk.locX && entity.ad == chunk.locZ) {
|
||||||
|
entitytrackerentry.updatePlayer(entityplayer);
|
||||||
|
if (entity instanceof EntityInsentient && ((EntityInsentient) entity).getLeashHolder() != null) {
|
||||||
|
arraylist.add(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.bF().isEmpty()) {
|
||||||
|
arraylist1.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entriesLock.writeLock().unlock(); // Akarin
|
||||||
|
|
||||||
|
Entity entity1;
|
||||||
|
|
||||||
|
if (!arraylist.isEmpty()) {
|
||||||
|
iterator = arraylist.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
entity1 = (Entity) iterator.next();
|
||||||
|
entityplayer.playerConnection.sendPacket(new PacketPlayOutAttachEntity(entity1, ((EntityInsentient) entity1).getLeashHolder()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arraylist1.isEmpty()) {
|
||||||
|
iterator = arraylist1.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
entity1 = (Entity) iterator.next();
|
||||||
|
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(entity1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(int i) {
|
||||||
|
this.e = (i - 1) * 16;
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
entriesLock.readLock().lock(); // Akarin
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
|
||||||
|
|
||||||
|
entitytrackerentry.a(this.e);
|
||||||
|
}
|
||||||
|
entriesLock.readLock().unlock(); // Akarin
|
||||||
|
}
|
||||||
|
}
|
||||||
317
sources/src/main/java/net/minecraft/server/EnumDirection.java
Normal file
317
sources/src/main/java/net/minecraft/server/EnumDirection.java
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
import com.google.common.collect.Maps;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Add OBFHELPER (panda redstone)
|
||||||
|
*/
|
||||||
|
public enum EnumDirection implements INamable {
|
||||||
|
|
||||||
|
DOWN(0, 1, -1, "down", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, -1, 0)), UP(1, 0, -1, "up", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Y, new BaseBlockPosition(0, 1, 0)), NORTH(2, 3, 2, "north", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, -1)), SOUTH(3, 2, 0, "south", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.Z, new BaseBlockPosition(0, 0, 1)), WEST(4, 5, 1, "west", EnumDirection.EnumAxisDirection.NEGATIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(-1, 0, 0)), EAST(5, 4, 3, "east", EnumDirection.EnumAxisDirection.POSITIVE, EnumDirection.EnumAxis.X, new BaseBlockPosition(1, 0, 0));
|
||||||
|
|
||||||
|
private final int g;
|
||||||
|
private final int h;
|
||||||
|
private final int i;
|
||||||
|
private final String j;
|
||||||
|
private final EnumDirection.EnumAxis k;
|
||||||
|
private final EnumDirection.EnumAxisDirection l;
|
||||||
|
private final BaseBlockPosition m; public BaseBlockPosition getDirectionPosition() { return m; } // Akarin - OBFHELPER
|
||||||
|
private static final EnumDirection[] n = new EnumDirection[6];
|
||||||
|
private static final EnumDirection[] o = new EnumDirection[4];
|
||||||
|
private static final Map<String, EnumDirection> p = Maps.newHashMap();
|
||||||
|
|
||||||
|
private EnumDirection(int i, int j, int k, String s, EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis, BaseBlockPosition baseblockposition) {
|
||||||
|
this.g = i;
|
||||||
|
this.i = k;
|
||||||
|
this.h = j;
|
||||||
|
this.j = s;
|
||||||
|
this.k = enumdirection_enumaxis;
|
||||||
|
this.l = enumdirection_enumaxisdirection;
|
||||||
|
this.m = baseblockposition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int a() {
|
||||||
|
return this.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int get2DRotationValue() {
|
||||||
|
return this.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection.EnumAxisDirection c() {
|
||||||
|
return this.l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection opposite() {
|
||||||
|
return fromType1(this.h);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection e() {
|
||||||
|
switch (this) {
|
||||||
|
case NORTH:
|
||||||
|
return EnumDirection.EAST;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
return EnumDirection.SOUTH;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
return EnumDirection.WEST;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
return EnumDirection.NORTH;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection f() {
|
||||||
|
switch (this) {
|
||||||
|
case NORTH:
|
||||||
|
return EnumDirection.WEST;
|
||||||
|
|
||||||
|
case EAST:
|
||||||
|
return EnumDirection.NORTH;
|
||||||
|
|
||||||
|
case SOUTH:
|
||||||
|
return EnumDirection.EAST;
|
||||||
|
|
||||||
|
case WEST:
|
||||||
|
return EnumDirection.SOUTH;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unable to get CCW facing of " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdjacentX() {
|
||||||
|
return this.k == EnumDirection.EnumAxis.X ? this.l.a() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdjacentY() {
|
||||||
|
return this.k == EnumDirection.EnumAxis.Y ? this.l.a() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAdjacentZ() {
|
||||||
|
return this.k == EnumDirection.EnumAxis.Z ? this.l.a() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String j() {
|
||||||
|
return this.j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection.EnumAxis getAxis() { return k(); } // Akarin - OBFHELPER
|
||||||
|
public EnumDirection.EnumAxis k() {
|
||||||
|
return this.k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection fromType1(int i) {
|
||||||
|
return EnumDirection.n[MathHelper.a(i % EnumDirection.n.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection fromType2(int i) {
|
||||||
|
return EnumDirection.o[MathHelper.a(i % EnumDirection.o.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection fromAngle(double d0) {
|
||||||
|
return fromType2(MathHelper.floor(d0 / 90.0D + 0.5D) & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float l() {
|
||||||
|
return (this.i & 3) * 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection a(Random random) {
|
||||||
|
return values()[random.nextInt(values().length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.j;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection a(EnumDirection.EnumAxisDirection enumdirection_enumaxisdirection, EnumDirection.EnumAxis enumdirection_enumaxis) {
|
||||||
|
EnumDirection[] aenumdirection = values();
|
||||||
|
int i = aenumdirection.length;
|
||||||
|
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
EnumDirection enumdirection = aenumdirection[j];
|
||||||
|
|
||||||
|
if (enumdirection.c() == enumdirection_enumaxisdirection && enumdirection.k() == enumdirection_enumaxis) {
|
||||||
|
return enumdirection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("No such direction: " + enumdirection_enumaxisdirection + " " + enumdirection_enumaxis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumDirection a(BlockPosition blockposition, EntityLiving entityliving) {
|
||||||
|
if (Math.abs(entityliving.locX - (blockposition.getX() + 0.5F)) < 2.0D && Math.abs(entityliving.locZ - (blockposition.getZ() + 0.5F)) < 2.0D) {
|
||||||
|
double d0 = entityliving.locY + entityliving.getHeadHeight();
|
||||||
|
|
||||||
|
if (d0 - blockposition.getY() > 2.0D) {
|
||||||
|
return EnumDirection.UP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blockposition.getY() - d0 > 0.0D) {
|
||||||
|
return EnumDirection.DOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entityliving.getDirection().opposite();
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
EnumDirection[] aenumdirection = values();
|
||||||
|
int i = aenumdirection.length;
|
||||||
|
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
EnumDirection enumdirection = aenumdirection[j];
|
||||||
|
|
||||||
|
EnumDirection.n[enumdirection.g] = enumdirection;
|
||||||
|
if (enumdirection.k().c()) {
|
||||||
|
EnumDirection.o[enumdirection.i] = enumdirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnumDirection.p.put(enumdirection.j().toLowerCase(Locale.ROOT), enumdirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum EnumDirectionLimit implements Predicate<EnumDirection>, Iterable<EnumDirection> {
|
||||||
|
|
||||||
|
HORIZONTAL, VERTICAL;
|
||||||
|
|
||||||
|
private EnumDirectionLimit() {}
|
||||||
|
|
||||||
|
public EnumDirection[] a() {
|
||||||
|
switch (this) {
|
||||||
|
case HORIZONTAL:
|
||||||
|
return new EnumDirection[] { EnumDirection.NORTH, EnumDirection.EAST, EnumDirection.SOUTH, EnumDirection.WEST};
|
||||||
|
|
||||||
|
case VERTICAL:
|
||||||
|
return new EnumDirection[] { EnumDirection.UP, EnumDirection.DOWN};
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error("Someone\'s been tampering with the universe!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection a(Random random) {
|
||||||
|
EnumDirection[] aenumdirection = this.a();
|
||||||
|
|
||||||
|
return aenumdirection[random.nextInt(aenumdirection.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(@Nullable EnumDirection enumdirection) {
|
||||||
|
return enumdirection != null && enumdirection.k().d() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<EnumDirection> iterator() {
|
||||||
|
return Iterators.forArray(this.a());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable EnumDirection object) {
|
||||||
|
return this.a(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum EnumAxisDirection {
|
||||||
|
|
||||||
|
POSITIVE(1, "Towards positive"), NEGATIVE(-1, "Towards negative");
|
||||||
|
|
||||||
|
private final int c;
|
||||||
|
private final String d;
|
||||||
|
|
||||||
|
private EnumAxisDirection(int i, String s) {
|
||||||
|
this.c = i;
|
||||||
|
this.d = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int a() {
|
||||||
|
return this.c;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static enum EnumAxis implements Predicate<EnumDirection>, INamable {
|
||||||
|
|
||||||
|
X("x", EnumDirection.EnumDirectionLimit.HORIZONTAL), Y("y", EnumDirection.EnumDirectionLimit.VERTICAL), Z("z", EnumDirection.EnumDirectionLimit.HORIZONTAL);
|
||||||
|
|
||||||
|
private static final Map<String, EnumDirection.EnumAxis> d = Maps.newHashMap();
|
||||||
|
private final String e;
|
||||||
|
private final EnumDirection.EnumDirectionLimit f;
|
||||||
|
|
||||||
|
private EnumAxis(String s, EnumDirection.EnumDirectionLimit enumdirection_enumdirectionlimit) {
|
||||||
|
this.e = s;
|
||||||
|
this.f = enumdirection_enumdirectionlimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String a() {
|
||||||
|
return this.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean b() {
|
||||||
|
return this.f == EnumDirection.EnumDirectionLimit.VERTICAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHorizontal() { return c(); } // Akarin - OBFHELPER
|
||||||
|
public boolean c() {
|
||||||
|
return this.f == EnumDirection.EnumDirectionLimit.HORIZONTAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(@Nullable EnumDirection enumdirection) {
|
||||||
|
return enumdirection != null && enumdirection.k() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumDirection.EnumDirectionLimit d() {
|
||||||
|
return this.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(@Nullable EnumDirection object) {
|
||||||
|
return this.a(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
EnumDirection.EnumAxis[] aenumdirection_enumaxis = values();
|
||||||
|
int i = aenumdirection_enumaxis.length;
|
||||||
|
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
EnumDirection.EnumAxis enumdirection_enumaxis = aenumdirection_enumaxis[j];
|
||||||
|
|
||||||
|
EnumDirection.EnumAxis.d.put(enumdirection_enumaxis.a().toLowerCase(Locale.ROOT), enumdirection_enumaxis);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
97
sources/src/main/java/net/minecraft/server/ItemEnderEye.java
Normal file
97
sources/src/main/java/net/minecraft/server/ItemEnderEye.java
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Add end portal disable feature (feature)
|
||||||
|
*/
|
||||||
|
public class ItemEnderEye extends Item {
|
||||||
|
|
||||||
|
public ItemEnderEye() {
|
||||||
|
this.b(CreativeModeTab.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
|
||||||
|
IBlockData iblockdata = world.getType(blockposition);
|
||||||
|
ItemStack itemstack = entityhuman.b(enumhand);
|
||||||
|
|
||||||
|
if (entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack) && iblockdata.getBlock() == Blocks.END_PORTAL_FRAME && !iblockdata.get(BlockEnderPortalFrame.EYE).booleanValue()) {
|
||||||
|
if (world.isClientSide) {
|
||||||
|
return EnumInteractionResult.SUCCESS;
|
||||||
|
} else {
|
||||||
|
world.setTypeAndData(blockposition, iblockdata.set(BlockEnderPortalFrame.EYE, Boolean.valueOf(true)), 2);
|
||||||
|
world.updateAdjacentComparators(blockposition, Blocks.END_PORTAL_FRAME);
|
||||||
|
itemstack.subtract(1);
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; ++i) {
|
||||||
|
double d0 = blockposition.getX() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F;
|
||||||
|
double d1 = blockposition.getY() + 0.8125F;
|
||||||
|
double d2 = blockposition.getZ() + (5.0F + ItemEnderEye.j.nextFloat() * 6.0F) / 16.0F;
|
||||||
|
double d3 = 0.0D;
|
||||||
|
double d4 = 0.0D;
|
||||||
|
double d5 = 0.0D;
|
||||||
|
|
||||||
|
world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, 0.0D, 0.0D, 0.0D, new int[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
world.a((EntityHuman) null, blockposition, SoundEffects.bp, SoundCategory.BLOCKS, 1.0F, 1.0F);
|
||||||
|
if (AkarinGlobalConfig.disableEndPortalCreate) return EnumInteractionResult.SUCCESS; // Akarin
|
||||||
|
ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockEnderPortalFrame.e().a(world, blockposition);
|
||||||
|
|
||||||
|
if (shapedetector_shapedetectorcollection != null) {
|
||||||
|
BlockPosition blockposition1 = shapedetector_shapedetectorcollection.a().a(-3, 0, -3);
|
||||||
|
|
||||||
|
for (int j = 0; j < 3; ++j) {
|
||||||
|
for (int k = 0; k < 3; ++k) {
|
||||||
|
world.setTypeAndData(blockposition1.a(j, 0, k), Blocks.END_PORTAL.getBlockData(), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
world.a(1038, blockposition1.a(1, 0, 1), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return EnumInteractionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return EnumInteractionResult.FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
|
||||||
|
ItemStack itemstack = entityhuman.b(enumhand);
|
||||||
|
MovingObjectPosition movingobjectposition = this.a(world, entityhuman, false);
|
||||||
|
|
||||||
|
if (movingobjectposition != null && movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK && world.getType(movingobjectposition.a()).getBlock() == Blocks.END_PORTAL_FRAME) {
|
||||||
|
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
|
||||||
|
} else {
|
||||||
|
entityhuman.c(enumhand);
|
||||||
|
if (!world.isClientSide) {
|
||||||
|
BlockPosition blockposition = ((WorldServer) world).getChunkProviderServer().a(world, "Stronghold", new BlockPosition(entityhuman), false);
|
||||||
|
|
||||||
|
if (blockposition != null) {
|
||||||
|
EntityEnderSignal entityendersignal = new EntityEnderSignal(world, entityhuman.locX, entityhuman.locY + entityhuman.length / 2.0F, entityhuman.locZ);
|
||||||
|
|
||||||
|
entityendersignal.a(blockposition);
|
||||||
|
world.addEntity(entityendersignal);
|
||||||
|
if (entityhuman instanceof EntityPlayer) {
|
||||||
|
CriterionTriggers.l.a((EntityPlayer) entityhuman, blockposition);
|
||||||
|
}
|
||||||
|
|
||||||
|
world.a((EntityHuman) null, entityhuman.locX, entityhuman.locY, entityhuman.locZ, SoundEffects.bc, SoundCategory.NEUTRAL, 0.5F, 0.4F / (ItemEnderEye.j.nextFloat() * 0.4F + 0.8F));
|
||||||
|
world.a((EntityHuman) null, 1003, new BlockPosition(entityhuman), 0);
|
||||||
|
if (!entityhuman.abilities.canInstantlyBuild) {
|
||||||
|
itemstack.subtract(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
entityhuman.b(StatisticList.b(this));
|
||||||
|
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package net.minecraft.server;
|
package net.minecraft.server;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
|
||||||
|
|
||||||
import io.akarin.api.CheckedConcurrentLinkedQueue;
|
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFuture;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
@@ -20,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;
|
||||||
@@ -31,11 +31,10 @@ import org.apache.logging.log4j.Marker;
|
|||||||
import org.apache.logging.log4j.MarkerManager;
|
import org.apache.logging.log4j.MarkerManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Akarin Changes Note</b><br>
|
* Akarin Changes Note
|
||||||
* <br>
|
* 2) Expose private members (nsc)
|
||||||
* 1) Add volatile to fields<br>
|
* 3) Changes lock type to updatable lock (compatibility)
|
||||||
* 2) Expose private members<br>
|
* 4) Removes unneed array creation (performance)
|
||||||
* @author cakoyo
|
|
||||||
*/
|
*/
|
||||||
public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
||||||
|
|
||||||
@@ -75,13 +74,13 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|||||||
};
|
};
|
||||||
private final EnumProtocolDirection h;
|
private final EnumProtocolDirection h;
|
||||||
private final Queue<NetworkManager.QueuedPacket> i = new CheckedConcurrentLinkedQueue<NetworkManager.QueuedPacket>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - Anti-Xray - OBFHELPER // Akarin
|
private final Queue<NetworkManager.QueuedPacket> i = new CheckedConcurrentLinkedQueue<NetworkManager.QueuedPacket>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - Anti-Xray - OBFHELPER // Akarin
|
||||||
private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
|
private final ReentrantReadWriteUpdateLock j = new ReentrantReadWriteUpdateLock(); // Akarin - use update lock
|
||||||
public Channel channel;
|
public Channel channel;
|
||||||
// Spigot Start // PAIL
|
// Spigot Start // PAIL
|
||||||
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;
|
||||||
@@ -165,7 +164,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
|
|||||||
this.j.writeLock().lock();
|
this.j.writeLock().lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.i.add(new NetworkManager.QueuedPacket(packet, new GenericFutureListener[0]));
|
this.i.add(new NetworkManager.QueuedPacket(packet)); // Akarin - remove fake listener creation
|
||||||
} finally {
|
} finally {
|
||||||
this.j.writeLock().unlock();
|
this.j.writeLock().unlock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
// Paper start
|
||||||
|
import com.destroystokyo.paper.antixray.PacketPlayOutMapChunkInfo; // Anti-Xray
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) WrappedByteBuf -> ByteBuf (compatibility)
|
||||||
|
*/
|
||||||
|
public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
|
||||||
|
|
||||||
|
private int a;
|
||||||
|
private int b;
|
||||||
|
private int c;
|
||||||
|
private ByteBuf d; // Akarin - byte[] -> ByteBuf
|
||||||
|
private List<NBTTagCompound> e;
|
||||||
|
private boolean f;
|
||||||
|
private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
|
||||||
|
|
||||||
|
// Paper start - Async-Anti-Xray - Set the ready flag to true
|
||||||
|
public PacketPlayOutMapChunk() {
|
||||||
|
this.ready = true;
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
public PacketPlayOutMapChunk(Chunk chunk, int i) {
|
||||||
|
PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo = chunk.world.chunkPacketBlockController.getPacketPlayOutMapChunkInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
|
||||||
|
this.a = chunk.locX;
|
||||||
|
this.b = chunk.locZ;
|
||||||
|
this.f = i == '\uffff';
|
||||||
|
boolean flag = chunk.getWorld().worldProvider.m();
|
||||||
|
|
||||||
|
this.d = allocateBuffer(this.a(chunk, flag, i)); // Akarin
|
||||||
|
|
||||||
|
// Paper start - Anti-Xray - Add chunk packet info
|
||||||
|
if (packetPlayOutMapChunkInfo != null) {
|
||||||
|
packetPlayOutMapChunkInfo.setData(this.d);
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
this.c = this.writeChunk(new PacketDataSerializer(this.d), chunk, flag, i, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
|
||||||
|
this.e = Lists.newArrayList();
|
||||||
|
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Entry entry = (Entry) iterator.next();
|
||||||
|
BlockPosition blockposition = (BlockPosition) entry.getKey();
|
||||||
|
TileEntity tileentity = (TileEntity) entry.getValue();
|
||||||
|
int j = blockposition.getY() >> 4;
|
||||||
|
|
||||||
|
if (this.e() || (i & 1 << j) != 0) {
|
||||||
|
NBTTagCompound nbttagcompound = tileentity.d();
|
||||||
|
|
||||||
|
this.e.add(nbttagcompound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.world.chunkPacketBlockController.modifyBlocks(this, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Modify blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
|
||||||
|
public boolean isReady() {
|
||||||
|
return this.ready;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReady(boolean ready) {
|
||||||
|
this.ready = ready;
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
public void a(PacketDataSerializer packetdataserializer) throws IOException {
|
||||||
|
this.a = packetdataserializer.readInt();
|
||||||
|
this.b = packetdataserializer.readInt();
|
||||||
|
this.f = packetdataserializer.readBoolean();
|
||||||
|
this.c = packetdataserializer.g();
|
||||||
|
int i = packetdataserializer.g();
|
||||||
|
|
||||||
|
if (i > 2097152) {
|
||||||
|
throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
|
||||||
|
} else {
|
||||||
|
this.d = Unpooled.buffer(i); // Akarin
|
||||||
|
packetdataserializer.readBytes(this.d);
|
||||||
|
int j = packetdataserializer.g();
|
||||||
|
|
||||||
|
this.e = Lists.newArrayList();
|
||||||
|
|
||||||
|
for (int k = 0; k < j; ++k) {
|
||||||
|
this.e.add(packetdataserializer.j());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void b(PacketDataSerializer packetdataserializer) throws IOException {
|
||||||
|
packetdataserializer.writeInt(this.a);
|
||||||
|
packetdataserializer.writeInt(this.b);
|
||||||
|
packetdataserializer.writeBoolean(this.f);
|
||||||
|
packetdataserializer.d(this.c);
|
||||||
|
packetdataserializer.d(this.d.array().length); // Akarin
|
||||||
|
packetdataserializer.writeBytes(this.d.array()); // Akarin
|
||||||
|
packetdataserializer.d(this.e.size());
|
||||||
|
Iterator iterator = this.e.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
NBTTagCompound nbttagcompound = (NBTTagCompound) iterator.next();
|
||||||
|
|
||||||
|
packetdataserializer.a(nbttagcompound);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(PacketListenerPlayOut packetlistenerplayout) {
|
||||||
|
packetlistenerplayout.a(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ByteBuf allocateBuffer(int expectedCapacity) { return g(expectedCapacity); } // Akarin - OBFHELPER
|
||||||
|
private ByteBuf g(int expectedCapacity) { // Akarin - added argument
|
||||||
|
ByteBuf bytebuf = Unpooled.buffer(expectedCapacity); // Akarin
|
||||||
|
|
||||||
|
bytebuf.writerIndex(0);
|
||||||
|
return bytebuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper start - Anti-Xray - Support default method
|
||||||
|
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector); } // OBFHELPER
|
||||||
|
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i) {
|
||||||
|
return this.a(packetdataserializer, chunk, flag, i, null);
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
|
||||||
|
public int writeChunk(PacketDataSerializer packetDataSerializer, Chunk chunk, boolean writeSkyLightArray, int chunkSectionSelector, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { return this.a(packetDataSerializer, chunk, writeSkyLightArray, chunkSectionSelector, packetPlayOutMapChunkInfo); } // Paper - Anti-Xray - OBFHELPER
|
||||||
|
public int a(PacketDataSerializer packetdataserializer, Chunk chunk, boolean flag, int i, PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo) { // Paper - Anti-Xray - Add chunk packet info
|
||||||
|
int j = 0;
|
||||||
|
ChunkSection[] achunksection = chunk.getSections();
|
||||||
|
int k = 0;
|
||||||
|
|
||||||
|
for (int l = achunksection.length; k < l; ++k) {
|
||||||
|
ChunkSection chunksection = achunksection[k];
|
||||||
|
|
||||||
|
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||||
|
j |= 1 << k;
|
||||||
|
chunksection.getBlocks().writeBlocks(packetdataserializer, packetPlayOutMapChunkInfo, k); // Paper - Anti-Xray - Add chunk packet info
|
||||||
|
packetdataserializer.writeBytes(chunksection.getEmittedLightArray().asBytes());
|
||||||
|
if (flag) {
|
||||||
|
packetdataserializer.writeBytes(chunksection.getSkyLightArray().asBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.e()) {
|
||||||
|
packetdataserializer.writeBytes(chunk.getBiomeIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int a(Chunk chunk, boolean flag, int i) {
|
||||||
|
int j = 0;
|
||||||
|
ChunkSection[] achunksection = chunk.getSections();
|
||||||
|
int k = 0;
|
||||||
|
|
||||||
|
for (int l = achunksection.length; k < l; ++k) {
|
||||||
|
ChunkSection chunksection = achunksection[k];
|
||||||
|
|
||||||
|
if (chunksection != Chunk.a && (!this.e() || !chunksection.a()) && (i & 1 << k) != 0) {
|
||||||
|
j += chunksection.getBlocks().a();
|
||||||
|
j += chunksection.getEmittedLightArray().asBytes().length;
|
||||||
|
if (flag) {
|
||||||
|
j += chunksection.getSkyLightArray().asBytes().length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.e()) {
|
||||||
|
j += chunk.getBiomeIndex().length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean e() {
|
||||||
|
return this.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
314
sources/src/main/java/net/minecraft/server/PlayerChunk.java
Normal file
314
sources/src/main/java/net/minecraft/server/PlayerChunk.java
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
// CraftBukkit Start
|
||||||
|
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Check whether players empty (performance, MC-120780)
|
||||||
|
*/
|
||||||
|
public class PlayerChunk {
|
||||||
|
|
||||||
|
private static final Logger a = LogManager.getLogger();
|
||||||
|
private final PlayerChunkMap playerChunkMap;
|
||||||
|
public final List<EntityPlayer> c = Lists.newArrayList(); // CraftBukkit - public
|
||||||
|
private final ChunkCoordIntPair location;
|
||||||
|
private final short[] dirtyBlocks = new short[64];
|
||||||
|
@Nullable
|
||||||
|
public Chunk chunk; // CraftBukkit - public
|
||||||
|
private int dirtyCount;
|
||||||
|
private int h;
|
||||||
|
private long i;
|
||||||
|
private boolean done;
|
||||||
|
|
||||||
|
// CraftBukkit start - add fields
|
||||||
|
boolean chunkExists; // Paper
|
||||||
|
private boolean loadInProgress = false;
|
||||||
|
private Runnable loadedRunnable = new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
loadInProgress = false;
|
||||||
|
PlayerChunk.this.chunk = PlayerChunk.this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(location.x, location.z);
|
||||||
|
markChunkUsed(); // Paper - delay chunk unloads
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Paper start - delay chunk unloads
|
||||||
|
public final void markChunkUsed() {
|
||||||
|
if (chunk != null && chunk.scheduledForUnload != null) {
|
||||||
|
chunk.scheduledForUnload = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Paper end
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) {
|
||||||
|
this.playerChunkMap = playerchunkmap;
|
||||||
|
this.location = new ChunkCoordIntPair(i, j);
|
||||||
|
// CraftBukkit start
|
||||||
|
loadInProgress = true;
|
||||||
|
this.chunk = playerchunkmap.getWorld().getChunkProviderServer().getChunkAt(i, j, loadedRunnable, false);
|
||||||
|
this.chunkExists = this.chunk != null || ChunkIOExecutor.hasQueuedChunkLoad(playerChunkMap.getWorld(), i, j); // Paper
|
||||||
|
markChunkUsed(); // Paper - delay chunk unloads
|
||||||
|
// CraftBukkit end
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkCoordIntPair a() {
|
||||||
|
return this.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument
|
||||||
|
if (this.c.contains(entityplayer)) {
|
||||||
|
PlayerChunk.a.debug("Failed to add player. {} already is in chunk {}, {}", entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z));
|
||||||
|
} else {
|
||||||
|
if (this.c.isEmpty()) {
|
||||||
|
this.i = this.playerChunkMap.getWorld().getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.c.add(entityplayer);
|
||||||
|
// CraftBukkit start - use async chunk io
|
||||||
|
// if (this.done) {
|
||||||
|
// this.sendChunk(entityplayer);
|
||||||
|
// }
|
||||||
|
if (this.done) {
|
||||||
|
this.sendChunk(entityplayer);
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void b(EntityPlayer entityplayer) {
|
||||||
|
if (this.c.contains(entityplayer)) {
|
||||||
|
// CraftBukkit start - If we haven't loaded yet don't load the chunk just so we can clean it up
|
||||||
|
if (!this.done) {
|
||||||
|
this.c.remove(entityplayer);
|
||||||
|
|
||||||
|
if (this.c.isEmpty()) {
|
||||||
|
ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.getWorld(), this.location.x, this.location.z, this.loadedRunnable);
|
||||||
|
this.playerChunkMap.b(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
if (this.done) {
|
||||||
|
entityplayer.playerConnection.sendPacket(new PacketPlayOutUnloadChunk(this.location.x, this.location.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.c.remove(entityplayer);
|
||||||
|
if (this.c.isEmpty()) {
|
||||||
|
this.playerChunkMap.b(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(boolean flag) {
|
||||||
|
if (this.chunk != null) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
/* CraftBukkit start
|
||||||
|
if (flag) {
|
||||||
|
this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z);
|
||||||
|
} else {
|
||||||
|
this.chunk = this.playerChunkMap.getWorld().getChunkProviderServer().getOrLoadChunkAt(this.location.x, this.location.z);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if (!loadInProgress) {
|
||||||
|
loadInProgress = true;
|
||||||
|
this.chunk = playerChunkMap.getWorld().getChunkProviderServer().getChunkAt(this.location.x, this.location.z, loadedRunnable, flag);
|
||||||
|
markChunkUsed(); // Paper - delay chunk unloads
|
||||||
|
}
|
||||||
|
// CraftBukkit end
|
||||||
|
|
||||||
|
return this.chunk != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean b() {
|
||||||
|
if (this.done) {
|
||||||
|
return true;
|
||||||
|
} else if (this.chunk == null) {
|
||||||
|
return false;
|
||||||
|
} else if (!this.chunk.isReady()) {
|
||||||
|
return false;
|
||||||
|
} else if (!this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', false)) { // Paper - Anti-Xray - Load nearby chunks if necessary
|
||||||
|
return false; // Paper - Anti-Xray - Wait and try again later
|
||||||
|
} else {
|
||||||
|
this.dirtyCount = 0;
|
||||||
|
this.h = 0;
|
||||||
|
this.done = true;
|
||||||
|
if (c.isEmpty()) return true; // Akarin - Fixes MC-120780
|
||||||
|
PacketPlayOutMapChunk packetplayoutmapchunk = new PacketPlayOutMapChunk(this.chunk, '\uffff');
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||||
|
|
||||||
|
entityplayer.playerConnection.sendPacket(packetplayoutmapchunk);
|
||||||
|
this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendChunk(EntityPlayer entityplayer) {
|
||||||
|
if (this.done) {
|
||||||
|
this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, '\uffff', true); // Paper - Anti-Xray - Load nearby chunks if necessary
|
||||||
|
entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(this.chunk, '\uffff'));
|
||||||
|
this.playerChunkMap.getWorld().getTracker().a(entityplayer, this.chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void c() {
|
||||||
|
long i = this.playerChunkMap.getWorld().getTime();
|
||||||
|
|
||||||
|
if (this.chunk != null) {
|
||||||
|
this.chunk.c(this.chunk.x() + i - this.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.i = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(int i, int j, int k) {
|
||||||
|
if (this.done) {
|
||||||
|
if (this.dirtyCount == 0) {
|
||||||
|
this.playerChunkMap.a(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.h |= 1 << (j >> 4);
|
||||||
|
if (this.dirtyCount < 64) {
|
||||||
|
short short0 = (short) (i << 12 | k << 8 | j);
|
||||||
|
|
||||||
|
for (int l = 0; l < this.dirtyCount; ++l) {
|
||||||
|
if (this.dirtyBlocks[l] == short0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dirtyBlocks[this.dirtyCount++] = short0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(Packet<?> packet) {
|
||||||
|
if (this.done) {
|
||||||
|
for (int i = 0; i < this.c.size(); ++i) {
|
||||||
|
this.c.get(i).playerConnection.sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d() {
|
||||||
|
if (this.done && this.chunk != null) {
|
||||||
|
if (this.dirtyCount != 0) {
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
int k;
|
||||||
|
|
||||||
|
if (this.dirtyCount == 1) {
|
||||||
|
i = (this.dirtyBlocks[0] >> 12 & 15) + this.location.x * 16;
|
||||||
|
j = this.dirtyBlocks[0] & 255;
|
||||||
|
k = (this.dirtyBlocks[0] >> 8 & 15) + this.location.z * 16;
|
||||||
|
BlockPosition blockposition = new BlockPosition(i, j, k);
|
||||||
|
|
||||||
|
this.a((new PacketPlayOutBlockChange(this.playerChunkMap.getWorld(), blockposition)));
|
||||||
|
if (this.playerChunkMap.getWorld().getType(blockposition).getBlock().isTileEntity()) {
|
||||||
|
this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition));
|
||||||
|
}
|
||||||
|
} else if (this.dirtyCount == 64) {
|
||||||
|
// Paper - Anti-Xray - Loading chunks here could cause a ConcurrentModificationException #1104
|
||||||
|
//this.chunk.world.chunkPacketBlockController.onChunkPacketCreate(this.chunk, this.h, true); // Paper - Anti-Xray - Load nearby chunks if necessary
|
||||||
|
this.a((new PacketPlayOutMapChunk(this.chunk, this.h)));
|
||||||
|
} else {
|
||||||
|
this.a((new PacketPlayOutMultiBlockChange(this.dirtyCount, this.dirtyBlocks, this.chunk)));
|
||||||
|
|
||||||
|
for (i = 0; i < this.dirtyCount; ++i) {
|
||||||
|
j = (this.dirtyBlocks[i] >> 12 & 15) + this.location.x * 16;
|
||||||
|
k = this.dirtyBlocks[i] & 255;
|
||||||
|
int l = (this.dirtyBlocks[i] >> 8 & 15) + this.location.z * 16;
|
||||||
|
BlockPosition blockposition1 = new BlockPosition(j, k, l);
|
||||||
|
|
||||||
|
if (this.playerChunkMap.getWorld().getType(blockposition1).getBlock().isTileEntity()) {
|
||||||
|
this.a(this.playerChunkMap.getWorld().getTileEntity(blockposition1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dirtyCount = 0;
|
||||||
|
this.h = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void a(@Nullable TileEntity tileentity) {
|
||||||
|
if (tileentity != null) {
|
||||||
|
PacketPlayOutTileEntityData packetplayouttileentitydata = tileentity.getUpdatePacket();
|
||||||
|
|
||||||
|
if (packetplayouttileentitydata != null) {
|
||||||
|
this.a(packetplayouttileentitydata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean d(EntityPlayer entityplayer) {
|
||||||
|
return this.c.contains(entityplayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(Predicate<EntityPlayer> predicate) {
|
||||||
|
return Iterables.tryFind(this.c, predicate).isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean a(double d0, Predicate<EntityPlayer> predicate) {
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (int j = this.c.size(); i < j; ++i) {
|
||||||
|
EntityPlayer entityplayer = this.c.get(i);
|
||||||
|
|
||||||
|
if (predicate.apply(entityplayer) && this.location.a(entityplayer) < d0 * d0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean e() {
|
||||||
|
return this.done;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Chunk f() {
|
||||||
|
return this.chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double g() {
|
||||||
|
double d0 = Double.MAX_VALUE;
|
||||||
|
Iterator iterator = this.c.iterator();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
|
||||||
|
double d1 = this.location.a(entityplayer);
|
||||||
|
|
||||||
|
if (d1 < d0) {
|
||||||
|
d0 = d1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package net.minecraft.server;
|
|||||||
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 io.akarin.api.internal.Akari;
|
||||||
import io.akarin.server.core.AkarinGlobalConfig;
|
import io.akarin.server.core.AkarinGlobalConfig;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.GenericFutureListener;
|
||||||
@@ -61,12 +62,11 @@ import co.aikar.timings.MinecraftTimings; // Paper
|
|||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <b>Akarin Changes Note</b><br>
|
* Akarin Changes Note
|
||||||
* <br>
|
* 1) Add volatile to fields (slack service)
|
||||||
* 1) Add volatile to fields<br>
|
* 2) Expose private members (slack service)
|
||||||
* 2) Expose private members<br>
|
* 3) Removed keep-alive codes (slack service)
|
||||||
* 3) Migrated keep alive packet handling to service thread<br>
|
* 4) Accessible keep-alive limit (feature, compatibility)
|
||||||
* @author cakoyo
|
|
||||||
*/
|
*/
|
||||||
public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
||||||
|
|
||||||
@@ -75,12 +75,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();
|
||||||
@@ -145,7 +146,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
private final static HashSet<Integer> invalidItems = new HashSet<Integer>(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 55, 59, 62, 63, 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 140, 141, 142, 144)); // TODO: Check after every update.
|
private final static HashSet<Integer> invalidItems = new HashSet<Integer>(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 55, 59, 62, 63, 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 140, 141, 142, 144)); // TODO: Check after every update.
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void e() {
|
public void e() {
|
||||||
this.syncPosition();
|
this.syncPosition();
|
||||||
this.player.playerTick();
|
this.player.playerTick();
|
||||||
@@ -188,7 +188,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.minecraftServer.methodProfiler.a("keepAlive");
|
this.minecraftServer.methodProfiler.a("keepAlive");
|
||||||
/* // Akarin start
|
/* // Akarin
|
||||||
// Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
// Paper Start - give clients a longer time to respond to pings as per pre 1.12.2 timings
|
||||||
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
|
||||||
long currentTime = this.getCurrentMillis();
|
long currentTime = this.getCurrentMillis();
|
||||||
@@ -208,11 +208,12 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Paper end
|
// Paper end
|
||||||
*/ // Akarin end
|
*/ // Akarin
|
||||||
|
|
||||||
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;
|
||||||
@@ -224,7 +225,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
--this.j;
|
--this.j;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.player.J() > 0L && this.minecraftServer.getIdleTimeout() > 0 && MinecraftServer.aw() - this.player.J() > this.minecraftServer.getIdleTimeout() * 1000 * 60) {
|
if (this.player.J() > 0L && this.minecraftServer.getIdleTimeout() > 0 && MinecraftServer.aw() - this.player.J() > (long) (this.minecraftServer.getIdleTimeout() * 1000 * 60)) {
|
||||||
this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854
|
this.player.resetIdleTimer(); // CraftBukkit - SPIGOT-854
|
||||||
this.disconnect(new ChatMessage("multiplayer.disconnect.idling", new Object[0]));
|
this.disconnect(new ChatMessage("multiplayer.disconnect.idling", new Object[0]));
|
||||||
}
|
}
|
||||||
@@ -274,7 +275,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
final ChatComponentText chatcomponenttext = new ChatComponentText(s);
|
final ChatComponentText chatcomponenttext = new ChatComponentText(s);
|
||||||
|
|
||||||
this.networkManager.sendPacket(new PacketPlayOutKickDisconnect(chatcomponenttext), new GenericFutureListener() {
|
this.networkManager.sendPacket(new PacketPlayOutKickDisconnect(chatcomponenttext), new GenericFutureListener() {
|
||||||
@Override
|
|
||||||
public void operationComplete(Future future) throws Exception { // CraftBukkit - decompile error
|
public void operationComplete(Future future) throws Exception { // CraftBukkit - decompile error
|
||||||
PlayerConnection.this.networkManager.close(chatcomponenttext);
|
PlayerConnection.this.networkManager.close(chatcomponenttext);
|
||||||
}
|
}
|
||||||
@@ -283,14 +283,12 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
this.networkManager.stopReading();
|
this.networkManager.stopReading();
|
||||||
// CraftBukkit - Don't wait
|
// CraftBukkit - Don't wait
|
||||||
this.minecraftServer.postToMainThread(new Runnable() {
|
this.minecraftServer.postToMainThread(new Runnable() {
|
||||||
@Override
|
|
||||||
public void run() {
|
public void run() {
|
||||||
PlayerConnection.this.networkManager.handleDisconnection();
|
PlayerConnection.this.networkManager.handleDisconnection();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) {
|
public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinsteervehicle, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinsteervehicle, this, this.player.x());
|
||||||
this.player.a(packetplayinsteervehicle.a(), packetplayinsteervehicle.b(), packetplayinsteervehicle.c(), packetplayinsteervehicle.d());
|
this.player.a(packetplayinsteervehicle.a(), packetplayinsteervehicle.b(), packetplayinsteervehicle.c(), packetplayinsteervehicle.d());
|
||||||
@@ -304,7 +302,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
return !Doubles.isFinite(packetplayinvehiclemove.getX()) || !Doubles.isFinite(packetplayinvehiclemove.getY()) || !Doubles.isFinite(packetplayinvehiclemove.getZ()) || !Floats.isFinite(packetplayinvehiclemove.getPitch()) || !Floats.isFinite(packetplayinvehiclemove.getYaw());
|
return !Doubles.isFinite(packetplayinvehiclemove.getX()) || !Doubles.isFinite(packetplayinvehiclemove.getY()) || !Doubles.isFinite(packetplayinvehiclemove.getZ()) || !Floats.isFinite(packetplayinvehiclemove.getPitch()) || !Floats.isFinite(packetplayinvehiclemove.getYaw());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInVehicleMove packetplayinvehiclemove) {
|
public void a(PacketPlayInVehicleMove packetplayinvehiclemove) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinvehiclemove, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinvehiclemove, this, this.player.x());
|
||||||
if (b(packetplayinvehiclemove)) {
|
if (b(packetplayinvehiclemove)) {
|
||||||
@@ -354,7 +351,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
|
speed *= 2f; // TODO: Get the speed of the vehicle instead of the player
|
||||||
|
|
||||||
if (d10 - d9 > Math.max(100.0D, Math.pow(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * i * speed, 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) { // Spigot
|
if (d10 - d9 > Math.max(100.0D, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(entity.getName()))) { // Spigot
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", entity.getName(), this.player.getName(), Double.valueOf(d6), Double.valueOf(d7), Double.valueOf(d8));
|
PlayerConnection.LOGGER.warn("{} (vehicle of {}) moved too quickly! {},{},{}", entity.getName(), this.player.getName(), Double.valueOf(d6), Double.valueOf(d7), Double.valueOf(d8));
|
||||||
this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
|
this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
|
||||||
@@ -472,7 +469,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInTeleportAccept packetplayinteleportaccept) {
|
public void a(PacketPlayInTeleportAccept packetplayinteleportaccept) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinteleportaccept, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinteleportaccept, this, this.player.x());
|
||||||
if (packetplayinteleportaccept.a() == this.teleportAwait && this.teleportPos != null) { // CraftBukkit
|
if (packetplayinteleportaccept.a() == this.teleportAwait && this.teleportPos != null) { // CraftBukkit
|
||||||
@@ -489,7 +485,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
|
public void a(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinrecipedisplayed, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinrecipedisplayed, this, this.player.x());
|
||||||
if (packetplayinrecipedisplayed.a() == PacketPlayInRecipeDisplayed.Status.SHOWN) {
|
if (packetplayinrecipedisplayed.a() == PacketPlayInRecipeDisplayed.Status.SHOWN) {
|
||||||
@@ -501,7 +496,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInAdvancements packetplayinadvancements) {
|
public void a(PacketPlayInAdvancements packetplayinadvancements) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinadvancements, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinadvancements, this, this.player.x());
|
||||||
if (packetplayinadvancements.b() == PacketPlayInAdvancements.Status.OPENED_TAB) {
|
if (packetplayinadvancements.b() == PacketPlayInAdvancements.Status.OPENED_TAB) {
|
||||||
@@ -515,7 +509,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInFlying packetplayinflying) {
|
public void a(PacketPlayInFlying packetplayinflying) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.x());
|
||||||
if (b(packetplayinflying)) {
|
if (b(packetplayinflying)) {
|
||||||
@@ -597,7 +590,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
if (!this.player.L() && (!this.player.x().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.cP())) {
|
if (!this.player.L() && (!this.player.x().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.cP())) {
|
||||||
float f2 = this.player.cP() ? 300.0F : 100.0F;
|
float f2 = this.player.cP() ? 300.0F : 100.0F;
|
||||||
|
|
||||||
if (d11 - d10 > Math.max(f2, Math.pow(org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * i * speed, 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(this.player.getName()))) { // Spigot
|
if (d11 - d10 > Math.max(f2, Math.pow((double) (org.spigotmc.SpigotConfig.movedTooQuicklyMultiplier * (float) i * speed), 2)) && (!this.minecraftServer.R() || !this.minecraftServer.Q().equals(this.player.getName()))) { // Spigot
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getName(), Double.valueOf(d7), Double.valueOf(d8), Double.valueOf(d9));
|
PlayerConnection.LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getName(), Double.valueOf(d7), Double.valueOf(d8), Double.valueOf(d9));
|
||||||
this.a(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch);
|
this.a(this.player.locX, this.player.locY, this.player.locZ, this.player.yaw, this.player.pitch);
|
||||||
@@ -854,7 +847,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0, d1, d2, f, f1, set, this.teleportAwait));
|
this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0, d1, d2, f, f1, set, this.teleportAwait));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInBlockDig packetplayinblockdig) {
|
public void a(PacketPlayInBlockDig packetplayinblockdig) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -917,13 +909,14 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
case START_DESTROY_BLOCK:
|
case START_DESTROY_BLOCK:
|
||||||
case ABORT_DESTROY_BLOCK:
|
case ABORT_DESTROY_BLOCK:
|
||||||
case STOP_DESTROY_BLOCK:
|
case STOP_DESTROY_BLOCK:
|
||||||
double d0 = this.player.locX - (blockposition.getX() + 0.5D);
|
double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D);
|
||||||
double d1 = this.player.locY - (blockposition.getY() + 0.5D) + 1.5D;
|
double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D;
|
||||||
double d2 = this.player.locZ - (blockposition.getZ() + 0.5D);
|
double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D);
|
||||||
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
||||||
|
|
||||||
if (d3 > 36.0D) {
|
if (d3 > 36.0D) {
|
||||||
this.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); // Paper - Fix block break desync
|
if (worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) // Paper - Fix block break desync - Don't send for unloaded chunks
|
||||||
|
this.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); // Paper - Fix block break desync
|
||||||
return;
|
return;
|
||||||
} else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight()) {
|
} else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight()) {
|
||||||
return;
|
return;
|
||||||
@@ -983,7 +976,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
// Spigot end
|
// Spigot end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInUseItem packetplayinuseitem) {
|
public void a(PacketPlayInUseItem packetplayinuseitem) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -1000,7 +992,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
|
chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
|
||||||
this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage, ChatMessageType.GAME_INFO));
|
this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage, ChatMessageType.GAME_INFO));
|
||||||
} else if (this.teleportPos == null && this.player.d(blockposition.getX() + 0.5D, blockposition.getY() + 0.5D, blockposition.getZ() + 0.5D) < 64.0D && !this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) {
|
} else if (this.teleportPos == null && this.player.d((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D) < 64.0D && !this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.getWorldBorder().a(blockposition)) {
|
||||||
// CraftBukkit start - Check if we can actually do something over this large a distance
|
// CraftBukkit start - Check if we can actually do something over this large a distance
|
||||||
Location eyeLoc = this.getPlayer().getEyeLocation();
|
Location eyeLoc = this.getPlayer().getEyeLocation();
|
||||||
double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ());
|
double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ());
|
||||||
@@ -1015,7 +1007,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition.shift(enumdirection)));
|
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition.shift(enumdirection)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInBlockPlace packetplayinblockplace) {
|
public void a(PacketPlayInBlockPlace packetplayinblockplace) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -1031,7 +1022,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
float f1 = this.player.pitch;
|
float f1 = this.player.pitch;
|
||||||
float f2 = this.player.yaw;
|
float f2 = this.player.yaw;
|
||||||
double d0 = this.player.locX;
|
double d0 = this.player.locX;
|
||||||
double d1 = this.player.locY + this.player.getHeadHeight();
|
double d1 = this.player.locY + (double) this.player.getHeadHeight();
|
||||||
double d2 = this.player.locZ;
|
double d2 = this.player.locZ;
|
||||||
Vec3D vec3d = new Vec3D(d0, d1, d2);
|
Vec3D vec3d = new Vec3D(d0, d1, d2);
|
||||||
|
|
||||||
@@ -1042,7 +1033,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
float f7 = f4 * f5;
|
float f7 = f4 * f5;
|
||||||
float f8 = f3 * f5;
|
float f8 = f3 * f5;
|
||||||
double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D;
|
double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D;
|
||||||
Vec3D vec3d1 = vec3d.add(f7 * d3, f6 * d3, f8 * d3);
|
Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3);
|
||||||
MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false);
|
MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false);
|
||||||
|
|
||||||
boolean cancelled;
|
boolean cancelled;
|
||||||
@@ -1068,7 +1059,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInSpectate packetplayinspectate) {
|
public void a(PacketPlayInSpectate packetplayinspectate) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinspectate, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinspectate, this, this.player.x());
|
||||||
if (this.player.isSpectator()) {
|
if (this.player.isSpectator()) {
|
||||||
@@ -1126,7 +1116,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {
|
public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinresourcepackstatus, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinresourcepackstatus, this, this.player.x());
|
||||||
// Paper start
|
// Paper start
|
||||||
@@ -1138,7 +1127,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInBoatMove packetplayinboatmove) {
|
public void a(PacketPlayInBoatMove packetplayinboatmove) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.x());
|
||||||
Entity entity = this.player.bJ();
|
Entity entity = this.player.bJ();
|
||||||
@@ -1149,7 +1137,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(IChatBaseComponent ichatbasecomponent) {
|
public void a(IChatBaseComponent ichatbasecomponent) {
|
||||||
// CraftBukkit start - Rarely it would send a disconnect line twice
|
// CraftBukkit start - Rarely it would send a disconnect line twice
|
||||||
if (this.processedDisconnect) {
|
if (this.processedDisconnect) {
|
||||||
@@ -1215,7 +1202,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
return packet.getClass().getCanonicalName();
|
return packet.getClass().getCanonicalName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object call() throws Exception {
|
public Object call() throws Exception {
|
||||||
return this.a();
|
return this.a();
|
||||||
}
|
}
|
||||||
@@ -1224,7 +1210,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
|
public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -1245,7 +1230,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInChat packetplayinchat) {
|
public void a(PacketPlayInChat packetplayinchat) {
|
||||||
// CraftBukkit start - async chat
|
// CraftBukkit start - async chat
|
||||||
// SPIGOT-3638
|
// SPIGOT-3638
|
||||||
@@ -1384,8 +1368,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
if (!async && s.startsWith("/")) {
|
if (!async && s.startsWith("/")) {
|
||||||
// Paper Start
|
// Paper Start
|
||||||
if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) {
|
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Akari.isPrimaryThread()) { // Akarin
|
||||||
final String fCommandLine = s;
|
final String fCommandLine = s;
|
||||||
|
Akari.callbackQueue.add(() -> chat(fCommandLine, false)); // Akarin
|
||||||
|
/* // Akarin
|
||||||
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine);
|
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine);
|
||||||
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());
|
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());
|
||||||
Waitable wait = new Waitable() {
|
Waitable wait = new Waitable() {
|
||||||
@@ -1404,6 +1390,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Exception processing chat command", e.getCause());
|
throw new RuntimeException("Exception processing chat command", e.getCause());
|
||||||
}
|
}
|
||||||
|
*/ // Akarin
|
||||||
}
|
}
|
||||||
// Paper End
|
// Paper End
|
||||||
this.handleCommand(s);
|
this.handleCommand(s);
|
||||||
@@ -1512,7 +1499,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInArmAnimation packetplayinarmanimation) {
|
public void a(PacketPlayInArmAnimation packetplayinarmanimation) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -1521,7 +1507,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
float f1 = this.player.pitch;
|
float f1 = this.player.pitch;
|
||||||
float f2 = this.player.yaw;
|
float f2 = this.player.yaw;
|
||||||
double d0 = this.player.locX;
|
double d0 = this.player.locX;
|
||||||
double d1 = this.player.locY + this.player.getHeadHeight();
|
double d1 = this.player.locY + (double) this.player.getHeadHeight();
|
||||||
double d2 = this.player.locZ;
|
double d2 = this.player.locZ;
|
||||||
Vec3D vec3d = new Vec3D(d0, d1, d2);
|
Vec3D vec3d = new Vec3D(d0, d1, d2);
|
||||||
|
|
||||||
@@ -1532,7 +1518,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
float f7 = f4 * f5;
|
float f7 = f4 * f5;
|
||||||
float f8 = f3 * f5;
|
float f8 = f3 * f5;
|
||||||
double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D;
|
double d3 = player.playerInteractManager.getGameMode()== EnumGamemode.CREATIVE ? 5.0D : 4.5D;
|
||||||
Vec3D vec3d1 = vec3d.add(f7 * d3, f6 * d3, f8 * d3);
|
Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3);
|
||||||
MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false);
|
MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false);
|
||||||
|
|
||||||
if (movingobjectposition == null || movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.BLOCK) {
|
if (movingobjectposition == null || movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.BLOCK) {
|
||||||
@@ -1548,7 +1534,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
this.player.a(packetplayinarmanimation.a());
|
this.player.a(packetplayinarmanimation.a());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInEntityAction packetplayinentityaction) {
|
public void a(PacketPlayInEntityAction packetplayinentityaction) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.x());
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
@@ -1650,12 +1635,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInUseEntity packetplayinuseentity) {
|
public void a(PacketPlayInUseEntity packetplayinuseentity) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
|
WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
|
||||||
Entity entity = packetplayinuseentity.a(worldserver);
|
Entity entity = packetplayinuseentity.a((World) worldserver);
|
||||||
// Spigot Start
|
// Spigot Start
|
||||||
if ( entity == player && !player.isSpectator() )
|
if ( entity == player && !player.isSpectator() )
|
||||||
{
|
{
|
||||||
@@ -1685,10 +1669,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem();
|
Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem();
|
||||||
PlayerInteractEntityEvent event;
|
PlayerInteractEntityEvent event;
|
||||||
if (packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT) {
|
if (packetplayinuseentity.a() == PacketPlayInUseEntity.EnumEntityUseAction.INTERACT) {
|
||||||
event = new PlayerInteractEntityEvent(this.getPlayer(), entity.getBukkitEntity(), (packetplayinuseentity.b() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND);
|
event = new PlayerInteractEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), (packetplayinuseentity.b() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND);
|
||||||
} else {
|
} else {
|
||||||
Vec3D target = packetplayinuseentity.c();
|
Vec3D target = packetplayinuseentity.c();
|
||||||
event = new PlayerInteractAtEntityEvent(this.getPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(target.x, target.y, target.z), (packetplayinuseentity.b() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND);
|
event = new PlayerInteractAtEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(target.x, target.y, target.z), (packetplayinuseentity.b() == EnumHand.OFF_HAND) ? EquipmentSlot.OFF_HAND : EquipmentSlot.HAND);
|
||||||
}
|
}
|
||||||
this.server.getPluginManager().callEvent(event);
|
this.server.getPluginManager().callEvent(event);
|
||||||
|
|
||||||
@@ -1703,6 +1687,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.isCancelled()) {
|
if (event.isCancelled()) {
|
||||||
|
this.player.updateInventory(this.player.activeContainer); // Paper - Refresh player inventory
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// CraftBukkit end
|
// CraftBukkit end
|
||||||
@@ -1754,7 +1739,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInClientCommand packetplayinclientcommand) {
|
public void a(PacketPlayInClientCommand packetplayinclientcommand) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinclientcommand, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinclientcommand, this, this.player.x());
|
||||||
this.player.resetIdleTimer();
|
this.player.resetIdleTimer();
|
||||||
@@ -1786,17 +1770,15 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInCloseWindow packetplayinclosewindow) {
|
public void a(PacketPlayInCloseWindow packetplayinclosewindow) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x());
|
||||||
|
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit
|
CraftEventFactory.handleInventoryCloseEvent(this.player, org.bukkit.event.inventory.InventoryCloseEvent.Reason.PLAYER); // CraftBukkit // Paper
|
||||||
|
|
||||||
this.player.r();
|
this.player.r();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInWindowClick packetplayinwindowclick) {
|
public void a(PacketPlayInWindowClick packetplayinwindowclick) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -1807,7 +1789,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
NonNullList nonnulllist = NonNullList.a();
|
NonNullList nonnulllist = NonNullList.a();
|
||||||
|
|
||||||
for (int i = 0; i < this.player.activeContainer.slots.size(); ++i) {
|
for (int i = 0; i < this.player.activeContainer.slots.size(); ++i) {
|
||||||
nonnulllist.add(this.player.activeContainer.slots.get(i).getItem());
|
nonnulllist.add(((Slot) this.player.activeContainer.slots.get(i)).getItem());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.player.a(this.player.activeContainer, nonnulllist);
|
this.player.a(this.player.activeContainer, nonnulllist);
|
||||||
@@ -2093,7 +2075,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
NonNullList nonnulllist1 = NonNullList.a();
|
NonNullList nonnulllist1 = NonNullList.a();
|
||||||
|
|
||||||
for (int j = 0; j < this.player.activeContainer.slots.size(); ++j) {
|
for (int j = 0; j < this.player.activeContainer.slots.size(); ++j) {
|
||||||
ItemStack itemstack1 = this.player.activeContainer.slots.get(j).getItem();
|
ItemStack itemstack1 = ((Slot) this.player.activeContainer.slots.get(j)).getItem();
|
||||||
ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1;
|
ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1;
|
||||||
|
|
||||||
nonnulllist1.add(itemstack2);
|
nonnulllist1.add(itemstack2);
|
||||||
@@ -2106,7 +2088,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInAutoRecipe packetplayinautorecipe) {
|
public void a(PacketPlayInAutoRecipe packetplayinautorecipe) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinautorecipe, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinautorecipe, this, this.player.x());
|
||||||
this.player.resetIdleTimer();
|
this.player.resetIdleTimer();
|
||||||
@@ -2115,7 +2096,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInEnchantItem packetplayinenchantitem) {
|
public void a(PacketPlayInEnchantItem packetplayinenchantitem) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -2127,7 +2107,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInSetCreativeSlot packetplayinsetcreativeslot) {
|
public void a(PacketPlayInSetCreativeSlot packetplayinsetcreativeslot) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinsetcreativeslot, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinsetcreativeslot, this, this.player.x());
|
||||||
if (this.player.playerInteractManager.isCreative()) {
|
if (this.player.playerInteractManager.isCreative()) {
|
||||||
@@ -2147,7 +2126,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
nbttagcompound1.remove("x");
|
nbttagcompound1.remove("x");
|
||||||
nbttagcompound1.remove("y");
|
nbttagcompound1.remove("y");
|
||||||
nbttagcompound1.remove("z");
|
nbttagcompound1.remove("z");
|
||||||
itemstack.a("BlockEntityTag", nbttagcompound1);
|
itemstack.a("BlockEntityTag", (NBTBase) nbttagcompound1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2213,11 +2192,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInTransaction packetplayintransaction) {
|
public void a(PacketPlayInTransaction packetplayintransaction) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
Short oshort = this.k.get(this.player.activeContainer.windowId);
|
Short oshort = (Short) this.k.get(this.player.activeContainer.windowId);
|
||||||
|
|
||||||
if (oshort != null && packetplayintransaction.b() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.a() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
|
if (oshort != null && packetplayintransaction.b() == oshort.shortValue() && this.player.activeContainer.windowId == packetplayintransaction.a() && !this.player.activeContainer.c(this.player) && !this.player.isSpectator()) {
|
||||||
this.player.activeContainer.a(this.player, true);
|
this.player.activeContainer.a(this.player, true);
|
||||||
@@ -2225,7 +2203,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInUpdateSign packetplayinupdatesign) {
|
public void a(PacketPlayInUpdateSign packetplayinupdatesign) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x());
|
||||||
if (this.player.isFrozen()) return; // CraftBukkit
|
if (this.player.isFrozen()) return; // CraftBukkit
|
||||||
@@ -2261,7 +2238,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
for (int i = 0; i < astring.length; ++i) {
|
for (int i = 0; i < astring.length; ++i) {
|
||||||
lines[i] = SharedConstants.a(astring[i]); //Paper - Replaced with anvil color stripping method to stop exploits that allow colored signs to be created.
|
lines[i] = SharedConstants.a(astring[i]); //Paper - Replaced with anvil color stripping method to stop exploits that allow colored signs to be created.
|
||||||
}
|
}
|
||||||
SignChangeEvent event = new SignChangeEvent(player.getWorld().getBlockAt(x, y, z), this.server.getPlayer(this.player), lines);
|
SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(x, y, z), this.server.getPlayer(this.player), lines);
|
||||||
this.server.getPluginManager().callEvent(event);
|
this.server.getPluginManager().callEvent(event);
|
||||||
|
|
||||||
if (!event.isCancelled()) {
|
if (!event.isCancelled()) {
|
||||||
@@ -2276,7 +2253,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInKeepAlive packetplayinkeepalive) {
|
public void a(PacketPlayInKeepAlive packetplayinkeepalive) {
|
||||||
//PlayerConnectionUtils.ensureMainThread(packetplayinkeepalive, this, this.player.x()); // CraftBukkit // Paper - This shouldn't be on the main thread
|
//PlayerConnectionUtils.ensureMainThread(packetplayinkeepalive, this, this.player.x()); // CraftBukkit // Paper - This shouldn't be on the main thread
|
||||||
if (this.g && packetplayinkeepalive.a() == this.h) {
|
if (this.g && packetplayinkeepalive.a() == this.h) {
|
||||||
@@ -2301,7 +2277,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
return System.nanoTime() / 1000000L;
|
return System.nanoTime() / 1000000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInAbilities packetplayinabilities) {
|
public void a(PacketPlayInAbilities packetplayinabilities) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.x());
|
||||||
// CraftBukkit start
|
// CraftBukkit start
|
||||||
@@ -2318,10 +2293,9 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Paper start - async tab completion
|
// Paper start - async tab completion
|
||||||
@Override
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -2365,13 +2339,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
// Paper end
|
// Paper end
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInSettings packetplayinsettings) {
|
public void a(PacketPlayInSettings packetplayinsettings) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayinsettings, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayinsettings, this, this.player.x());
|
||||||
this.player.a(packetplayinsettings);
|
this.player.a(packetplayinsettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void a(PacketPlayInCustomPayload packetplayincustompayload) {
|
public void a(PacketPlayInCustomPayload packetplayincustompayload) {
|
||||||
PlayerConnectionUtils.ensureMainThread(packetplayincustompayload, this, this.player.x());
|
PlayerConnectionUtils.ensureMainThread(packetplayincustompayload, this, this.player.x());
|
||||||
String s = packetplayincustompayload.a();
|
String s = packetplayincustompayload.a();
|
||||||
@@ -2406,7 +2378,7 @@ 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 = new ItemStack(Items.WRITABLE_BOOK); // CraftBukkit
|
||||||
itemstack1.a("pages", itemstack.getTag().getList("pages", 8));
|
itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8));
|
||||||
CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
|
CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
|
||||||
}
|
}
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
@@ -2443,19 +2415,19 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) {
|
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) {
|
||||||
ItemStack itemstack2 = new ItemStack(Items.WRITTEN_BOOK);
|
ItemStack itemstack2 = new ItemStack(Items.WRITTEN_BOOK);
|
||||||
|
|
||||||
itemstack2.a("author", (new NBTTagString(this.player.getName())));
|
itemstack2.a("author", (NBTBase) (new NBTTagString(this.player.getName())));
|
||||||
itemstack2.a("title", (new NBTTagString(itemstack.getTag().getString("title"))));
|
itemstack2.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title"))));
|
||||||
NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8);
|
NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8);
|
||||||
|
|
||||||
for (int i = 0; i < nbttaglist.size(); ++i) {
|
for (int i = 0; i < nbttaglist.size(); ++i) {
|
||||||
s1 = nbttaglist.getString(i);
|
s1 = nbttaglist.getString(i);
|
||||||
ChatComponentText chatcomponenttext = new ChatComponentText(s1);
|
ChatComponentText chatcomponenttext = new ChatComponentText(s1);
|
||||||
|
|
||||||
s1 = IChatBaseComponent.ChatSerializer.a(chatcomponenttext);
|
s1 = IChatBaseComponent.ChatSerializer.a((IChatBaseComponent) chatcomponenttext);
|
||||||
nbttaglist.a(i, new NBTTagString(s1));
|
nbttaglist.a(i, new NBTTagString(s1));
|
||||||
}
|
}
|
||||||
|
|
||||||
itemstack2.a("pages", nbttaglist);
|
itemstack2.a("pages", (NBTBase) nbttaglist);
|
||||||
CraftEventFactory.handleEditBookEvent(player, itemstack2); // CraftBukkit
|
CraftEventFactory.handleEditBookEvent(player, itemstack2); // CraftBukkit
|
||||||
}
|
}
|
||||||
} catch (Exception exception1) {
|
} catch (Exception exception1) {
|
||||||
@@ -2553,7 +2525,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
boolean flag3 = packetdataserializer.readBoolean();
|
boolean flag3 = packetdataserializer.readBoolean();
|
||||||
|
|
||||||
if (commandblocklistenerabstract1 != null) {
|
if (commandblocklistenerabstract1 != null) {
|
||||||
EnumDirection enumdirection = this.player.world.getType(blockposition).get(BlockCommand.a);
|
EnumDirection enumdirection = (EnumDirection) this.player.world.getType(blockposition).get(BlockCommand.a);
|
||||||
IBlockData iblockdata;
|
IBlockData iblockdata;
|
||||||
|
|
||||||
switch (tileentitycommand_type) {
|
switch (tileentitycommand_type) {
|
||||||
@@ -2673,23 +2645,23 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
|
|||||||
|
|
||||||
if (b1 == 2) {
|
if (b1 == 2) {
|
||||||
if (tileentitystructure.q()) {
|
if (tileentitystructure.q()) {
|
||||||
this.player.a((new ChatMessage("structure_block.save_success", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.save_success", new Object[] { s7})), false);
|
||||||
} else {
|
} else {
|
||||||
this.player.a((new ChatMessage("structure_block.save_failure", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.save_failure", new Object[] { s7})), false);
|
||||||
}
|
}
|
||||||
} else if (b1 == 3) {
|
} else if (b1 == 3) {
|
||||||
if (!tileentitystructure.E()) {
|
if (!tileentitystructure.E()) {
|
||||||
this.player.a((new ChatMessage("structure_block.load_not_found", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_not_found", new Object[] { s7})), false);
|
||||||
} else if (tileentitystructure.r()) {
|
} else if (tileentitystructure.r()) {
|
||||||
this.player.a((new ChatMessage("structure_block.load_success", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_success", new Object[] { s7})), false);
|
||||||
} else {
|
} else {
|
||||||
this.player.a((new ChatMessage("structure_block.load_prepare", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.load_prepare", new Object[] { s7})), false);
|
||||||
}
|
}
|
||||||
} else if (b1 == 4) {
|
} else if (b1 == 4) {
|
||||||
if (tileentitystructure.p()) {
|
if (tileentitystructure.p()) {
|
||||||
this.player.a((new ChatMessage("structure_block.size_success", new Object[] { s7})), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.size_success", new Object[] { s7})), false);
|
||||||
} else {
|
} else {
|
||||||
this.player.a((new ChatMessage("structure_block.size_failure", new Object[0])), false);
|
this.player.a((IChatBaseComponent) (new ChatMessage("structure_block.size_failure", new Object[0])), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1558
sources/src/main/java/net/minecraft/server/PlayerList.java
Normal file
1558
sources/src/main/java/net/minecraft/server/PlayerList.java
Normal file
File diff suppressed because it is too large
Load Diff
127
sources/src/main/java/net/minecraft/server/RegionFileCache.java
Normal file
127
sources/src/main/java/net/minecraft/server/RegionFileCache.java
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
163
sources/src/main/java/net/minecraft/server/RegistryID.java
Normal file
163
sources/src/main/java/net/minecraft/server/RegistryID.java
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.base.Predicates;
|
||||||
|
import com.google.common.collect.Iterators;
|
||||||
|
|
||||||
|
import java.util.BitSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) BitSet for faster access (performance)
|
||||||
|
*/
|
||||||
|
public class RegistryID<K> implements Registry {
|
||||||
|
|
||||||
|
private static final Object a = null;
|
||||||
|
private K[] b;
|
||||||
|
private int[] c;
|
||||||
|
private K[] d;
|
||||||
|
private int e;
|
||||||
|
private int f;
|
||||||
|
private java.util.BitSet usedIds; // Akarin - 1.13 backport
|
||||||
|
|
||||||
|
public RegistryID(int i) {
|
||||||
|
i = (int) ((float) i / 0.8F);
|
||||||
|
this.b = (K[]) (new Object[i]);
|
||||||
|
this.c = new int[i];
|
||||||
|
this.d = (K[]) (new Object[i]);
|
||||||
|
this.usedIds = new BitSet(); // Akarin - 1.13 backport
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId(@Nullable K k0) {
|
||||||
|
return this.c(this.b(k0, this.d(k0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public K fromId(int i) {
|
||||||
|
return i >= 0 && i < this.d.length ? this.d[i] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int c(int i) {
|
||||||
|
return i == -1 ? -1 : this.c[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
public int c(K k0) {
|
||||||
|
int i = this.c();
|
||||||
|
|
||||||
|
this.a(k0, i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int c() {
|
||||||
|
// Akarin start - 1.13 backport
|
||||||
|
/*
|
||||||
|
while (this.e < this.d.length && this.d[this.e] != null) {
|
||||||
|
++this.e;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
this.e = this.usedIds.nextClearBit(0);
|
||||||
|
// Akarin end - 1.13 backport
|
||||||
|
|
||||||
|
return this.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void d(int i) {
|
||||||
|
K[] aobject = this.b;
|
||||||
|
int[] aint = this.c;
|
||||||
|
|
||||||
|
this.b = (K[]) (new Object[i]);
|
||||||
|
this.c = new int[i];
|
||||||
|
this.d = (K[]) (new Object[i]);
|
||||||
|
this.e = 0;
|
||||||
|
this.f = 0;
|
||||||
|
this.usedIds.clear(); // Akarin - 1.13 backport
|
||||||
|
|
||||||
|
for (int j = 0; j < aobject.length; ++j) {
|
||||||
|
if (aobject[j] != null) {
|
||||||
|
this.a(aobject[j], aint[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(K k0, int i) {
|
||||||
|
int j = Math.max(i, this.f + 1);
|
||||||
|
int k;
|
||||||
|
|
||||||
|
if ((float) j >= (float) this.b.length * 0.8F) {
|
||||||
|
for (k = this.b.length << 1; k < i; k <<= 1) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.d(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
k = this.e(this.d(k0));
|
||||||
|
this.b[k] = k0;
|
||||||
|
this.c[k] = i;
|
||||||
|
this.d[i] = k0;
|
||||||
|
this.usedIds.set(i); // Akarin - 1.13 backport
|
||||||
|
++this.f;
|
||||||
|
if (i == this.e) {
|
||||||
|
++this.e;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int d(@Nullable K k0) {
|
||||||
|
return (MathHelper.f(System.identityHashCode(k0)) & Integer.MAX_VALUE) % this.b.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int b(@Nullable K k0, int i) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = i; j < this.b.length; ++j) {
|
||||||
|
if (this.b[j] == k0) {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.b[j] == RegistryID.a) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < i; ++j) {
|
||||||
|
if (this.b[j] == k0) {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.b[j] == RegistryID.a) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int e(int i) {
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = i; j < this.b.length; ++j) {
|
||||||
|
if (this.b[j] == RegistryID.a) {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < i; ++j) {
|
||||||
|
if (this.b[j] == RegistryID.a) {
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new RuntimeException("Overflowed :(");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<K> iterator() {
|
||||||
|
return Iterators.filter(Iterators.forArray(this.d), Predicates.notNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
public int b() {
|
||||||
|
return this.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
265
sources/src/main/java/net/minecraft/server/TileEntitySkull.java
Normal file
265
sources/src/main/java/net/minecraft/server/TileEntitySkull.java
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
package net.minecraft.server;
|
||||||
|
|
||||||
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import com.mojang.authlib.minecraft.MinecraftSessionService;
|
||||||
|
import com.mojang.authlib.properties.Property;
|
||||||
|
import java.util.UUID;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
// Spigot start
|
||||||
|
import com.google.common.base.Predicate;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.mojang.authlib.Agent;
|
||||||
|
import com.mojang.authlib.ProfileLookupCallback;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Akarin Changes Note
|
||||||
|
* 1) Guava -> Caffeine (performance)
|
||||||
|
*/
|
||||||
|
public class TileEntitySkull extends TileEntity /*implements ITickable*/ { // Paper - remove tickable
|
||||||
|
|
||||||
|
private int a;
|
||||||
|
public int rotation;
|
||||||
|
private GameProfile g;
|
||||||
|
private int h;
|
||||||
|
private boolean i;
|
||||||
|
private static UserCache j;
|
||||||
|
private static MinecraftSessionService k;
|
||||||
|
// Spigot start
|
||||||
|
public static final ExecutorService executor = Executors.newFixedThreadPool(3,
|
||||||
|
new ThreadFactoryBuilder()
|
||||||
|
.setNameFormat("Head Conversion Thread - %1$d")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
public static final com.github.benmanes.caffeine.cache.LoadingCache<String, GameProfile> skinCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() // Akarin - caffeine
|
||||||
|
.maximumSize( 5000 )
|
||||||
|
.expireAfterAccess( 60, TimeUnit.MINUTES )
|
||||||
|
.build( new com.github.benmanes.caffeine.cache.CacheLoader<String, GameProfile>() // Akarin - caffeine
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public GameProfile load(String key) // Akarin - remove exception
|
||||||
|
{
|
||||||
|
final GameProfile[] profiles = new GameProfile[1];
|
||||||
|
ProfileLookupCallback gameProfileLookup = new ProfileLookupCallback() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfileLookupSucceeded(GameProfile gp) {
|
||||||
|
profiles[0] = gp;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProfileLookupFailed(GameProfile gp, Exception excptn) {
|
||||||
|
profiles[0] = gp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MinecraftServer.getServer().getGameProfileRepository().findProfilesByNames(new String[] { key }, Agent.MINECRAFT, gameProfileLookup);
|
||||||
|
|
||||||
|
GameProfile profile = profiles[ 0 ];
|
||||||
|
if (profile == null) {
|
||||||
|
UUID uuid = EntityHuman.a(new GameProfile(null, key));
|
||||||
|
profile = new GameProfile(uuid, key);
|
||||||
|
|
||||||
|
gameProfileLookup.onProfileLookupSucceeded(profile);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
|
||||||
|
Property property = Iterables.getFirst( profile.getProperties().get( "textures" ), null );
|
||||||
|
|
||||||
|
if ( property == null )
|
||||||
|
{
|
||||||
|
profile = MinecraftServer.getServer().az().fillProfileProperties( profile, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
public TileEntitySkull() {}
|
||||||
|
|
||||||
|
public static void a(UserCache usercache) {
|
||||||
|
TileEntitySkull.j = usercache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void a(MinecraftSessionService minecraftsessionservice) {
|
||||||
|
TileEntitySkull.k = minecraftsessionservice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
|
||||||
|
super.save(nbttagcompound);
|
||||||
|
nbttagcompound.setByte("SkullType", (byte) (this.a & 255));
|
||||||
|
nbttagcompound.setByte("Rot", (byte) (this.rotation & 255));
|
||||||
|
if (this.g != null) {
|
||||||
|
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
|
||||||
|
|
||||||
|
GameProfileSerializer.serialize(nbttagcompound1, this.g);
|
||||||
|
nbttagcompound.set("Owner", nbttagcompound1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nbttagcompound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void load(NBTTagCompound nbttagcompound) {
|
||||||
|
super.load(nbttagcompound);
|
||||||
|
this.a = nbttagcompound.getByte("SkullType");
|
||||||
|
this.rotation = nbttagcompound.getByte("Rot");
|
||||||
|
if (this.a == 3) {
|
||||||
|
if (nbttagcompound.hasKeyOfType("Owner", 10)) {
|
||||||
|
this.g = GameProfileSerializer.deserialize(nbttagcompound.getCompound("Owner"));
|
||||||
|
} else if (nbttagcompound.hasKeyOfType("ExtraType", 8)) {
|
||||||
|
String s = nbttagcompound.getString("ExtraType");
|
||||||
|
|
||||||
|
if (!UtilColor.b(s)) {
|
||||||
|
this.g = new GameProfile((UUID) null, s);
|
||||||
|
this.i();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e() {
|
||||||
|
if (this.a == 5) {
|
||||||
|
if (this.world.isBlockIndirectlyPowered(this.position)) {
|
||||||
|
this.i = true;
|
||||||
|
++this.h;
|
||||||
|
} else {
|
||||||
|
this.i = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public GameProfile getGameProfile() {
|
||||||
|
return this.g;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public PacketPlayOutTileEntityData getUpdatePacket() {
|
||||||
|
return new PacketPlayOutTileEntityData(this.position, 4, this.d());
|
||||||
|
}
|
||||||
|
|
||||||
|
public NBTTagCompound d() {
|
||||||
|
return this.save(new NBTTagCompound());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkullType(int i) {
|
||||||
|
this.a = i;
|
||||||
|
this.g = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGameProfile(@Nullable GameProfile gameprofile) {
|
||||||
|
this.a = 3;
|
||||||
|
this.g = gameprofile;
|
||||||
|
this.i();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void i() {
|
||||||
|
// Spigot start
|
||||||
|
GameProfile profile = this.g;
|
||||||
|
setSkullType( 0 ); // Work around client bug
|
||||||
|
b(profile, new Predicate<GameProfile>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(GameProfile input) {
|
||||||
|
setSkullType(3); // Work around client bug
|
||||||
|
g = input;
|
||||||
|
update();
|
||||||
|
if (world != null) {
|
||||||
|
world.m(position); // PAIL: notify
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
// Spigot end
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spigot start - Support async lookups
|
||||||
|
public static Future<GameProfile> b(final GameProfile gameprofile, final Predicate<GameProfile> callback, boolean sync) {
|
||||||
|
if (gameprofile != null && !UtilColor.b(gameprofile.getName())) {
|
||||||
|
if (gameprofile.isComplete() && gameprofile.getProperties().containsKey("textures")) {
|
||||||
|
callback.apply(gameprofile);
|
||||||
|
} else if (MinecraftServer.getServer() == null) {
|
||||||
|
callback.apply(gameprofile);
|
||||||
|
} else {
|
||||||
|
GameProfile profile = skinCache.getIfPresent(gameprofile.getName().toLowerCase(java.util.Locale.ROOT));
|
||||||
|
if (profile != null && Iterables.getFirst(profile.getProperties().get("textures"), (Object) null) != null) {
|
||||||
|
callback.apply(profile);
|
||||||
|
|
||||||
|
return Futures.immediateFuture(profile);
|
||||||
|
} else {
|
||||||
|
Callable<GameProfile> callable = new Callable<GameProfile>() {
|
||||||
|
@Override
|
||||||
|
public GameProfile call() {
|
||||||
|
final GameProfile profile = skinCache.get(gameprofile.getName().toLowerCase(java.util.Locale.ROOT)); // Akarin - caffeine
|
||||||
|
MinecraftServer.getServer().processQueue.add(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (profile == null) {
|
||||||
|
callback.apply(gameprofile);
|
||||||
|
} else {
|
||||||
|
callback.apply(profile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (sync) {
|
||||||
|
try {
|
||||||
|
return Futures.immediateFuture(callable.call());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
com.google.common.base.Throwables.throwIfUnchecked(ex);
|
||||||
|
throw new RuntimeException(ex); // Not possible
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return executor.submit(callable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.apply(gameprofile);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Futures.immediateFuture(gameprofile);
|
||||||
|
}
|
||||||
|
// Spigot end
|
||||||
|
|
||||||
|
public int getSkullType() {
|
||||||
|
return this.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRotation(int i) {
|
||||||
|
this.rotation = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(EnumBlockMirror enumblockmirror) {
|
||||||
|
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
|
||||||
|
this.rotation = enumblockmirror.a(this.rotation, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void a(EnumBlockRotation enumblockrotation) {
|
||||||
|
if (this.world != null && this.world.getType(this.getPosition()).get(BlockSkull.FACING) == EnumDirection.UP) {
|
||||||
|
this.rotation = enumblockrotation.a(this.rotation, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
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,775 +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
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>Akarin Changes Note</b><br>
|
|
||||||
* <br>
|
|
||||||
* 1) Add volatile to fields<br>
|
|
||||||
* @author cakoyo
|
|
||||||
*/
|
|
||||||
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 - PAIL: time
|
|
||||||
private volatile long l; // Akarin - volatile - PAIL: 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 EnumDifficulty C;
|
|
||||||
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
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user