138 Commits

Author SHA1 Message Date
Sotr
f2c15275e9 Triggers a build to give a chance to sync LegacyLauncher 1.25 2018-07-27 09:39:47 +08:00
Sotr
c58612f271 Akarin 1.12.2 0.4 Release 2018-07-27 09:14:56 +08:00
Sotr
4d44c9f8cc Upstream Paper fully 2018-07-27 09:10:36 +08:00
Sotr
3d34b1e011 [CI-SKIP] Cleanup 2018-07-27 08:51:26 +08:00
Sotr
032ffea438 Bump LegacyLauncher to 1.25 for disable async lighting by default 2018-07-27 08:49:12 +08:00
Sotr
2b6175edcb More default workers 2018-07-27 08:47:01 +08:00
Sotr
c06c7e2776 A really bad workaround for lighting update - Close #33 2018-07-27 08:45:37 +08:00
Sotr
af678041f0 [CI-SKIP] Akarin-repo link update 2018-07-27 00:50:42 +08:00
Sotr
5c03cad4f2 Ensures chunk data to be synced (i guess) 2018-07-26 00:37:24 +08:00
Sotr
6de709c4c6 Upstream Paper 2018-07-25 01:55:59 +08:00
Sotr
bca8f20393 Paper 1.13 Backport: Optimize RegistryID.c() w/ Add changes note 2018-07-25 01:50:47 +08:00
Sotr
144c9e08f5 Import necessary nms and fix decompile errors 2018-07-25 00:48:51 +08:00
Sotr
bd47cab2ff Paper 1.13 Backport: Optimize Region File Cache 2018-07-25 00:38:39 +08:00
Sotr
4c7da04d9f [CI-SKIP] Cleanup: PAIL -> OBFHELPER 2018-07-25 00:37:41 +08:00
Sotr
cf300713b4 Import necessary nms 2018-07-25 00:32:33 +08:00
Sotr
4c93b2b13a [CI-SKIP] Cleanup 2018-07-24 02:47:14 +08:00
Sotr
78ddbbd7c7 Removed unused list creation 2018-07-24 00:49:20 +08:00
Sotr
ecb28a58a6 Add missing header 2018-07-23 22:46:56 +08:00
Sotr
9e62b22fb8 ByteBuf instead of array to allow chunks with addition data - Resolves GH-34, GH-35 2018-07-23 22:45:29 +08:00
Sotr
2ace17105f Missing overwrite annotation 2018-07-23 19:18:22 +08:00
Sotr
528ab01026 Fixes typo cause timings not ending 2018-07-23 18:55:08 +08:00
Sotr
87583d4e7c Re-add thread configurations for async lighting w/ Removed craps 2018-07-23 18:33:30 +08:00
Sotr
f77f039dbb Upstream Paper 2018-07-23 01:34:48 +08:00
Sotr
3fbba5de34 Upstream Paper 2018-07-21 17:21:31 +08:00
Sotr
79d3980be2 Improve async lighting a bit 2018-07-21 17:20:50 +08:00
Sotr
280db63b42 Upstream Paper 2018-07-20 02:10:32 +08:00
Sotr
f93abbf076 Fixes entity updates 2018-07-19 20:53:58 +08:00
cakoyo
03a5ef5149 Merge pull request #30 from MatrixTunnel/master
Organize the Readme and License
2018-07-18 15:23:12 +08:00
MatrixTunnel
58de950203 Update README.md 2018-07-17 21:08:17 -07:00
MatrixTunnel
2887399202 Cleanup License 2018-07-17 21:05:39 -07:00
MatrixTunnel
cbfe35f062 Cleanup Readme 2018-07-17 21:01:40 -07:00
Sotr
1a330a4c07 [CI-SKIP] Saves one mixin 2018-07-18 01:34:48 +08:00
Sotr
c932a0d1b1 2.6.2! Thanks MatrixTunnel 2018-07-17 15:46:44 +08:00
Sotr
515e9d86b9 Correts caffeine usage - Close GH-29 2018-07-17 15:22:46 +08:00
Sotr
5f917f8253 LightRNG for better entity performance 2018-07-17 00:38:36 +08:00
Sotr
cede9c99fa [CI-SKIP] Removes unused nms changes w/ Better changes note for unmixinable classes 2018-07-16 04:32:37 +08:00
Sotr
fd733df52a Caffeine to improve cache performance 2018-07-16 04:05:03 +08:00
Sotr
5f6671cd50 Optimizes lava check 2018-07-16 03:38:16 +08:00
Sotr
ff00b7556e Fixes missing upstream 2018-07-15 21:58:30 +08:00
Sotr
3875266bb7 Update locks - resolves GH-25 2018-07-14 03:05:59 +08:00
Sotr
ca8e0d7c0f Reverts changes since they didnt resolve issues 2018-07-14 02:40:35 +08:00
Sotr
269e1c30f1 Fixes versioning concurrently 2018-07-13 22:53:13 +08:00
Sotr
79d0207801 [CI-SKIP] Cleanup 2018-07-13 22:18:42 +08:00
Sotr
a2c09040b4 Fixes packet send 2018-07-13 22:15:50 +08:00
Sotr
c50f6f2a68 Super lock track method - Resolves GH-23 2018-07-13 21:17:50 +08:00
Sotr
9cfe108024 Make entity tracker update parallel - #24 2018-07-13 21:00:09 +08:00
Sotr
545ce61711 Oops 2018-07-13 20:55:02 +08:00
Sotr
e881bb1c23 No need 2018-07-13 20:49:10 +08:00
Sotr
c926121dd6 Safer entity tracker - Close GH-7 2018-07-13 20:40:24 +08:00
Sotr
b859bf6b4d Fixes missing upstream 2018-07-13 20:17:52 +08:00
Sotr
5113e99853 Ensures packet safety w/ Improve async packet sending - Close #22 2018-07-13 20:14:25 +08:00
Sotr
68b4db992f Upstream Paper 2018-07-13 18:46:44 +08:00
Sotr
2765bb8891 Workaround GH-23 2018-07-12 17:22:13 +08:00
Sotr
067b86b7cc Import dev nms 2018-07-12 17:19:10 +08:00
Sotr
08013d2b4a Upstream Paper 2018-07-10 19:36:06 +08:00
Sotr
a6a51f9805 Configurable hardcore difficulty 2018-07-09 17:34:51 +08:00
Sotr
d223e4ad00 Bump slack service priority 2018-07-09 17:32:46 +08:00
Sotr
44d1491789 Upstream Paper 2018-07-09 15:55:02 +08:00
Sotr
e82a99d4d8 Configurable chunk unload optimization - #21 2018-07-08 02:16:28 +08:00
Sotr
5e912befc0 Akarin 1.12.2 0.3.2 LTS Release 2018-07-07 16:00:57 +08:00
Sotr
45d6b0c072 Fixes chunk unloading - close #17 2018-07-07 01:59:27 +08:00
Sotr
d596840e83 Fixes #17 2018-07-06 20:47:20 +08:00
Sotr
9e06a9fe5c Upstream Paper 2018-07-06 16:42:20 +08:00
Sotr
452dd1f3b5 Upstream Paper 2018-07-05 15:03:45 +08:00
Sotr
8dbfbd2650 Check null for chunk unload #13 2018-07-04 20:50:28 +08:00
Sotr
fc17d8fdc7 Upstream Paper 2018-07-04 03:35:50 +08:00
Sotr
bbd1057666 Upstream Paper 2018-07-02 14:49:02 +08:00
Sotr
671c5fbc49 Avoid twice thread check for #13 w/ Fixes double player updating 2018-06-30 14:14:26 +08:00
Sotr
405a299b36 Register mixin >//< 2018-06-27 22:10:23 +08:00
Sotr
8b6de7576a Configurable light chunk sending 2018-06-27 22:00:26 +08:00
Sotr
f6f2d1121c Upstream Paper 2018-06-26 18:04:23 +08:00
Sotr
8a6c36ab2c Configurable version update interval 2018-06-24 21:04:36 +08:00
Sotr
4c622207e1 Fixes plugin versioning #12 2018-06-24 21:02:02 +08:00
Sotr
8db031c856 Upstream Paper 2018-06-24 00:18:16 +08:00
Sotr
7c6627edc8 Ensures async-lighting safety 2018-06-23 16:30:58 +08:00
Sotr
343666756c Fixes MC-103516 2018-06-23 00:36:31 +08:00
Sotr
99b9880f0e Upstream Paper 2018-06-22 15:05:48 +08:00
Sotr
022a6468d5 Better text style for spawn chunks 2018-06-20 00:56:11 +08:00
Sotr
9d5d638670 [Edge] Parallel generate spawn chunks 2018-06-20 00:39:05 +08:00
Sotr
4e920243d1 oops 2018-06-18 17:44:14 +08:00
Sotr
239f83e1d3 Ensures timings safety 2018-06-18 17:36:03 +08:00
Sotr
93588656d1 Async saving persistent collection 2018-06-18 17:29:53 +08:00
Sotr
8a9ad35a57 Refactor main-thread checks w/ Update Mixin and requirement to 1.7.10 2018-06-18 15:53:33 +08:00
Sotr
e4ed177d7b Safer world iterate 2018-06-18 02:17:05 +08:00
Sotr
fb7bf16869 Move players info update to slack service w/ config changes 2018-06-18 02:11:37 +08:00
Sotr
1e93929534 Move difficulty detect to slack service 2018-06-18 01:50:48 +08:00
Sotr
30f6e3f56f Configurable primary thread priority 2018-06-18 01:07:18 +08:00
Sotr
c55568a01c [ci skip] Removes old repo link 2018-06-18 00:29:34 +08:00
Sotr
04ff47a598 [ci skip] Replaces TIM with Discord 2018-06-17 23:52:50 +08:00
Sotr
fdec006644 [ci skip] Fixes paper not being deploy locally 2018-06-17 22:21:35 +08:00
Sotr
3eb2067ffa Upstream Paper 2018-06-17 22:19:46 +08:00
Sotr
b89d955eb5 Fixes boot 2018-06-17 20:44:24 +08:00
Sotr
54205afb71 Fixes mixin not being apply 2018-06-17 17:40:26 +08:00
Sotr
711fc08eeb Configurable end portal creation 2018-06-17 17:32:13 +08:00
Sotr
e8dfb64f42 . 2018-06-17 17:18:10 +08:00
Sotr
a78ae42d9c Fixes bootstrap 2018-06-17 16:50:15 +08:00
Sotr
f7792c2510 Fixes gc hang 2018-06-17 16:39:27 +08:00
Sotr
9fedd2d94e fixes restart gc 2018-06-16 23:02:02 +08:00
Sotr
cf62349837 Better world lock 2018-06-16 20:36:18 +08:00
Sotr
1d9f3a0584 Fixes exception handling 2018-06-16 20:16:21 +08:00
Sotr
8925ac73d4 Configurable server brand name, #11 w/ Clean up 2018-06-16 02:30:49 +08:00
Sotr
5c39b7eabd Better reason and expire message 2018-06-15 00:54:07 +08:00
Sotr
d8f0674b89 Shared light executor 2018-06-14 21:13:26 +08:00
Sotr
cda8c79514 Update LegacyLauncher to 1.23 w/ Add mixin config 2018-06-14 20:02:51 +08:00
Sotr
080b080c15 [ci skip] Fixes indentation 2018-06-14 19:59:43 +08:00
Sotr
19e1d0d927 Controllable mixin core features w/ Update LegacyLauncher to 1.22 2018-06-14 19:57:23 +08:00
Sotr
974cf681d0 Clean up 2018-06-14 19:38:31 +08:00
Sotr
3e38351ce7 Better handle async timings 2018-06-14 19:33:08 +08:00
Sotr
dc30e8c8d5 Sponge real time ticking 2018-06-14 18:45:47 +08:00
Sotr
427a4152f3 Bump version to R0.3-DEV 2018-06-14 17:22:37 +08:00
Sotr
68816a0b5a [ci skip] adjust pos 2018-06-14 16:09:14 +08:00
Sotr
f6e54678bb Merge branch 'master' of github.com:Akarin-project/Akarin 2018-06-14 16:06:59 +08:00
Sotr
aed67213c2 [ci skip] just dont need double icon 2018-06-14 16:06:34 +08:00
Sotr
9038d2d2b6 Merge pull request #9 from shinohara-rin/master
chmod u+x scripts/inst.sh
2018-06-14 15:51:18 +08:00
shinohara-rin
eb96ab4f5b chmod u+x scripts/inst.sh 2018-06-14 15:32:31 +08:00
Sotr
89ca2f2bc5 Fire BlockRedstoneEvent for PandaWire 2018-06-14 14:59:29 +08:00
Sotr
97798e18ce [Edge] Panda wire port 2018-06-14 03:31:01 +08:00
Sotr
db54c87abd rename api package 2018-06-14 01:05:07 +08:00
Sotr
f45908ae38 Fixes a crash, #6 #7, also fixes typo #8 2018-06-13 22:45:08 +08:00
Sotr
2b49aba77c Fixes typo 2018-06-13 20:55:41 +08:00
Sotr
bc2a639c52 Update kick / join messages 2018-06-13 20:53:11 +08:00
Sotr
3938713380 Better handle mock 2018-06-13 17:09:33 +08:00
Sotr
dc10b10979 Fixes time update 2018-06-13 16:50:56 +08:00
Sotr
876bf4d7cd [ci skip] query plugins default false 2018-06-13 15:40:00 +08:00
Sotr
3ee0111f24 Fixes player ping w/ configurable timeout keep-alive kick message 2018-06-13 15:39:11 +08:00
Sotr
42a90440b9 Better bukkit.yml (not really) 2018-06-13 03:08:49 +08:00
Sotr
b58e98e0de Removed unused mixin 2018-06-13 03:07:26 +08:00
Sotr
dc9c0655de Upstream Paper 2018-06-13 03:04:04 +08:00
Sotr
c3e9767dbc Exposes ban and kick messages 2018-06-13 02:51:42 +08:00
Sotr
01ecfef964 Fixes MC-120780 2018-06-13 00:52:03 +08:00
Sotr
8a7ebe839c [Major] Finally correct async lighting w/ Add watchcat gc feature 2018-06-13 00:37:27 +08:00
Sotr
c11a88d1ba Fixes chunk unloading 2018-06-12 15:46:52 +08:00
Sotr
9626867217 Clean up 2018-06-12 02:30:29 +08:00
Sotr
55248e4deb Correct async lighting 2018-06-12 01:46:43 +08:00
Sotr
095a6e4d0b Fixes light calc 2018-06-11 22:35:54 +08:00
Sotr
559896e14b Cleanup 2018-06-11 22:16:10 +08:00
Sotr
6855fbe294 Fixes chunk iterate 2018-06-11 20:59:26 +08:00
Sotr
41d97284be Fixes version lookup 2018-06-11 19:28:12 +08:00
105 changed files with 17162 additions and 789 deletions

View File

@@ -4,16 +4,17 @@ As such, Akarin is licensed under the
[GNU General Public License version 3](licenses/GPL.md); as it inherits it from Paper,
who in turn inherits it from the original Spigot projects.
Any author who is _not_ listed below should be presumed to have released their work
Any author who is _not_ listed below should be presumed to have their work released
under the original [GPL](licenses/GPL.md) license.
In the interest of promoting a better Minecraft platform for everyone, contributors
may choose to release their code under the more permissive [MIT License](licenses/MIT.md).
The authors listed below have chosen to release their code under that more permissive
The authors listed below have chosen to release their code under the more permissive
[MIT License](licenses/MIT.md). Any contributor who wants their name added below
should submit a pull request to this project to add their name.
should submit a Pull Request to this project and add their name.
```text
Sotr <kira@kira.moe>
MatrixTunnel <https://github.com/MatrixTunnel>
```

View File

@@ -1,29 +1,33 @@
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
[![Build Status](http://ci.pcd.ac.cn/job/Akarin/badge/icon)](http://ci.ilummc.com/job/Akarin/)
[![bStats](https://img.shields.io/badge/bStats-Torch-0099ff.svg?style=flat)](https://bstats.org/plugin/bukkit/Torch)
[![Powered by](https://img.shields.io/badge/Powered_by-Akarin_project-ee6aa7.svg?style=flat)](https://akarin.io)
[![bStats](https://img.shields.io/badge/bStats-Torch-0099ff.svg?style=flat)](https://bstats.org/plugin/bukkit/Torch)
[![Build Status](http://ci.pcd.ac.cn/job/Akarin/badge/icon)](http://ci.ilummc.com/job/Akarin/)
[![Circle CI](https://circleci.com/gh/Akarin-project/Akarin/tree/master.svg?style=svg)](https://circleci.com/gh/Akarin-project/Akarin/tree/master)
Akarin is currently **under heavy development** and contributions are welcome!
Introduction
---
> Akarin is a powerful server software form the 'new dimension', formerly known as [Torch](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:
* **Open Access** - Make more game mechanism configurable.
* **Bedrock** - Safety and stable is important for a server.
* **Fast** - Simplify the logic and import the multi-thread compute.
Our project has a few key goals:
Akarin is **under heavy development** yet, contribution is welcome and run a test before putting into production.
* **Open Access** - Make more game mechanics configurable.
* **Bedrock** - Make the server more safe and stable.
* **Fast** - Simplify the logic and implement multi-threaded computing.
*Issues and Pull Requests will be labeled accordingly*
Get Akarin
---
### Download
#### Recommended Sites
+ [![Circle CI](https://circleci.com/gh/Akarin-project/Akarin/tree/master.svg?style=svg) **Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build *Login required*
+ [![PCD Jenkins](http://ci.pcd.ac.cn/job/Akarin/badge/icon) **Jenkins**](http://ci.ilummc.com/job/Akarin/) - *Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)*
#### Recommended
+ [**Jenkins**](http://ci.ilummc.com/job/Akarin/) - Kudos to [Izzel_Aliz](https://github.com/IzzelAliz)
+ [**Circle CI**](https://circleci.com/gh/Akarin-project/Akarin/tree/master) - Checkout the 'Artifacts' tab of the latest build
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your website here*
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
### Build
#### Requirements
@@ -34,20 +38,28 @@ Get Akarin
```sh
./scripts/inst.sh --setup --fast
```
*For non-modification compile, add `--fast` option to skip the test is recommended.*
*Futhermore, if your machine have a insufficient memory, you may add `--remote` option to avoid decompile locally.*
Demonstration servers
**Notes**
* You must use `--setup` at least once to deploy necessary dependencies otherwise some imports cannot be organized.
* For non-modified projects, it is recommended to add the `--fast` option to skip any tests.
* If your machine has insufficient memory, you may want to add the `--remote` option to avoid decompiling locally.
Demo Servers
---
+ **demo.akarin.io**
* `demo.akarin.io` (official)
*Contact me via the email below or open an [Issue](https://github.com/Akarin-project/akarin/issues) if you want to add your server here*
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
Contributing
---
* Feel free to open an [Issue](https://github.com/Akarin-project/akarin/issues) if you have any problem with Akarin.
* [Pull Request](https://github.com/Akarin-project/akarin/pulls) is welcomed, Akarin use [Mixin](https://github.com/SpongePowered/Mixin) to modify the code, you can checkout `sources` folder to see them. Moreover, add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send an email to `kira@kira.moe` with your experience and necessary information. Besides, welcome to join our [TIM Group](https://jq.qq.com/?_wv=1027&k=59q2kV4) to chat *(Chinese)*.
* Note that you must `--setup` at least once to deploy necessary dependency otherwise some imports cannot be organized.
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
* Add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send us an email with your experience and necessary information.
Contact
---
[Discord](https://discord.gg/D3Rsukh)
Email: `kira@kira.moe`
![Akarin project](https://i.loli.net/2018/05/13/5af7fbbfbcddf.png)

View File

@@ -13,4 +13,4 @@ dependencies:
test:
post:
- yes|cp -rf ./akarin-1.12.2.jar $CIRCLE_ARTIFACTS
- yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS

View File

@@ -19,7 +19,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
fi
cd "$paperbasedir"
./paper patch
./paper jar
)
fi

0
scripts/inst.sh Normal file → Executable file
View File

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>akarin</artifactId>
<packaging>jar</packaging>
<version>1.12.2-R0.2.1-RELEASE</version>
<version>1.12.2-R0.4-RELEASE</version>
<name>Akarin</name>
<url>https://github.com/Akarin-project/Akarin</url>
@@ -73,12 +73,12 @@
<dependency>
<groupId>net.minecrell</groupId>
<artifactId>terminalconsoleappender</artifactId>
<version>1.0.0</version>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.4.0</version>
<version>4.5.2</version>
<scope>runtime</scope>
</dependency>
@@ -108,6 +108,14 @@
<version>2.8.1</version>
</dependency>
<!-- Paper - Async loggers -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
<scope>runtime</scope>
</dependency>
<!-- testing -->
<dependency>
<groupId>junit</groupId>
@@ -126,29 +134,34 @@
<dependency>
<groupId>io.akarin</groupId>
<artifactId>legacylauncher</artifactId>
<version>1.20</version>
<version>1.25</version>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
<artifactId>mixin</artifactId>
<version>0.7.8-SNAPSHOT</version>
<version>0.7.11-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>me.nallar.whocalled</groupId>
<artifactId>WhoCalled</artifactId>
<version>1.1</version>
<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>
</dependencies>
<repositories>
<repository>
<id>spigotmc-public</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>akarin-repo</id>
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
</repository>
<repository>
<id>spigotmc-public</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
<repository>
<id>spongepowered-repo</id>
<url>https://repo.spongepowered.org/maven/</url>
@@ -177,7 +190,7 @@
<version>1.3</version>
<configuration>
<outputPrefix>git-Akarin-</outputPrefix>
<scmDirectory>..</scmDirectory>
<scmDirectory>../..</scmDirectory> <!-- Akarin -->
</configuration>
<executions>
<execution>

View File

@@ -30,10 +30,8 @@ import org.bukkit.Bukkit;
import java.util.logging.Level;
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Add volatile to fields<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add volatile to fields (safety issue)
*/
class TimingHandler implements Timing {

View File

@@ -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;
}
}

View File

@@ -1,4 +1,4 @@
package io.akarin.api;
package io.akarin.api.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -6,49 +6,69 @@ 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 org.bukkit.entity.Minecart;
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.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
@SuppressWarnings("restriction")
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();
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 {
@Override
public Thread newThread(Runnable run) {
Thread thread = new AssignableThread(run);
thread.setName("Akarin Parallel Schedule Thread");
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
return thread;
}
}
/**
* A common tick pool
*/
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(Akari.STAGE_FACTORY));
public static volatile boolean mayMock;
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
public static boolean isPrimaryThread() {
return Thread.currentThread().equals(MinecraftServer.getServer().primaryThread);
return isPrimaryThread(true);
}
public static boolean isPrimaryThread(boolean assign) {
Thread current = Thread.currentThread();
return current == MinecraftServer.getServer().primaryThread || (assign ? current instanceof AssignableThread : false);
}
public static final String EMPTY_STRING = "";
/*
* The unsafe
*/
@@ -65,10 +85,18 @@ public abstract class Akari {
}
}
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 - World");
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Callback");
public final static Timing callbackTiming = getTiming("Akarin - Callback");

View File

@@ -1,4 +1,4 @@
package io.akarin.api;
package io.akarin.api.internal;
import java.net.InetAddress;

View File

@@ -1,4 +1,4 @@
package io.akarin.api.mixin;
package io.akarin.api.internal.mixin;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

View File

@@ -0,0 +1,5 @@
package io.akarin.api.internal.mixin;
public interface IMixinLockProvider {
public Object lock();
}

View File

@@ -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();
}

View File

@@ -1,5 +1,6 @@
package io.akarin.api.mixin;
package io.akarin.api.internal.mixin;
import java.util.List;
import java.util.concurrent.ExecutorService;
import net.minecraft.server.BlockPosition;
@@ -9,5 +10,7 @@ import net.minecraft.server.EnumSkyBlock;
public interface IMixinWorldServer {
boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk);
boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors);
ExecutorService getLightingExecutor();
}

View File

@@ -33,7 +33,7 @@
* 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.ArrayList;
@@ -45,6 +45,8 @@ import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
import io.akarin.api.internal.Akari;
/**
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
* This queue orders elements FIFO (first-in-first-out).

View File

@@ -1,4 +1,4 @@
package io.akarin.api;
package io.akarin.api.internal.utils;
import java.io.Serializable;
import java.io.ObjectOutputStream;

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -3,6 +3,8 @@ package io.akarin.server.core;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import io.akarin.api.internal.Akari;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -14,7 +16,6 @@ import java.util.regex.Pattern;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import io.akarin.api.Akari;
@SuppressWarnings("unused")
public class AkarinGlobalConfig {
@@ -169,44 +170,24 @@ public class AkarinGlobalConfig {
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
}
public static int timeUpdateInterval;
public static long 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() {
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() {
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s"));
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
}
public static int asyncLightingThreads;
private static void asyncLightingThreads() {
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 2);
}
public static boolean enableMockPlugin;
private static void enableMockPlugin() {
enableMockPlugin = getBoolean("core.thread-safe.enable-mock-plugins", false);
}
public static List<String> mockPackageList;
private static void mockPluginList() {
mockPackageList = getList("core.thread-safe.mock-package-name-contains", Lists.newArrayList("me.konsolas.aac"));
}
public static boolean enableAsyncCatcher;
private static void enableAsyncCatcher() {
enableAsyncCatcher = getBoolean("core.thread-safe.async-catcher.enable", false);
}
public static boolean throwOnAsyncCaught;
private static void throwOnAsyncCaught() {
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 4);
}
public static boolean asyncLightingWorkStealing;
@@ -214,8 +195,103 @@ public class AkarinGlobalConfig {
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
}
public static boolean throwOnAsyncCaught;
private static void throwOnAsyncCaught() {
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
}
public static boolean allowSpawnerModify;
private static void allowSpawnerModify() {
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;
private static void messageKick() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
}
public static String messageBan;
private static void messageBan() {
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
}
public static String messageBanReason;
private static void messageBanReason() {
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
}
public static String messageBanExpires;
private static void messageBanExpires() {
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
}
public static String messageBanIp;
private static void messageBanIp() {
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
}
public static String messageDupLogin;
private static void messageDupLogin() {
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
}
public static String messageJoin;
private static void messageJoin() {
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
}
public static String messageJoinRenamed;
private static void messageJoinRenamed() {
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
}
public static String messageKickKeepAlive;
private static void messagekickKeepAlive() {
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
}
public static String messagePlayerQuit;
private static void messagePlayerQuit() {
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);
}
}

View File

@@ -1,22 +1,27 @@
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.EnumDifficulty;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PacketPlayOutKeepAlive;
import net.minecraft.server.PacketPlayOutPlayerInfo;
import net.minecraft.server.PacketPlayOutUpdateTime;
import net.minecraft.server.PlayerConnection;
import net.minecraft.server.WorldServer;
public class AkarinSlackScheduler extends Thread {
public static AkarinSlackScheduler get() {
return Singleton.instance;
}
public static void boot() {
Singleton.instance.setName("Akarin Slack Scheduler Thread");
Singleton.instance.setPriority(MIN_PRIORITY);
Singleton.instance.setDaemon(true);
Singleton.instance.start();
public void boot() {
setName("Akarin Slack Scheduler Thread");
setDaemon(true);
start();
Akari.logger.info("Slack scheduler service started");
}
@@ -24,44 +29,73 @@ public class AkarinSlackScheduler extends Thread {
private static final AkarinSlackScheduler instance = new AkarinSlackScheduler();
}
/*
* Timers
*/
private long updateTime;
private long resendPlayersInfo;
@Override
public void run() {
MinecraftServer server = MinecraftServer.getServer();
while (server.isRunning()) {
// Send time updates to everyone, it will get the right time from the world the player is in.
if (++updateTime == AkarinGlobalConfig.timeUpdateInterval * 10) {
// Time update, from MinecraftServer#D
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
for (EntityPlayer player : server.getPlayerList().players) {
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time
// Add support for per player time
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle")));
}
updateTime = 0;
}
// Keep alive, from PlayerConnection#e
for (EntityPlayer player : server.getPlayerList().players) {
PlayerConnection conn = player.playerConnection;
// Paper - 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
long currentTime = System.currentTimeMillis();
long currentTime = System.nanoTime() / 1000000L;
long elapsedTime = currentTime - conn.getLastPing();
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
if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout) { // check keepalive limit, don't fire if already disconnected
Akari.callbackQueue.add(() -> {
Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info
conn.disconnect("disconnect.timeout");
});
}
} else {
if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval * 1000L) { // 15 seconds default
if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval) { // 15 seconds default
conn.setPendingPing(true);
conn.setLastPing(currentTime);
conn.setKeepAliveID(currentTime);
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID()));
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); // 15s lagg you should stop your server
}
}
}
// Force hardcore difficulty, from WorldServer#doTick
if (AkarinGlobalConfig.forceHardcoreDifficulty)
for (WorldServer world : server.worlds) {
if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) {
world.getWorldData().setDifficulty(EnumDifficulty.HARD);
}
}
// 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);
} catch (InterruptedException ex) {
@@ -69,5 +103,6 @@ public class AkarinSlackScheduler extends Thread {
ex.printStackTrace();
}
}
}
}

View File

@@ -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));
}
}

View File

@@ -10,11 +10,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.Akari;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
@Mixin(value = Main.class, remap = false)
public class Bootstrap {
public abstract class Bootstrap {
@Inject(method = "main([Ljava/lang/String;)V", at = @At("HEAD"))
private static void premain(CallbackInfo info) {
AkarinGlobalConfig.init(new File("akarin.yml"));

View File

@@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.Overwrite;
import net.minecraft.server.EULA;
@Mixin(value = EULA.class, remap = false)
public class DummyEula {
public abstract class DummyEula {
/**
* Read then check the EULA file <i>formerly</i>
* @param file

View File

@@ -14,7 +14,7 @@ import com.destroystokyo.paper.Metrics;
import net.minecraft.server.MinecraftServer;
@Mixin(targets = "com.destroystokyo.paper.Metrics$PaperMetrics", remap = false)
public class MetricsBootstrap {
public abstract class MetricsBootstrap {
@Overwrite
static void startMetrics() {
// Get the config file

View File

@@ -17,7 +17,7 @@ import com.destroystokyo.paper.Metrics;
import com.destroystokyo.paper.Metrics.CustomChart;
@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)
private final static String URL = "https://bStats.org/submitData/bukkit";

View File

@@ -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();
}
}
}

View File

@@ -10,7 +10,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.Akari;
import io.akarin.api.internal.Akari;
import net.minecraft.server.BiomeBase;
import net.minecraft.server.Block;
import net.minecraft.server.BlockFire;
@@ -24,7 +24,7 @@ import net.minecraft.server.PotionRegistry;
import net.minecraft.server.SoundEffect;
@Mixin(value = DispenserRegistry.class, remap = false)
public class ParallelRegistry {
public abstract class ParallelRegistry {
/**
* Registry order: SoundEffect -> Block
*/

View File

@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.server.MinecraftServer;
@Mixin(value = WatchdogThread.class, remap = false)
public class Watchcat extends Thread {
public abstract class Watchcat extends Thread {
@Shadow private static WatchdogThread instance;
@Shadow private @Final long timeoutTime;
@Shadow private @Final boolean restart;
@@ -74,7 +74,7 @@ public class Watchcat extends Thread {
}
log.log(Level.SEVERE, "------------------------------");
if (restart) RestartCommand.restart();
if (restart) RestartCommand.restart(); // GC Inlined
break;
}

View File

@@ -5,17 +5,18 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.Akari;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
@Mixin(value = AsyncCatcher.class, remap = false)
public class MixinAsyncCatcher {
public abstract class MixinAsyncCatcher {
@Shadow public static boolean enabled;
@Overwrite
public static void catchOp(String reason) {
if (AkarinGlobalConfig.enableAsyncCatcher && enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
if (enabled) {
if (Akari.isPrimaryThread()) return;
if (AkarinGlobalConfig.throwOnAsyncCaught) {
throw new IllegalStateException("Asynchronous " + reason + "!");
} else {

View File

@@ -12,7 +12,7 @@ import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.Chunk;
@Mixin(value = ChunkIOExecutor.class, remap = false)
public class MixinChunkIOExecutor {
public abstract class MixinChunkIOExecutor {
@Shadow @Final static int BASE_THREADS;
@Shadow @Mutable @Final static int PLAYERS_PER_THREAD;
@Shadow @Final private static AsynchronousExecutor<?, Chunk, Runnable, RuntimeException> instance;

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -7,38 +7,40 @@ import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import me.nallar.whocalled.WhoCalled;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CraftServer.class, remap = false)
public class MixinCraftServer {
public abstract class MixinCraftServer {
@Shadow @Final @Mutable private String serverName;
@Shadow @Final @Mutable private String serverVersion;
@Shadow @Final protected MinecraftServer console;
private boolean needApplyServerName = true;
private boolean needApplyServerVersion = true;
@Overwrite
public String getName() {
// We cannot apply the name modification in <init> method,
// cause the initializer will be added to the tail
if (needApplyServerName) {
serverName = "Akarin";
serverName = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? "Akarin" : AkarinGlobalConfig.serverBrandName;
needApplyServerName = false;
}
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() {
if (AkarinGlobalConfig.enableMockPlugin) {
// Mock forcely main thread plugins
String callerPackage = WhoCalled.$.getCallingClass().getPackage().getName();
if (callerPackage.startsWith("net.minecraft") || callerPackage.startsWith("org.bukkit") ||
callerPackage.startsWith("co.aikar") || callerPackage.startsWith("io.akarin")) return Thread.currentThread().equals(console.primaryThread);
for (String contains : AkarinGlobalConfig.mockPackageList) {
if (callerPackage.contains(contains)) return true;
}
}
return Thread.currentThread().equals(console.primaryThread);
return Akari.isPrimaryThread();
}
}

View File

@@ -11,9 +11,8 @@ import net.minecraft.server.Blocks;
import net.minecraft.server.ItemMonsterEgg;
@Mixin(value = ItemMonsterEgg.class, remap = false)
public class MonsterEggGuardian {
@Redirect(method = "a", at = @At(
public abstract class MixinItemMonsterEgg {
@Redirect(method = "a*", at = @At(
value = "FIELD",
target = "net/minecraft/server/Blocks.MOB_SPAWNER:Lnet/minecraft/server/Block;",
opcode = Opcodes.GETSTATIC

View File

@@ -1,16 +1,38 @@
package io.akarin.server.mixin.core;
import java.util.concurrent.ExecutionException;
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.Overwrite;
import io.akarin.api.internal.Akari;
import net.minecraft.server.MCUtil;
import net.minecraft.server.MinecraftServer;
@Mixin(value = MCUtil.class, remap = false)
public class MixinMCUtil {
public abstract class MixinMCUtil {
@Overwrite
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();
}
}

View File

@@ -2,10 +2,15 @@ package io.akarin.server.mixin.core;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
@@ -16,9 +21,12 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
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.IMixinLockProvider;
import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.AkarinSlackScheduler;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.CrashReport;
import net.minecraft.server.CustomFunctionData;
import net.minecraft.server.ITickable;
@@ -33,11 +41,34 @@ import net.minecraft.server.WorldServer;
@Mixin(value = MinecraftServer.class, remap = false)
public abstract class MixinMinecraftServer {
@Shadow @Final public Thread primaryThread;
@Overwrite
public String getServerModName() {
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));
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
*/
@@ -47,30 +78,69 @@ public abstract class MixinMinecraftServer {
@Overwrite
public void b(MojangStatisticsGenerator generator) {}
@Inject(method = "run()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/MinecraftServer.aw()J",
shift = At.Shift.BEFORE
))
private void prerun(CallbackInfo info) {
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
}
AkarinSlackScheduler.boot();
/*
* Parallel spawn chunks generation
*/
@Shadow public abstract boolean isRunning();
@Shadow(aliases = "a_") protected abstract void output(String s, int i);
@Shadow(aliases = "t") protected abstract void enablePluginsPostWorld();
private void prepareChunks(WorldServer world, int index) {
MinecraftServer.LOGGER.info("Preparing start region for level " + index + " (Seed: " + world.getSeed() + ")");
BlockPosition spawnPos = world.getSpawn();
long lastRecord = System.currentTimeMillis();
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()));
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();
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
}
enablePluginsPostWorld();
}
/*
* Parallel world ticking
*/
@Shadow public CraftServer server;
@Shadow @Mutable protected Queue<FutureTask<?>> j;
@Shadow public Queue<Runnable> processQueue;
@Shadow private int ticks;
@Shadow public List<WorldServer> worlds;
@Shadow private PlayerList v;
@Shadow @Final private List<ITickable> o;
@Shadow(aliases = "v") private PlayerList playerList;
@Shadow(aliases = "o") @Final private List<ITickable> tickables;
@Shadow public abstract PlayerList getPlayerList();
@Shadow public abstract ServerConnection an();
@Shadow public abstract CustomFunctionData aL();
@Shadow(aliases = "an") public abstract ServerConnection serverConnection();
@Shadow(aliases = "aL") public abstract CustomFunctionData functionManager();
private boolean tickEntities(WorldServer world) {
try {
@@ -106,10 +176,6 @@ public abstract class MixinMinecraftServer {
@Overwrite
public void D() throws InterruptedException {
Runnable runnable;
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.bukkitSchedulerTimer.startTiming();
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
@@ -133,17 +199,16 @@ public abstract class MixinMinecraftServer {
Akari.worldTiming.startTiming();
if (AkarinGlobalConfig.legacyWorldTimings) {
for (int i = 0; i < worlds.size(); ++i) {
worlds.get(i).timings.tickEntities.startTiming();
worlds.get(i).timings.doTick.startTiming();
WorldServer world = worlds.get(i);
world.timings.tickEntities.startTiming();
world.timings.doTick.startTiming();
}
}
Akari.silentTiming = true; // Disable timings
Akari.STAGE_TICK.submit(() -> {
// Never tick one world concurrently!
// TODO better treat world index
for (int i = 1; i <= worlds.size(); ++i) {
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
synchronized (world) {
synchronized (((IMixinLockProvider) world).lock()) {
tickEntities(world);
}
}
@@ -151,18 +216,22 @@ public abstract class MixinMinecraftServer {
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
synchronized (world) {
synchronized (((IMixinLockProvider) world).lock()) {
tickWorld(world);
world.explosionDensityCache.clear(); // Paper - Optimize explosions
}
}
Akari.entityCallbackTiming.startTiming();
Akari.STAGE_TICK.take();
Akari.silentTiming = false; // Enable timings
Akari.entityCallbackTiming.stopTiming();
Akari.worldTiming.stopTiming();
if (AkarinGlobalConfig.legacyWorldTimings) {
for (int i = 0; i < worlds.size(); ++i) {
worlds.get(i).timings.tickEntities.stopTiming();
worlds.get(i).timings.doTick.startTiming();
WorldServer world = worlds.get(i);
world.timings.tickEntities.stopTiming();
world.timings.doTick.stopTiming();
}
}
@@ -172,33 +241,33 @@ public abstract class MixinMinecraftServer {
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
tickConflictSync(world);
world.getTracker().updatePlayers();
world.explosionDensityCache.clear(); // Paper - Optimize explosions
tickUnsafeSync(world);
}
MinecraftTimings.connectionTimer.startTiming();
this.an().c();
serverConnection().c();
MinecraftTimings.connectionTimer.stopTiming();
MinecraftTimings.playerListTimer.startTiming();
this.v.tick();
MinecraftTimings.playerListTimer.stopTiming();
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.commandFunctionsTimer.startTiming();
this.aL().e();
functionManager().e();
MinecraftTimings.commandFunctionsTimer.stopTiming();
MinecraftTimings.tickablesTimer.startTiming();
for (int i = 0; i < this.o.size(); ++i) {
this.o.get(i).e();
for (int i = 0; i < this.tickables.size(); ++i) {
tickables.get(i).e();
}
MinecraftTimings.tickablesTimer.stopTiming();
}
public void tickConflictSync(WorldServer world) {
;
}
public void tickUnsafeSync(WorldServer world) {
world.timings.doChunkMap.startTiming();
world.manager.flush();
world.timings.doChunkMap.stopTiming();
world.getTracker().updatePlayers();
}
}

View File

@@ -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;
}
}
}

View File

@@ -7,20 +7,43 @@ import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import io.akarin.api.Akari;
import co.aikar.timings.Timing;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableThread;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
public class MixinTimingHandler {
public abstract class MixinTimingHandler {
@Shadow @Final String name;
@Shadow private boolean enabled;
@Shadow private volatile long start;
@Shadow private volatile int timingDepth;
@Shadow abstract void addDiff(long diff);
@Shadow public abstract Timing startTiming();
@Overwrite
public Timing startTimingIfSync() {
if (Akari.isPrimaryThread(false)) {
startTiming();
}
return (Timing) this;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Inject(method = "startTiming", at = @At("HEAD"), cancellable = true)
public void onStartTiming(CallbackInfoReturnable ci) {
if (!Akari.isPrimaryThread(false)) ci.setReturnValue(this); // Avoid modify any field
}
@Overwrite
public void stopTimingIfSync() {
if (Akari.isPrimaryThread()) { // Akarin
if (Akari.isPrimaryThread(false)) {
stopTiming(true); // Avoid twice thread check
}
}
@@ -30,23 +53,18 @@ public class MixinTimingHandler {
stopTiming(false);
}
@Shadow void addDiff(long diff) {}
public void stopTiming(boolean alreadySync) {
Thread curThread = Thread.currentThread();
if (!enabled || curThread instanceof AssignableThread) return;
if (!alreadySync && curThread != MinecraftServer.getServer().primaryThread) {
if (AkarinGlobalConfig.silentAsyncTimings) return;
public void stopTiming(boolean sync) {
if (enabled && --timingDepth == 0 && start != 0) {
if (Akari.silentTiming) { // It must be off-main thread now
start = 0;
return;
} else {
if (!sync && !Akari.isPrimaryThread()) { // Akarin
if (AkarinGlobalConfig.silentAsyncTimings) {
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
new Throwable().printStackTrace();
}
start = 0;
return;
}
Thread.dumpStack();
}
// Main thread ensured
if (--timingDepth == 0 && start != 0) {
addDiff(System.nanoTime() - start);
start = 0;
}

View File

@@ -9,12 +9,12 @@ import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.Akari;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MCUtil;
@Mixin(value = VersionCommand.class, remap = false)
public class MixinVersionCommand {
public abstract class MixinVersionCommand {
@Shadow private static int getFromRepo(String repo, String hash) { return 0; }
/**
@@ -59,6 +59,9 @@ public class MixinVersionCommand {
// This should be lying in 'obtainVersion' method, but bump for faster returning
if (customVersion) return;
synchronized (versionWaiters) {
versionWaiters.add(sender);
}
if (versionObtaining) return;
// The volatile guarantees the safety between different threads.
// Remembers that we are still on main thread now,
@@ -70,7 +73,7 @@ public class MixinVersionCommand {
if (hasVersion) {
long current = System.currentTimeMillis();
if (current - lastCheckMillis > 7200000 /* 2 hours */) {
if (current - lastCheckMillis > AkarinGlobalConfig.versionUpdateInterval) {
lastCheckMillis = current;
hasVersion = false;
} else {
@@ -100,11 +103,9 @@ public class MixinVersionCommand {
// We post all things because a custom version is rare (expiring is not rare),
// and we'd better post this task as early as we can, since it's a will (horrible destiny).
MCUtil.scheduleAsyncTask(() -> {
// This should be lying in 'sendVersion' method, but comes here for relax main thread
versionWaiters.add(sender);
sender.sendMessage("Checking version, please wait...");
String version = Bukkit.getVersion();
String version = Akari.getServerVersion();
if (version == null) {
version = "Unique"; // Custom - > Unique
customVersion = true;
@@ -140,9 +141,11 @@ public class MixinVersionCommand {
versionMessage = message;
hasVersion = true;
synchronized (versionWaiters) {
for (CommandSender sender : versionWaiters) {
sender.sendMessage(versionMessage);
}
versionWaiters.clear();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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.lock(); // Akarin
this.world.getTracker().track(entity);
this.world.getTracker().entriesLock.unlock(); // Akarin
if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity);
}
}
}

View File

@@ -0,0 +1,24 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinLockProvider;
import net.minecraft.server.WorldServer;
@Mixin(value = WorldServer.class, remap = false)
public abstract class MixinWorldServer implements IMixinLockProvider {
@Redirect(method = "doTick()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/PlayerChunkMap.flush()V"
))
public void onFlush() {} // Migrated to main thread
private final Object tickLock = new Object();
@Override
public Object lock() {
return tickLock;
}
}

View File

@@ -36,7 +36,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.common.collect.Lists;
import io.akarin.api.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinChunk;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.MCUtil;
@@ -99,8 +100,10 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "addEntities", at = @At("RETURN"))
public void onLoadReturn(CallbackInfo ci) {
BlockPosition origin = new BlockPosition(locX, 0, locZ);
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), locX, locZ);
BlockPosition shift = origin.shift(direction);
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
if (neighbor != null) {
int neighborIndex = directionToIndex(direction);
int oppositeNeighborIndex = directionToIndex(direction.opposite());
@@ -112,8 +115,10 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "removeEntities", at = @At("RETURN"))
public void onUnload(CallbackInfo ci) {
BlockPosition origin = new BlockPosition(locX, 0, locZ);
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), locX, locZ);
BlockPosition shift = origin.shift(direction);
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
if (neighbor != null) {
int neighborIndex = directionToIndex(direction);
int oppositeNeighborIndex = directionToIndex(direction.opposite());

View File

@@ -1,14 +1,16 @@
package io.akarin.server.mixin.cps;
import java.util.Iterator;
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;
@@ -19,13 +21,10 @@ public abstract class MixinChunkProviderServer {
@Shadow @Final public WorldServer world;
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
public int pendingUnloadChunks; // For keeping unload target-size feature
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)));
pendingUnloadChunks++;
chunk.setShouldUnload(true);
}
}
@@ -37,43 +36,52 @@ public abstract class MixinChunkProviderServer {
@Overwrite
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
Iterator<Chunk> it = chunks.values().iterator();
long now = System.currentTimeMillis();
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
int targetSize = Math.min(pendingUnloadChunks - 100, (int) (pendingUnloadChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
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
for (int i = 0; i < chunks.size() && pendingUnloadChunks > targetSize; i++) { // CraftBukkit removes unload logic to its method, we must check index
Chunk chunk = it.next();
while (it.hasNext()) {
Entry<Chunk> entry = it.next();
Chunk chunk = entry.getValue();
if (chunk == null) continue;
if (chunk != null && chunk.isUnloading()) {
if (unloadAfter > 0) {
// We changed Paper's delay unload logic, the original behavior is just mark as unloading
if (chunk.scheduledForUnload == null || now - chunk.scheduledForUnload < unloadAfter) {
if (chunk.isUnloading()) {
if (chunk.scheduledForUnload != null) {
if (now - chunk.scheduledForUnload > unloadAfter) {
chunk.scheduledForUnload = null;
} else continue;
}
if (!unloadChunk(chunk, true)) { // Event cancelled
// If a plugin cancelled it, we shouldn't trying unload it for a while
chunk.setShouldUnload(false);
continue;
}
chunk.scheduledForUnload = null;
}
// If a plugin cancelled it, we shouldn't trying unload it for a while
chunk.setShouldUnload(false); // Paper
if (!unloadChunk(chunk, true)) continue; // Event cancelled
pendingUnloadChunks--;
if (activityAccountant.activityTimeIsExhausted()) break;
it.remove();
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
}
}
activityAccountant.endActivity();
this.chunkLoader.b(); // PAIL: chunkTick
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(); // Akarin - remove unload queue
return "ServerChunkCache: " + chunks.size();
}
}

View File

@@ -4,24 +4,18 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.Inject;
import net.minecraft.server.Chunk;
import net.minecraft.server.PlayerChunk;
import net.minecraft.server.WorldServer;
@Mixin(value = CraftWorld.class, remap = false)
public class MixinCraftWorld {
@Inject(method = "processChunkGC()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/ChunkProviderServer.unload(Lnet/minecraft/server/Chunk;)V"
))
public void cancelUnloading(Chunk chunk, CallbackInfo ci) {
if (chunk.isUnloading()) ci.cancel();
}
public abstract class MixinCraftWorld {
@Shadow @Final private WorldServer world;
@Redirect(method = "processChunkGC()V", at = @At(
value = "INVOKE",
@@ -38,15 +32,8 @@ public class MixinCraftWorld {
opcode = Opcodes.INVOKEINTERFACE
))
public boolean regenChunk(Set<Long> set, Object chunkHash) {
return false;
}
@Inject(method = "processChunkGC()V", at = @At(
value = "FIELD",
target = "net/minecraft/server/PlayerChunk.chunk:Lnet/minecraft/server/Chunk;",
opcode = Opcodes.PUTFIELD
))
public void noUnload(PlayerChunk playerChunk, Chunk chunk, CallbackInfo ci) {
chunk.setShouldUnload(false);
Chunk chunk = world.getChunkProviderServer().chunks.get(chunkHash);
if (chunk != null) chunk.setShouldUnload(false);
return true;
}
}

View File

@@ -34,7 +34,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.bukkit.Bukkit;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -43,11 +42,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.common.collect.Lists;
import io.akarin.api.Akari;
import io.akarin.api.mixin.IMixinChunk;
import io.akarin.api.mixin.IMixinWorldServer;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Blocks;
import net.minecraft.server.Chunk;
@@ -69,7 +66,7 @@ public abstract class MixinChunk implements IMixinChunk {
private CopyOnWriteArrayList<Short> queuedBlockLightingUpdates = new CopyOnWriteArrayList<>();
private AtomicInteger pendingLightUpdates = new AtomicInteger();
private long lightUpdateTime;
private ExecutorService lightExecutorService;
private static ExecutorService lightExecutorService;
@Shadow(aliases = "m") private boolean isGapLightingUpdated;
@Shadow(aliases = "r") private boolean ticked;
@@ -103,7 +100,7 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "<init>", at = @At("RETURN"))
public void onConstruct(World worldIn, int x, int z, CallbackInfo ci) {
this.lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor();
lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor();
}
@Override
@@ -124,8 +121,8 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true)
private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) {
final List<Chunk> neighbors = this.getSurroundingChunks();
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // PAIL: isGapLightingUpdated - hasSkyLight
this.lightExecutorService.execute(() -> {
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // OBFHELPER: hasSkyLight
lightExecutorService.execute(() -> {
this.recheckGapsAsync(neighbors);
});
this.isGapLightingUpdated = false;
@@ -133,8 +130,8 @@ public abstract class MixinChunk implements IMixinChunk {
this.ticked = true;
if (!this.isLightPopulated && this.isTerrainPopulated && !neighbors.isEmpty()) {
this.lightExecutorService.execute(() -> {
if ((true || !this.isLightPopulated) && this.isTerrainPopulated && !neighbors.isEmpty()) {
lightExecutorService.execute(() -> {
this.checkLightAsync(neighbors);
});
// set to true to avoid requeuing the same task when not finished
@@ -144,10 +141,10 @@ public abstract class MixinChunk implements IMixinChunk {
while (!this.tileEntityPosQueue.isEmpty()) {
BlockPosition blockpos = this.tileEntityPosQueue.poll();
if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // PAIL: getTileEntity
if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // OBFHELPER: getTileEntity
TileEntity tileentity = this.createNewTileEntity(blockpos);
this.world.setTileEntity(blockpos, tileentity);
this.world.b(blockpos, blockpos); // PAIL: markBlockRangeForRenderUpdate
this.world.b(blockpos, blockpos); // OBFHELPER: markBlockRangeForRenderUpdate
}
}
ci.cancel();
@@ -160,7 +157,7 @@ public abstract class MixinChunk implements IMixinChunk {
return BlockPosition.ZERO;
}
return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // PAIL: getHeightValue
return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // OBFHELPER: getHeightValue
}
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.areChunksLoaded(Lnet/minecraft/server/BlockPosition;I)Z"))
@@ -173,12 +170,47 @@ public abstract class MixinChunk implements IMixinChunk {
return this.checkWorldLightFor(enumSkyBlock, pos);
}
@Inject(method = "h(Z)V", at = @At("HEAD"), cancellable = true)
private void onRecheckGaps(CallbackInfo ci) {
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return;
}
if (this.isUnloading()) {
return;
}
final List<Chunk> neighborChunks = this.getSurroundingChunks();
if (neighborChunks.isEmpty()) {
this.isGapLightingUpdated = true;
return;
}
if (Akari.isPrimaryThread()) {
try {
lightExecutorService.execute(() -> {
this.recheckGapsAsync(neighborChunks);
});
} catch (RejectedExecutionException ex) {
// This could happen if ServerHangWatchdog kills the server
// between the start of the method and the execute() call.
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
throw ex;
}
}
} else {
this.recheckGapsAsync(neighborChunks);
}
ci.cancel();
}
/**
* Rechecks chunk gaps async.
*
* @param neighbors A thread-safe list of surrounding neighbor chunks
*/
private void recheckGapsAsync(List<Chunk> neighbors) {
this.isLightPopulated = false;
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (this.updateSkylightColumns[i + j * 16]) {
@@ -193,7 +225,7 @@ public abstract class MixinChunk implements IMixinChunk {
if (chunk == null || chunk.isUnloading()) {
continue;
}
j1 = Math.min(j1, chunk.w()); // PAIL: getLowestHeight
j1 = Math.min(j1, chunk.w()); // OBFHELPER: getLowestHeight
}
this.checkSkylightNeighborHeight(l, i1, j1);
@@ -204,7 +236,7 @@ public abstract class MixinChunk implements IMixinChunk {
}
}
// this.isGapLightingUpdated = false;
this.isGapLightingUpdated = false;
}
}
@@ -233,7 +265,7 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "o()V", at = @At("HEAD"), cancellable = true)
private void checkLightHead(CallbackInfo ci) {
if (this.world.getMinecraftServer().isStopped() || this.lightExecutorService.isShutdown()) {
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return;
}
@@ -246,15 +278,15 @@ public abstract class MixinChunk implements IMixinChunk {
return;
}
if (Akari.isPrimaryThread()) { // Akarin
if (Akari.isPrimaryThread()) {
try {
this.lightExecutorService.execute(() -> {
lightExecutorService.execute(() -> {
this.checkLightAsync(neighborChunks);
});
} catch (RejectedExecutionException ex) {
// This could happen if ServerHangWatchdog kills the server
// between the start of the method and the execute() call.
if (!this.world.getMinecraftServer().isStopped() && !this.lightExecutorService.isShutdown()) {
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
throw ex;
}
}
@@ -274,27 +306,26 @@ public abstract class MixinChunk implements IMixinChunk {
this.isLightPopulated = true;
BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4);
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
label44:
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
CHECK_LIGHT:
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (!this.checkLightAsync(i, j, neighbors)) {
this.isLightPopulated = false;
break label44;
break CHECK_LIGHT;
}
}
}
if (this.isLightPopulated) {
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // PAIL: getAxisDirection
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // OBFHELPER: getAxisDirection
final BlockPosition pos = blockpos.shift(enumfacing, k);
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
if (chunk == null) {
continue;
}
chunk.a(enumfacing.opposite()); // PAIL: checkLightSide
chunk.a(enumfacing.opposite()); // OBFHELPER: checkLightSide
}
this.setSkylightUpdated();
@@ -318,7 +349,7 @@ public abstract class MixinChunk implements IMixinChunk {
for (int j = i + 16 - 1; j > this.world.getSeaLevel() || j > 0 && !flag1; --j) {
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), j, blockpos$mutableblockpos.getZ());
int k = this.getBlockData(blockpos$mutableblockpos).c(); // PAIL: getLightOpacity
int k = this.getBlockData(blockpos$mutableblockpos).c(); // OBFHELPER: getLightOpacity
if (k == 255 && blockpos$mutableblockpos.getY() < this.world.getSeaLevel()) {
flag1 = true;
@@ -352,7 +383,7 @@ public abstract class MixinChunk implements IMixinChunk {
*/
private Chunk getLightChunk(int chunkX, int chunkZ, List<Chunk> neighbors) {
final Chunk currentChunk = (Chunk) (Object) this;
if (currentChunk.a(chunkX, chunkZ)) { // PAIL: isAtLocation
if (currentChunk.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
if (currentChunk.isUnloading()) {
return null;
}
@@ -365,7 +396,7 @@ public abstract class MixinChunk implements IMixinChunk {
}
}
for (Chunk neighbor : neighbors) {
if (neighbor.a(chunkX, chunkZ)) { // PAIL: isAtLocation
if (neighbor.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
if (neighbor.isUnloading()) {
return null;
}
@@ -441,8 +472,7 @@ public abstract class MixinChunk implements IMixinChunk {
return Collections.emptyList();
}
List<Chunk> chunkList = Lists.newArrayList();
chunkList = this.getNeighbors();
List<Chunk> chunkList = this.getNeighbors();
chunkList.add(southEastChunk);
chunkList.add(southWestChunk);
chunkList.add(northEastChunk);
@@ -452,7 +482,7 @@ public abstract class MixinChunk implements IMixinChunk {
@Inject(method = "c(III)V", at = @At("HEAD"), cancellable = true)
private void onRelightBlock(int x, int y, int z, CallbackInfo ci) {
this.lightExecutorService.execute(() -> {
lightExecutorService.execute(() -> {
this.relightBlockAsync(x, y, z);
});
ci.cancel();
@@ -473,7 +503,7 @@ public abstract class MixinChunk implements IMixinChunk {
j = y;
}
while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) {
while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) { // OBFHELPER: getLightOpacity
--j;
}
@@ -483,14 +513,14 @@ public abstract class MixinChunk implements IMixinChunk {
int k = this.locX * 16 + x;
int l = this.locZ * 16 + z;
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
if (j < i) {
for (int j1 = j; j1 < i; ++j1) {
ChunkSection extendedblockstorage2 = this.sections[j1 >> 4];
if (extendedblockstorage2 != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage2.a(x, j1 & 15, z, 15); // PAIL: setSkyLight
this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // PAIL: notifyLightSet
extendedblockstorage2.a(x, j1 & 15, z, 15); // OBFHELPER: setSkyLight
// this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
}
}
} else {
@@ -498,8 +528,8 @@ public abstract class MixinChunk implements IMixinChunk {
ChunkSection extendedblockstorage = this.sections[i1 >> 4];
if (extendedblockstorage != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage.a(x, i1 & 15, z, 0); // PAIL: setSkyLight
this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // PAIL: notifyLightSet
extendedblockstorage.a(x, i1 & 15, z, 0); // OBFHELPER: setSkyLight
// this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
}
}
}
@@ -523,7 +553,7 @@ public abstract class MixinChunk implements IMixinChunk {
ChunkSection extendedblockstorage1 = this.sections[j >> 4];
if (extendedblockstorage1 != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage1.a(x, j & 15, z, k1); // PAIL: setSkyLight
extendedblockstorage1.a(x, j & 15, z, k1); // OBFHELPER: setSkyLight
}
}
}
@@ -541,9 +571,9 @@ public abstract class MixinChunk implements IMixinChunk {
this.heightMapMinimum = l1;
}
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // PAIL: updateSkylightNeighborHeight
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // OBFHELPER: updateSkylightNeighborHeight
}
this.updateSkylightNeighborHeight(k, l, j2, k2);
@@ -569,7 +599,7 @@ public abstract class MixinChunk implements IMixinChunk {
x2 = i;
}
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
for (int j = x2; j <= z2; ++j) {
final BlockPosition pos = new BlockPosition(x1, j, z1);
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
@@ -580,7 +610,7 @@ public abstract class MixinChunk implements IMixinChunk {
}
}
this.world.b(x1, x2, z1, x1, z2, z1); // PAIL: markBlockRangeForRenderUpdate
this.world.b(x1, x2, z1, x1, z2, z1); // OBFHELPER: markBlockRangeForRenderUpdate
}
/**
@@ -617,7 +647,7 @@ public abstract class MixinChunk implements IMixinChunk {
return false;
}
if (this.world.worldProvider.m()) { // PAIL: hasSkyLight
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
flag |= ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, pos, chunk);
}

View File

@@ -30,12 +30,12 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinChunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.WorldServer;
@Mixin(value = ChunkProviderServer.class, remap = false, priority = 1001)
public class MixinChunkProviderServer {
public abstract class MixinChunkProviderServer {
@Shadow @Final public WorldServer world;
@Redirect(method = "unloadChunks", at = @At(

View File

@@ -26,20 +26,20 @@ package io.akarin.server.mixin.lighting;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumSkyBlock;
import net.minecraft.server.IChunkProvider;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
@Mixin(value = World.class, remap = false)
@Mixin(value = World.class, remap = false, priority = 1002)
public abstract class MixinWorld {
@Shadow protected IChunkProvider chunkProvider;
@Shadow int[] J; // PAIL: lightUpdateBlockList
@Shadow int[] J; // OBFHELPER: lightUpdateBlockList
@Shadow(aliases = "c") public abstract boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos);
@Shadow public abstract MinecraftServer getMinecraftServer();
@Shadow public abstract boolean areChunksLoaded(BlockPosition center, int radius, boolean allowEmpty);
@Shadow(aliases = "m") public abstract void notifyLightSet(BlockPosition pos);
@Shadow public abstract Chunk getChunkIfLoaded(int x, int z);
}

View File

@@ -32,9 +32,9 @@ import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.akarin.api.Akari;
import io.akarin.api.mixin.IMixinChunk;
import io.akarin.api.mixin.IMixinWorldServer;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
@@ -42,10 +42,11 @@ import net.minecraft.server.EnumDirection;
import net.minecraft.server.EnumSkyBlock;
import net.minecraft.server.IBlockData;
import net.minecraft.server.MCUtil;
import net.minecraft.server.MathHelper;
import net.minecraft.server.WorldServer;
import net.minecraft.server.BlockPosition.PooledBlockPosition;
@Mixin(value = WorldServer.class, remap = false)
@Mixin(value = WorldServer.class, remap = false, priority = 1002)
public abstract class MixinWorldServer extends MixinWorld implements IMixinWorldServer {
private static final int NUM_XZ_BITS = 4;
@@ -53,90 +54,92 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
private static final short XZ_MASK = 0xF;
private static final short Y_SHORT_MASK = 0xFF;
private final ExecutorService lightExecutorService = preparExecutorService();;
private ExecutorService preparExecutorService() {
private final static ExecutorService lightExecutorService = getExecutorService();
private static ExecutorService getExecutorService() {
return AkarinGlobalConfig.asyncLightingWorkStealing ?
Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build()) : Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads);
Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build())
:
Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads);
}
@Override
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // PAIL: checkLightFor
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // OBFHELPER: checkLightFor
return updateLightAsync(lightType, pos, null);
}
@Override
public boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
// Sponge - This check is not needed as neighbors are checked in updateLightAsync
if (false && !this.areChunksLoaded(pos, 17, false)) {
return false;
} else {
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
int i = 0;
int j = 0;
int k = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int l = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int i1 = pos.getX();
int j1 = pos.getY();
int k1 = pos.getZ();
int recheckIndex = 0;
int blockIndex = 0;
int current = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int rawLight = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
if (l > k) {
this.J[j++] = 133152; // PAIL: lightUpdateBlockList
} else if (l < k) {
this.J[j++] = 133152 | k << 18; // PAIL: lightUpdateBlockList
if (rawLight > current) {
this.J[blockIndex++] = 133152; // OBFHELPER: lightUpdateBlockList
} else if (rawLight < current) {
this.J[blockIndex++] = 133152 | current << 18; // OBFHELPER: lightUpdateBlockList
while (i < j) {
int l1 = this.J[i++]; // PAIL: lightUpdateBlockList
int i2 = (l1 & 63) - 32 + i1;
int j2 = (l1 >> 6 & 63) - 32 + j1;
int k2 = (l1 >> 12 & 63) - 32 + k1;
int l2 = l1 >> 18 & 15;
while (recheckIndex < blockIndex) {
int blockData = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
int i2 = (blockData & 63) - 32 + x;
int j2 = (blockData >> 6 & 63) - 32 + y;
int k2 = (blockData >> 12 & 63) - 32 + z;
int l2 = blockData >> 18 & 15;
BlockPosition blockpos = new BlockPosition(i2, j2, k2);
int i3 = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method
int lightLevel = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method
if (i3 == l2) {
if (lightLevel == l2) {
this.setLightForAsync(lightType, blockpos, 0, currentChunk, neighbors); // Sponge - use thread safe method
if (l2 > 0) {
int j3 = Math.abs(i2 - i1); // TODO MathHelper
int k3 = Math.abs(j2 - j1);
int l3 = Math.abs(k2 - k1);
int j3 = MathHelper.a(i2 - x); // abs
int k3 = MathHelper.a(j2 - y);
int l3 = MathHelper.a(k2 - z);
if (j3 + k3 + l3 < 17) {
PooledBlockPosition blockpos$pooledmutableblockpos = PooledBlockPosition.aquire();
PooledBlockPosition mutableBlockpos = PooledBlockPosition.aquire();
for (EnumDirection enumfacing : EnumDirection.values()) {
int i4 = i2 + enumfacing.getAdjacentX();
int j4 = j2 + enumfacing.getAdjacentX();
int k4 = k2 + enumfacing.getAdjacentX();
blockpos$pooledmutableblockpos.setValues(i4, j4, k4);
mutableBlockpos.setValues(i4, j4, k4);
// Sponge start - get chunk safely
final Chunk pooledChunk = this.getLightChunk(blockpos$pooledmutableblockpos, currentChunk, neighbors);
final Chunk pooledChunk = this.getLightChunk(mutableBlockpos, currentChunk, neighbors);
if (pooledChunk == null) {
continue;
}
int l4 = Math.max(1, pooledChunk.getBlockData(blockpos$pooledmutableblockpos).c()); // PAIL: getLightOpacity
i3 = this.getLightForAsync(lightType, blockpos$pooledmutableblockpos, currentChunk, neighbors);
int opacity = Math.max(1, pooledChunk.getBlockData(mutableBlockpos).c()); // OBFHELPER: getLightOpacity
lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors);
// Sponge end
if (i3 == l2 - l4 && j < this.J.length) { // PAIL: lightUpdateBlockList
this.J[j++] = i4 - i1 + 32 | j4 - j1 + 32 << 6 | k4 - k1 + 32 << 12 | l2 - l4 << 18; // PAIL: lightUpdateBlockList
if (lightLevel == l2 - opacity && blockIndex < this.J.length) { // OBFHELPER: lightUpdateBlockList
this.J[blockIndex++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // OBFHELPER: lightUpdateBlockList
}
}
blockpos$pooledmutableblockpos.free();
mutableBlockpos.free();
}
}
}
}
i = 0;
recheckIndex = 0;
}
while (i < j) {
int i5 = this.J[i++]; // PAIL: lightUpdateBlockList
int j5 = (i5 & 63) - 32 + i1;
int k5 = (i5 >> 6 & 63) - 32 + j1;
int l5 = (i5 >> 12 & 63) - 32 + k1;
while (recheckIndex < blockIndex) {
int i5 = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
int j5 = (i5 & 63) - 32 + x;
int k5 = (i5 >> 6 & 63) - 32 + y;
int l5 = (i5 >> 12 & 63) - 32 + z;
BlockPosition blockpos1 = new BlockPosition(j5, k5, l5);
int i6 = this.getLightForAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
int j6 = this.getRawBlockLightAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
@@ -145,35 +148,35 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
this.setLightForAsync(lightType, blockpos1, j6, currentChunk, neighbors); // Sponge - use thread safe method
if (j6 > i6) {
int k6 = Math.abs(j5 - i1);
int l6 = Math.abs(k5 - j1);
int i7 = Math.abs(l5 - k1);
boolean flag = j < this.J.length - 6; // PAIL: lightUpdateBlockList
int k6 = Math.abs(j5 - x);
int l6 = Math.abs(k5 - y);
int i7 = Math.abs(l5 - z);
boolean flag = blockIndex < this.J.length - 6; // OBFHELPER: lightUpdateBlockList
if (k6 + l6 + i7 < 17 && flag) {
// Sponge start - use thread safe method getLightForAsync
if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 - 1 - i1 + 32 + (k5 - j1 + 32 << 6) + (l5 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 + 1 - i1 + 32 + (k5 - j1 + 32 << 6) + (l5 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 - i1 + 32 + (k5 - 1 - j1 + 32 << 6) + (l5 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 - i1 + 32 + (k5 + 1 - j1 + 32 << 6) + (l5 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 - i1 + 32 + (k5 - j1 + 32 << 6) + (l5 - 1 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) {
this.J[j++] = j5 - i1 + 32 + (k5 - j1 + 32 << 6) + (l5 + 1 - k1 + 32 << 12); // PAIL: lightUpdateBlockList
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
// Sponge end
}
@@ -188,7 +191,6 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
neighbor.getPendingLightUpdates().decrementAndGet();
}
// Sponge end
return true;
}
@@ -196,7 +198,7 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
@Override
public boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, @Nullable Chunk currentChunk) {
if (this.getMinecraftServer().isStopped() || this.lightExecutorService.isShutdown()) {
if (this.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return false;
}
@@ -245,7 +247,7 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
}
if (Akari.isPrimaryThread()) { // Akarin
this.lightExecutorService.execute(() -> {
lightExecutorService.execute(() -> {
this.checkLightAsync(lightType, pos, chunk, neighbors);
});
} else {
@@ -257,20 +259,20 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
@Override
public ExecutorService getLightingExecutor() {
return this.lightExecutorService;
return lightExecutorService;
}
// Thread safe methods to retrieve a chunk during async light updates
// Each method avoids calling getLoadedChunk and instead accesses the passed neighbor chunk list to avoid concurrency issues
public Chunk getLightChunk(BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // PAIL: isAtLocation
if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
if (currentChunk.isUnloading()) {
return null;
}
return currentChunk;
}
for (Chunk neighbor : neighbors) {
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // PAIL: isAtLocation
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
if (neighbor.isUnloading()) {
return null;
}
@@ -285,13 +287,13 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
if (pos.getY() < 0) {
pos = new BlockPosition(pos.getX(), 0, pos.getZ());
}
if (!(pos.isValidLocation())) {
return lightType.c; // PAIL: defaultLightValue
if (!pos.isValidLocation()) {
return lightType.c; // OBFHELPER: defaultLightValue
}
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
if (chunk == null || chunk.isUnloading()) {
return lightType.c; // PAIL: defaultLightValue
return lightType.c; // OBFHELPER: defaultLightValue
}
return chunk.getBrightness(lightType, pos);
@@ -300,43 +302,43 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
private int getRawBlockLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
final Chunk chunk = getLightChunk(pos, currentChunk, neighbors);
if (chunk == null || chunk.isUnloading()) {
return lightType.c; // PAIL: defaultLightValue
return lightType.c; // OBFHELPER: defaultLightValue
}
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // PAIL: canSeeSky
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // OBFHELPER: canSeeSky
return 15;
} else {
IBlockData blockState = chunk.getBlockData(pos);
int blockLight = blockState.d(); // getLightValue
int i = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
int j = blockState.c(); // PAIL: getLightOpacity
IBlockData blockData = chunk.getBlockData(pos);
int blockLight = blockData.d(); // getLightValue
int rawLight = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
int opacity = blockData.c(); // OBFHELPER: getLightOpacity
if (j >= 15 && blockLight > 0) {
j = 1;
if (opacity >= 15 && blockLight > 0) {
opacity = 1;
}
if (j < 1) {
j = 1;
if (opacity < 1) {
opacity = 1;
}
if (j >= 15) {
if (opacity >= 15) {
return 0;
} else if (i >= 14) {
return i;
} else if (rawLight >= 14) {
return rawLight;
} else {
for (EnumDirection facing : EnumDirection.values()) {
BlockPosition blockpos = pos.shift(facing);
int k = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - j;
int current = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - opacity;
if (k > i) {
i = k;
if (current > rawLight) {
rawLight = current;
}
if (i >= 14) {
return i;
if (rawLight >= 14) {
return rawLight;
}
}
return i;
return rawLight;
}
}
}
@@ -345,8 +347,8 @@ public abstract class MixinWorldServer extends MixinWorld implements IMixinWorld
if (pos.isValidLocation()) {
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
if (chunk != null && !chunk.isUnloading()) {
chunk.a(type, pos, lightValue); // PAIL: setBrightness
this.notifyLightSet(pos);
chunk.a(type, pos, lightValue); // OBFHELPER: setLightFor
// this.notifyLightSet(pos); // client side
}
}
}

View File

@@ -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
}
}

View File

@@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spigotmc.SpigotConfig;
@@ -17,26 +18,37 @@ import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
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.ChannelAdapter;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
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.NetworkManager;
import net.minecraft.server.PacketDecoder;
import net.minecraft.server.PacketEncoder;
import net.minecraft.server.PacketPlayOutKickDisconnect;
import net.minecraft.server.PacketPrepender;
import net.minecraft.server.PacketSplitter;
import net.minecraft.server.ServerConnection;
@Mixin(value = ServerConnection.class, remap = false)
public class NonblockingServerConnection {
public abstract class NonblockingServerConnection {
private final static Logger logger = LogManager.getLogger("NSC");
/**
@@ -65,7 +77,7 @@ public class NonblockingServerConnection {
Class<? extends ServerChannel> channelClass;
EventLoopGroup loopGroup;
if (Epoll.isAvailable() && this.server.af()) { // PAIL: MinecraftServer::useNativeTransport
if (Epoll.isAvailable() && this.server.af()) { // OBFHELPER: MinecraftServer::useNativeTransport
channelClass = EpollServerSocketChannel.class;
loopGroup = ServerConnection.b.c();
logger.info("Using epoll channel type");
@@ -75,7 +87,26 @@ public class NonblockingServerConnection {
logger.info("Using nio channel type");
}
ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(ChannelAdapter.create(networkManagers)).group(loopGroup);
ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(new ChannelInitializer<Channel>() {
@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 -> {
String[] info = s.split(":");
@@ -91,7 +122,7 @@ public class NonblockingServerConnection {
}
}
@Shadow public volatile boolean d; // PAIL: neverTerminate
@Shadow public volatile boolean d; // OBFHELPER: neverTerminate
/**
* Shuts down all open endpoints
*/
@@ -108,15 +139,15 @@ public class NonblockingServerConnection {
public void processPackets(NetworkManager manager) {
try {
manager.a(); // PAIL: NetworkManager::processReceivedPackets
manager.a(); // OBFHELPER: NetworkManager::processReceivedPackets
} catch (Exception ex) {
logger.warn("Failed to handle packet for {}", new Object[] { manager.getSocketAddress(), ex });
final ChatComponentText kick = new ChatComponentText("Internal server error");
logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex);
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
public void operationComplete(Future<? super Void> future) throws Exception {
manager.close(kick);
manager.close(message);
}
}, new GenericFutureListener[0]);
manager.stopReading();
@@ -137,7 +168,7 @@ public class NonblockingServerConnection {
Iterator<NetworkManager> it = networkManagers.iterator();
while (it.hasNext()) {
NetworkManager manager = it.next();
if (manager.h()) continue; // PAIL: NetworkManager::hasNoChannel
if (manager.h()) continue; // OBFHELPER: NetworkManager::hasNoChannel
if (manager.isConnected()) {
processPackets(manager);

View File

@@ -1,14 +1,14 @@
package io.akarin.server.mixin.nsc;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.CheckedConcurrentLinkedQueue;
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
import io.akarin.api.internal.utils.CheckedConcurrentLinkedQueue;
import io.netty.channel.Channel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
@@ -21,21 +21,22 @@ import net.minecraft.server.PacketPlayOutMapChunk;
public abstract class OptimisticNetworkManager {
@Shadow public Channel channel;
@Shadow(aliases = "i") @Final private Queue<NetworkManager.QueuedPacket> packets;
@Shadow(aliases = "j") @Final private ReentrantReadWriteLock queueLock;
@Shadow(aliases = "j") @Final private ReentrantReadWriteUpdateLock queueLock;
@Shadow public abstract Queue<NetworkManager.QueuedPacket> getPacketQueue();
@Shadow public abstract void dispatchPacket(Packet<?> packet, GenericFutureListener<? extends Future<? super Void>>[] genericFutureListeners);
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null, null);
@SuppressWarnings("unchecked")
private static final QueuedPacket SIGNAL_PACKET = new QueuedPacket(null);
@Overwrite
@Overwrite // OBFHELPER: trySendQueue
private boolean m() {
if (this.channel != null && this.channel.isOpen()) {
if (this.packets.isEmpty()) { // return if the packet queue is empty so that the write lock by Anti-Xray doesn't affect the vanilla performance at all
return true;
}
this.queueLock.readLock().lock();
this.queueLock.updateLock().lock();
try {
while (!this.packets.isEmpty()) {
NetworkManager.QueuedPacket packet = ((CheckedConcurrentLinkedQueue<QueuedPacket>) getPacketQueue()).poll(item -> {
@@ -51,7 +52,7 @@ public abstract class OptimisticNetworkManager {
}
}
} finally {
this.queueLock.readLock().unlock();
this.queueLock.updateLock().unlock();
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -5,7 +5,7 @@ import org.spongepowered.asm.mixin.Overwrite;
import net.minecraft.server.TileEntityEnchantTable;
@Mixin(value = TileEntityEnchantTable.class, remap = false)
public class MixinTileEntityEnchantTable {
public abstract class MixinTileEntityEnchantTable {
@Overwrite
public void e() {} // No tickable
}

View File

@@ -0,0 +1,508 @@
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);
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);
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
}
}
}

View File

@@ -16,7 +16,7 @@ import net.minecraft.server.WorldGenBigTree;
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
*/
@Mixin(value = WorldGenBigTree.class, remap = false)
public class WeakBigTree {
public abstract class WeakBigTree {
@Shadow(aliases = "l") private World worldReference;
@Inject(method = "generate", at = @At("RETURN"))

View File

@@ -40,7 +40,7 @@ import net.minecraft.server.ItemStack;
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
*/
@Mixin(value = EnchantmentManager.class, remap = false)
public class WeakEnchantmentManager {
public abstract class WeakEnchantmentManager {
@Shadow(aliases = "a") @Final private static EnchantmentManager.EnchantmentModifierProtection protection;
@Shadow(aliases = "c") @Final private static EnchantmentManager.EnchantmentModifierThorns thorns;
@Shadow(aliases = "d") @Final private static EnchantmentManager.EnchantmentModifierArthropods arthropods;
@@ -50,23 +50,23 @@ public class WeakEnchantmentManager {
@Overwrite
public static int a(Iterable<ItemStack> iterable, DamageSource damageSource) {
protection.a = 0; // PAIL: damageModifier
protection.a = 0; // OBFHELPER: damageModifier
protection.b = damageSource;
a(protection, iterable); // PAIL: applyEnchantmentModifierArray
a(protection, iterable); // OBFHELPER: applyEnchantmentModifierArray
protection.b = null; // Akarin - Remove reference to Damagesource
return protection.a;
}
@Overwrite
public static void a(EntityLiving user, Entity attacker) { // PAIL: applyThornEnchantments
public static void a(EntityLiving user, Entity attacker) { // OBFHELPER: applyThornEnchantments
thorns.b = attacker;
thorns.a = user;
if (user != null) {
a(thorns, user.aQ()); // PAIL: applyEnchantmentModifierArray - getEquipmentAndArmor
a(thorns, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
}
if (attacker instanceof EntityHuman) {
a(thorns, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
a(thorns, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
}
// Akarin Start - remove references to entity objects to avoid memory leaks
@@ -76,15 +76,15 @@ public class WeakEnchantmentManager {
}
@Overwrite
public static void b(EntityLiving user, Entity target) { // PAIL: applyArthropodEnchantments
public static void b(EntityLiving user, Entity target) { // OBFHELPER: applyArthropodEnchantments
arthropods.a = user;
arthropods.b = target;
if (user != null) {
a(arthropods, user.aQ()); // PAIL: applyEnchantmentModifierArray - getEquipmentAndArmor
a(arthropods, user.aQ()); // OBFHELPER: applyEnchantmentModifierArray - getEquipmentAndArmor
}
if (user instanceof EntityHuman) {
a(arthropods, user.getItemInMainHand()); // PAIL: applyEnchantmentModifier
a(arthropods, user.getItemInMainHand()); // OBFHELPER: applyEnchantmentModifier
}
// Akarin Start - remove references to entity objects to avoid memory leaks

View File

@@ -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
}
}

View File

@@ -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));
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -1,14 +1,18 @@
package net.minecraft.server;
import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode;
import com.destroystokyo.paper.exception.ServerInternalException;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.google.common.collect.Queues;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import javax.annotation.Nullable;
import org.apache.logging.log4j.LogManager;
@@ -18,6 +22,11 @@ import com.google.common.collect.Lists; // CraftBukkit
import org.bukkit.Server; // CraftBukkit
import org.bukkit.craftbukkit.util.CraftMagicNumbers; // Paper
/**
* Akarin Changes Note
* 1) Add volatile to fields (async lighting)
* 2) Expose private methods (async lighting)
*/
public class Chunk {
private static final Logger e = LogManager.getLogger();
@@ -26,19 +35,49 @@ public class Chunk {
private final byte[] g;
private final int[] h;
private final boolean[] i;
private boolean j;
private boolean j; public boolean isLoaded() { return j; } // Paper - OBFHELPER
public final World world;
public final int[] heightMap;
public Long scheduledForUnload; // Paper - delay chunk unloads
private static final Logger logger = LogManager.getLogger(); // Paper
public final int locX;
public final int locZ;
private boolean m;
public final Map<BlockPosition, TileEntity> tileEntities;
public final List<Entity>[] entitySlices; // Spigot
final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Paper
private boolean done;
private boolean lit;
private boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER
// Paper start
public final co.aikar.util.Counter<String> entityCounts = new co.aikar.util.Counter<>();
public final co.aikar.util.Counter<String> tileEntityCounts = new co.aikar.util.Counter<>();
private class TileEntityHashMap extends java.util.HashMap<BlockPosition, TileEntity> {
@Override
public TileEntity put(BlockPosition key, TileEntity value) {
TileEntity replaced = super.put(key, value);
if (replaced != null) {
replaced.setCurrentChunk(null);
tileEntityCounts.decrement(replaced.tileEntityKeyString);
}
if (value != null) {
value.setCurrentChunk(Chunk.this);
tileEntityCounts.increment(value.tileEntityKeyString);
}
return replaced;
}
@Override
public TileEntity remove(Object key) {
TileEntity removed = super.remove(key);
if (removed != null) {
removed.setCurrentChunk(null);
tileEntityCounts.decrement(removed.tileEntityKeyString);
}
return removed;
}
}
public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public
// Paper end
private volatile boolean done; // Akarin - volatile
private volatile boolean lit; // Akarin - volatile
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
private boolean s;
private boolean t;
private long lastSaved;
@@ -90,10 +129,10 @@ public class Chunk {
this.g = new byte[256];
this.h = new int[256];
this.i = new boolean[256];
this.tileEntities = Maps.newHashMap();
this.tileEntities = new TileEntityHashMap(); // Paper
this.x = 4096;
this.y = Queues.newConcurrentLinkedQueue();
this.entitySlices = (new List[16]); // Spigot
this.entitySlices = (List[]) (new List[16]); // Spigot
this.world = world;
this.locX = i;
this.locZ = j;
@@ -459,7 +498,6 @@ public class Chunk {
return CrashReportSystemDetails.a(i, j, k);
}
@Override
public Object call() throws Exception {
return this.a();
}
@@ -645,8 +683,36 @@ public class Chunk {
entity.ab = this.locX;
entity.ac = k;
entity.ad = this.locZ;
this.entitySlices[k].add(entity);
// Paper start - update count
// Paper start
List<Entity> entitySlice = this.entitySlices[k];
boolean inThis = entitySlice.contains(entity);
if (entity.entitySlice != null || inThis) {
if (entity.entitySlice == entitySlice || inThis) {
LogManager.getLogger().warn(entity + " was already in this chunk section! Report this to https://github.com/PaperMC/Paper/issues/1223");
new Throwable().printStackTrace();
return;
} else {
LogManager.getLogger().warn(entity + " is still in another ChunkSection! Report this to https://github.com/PaperMC/Paper/issues/1223");
Chunk chunk = entity.getCurrentChunk();
if (chunk != null) {
if (chunk != this) {
LogManager.getLogger().warn(entity + " was in another chunk at that! " + chunk.locX + "," + chunk.locZ);
}
chunk.removeEntity(entity);
} else {
removeEntity(entity);
}
new Throwable().printStackTrace();
}
}
entity.entitySlice = entitySlice;
entitySlice.add(entity);
this.markDirty();
entity.setCurrentChunk(this);
entityCounts.increment(entity.entityKeyString);
if (entity instanceof EntityItem) {
itemCounts[k]++;
} else if (entity instanceof IInventory) {
@@ -671,6 +737,7 @@ public class Chunk {
// Spigot end
}
public void removeEntity(Entity entity) { b(entity); } // Paper - OBFHELPER
public void b(Entity entity) {
this.a(entity, entity.ac);
}
@@ -684,8 +751,17 @@ public class Chunk {
i = this.entitySlices.length - 1;
}
if (!this.entitySlices[i].remove(entity)) { return; } // Paper
// Paper start - update counts
// Paper start
if (!this.entitySlices[i].remove(entity)) { return; }
if (entitySlices[i] == entity.entitySlice) {
entity.entitySlice = null;
} else {
LogManager.getLogger().warn(entity + " was removed from a entitySlice we did not expect. Report this to https://github.com/PaperMC/Paper/issues/1223");
new Throwable().printStackTrace();
}
this.markDirty();
entity.setCurrentChunk(null);
entityCounts.decrement(entity.entityKeyString);
if (entity instanceof EntityItem) {
itemCounts[i]--;
} else if (entity instanceof IInventory) {
@@ -735,7 +811,7 @@ public class Chunk {
tileentity = world.capturedTileEntities.get(blockposition);
}
if (tileentity == null) {
tileentity = this.tileEntities.get(blockposition);
tileentity = (TileEntity) this.tileEntities.get(blockposition);
}
// CraftBukkit end
@@ -767,7 +843,7 @@ public class Chunk {
tileentity.setPosition(blockposition);
if (this.getBlockData(blockposition).getBlock() instanceof ITileEntity) {
if (this.tileEntities.containsKey(blockposition)) {
this.tileEntities.get(blockposition).z();
((TileEntity) this.tileEntities.get(blockposition)).z();
}
tileentity.A();
@@ -789,6 +865,7 @@ public class Chunk {
if (this.world.paperConfig.removeCorruptTEs) {
this.removeTileEntity(tileentity.getPosition());
this.markDirty();
org.bukkit.Bukkit.getLogger().info("Removing corrupt tile entity");
}
// Paper end
@@ -799,7 +876,7 @@ public class Chunk {
public void removeTileEntity(BlockPosition blockposition) { this.d(blockposition); } // Paper - OBFHELPER
public void d(BlockPosition blockposition) {
if (this.j) {
TileEntity tileentity = this.tileEntities.remove(blockposition);
TileEntity tileentity = (TileEntity) this.tileEntities.remove(blockposition);
if (tileentity != null) {
tileentity.z();
@@ -816,8 +893,38 @@ public class Chunk {
for (int j = 0; j < i; ++j) {
List entityslice = aentityslice[j]; // Spigot
// Paper start
DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode;
if (mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.REGEN) {
Map<UUID, Entity> thisChunk = new HashMap<>();
for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) {
Entity entity = iterator.next();
if (entity.dead) continue;
Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID);
if (other == null) {
other = thisChunk.get(entity.uniqueID);
}
if (other != null && !other.dead) {
switch (mode) {
case REGEN: {
entity.setUUID(UUID.randomUUID());
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
break;
}
case DELETE: {
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
entity.die();
iterator.remove();
break;
}
}
}
thisChunk.put(entity.uniqueID, entity);
}
}
// Paper end
this.world.a(entityslice);
this.world.a((Collection) entityslice);
}
}
@@ -831,11 +938,11 @@ public class Chunk {
// Spigot Start
if ( tileentity instanceof IInventory )
{
for ( org.bukkit.entity.HumanEntity h : Lists.<org.bukkit.entity.HumanEntity>newArrayList(( (IInventory) tileentity ).getViewers() ) )
for ( org.bukkit.entity.HumanEntity h : Lists.<org.bukkit.entity.HumanEntity>newArrayList((List<org.bukkit.entity.HumanEntity>) ( (IInventory) tileentity ).getViewers() ) )
{
if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity )
{
( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory();
( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper
}
}
}
@@ -856,11 +963,11 @@ public class Chunk {
// Spigot Start
if ( entity instanceof IInventory )
{
for ( org.bukkit.entity.HumanEntity h : Lists.<org.bukkit.entity.HumanEntity>newArrayList( ( (IInventory) entity ).getViewers() ) )
for ( org.bukkit.entity.HumanEntity h : Lists.<org.bukkit.entity.HumanEntity>newArrayList( (List<org.bukkit.entity.HumanEntity>) ( (IInventory) entity ).getViewers() ) )
{
if ( h instanceof org.bukkit.craftbukkit.entity.CraftHumanEntity )
{
( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory();
( (org.bukkit.craftbukkit.entity.CraftHumanEntity) h).getHandle().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED); // Paper
}
}
}
@@ -975,7 +1082,7 @@ public class Chunk {
}
public Random a(long i) {
return new Random(this.world.getSeed() + this.locX * this.locX * 4987142 + this.locX * 5947611 + this.locZ * this.locZ * 4392871L + this.locZ * 389711 ^ i);
return new Random(this.world.getSeed() + (long) (this.locX * this.locX * 4987142) + (long) (this.locX * 5947611) + (long) (this.locZ * this.locZ) * 4392871L + (long) (this.locZ * 389711) ^ i);
}
public boolean isEmpty() {
@@ -1055,7 +1162,7 @@ public class Chunk {
random.setSeed(world.getSeed());
long xRand = random.nextLong() / 2L * 2L + 1L;
long zRand = random.nextLong() / 2L * 2L + 1L;
random.setSeed(locX * xRand + locZ * zRand ^ world.getSeed());
random.setSeed((long) locX * xRand + (long) locZ * zRand ^ world.getSeed());
org.bukkit.World world = this.world.getWorld();
if (world != null) {
@@ -1116,7 +1223,7 @@ public class Chunk {
}
while (!this.y.isEmpty()) {
BlockPosition blockposition = this.y.poll();
BlockPosition blockposition = (BlockPosition) this.y.poll();
if (this.a(blockposition, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockposition).getBlock().isTileEntity()) {
TileEntity tileentity = this.g(blockposition);
@@ -1137,7 +1244,7 @@ public class Chunk {
* For now at least we will simply send all chunks, in accordance with pre 1.7 behaviour.
*/
// Paper Start
// if randomLightUpdates are enabled, we should always return true, otherwise chunks may never send
// if randomLightUpdates are disabled, we should always return true, otherwise chunks may never send
// to the client due to not being lit, otherwise retain standard behavior and only send properly lit chunks.
return !this.world.spigotConfig.randomLightUpdates || (this.isTicked() && this.done && this.lit);
// Paper End
@@ -1298,7 +1405,7 @@ public class Chunk {
this.h(false);
}
public void a(EnumDirection enumdirection) { // Akarin - private -> public - PAIL: checkLightSide
public void a(EnumDirection enumdirection) { // Akarin - private -> public
if (this.done) {
int i;
@@ -1333,7 +1440,7 @@ public class Chunk {
for (l = k + 16 - 1; l > this.world.getSeaLevel() || l > 0 && !flag1; --l) {
blockposition_mutableblockposition.c(blockposition_mutableblockposition.getX(), l, blockposition_mutableblockposition.getZ());
int i1 = this.b(blockposition_mutableblockposition);
int i1 = this.b((BlockPosition) blockposition_mutableblockposition);
if (i1 == 255 && blockposition_mutableblockposition.getY() < this.world.getSeaLevel()) {
flag1 = true;

View File

@@ -11,10 +11,8 @@ import java.util.Random;
import java.util.Map.Entry;
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Expose private members<br>
* @author cakoyo
* Akarin Changes Note
* 1) Expose private members (cause mixin errors)
*/
public class EnchantmentManager {

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ package net.minecraft.server;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import io.netty.buffer.Unpooled;
import java.util.ArrayDeque; // Paper
import java.util.ArrayList;
@@ -31,10 +32,9 @@ import org.bukkit.inventory.MainHand;
// CraftBukkit end
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Add volatile to fields<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add volatile to fields (time update)
* 2) Add lock to player track (safety issue)
*/
public class EntityPlayer extends EntityHuman implements ICrafting {
@@ -129,7 +129,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (worldserver.worldProvider.m() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) {
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) {
i = j;
@@ -146,7 +146,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit end
@Override
public void a(NBTTagCompound nbttagcompound) {
super.a(nbttagcompound);
if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world
@@ -174,7 +173,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.PLAYER, new DataInspector() {
@Override
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("RootVehicle", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
@@ -189,7 +187,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
});
}
@Override
public void b(NBTTagCompound nbttagcompound) {
super.b(nbttagcompound);
nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId());
@@ -221,7 +218,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit start - World fallback code, either respawn location or global spawn
@Override
public void spawnIn(World world) {
super.spawnIn(world);
if (world == null) {
@@ -246,13 +242,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit end
@Override
public void levelDown(int i) {
super.levelDown(i);
this.lastSentExp = -1;
}
@Override
public void enchantDone(ItemStack itemstack, int i) {
super.enchantDone(itemstack, i);
this.lastSentExp = -1;
@@ -262,29 +256,24 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.activeContainer.addSlotListener(this);
}
@Override
public void enterCombat() {
super.enterCombat();
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT));
}
@Override
public void exitCombat() {
super.exitCombat();
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT));
}
@Override
protected void a(IBlockData iblockdata) {
CriterionTriggers.d.a(this, iblockdata);
}
@Override
protected ItemCooldown l() {
return new ItemCooldownPlayer(this);
}
@Override
public void B_() {
// CraftBukkit start
if (this.joining) {
@@ -304,7 +293,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// Paper end
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;
}
@@ -359,7 +348,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
ItemStack itemstack = this.inventory.getItem(i);
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) {
this.playerConnection.sendPacket(packet);
@@ -425,7 +414,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
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;
}
// CraftBukkit end
@@ -450,7 +439,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public void die(DamageSource damagesource) {
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
@@ -483,9 +471,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) {
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) {
this.server.getPlayerList().b(this, chatmessage);
this.server.getPlayerList().b((EntityHuman) this, chatmessage);
}
} else {
this.server.getPlayerList().sendMessage(chatmessage);
@@ -501,7 +489,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.inventory.clear();
}
this.closeInventory();
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DEATH); // Paper
this.setSpectatorTarget(this); // Remove spectated target
// CraftBukkit end
@@ -518,7 +506,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
EntityLiving entityliving = this.ci();
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) {
this.b(entitytypes_monsteregginfo.killedByEntityStatistic);
@@ -534,7 +522,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.getCombatTracker().g();
}
@Override
public void a(Entity entity, int i, DamageSource damagesource) {
if (entity != this) {
super.a(entity, i, damagesource);
@@ -603,7 +590,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return Lists.newArrayList();
}
@Override
public boolean damageEntity(DamageSource damagesource, float f) {
if (this.isInvulnerable(damagesource)) {
return false;
@@ -634,7 +620,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public boolean a(EntityHuman entityhuman) {
return !this.canPvP() ? false : super.a(entityhuman);
}
@@ -644,7 +629,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return this.world.pvpMode;
}
@Override
@Nullable
public Entity b(int i) {
if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154
@@ -683,7 +667,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public boolean a(EntityPlayer entityplayer) {
return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer));
}
@@ -699,13 +682,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public void receive(Entity entity, int i) {
super.receive(entity, i);
this.activeContainer.b();
}
@Override
public EntityHuman.EnumBedResult a(BlockPosition blockposition) {
EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition);
@@ -713,7 +694,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.b(StatisticList.ab);
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.sendPacket(packetplayoutbed);
CriterionTriggers.p.a(this);
@@ -722,7 +703,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return entityhuman_enumbedresult;
}
@Override
public void a(boolean flag, boolean flag1, boolean flag2) {
if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one!
if (this.isSleeping()) {
@@ -736,7 +716,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public boolean a(Entity entity, boolean flag) {
Entity entity1 = this.bJ();
@@ -753,7 +732,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public void stopRiding() {
Entity entity = this.bJ();
@@ -767,21 +745,20 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (entity instanceof EntityPlayer) {
WorldServer worldServer = (WorldServer) entity.getWorld();
worldServer.tracker.untrackEntity(this);
worldServer.tracker.entriesLock.lock(); // Akarin
worldServer.tracker.track(this);
worldServer.tracker.entriesLock.unlock(); // Akarin
}
// Paper end
}
@Override
public boolean isInvulnerable(DamageSource damagesource) {
return super.isInvulnerable(damagesource) || this.L();
}
@Override
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
@Override
protected void b(BlockPosition blockposition) {
if (!this.isSpectator()) {
super.b(blockposition);
@@ -810,9 +787,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
super.a(d0, flag, iblockdata, blockposition);
}
@Override
public void openSign(TileEntitySign tileentitysign) {
tileentitysign.a(this);
tileentitysign.a((EntityHuman) this);
this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition()));
}
@@ -821,7 +797,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return containerCounter; // CraftBukkit
}
@Override
public void openTileEntity(ITileEntityContainer itileentitycontainer) {
// CraftBukkit start - Inventory open hook
if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).b() != null && this.isSpectator()) {
@@ -841,7 +816,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public void openContainer(IInventory iinventory) {
// CraftBukkit start - Inventory open hook
// Copied from below
@@ -871,7 +845,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).setChatModifier((new ChatModifier()).setColor(EnumChatFormat.RED)), true);
} else {
if (this.activeContainer != this.defaultContainer) {
this.closeInventory();
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
}
if (iinventory instanceof ITileInventory) {
@@ -901,7 +875,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public void openTrade(IMerchant imerchant) {
// CraftBukkit start - Inventory open hook
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world));
@@ -929,7 +902,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) {
// CraftBukkit start - Inventory open hook
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this));
@@ -939,7 +911,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit end
if (this.activeContainer != this.defaultContainer) {
this.closeInventory();
this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
}
this.nextContainerCounter();
@@ -949,26 +921,23 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.activeContainer.addSlotListener(this);
}
@Override
public void a(ItemStack itemstack, EnumHand enumhand) {
Item item = itemstack.getItem();
if (item == Items.WRITTEN_BOOK) {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
packetdataserializer.a(enumhand);
packetdataserializer.a((Enum) enumhand);
this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", packetdataserializer));
}
}
@Override
public void a(TileEntityCommand tileentitycommand) {
tileentitycommand.c(true);
this.a((TileEntity) tileentitycommand);
}
@Override
public void a(Container container, int i, ItemStack itemstack) {
if (!(container.getSlot(i) instanceof SlotResult)) {
if (container == this.defaultContainer) {
@@ -985,7 +954,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a(container, container.a());
}
@Override
public void a(Container container, NonNullList<ItemStack> nonnulllist) {
this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist));
this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried()));
@@ -996,12 +964,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// CraftBukkit end
}
@Override
public void setContainerData(Container container, int i, int j) {
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j));
}
@Override
public void setContainerData(Container container, IInventory iinventory) {
for (int i = 0; i < iinventory.h(); ++i) {
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i)));
@@ -1009,9 +975,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
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.r();
}
@@ -1023,7 +993,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
public void r() {
this.activeContainer.b(this);
this.activeContainer.b((EntityHuman) this);
this.activeContainer = this.defaultContainer;
}
@@ -1043,7 +1013,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public void a(Statistic statistic, int i) {
if (statistic != null) {
this.bZ.b(this, statistic, i);
@@ -1058,7 +1027,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public void a(Statistic statistic) {
if (statistic != null) {
this.bZ.setStatistic(this, statistic, 0);
@@ -1073,12 +1041,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
@Override
public void a(List<IRecipe> list) {
this.cr.a(list, this);
}
@Override
public void a(MinecraftKey[] aminecraftkey) {
ArrayList arraylist = Lists.newArrayList();
MinecraftKey[] aminecraftkey1 = aminecraftkey;
@@ -1099,7 +1065,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a((List<IRecipe>) arraylist); // CraftBukkit - decompile error
}
@Override
public void b(List<IRecipe> list) {
this.cr.b(list, this);
}
@@ -1130,12 +1095,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit end
@Override
public void a(IChatBaseComponent ichatbasecomponent, boolean flag) {
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT));
}
@Override
protected void v() {
if (!this.activeItem.isEmpty() && this.isHandRaised()) {
this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9));
@@ -1182,7 +1145,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.setShoulderEntityRight(entityplayer.getShoulderEntityRight());
}
@Override
protected void a(MobEffect mobeffect) {
super.a(mobeffect);
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
@@ -1194,14 +1156,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
CriterionTriggers.z.a(this);
}
@Override
protected void a(MobEffect mobeffect, boolean flag) {
super.a(mobeffect, flag);
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
CriterionTriggers.z.a(this);
}
@Override
protected void b(MobEffect mobeffect) {
super.b(mobeffect);
this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect()));
@@ -1212,22 +1172,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
CriterionTriggers.z.a(this);
}
@Override
public void enderTeleportTo(double d0, double d1, double d2) {
this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch);
}
@Override
public void a(Entity entity) {
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4));
}
@Override
public void b(Entity entity) {
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5));
}
@Override
public void updateAbilities() {
if (this.playerConnection != null) {
this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities));
@@ -1239,7 +1195,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return (WorldServer) this.world;
}
@Override
public void a(EnumGamemode enumgamemode) {
// CraftBukkit start
if (enumgamemode == this.playerInteractManager.getGameMode()) {
@@ -1254,7 +1209,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// CraftBukkit end
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) {
this.releaseShoulderEntities();
this.stopRiding();
@@ -1266,22 +1221,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.cE();
}
@Override
public boolean isSpectator() {
return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR;
}
@Override
public boolean z() {
return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE;
}
@Override
public void sendMessage(IChatBaseComponent ichatbasecomponent) {
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent));
}
@Override
public boolean a(int i, String s) {
/* CraftBukkit start
if ("seed".equals(s) && !this.server.aa()) {
@@ -1354,7 +1305,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1));
}
@Override
public BlockPosition getChunkCoordinates() {
return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ);
}
@@ -1384,7 +1334,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.removeQueue.remove(Integer.valueOf(entity.getId()));
}
@Override
protected void G() {
if (this.isSpectator()) {
this.bY();
@@ -1397,13 +1346,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
public Entity getSpecatorTarget() {
return this.co == null ? this : this.co;
return (Entity) (this.co == null ? this : this.co);
}
public void setSpectatorTarget(Entity entity) {
Entity entity1 = this.getSpecatorTarget();
this.co = entity == null ? this : entity;
this.co = (Entity) (entity == null ? this : entity);
if (entity1 != 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
@@ -1411,7 +1360,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
protected void I() {
if (this.portalCooldown > 0 && !this.worldChangeInvuln) {
--this.portalCooldown;
@@ -1419,7 +1367,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
@Override
public void attack(Entity entity) {
if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
this.setSpectatorTarget(entity);
@@ -1438,7 +1385,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return listName; // CraftBukkit
}
@Override
public void a(EnumHand enumhand) {
super.a(enumhand);
this.ds();

View File

@@ -6,24 +6,21 @@ import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Make entries-set concurrent<br>
* 2) Add lock for entries-set operations<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add lock for entries set operations (safety issue)
*/
public class EntityTracker {
private static final Logger a = LogManager.getLogger();
private final WorldServer world;
private final Set<EntityTrackerEntry> c = Sets.newConcurrentHashSet(); // Akarin - make concurrent
private final ReentrantReadWriteLock entriesLock = new ReentrantReadWriteLock(); // Akarin - add lock
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
public final ReentrantLock entriesLock = new ReentrantLock(); // Akarin - add lock
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
private int e;
@@ -40,8 +37,8 @@ public class EntityTracker {
if (entity instanceof EntityPlayer) {
this.addEntity(entity, 512, 2);
EntityPlayer entityplayer = (EntityPlayer) entity;
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -50,7 +47,7 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer);
}
}
entriesLock.readLock().unlock(); // Akarin
entriesLock.unlock(); // Akarin
} else if (entity instanceof EntityFishingHook) {
this.addEntity(entity, 64, 5, true);
} else if (entity instanceof EntityArrow) {
@@ -127,12 +124,12 @@ public class EntityTracker {
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
entriesLock.writeLock().lock(); // Akarin
entriesLock.lock(); // Akarin
this.c.add(entitytrackerentry);
entriesLock.writeLock().unlock(); // Akarin
this.trackedEntities.a(entity.getId(), entitytrackerentry);
entitytrackerentry.scanPlayers(this.world.players);
entriesLock.unlock(); // Akarin
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
@@ -169,32 +166,34 @@ public class EntityTracker {
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();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(entityplayer);
}
entriesLock.unlock(); // Akarin
}
EntityTrackerEntry entitytrackerentry1 = this.trackedEntities.d(entity.getId());
if (entitytrackerentry1 != null) {
entriesLock.lock(); // Akarin
this.c.remove(entitytrackerentry1);
entitytrackerentry1.a();
entriesLock.unlock(); // Akarin
}
entriesLock.writeLock().unlock(); // Akarin
}
public void updatePlayers() {
ArrayList arraylist = Lists.newArrayList();
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
world.timings.tracker1.startTiming(); // Spigot
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -207,7 +206,6 @@ public class EntityTracker {
}
}
}
entriesLock.readLock().unlock(); // Akarin
world.timings.tracker1.stopTiming(); // Spigot
world.timings.tracker2.startTiming(); // Spigot
@@ -223,13 +221,14 @@ public class EntityTracker {
}
}
}
entriesLock.unlock(); // Akarin
world.timings.tracker2.stopTiming(); // Spigot
}
public void a(EntityPlayer entityplayer) {
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -240,7 +239,7 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer);
}
}
entriesLock.readLock().unlock(); // Akarin
entriesLock.unlock(); // Akarin
}
public void a(Entity entity, Packet<?> packet) {
@@ -262,22 +261,22 @@ public class EntityTracker {
}
public void untrackPlayer(EntityPlayer entityplayer) {
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.clear(entityplayer);
}
entriesLock.readLock().unlock(); // Akarin
entriesLock.unlock(); // Akarin
}
public void a(EntityPlayer entityplayer, Chunk chunk) {
ArrayList arraylist = Lists.newArrayList();
ArrayList arraylist1 = Lists.newArrayList();
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -294,7 +293,7 @@ public class EntityTracker {
}
}
}
entriesLock.readLock().unlock(); // Akarin
entriesLock.unlock(); // Akarin
Entity entity1;
@@ -320,14 +319,14 @@ public class EntityTracker {
public void a(int i) {
this.e = (i - 1) * 16;
entriesLock.readLock().lock(); // Akarin
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(this.e);
}
entriesLock.readLock().unlock(); // Akarin
entriesLock.unlock(); // Akarin
}
}

View File

@@ -0,0 +1,658 @@
package net.minecraft.server;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerVelocityEvent;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Make trackedPlayerMap thread-safe (safety issue)
*/
public class EntityTrackerEntry {
private static final Logger c = LogManager.getLogger();
private final Entity tracker;
private final int e;
private int f;
private final int g;
private long xLoc;
private long yLoc;
private long zLoc;
private int yRot;
private int xRot;
private int headYaw;
private double n;
private double o;
private double p;
public int a;
private double q;
private double r;
private double s;
private boolean isMoving;
private final boolean u;
private int v;
private List<Entity> w = Collections.emptyList();
private boolean x;
private boolean y;
public boolean b;
// Paper start
// Replace trackedPlayers Set with a Map. The value is true until the player receives
// their first update (which is forced to have absolute coordinates), false afterward.
public java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.concurrent.ConcurrentHashMap<EntityPlayer, Boolean>(); // Akarin - make concurrent
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
// Paper end
public EntityTrackerEntry(Entity entity, int i, int j, int k, boolean flag) {
entity.tracker = this; // Paper
this.tracker = entity;
this.e = i;
this.f = j;
this.g = k;
this.u = flag;
this.xLoc = EntityTracker.a(entity.locX);
this.yLoc = EntityTracker.a(entity.locY);
this.zLoc = EntityTracker.a(entity.locZ);
this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F);
this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F);
this.headYaw = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F);
this.y = entity.onGround;
}
public boolean equals(Object object) {
return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false;
}
public int hashCode() {
return this.tracker.getId();
}
public void track(List<EntityHuman> list) {
this.b = false;
if (!this.isMoving || this.tracker.d(this.q, this.r, this.s) > 16.0D) {
this.q = this.tracker.locX;
this.r = this.tracker.locY;
this.s = this.tracker.locZ;
this.isMoving = true;
this.b = true;
this.scanPlayers(list);
}
List list1 = this.tracker.bF();
if (!list1.equals(this.w)) {
this.w = list1;
this.broadcastIncludingSelf(new PacketPlayOutMount(this.tracker)); // CraftBukkit
}
// PAIL : rename
if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
ItemStack itemstack = entityitemframe.getItem();
if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // Paper - moved back up
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world);
Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit
while (iterator.hasNext()) {
EntityHuman entityhuman = (EntityHuman) iterator.next();
EntityPlayer entityplayer = (EntityPlayer) entityhuman;
worldmap.a(entityplayer, itemstack);
Packet packet = Items.FILLED_MAP.a(itemstack, this.tracker.world, (EntityHuman) entityplayer);
if (packet != null) {
entityplayer.playerConnection.sendPacket(packet);
}
}
}
this.d();
}
if (this.a % this.g == 0 || this.tracker.impulse || this.tracker.getDataWatcher().a()) {
int i;
if (this.tracker.isPassenger()) {
i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
int j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
boolean flag = Math.abs(i - this.yRot) >= 1 || Math.abs(j - this.xRot) >= 1;
if (flag) {
this.broadcast(new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, this.tracker.onGround));
this.yRot = i;
this.xRot = j;
}
this.xLoc = EntityTracker.a(this.tracker.locX);
this.yLoc = EntityTracker.a(this.tracker.locY);
this.zLoc = EntityTracker.a(this.tracker.locZ);
this.d();
this.x = true;
} else {
++this.v;
long k = EntityTracker.a(this.tracker.locX);
long l = EntityTracker.a(this.tracker.locY);
long i1 = EntityTracker.a(this.tracker.locZ);
int j1 = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F);
int k1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F);
long l1 = k - this.xLoc;
long i2 = l - this.yLoc;
long j2 = i1 - this.zLoc;
Object object = null;
boolean flag1 = l1 * l1 + i2 * i2 + j2 * j2 >= 128L || this.a % 60 == 0;
boolean flag2 = Math.abs(j1 - this.yRot) >= 1 || Math.abs(k1 - this.xRot) >= 1;
if (this.a > 0 || this.tracker instanceof EntityArrow) { // Paper - Moved up
// CraftBukkit start - Code moved from below
if (flag1) {
this.xLoc = k;
this.yLoc = l;
this.zLoc = i1;
}
if (flag2) {
this.yRot = j1;
this.xRot = k1;
}
// CraftBukkit end
if (l1 >= -32768L && l1 < 32768L && i2 >= -32768L && i2 < 32768L && j2 >= -32768L && j2 < 32768L && this.v <= 400 && !this.x && this.y == this.tracker.onGround) {
if ((!flag1 || !flag2) && !(this.tracker instanceof EntityArrow)) {
if (flag1) {
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), l1, i2, j2, this.tracker.onGround);
} else if (flag2) {
object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) j1, (byte) k1, this.tracker.onGround);
}
} else {
object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), l1, i2, j2, (byte) j1, (byte) k1, this.tracker.onGround);
}
} else {
this.y = this.tracker.onGround;
this.v = 0;
// CraftBukkit start - Refresh list of who can see a player before sending teleport packet
if (this.tracker instanceof EntityPlayer) {
this.scanPlayers(new java.util.ArrayList(this.trackedPlayers));
}
// CraftBukkit end
this.c();
object = new PacketPlayOutEntityTeleport(this.tracker);
}
}
boolean flag3 = this.u;
if (this.tracker instanceof EntityLiving && ((EntityLiving) this.tracker).cP()) {
flag3 = true;
}
if (flag3 && this.a > 0) {
double d0 = this.tracker.motX - this.n;
double d1 = this.tracker.motY - this.o;
double d2 = this.tracker.motZ - this.p;
double d3 = 0.02D;
double d4 = d0 * d0 + d1 * d1 + d2 * d2;
if (d4 > 4.0E-4D || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) {
this.n = this.tracker.motX;
this.o = this.tracker.motY;
this.p = this.tracker.motZ;
this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.n, this.o, this.p));
}
}
if (object != null) {
// Paper start - ensure fresh viewers get an absolute position on their first update,
// since we can't be certain what position they received in the spawn packet.
if (object instanceof PacketPlayOutEntityTeleport) {
this.broadcast((Packet) object);
} else {
PacketPlayOutEntityTeleport teleportPacket = null;
for (java.util.Map.Entry<EntityPlayer, Boolean> viewer : trackedPlayerMap.entrySet()) {
if (viewer.getValue()) {
viewer.setValue(false);
if (teleportPacket == null) {
teleportPacket = new PacketPlayOutEntityTeleport(this.tracker);
}
viewer.getKey().playerConnection.sendPacket(teleportPacket);
} else {
viewer.getKey().playerConnection.sendPacket((Packet) object);
}
}
}
// Paper end
}
this.d();
/* CraftBukkit start - Code moved up
if (flag1) {
this.xLoc = k;
this.yLoc = l;
this.zLoc = i1;
}
if (flag2) {
this.yRot = j1;
this.xRot = k1;
}
// CraftBukkit end */
this.x = false;
}
i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
if (Math.abs(i - this.headYaw) >= 1) {
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i));
this.headYaw = i;
}
this.tracker.impulse = false;
}
++this.a;
if (this.tracker.velocityChanged) {
// CraftBukkit start - Create PlayerVelocity event
boolean cancelled = false;
if (this.tracker instanceof EntityPlayer) {
Player player = (Player) this.tracker.getBukkitEntity();
org.bukkit.util.Vector velocity = player.getVelocity();
PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone());
this.tracker.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
cancelled = true;
} else if (!velocity.equals(event.getVelocity())) {
player.setVelocity(event.getVelocity());
}
}
if (!cancelled) {
this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker));
}
// CraftBukkit end
this.tracker.velocityChanged = false;
}
}
private void d() {
DataWatcher datawatcher = this.tracker.getDataWatcher();
if (datawatcher.a()) {
this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false));
}
if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Set set = attributemapserver.getAttributes();
if (!set.isEmpty()) {
// CraftBukkit start - Send scaled max health
if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false);
}
// CraftBukkit end
this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set));
}
set.clear();
}
}
public void broadcast(Packet<?> packet) {
Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
entityplayer.playerConnection.sendPacket(packet);
}
}
public void broadcastIncludingSelf(Packet<?> packet) {
this.broadcast(packet);
if (this.tracker instanceof EntityPlayer) {
((EntityPlayer) this.tracker).playerConnection.sendPacket(packet);
}
}
public void a() {
Iterator iterator = this.trackedPlayers.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
this.tracker.c(entityplayer);
entityplayer.c(this.tracker);
}
}
public void a(EntityPlayer entityplayer) {
if (this.trackedPlayers.contains(entityplayer)) {
this.tracker.c(entityplayer);
entityplayer.c(this.tracker);
this.trackedPlayers.remove(entityplayer);
}
}
public void updatePlayer(EntityPlayer entityplayer) {
org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot
if (entityplayer != this.tracker) {
if (this.c(entityplayer)) {
if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) {
// CraftBukkit start - respect vanish API
if (this.tracker instanceof EntityPlayer) {
Player player = ((EntityPlayer) this.tracker).getBukkitEntity();
if (!entityplayer.getBukkitEntity().canSee(player)) {
return;
}
}
entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId()));
// CraftBukkit end
this.trackedPlayerMap.put(entityplayer, true); // Paper
Packet packet = this.e();
entityplayer.playerConnection.sendPacket(packet);
if (!this.tracker.getDataWatcher().d()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true));
}
boolean flag = this.u;
if (this.tracker instanceof EntityLiving) {
AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap();
Collection collection = attributemapserver.c();
// CraftBukkit start - If sending own attributes send scaled health instead of current maximum health
if (this.tracker.getId() == entityplayer.getId()) {
((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(collection, false);
}
// CraftBukkit end
if (!collection.isEmpty()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection));
}
if (((EntityLiving) this.tracker).cP()) {
flag = true;
}
}
this.n = this.tracker.motX;
this.o = this.tracker.motY;
this.p = this.tracker.motZ;
if (flag && !(packet instanceof PacketPlayOutSpawnEntityLiving)) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ));
}
if (this.tracker instanceof EntityLiving) {
EnumItemSlot[] aenumitemslot = EnumItemSlot.values();
int i = aenumitemslot.length;
for (int j = 0; j < i; ++j) {
EnumItemSlot enumitemslot = aenumitemslot[j];
ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(enumitemslot);
if (!itemstack.isEmpty()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), enumitemslot, itemstack));
}
}
}
if (this.tracker instanceof EntityHuman) {
EntityHuman entityhuman = (EntityHuman) this.tracker;
if (entityhuman.isSleeping()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker)));
}
}
// CraftBukkit start - Fix for nonsensical head yaw
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw));
// CraftBukkit end
if (this.tracker instanceof EntityLiving) {
EntityLiving entityliving = (EntityLiving) this.tracker;
Iterator iterator = entityliving.getEffects().iterator();
while (iterator.hasNext()) {
MobEffect mobeffect = (MobEffect) iterator.next();
entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect));
}
}
if (!this.tracker.bF().isEmpty()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
}
if (this.tracker.isPassenger()) {
entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker.bJ()));
}
this.tracker.b(entityplayer);
entityplayer.d(this.tracker);
updatePassengers(entityplayer); // Paper
}
} else if (this.trackedPlayers.contains(entityplayer)) {
this.trackedPlayers.remove(entityplayer);
this.tracker.c(entityplayer);
entityplayer.c(this.tracker);
updatePassengers(entityplayer); // Paper
}
}
}
public boolean c(EntityPlayer entityplayer) {
// Paper start
if (tracker.isPassenger()) {
return isTrackedBy(tracker.getVehicle(), entityplayer);
} else if (hasPassengerInRange(tracker, entityplayer)) {
return true;
}
return isInRangeOfPlayer(entityplayer);
}
private static boolean hasPassengerInRange(Entity entity, EntityPlayer entityplayer) {
if (!entity.isVehicle()) {
return false;
}
for (Entity passenger : entity.passengers) {
if (passenger.tracker != null && passenger.tracker.isInRangeOfPlayer(entityplayer)) {
return true;
}
if (passenger.isVehicle()) {
if (hasPassengerInRange(passenger, entityplayer)) {
return true;
}
}
}
return false;
}
private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) {
return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer);
}
private void updatePassengers(EntityPlayer player) {
if (tracker.isVehicle()) {
tracker.passengers.forEach((e) -> {
if (e.tracker != null) {
e.tracker.updatePlayer(player);
}
});
player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker));
}
}
private boolean isInRangeOfPlayer(EntityPlayer entityplayer) {
// Paper end
double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D;
double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D;
int i = Math.min(this.e, this.f);
return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer);
}
private boolean e(EntityPlayer entityplayer) {
return entityplayer.x().getPlayerChunkMap().a(entityplayer, this.tracker.ab, this.tracker.ad);
}
public void scanPlayers(List<EntityHuman> list) {
for (int i = 0; i < list.size(); ++i) {
this.updatePlayer((EntityPlayer) list.get(i));
}
}
private Packet<?> e() {
if (this.tracker.dead) {
// CraftBukkit start - Remove useless error spam, just return
// EntityTrackerEntry.d.warn("Fetching addPacket for removed entity");
return null;
// CraftBukkit end
}
if (this.tracker instanceof EntityPlayer) {
return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker);
} else if (this.tracker instanceof IAnimal) {
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker);
} else if (this.tracker instanceof EntityPainting) {
return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker);
} else if (this.tracker instanceof EntityItem) {
return new PacketPlayOutSpawnEntity(this.tracker, 2, 1);
} else if (this.tracker instanceof EntityMinecartAbstract) {
EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker;
return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.v().a());
} else if (this.tracker instanceof EntityBoat) {
return new PacketPlayOutSpawnEntity(this.tracker, 1);
} else if (this.tracker instanceof EntityExperienceOrb) {
return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker);
} else if (this.tracker instanceof EntityFishingHook) {
EntityHuman entityhuman = ((EntityFishingHook) this.tracker).l();
return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman == null ? this.tracker.getId() : entityhuman.getId());
} else {
Entity entity;
if (this.tracker instanceof EntitySpectralArrow) {
entity = ((EntitySpectralArrow) this.tracker).shooter;
return new PacketPlayOutSpawnEntity(this.tracker, 91, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
} else if (this.tracker instanceof EntityTippedArrow) {
entity = ((EntityArrow) this.tracker).shooter;
return new PacketPlayOutSpawnEntity(this.tracker, 60, 1 + (entity == null ? this.tracker.getId() : entity.getId()));
} else if (this.tracker instanceof EntitySnowball) {
return new PacketPlayOutSpawnEntity(this.tracker, 61);
} else if (this.tracker instanceof EntityLlamaSpit) {
return new PacketPlayOutSpawnEntity(this.tracker, 68);
} else if (this.tracker instanceof EntityPotion) {
return new PacketPlayOutSpawnEntity(this.tracker, 73);
} else if (this.tracker instanceof EntityThrownExpBottle) {
return new PacketPlayOutSpawnEntity(this.tracker, 75);
} else if (this.tracker instanceof EntityEnderPearl) {
return new PacketPlayOutSpawnEntity(this.tracker, 65);
} else if (this.tracker instanceof EntityEnderSignal) {
return new PacketPlayOutSpawnEntity(this.tracker, 72);
} else if (this.tracker instanceof EntityFireworks) {
return new PacketPlayOutSpawnEntity(this.tracker, 76);
} else if (this.tracker instanceof EntityFireball) {
EntityFireball entityfireball = (EntityFireball) this.tracker;
PacketPlayOutSpawnEntity packetplayoutspawnentity = null;
byte b0 = 63;
if (this.tracker instanceof EntitySmallFireball) {
b0 = 64;
} else if (this.tracker instanceof EntityDragonFireball) {
b0 = 93;
} else if (this.tracker instanceof EntityWitherSkull) {
b0 = 66;
}
if (entityfireball.shooter != null) {
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId());
} else {
packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0);
}
packetplayoutspawnentity.a((int) (entityfireball.dirX * 8000.0D));
packetplayoutspawnentity.b((int) (entityfireball.dirY * 8000.0D));
packetplayoutspawnentity.c((int) (entityfireball.dirZ * 8000.0D));
return packetplayoutspawnentity;
} else if (this.tracker instanceof EntityShulkerBullet) {
PacketPlayOutSpawnEntity packetplayoutspawnentity1 = new PacketPlayOutSpawnEntity(this.tracker, 67, 0);
packetplayoutspawnentity1.a((int) (this.tracker.motX * 8000.0D));
packetplayoutspawnentity1.b((int) (this.tracker.motY * 8000.0D));
packetplayoutspawnentity1.c((int) (this.tracker.motZ * 8000.0D));
return packetplayoutspawnentity1;
} else if (this.tracker instanceof EntityEgg) {
return new PacketPlayOutSpawnEntity(this.tracker, 62);
} else if (this.tracker instanceof EntityEvokerFangs) {
return new PacketPlayOutSpawnEntity(this.tracker, 79);
} else if (this.tracker instanceof EntityTNTPrimed) {
return new PacketPlayOutSpawnEntity(this.tracker, 50);
} else if (this.tracker instanceof EntityEnderCrystal) {
return new PacketPlayOutSpawnEntity(this.tracker, 51);
} else if (this.tracker instanceof EntityFallingBlock) {
EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker;
return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getCombinedId(entityfallingblock.getBlock()));
} else if (this.tracker instanceof EntityArmorStand) {
return new PacketPlayOutSpawnEntity(this.tracker, 78);
} else if (this.tracker instanceof EntityItemFrame) {
EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker;
return new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction.get2DRotationValue(), entityitemframe.getBlockPosition());
} else if (this.tracker instanceof EntityLeash) {
EntityLeash entityleash = (EntityLeash) this.tracker;
return new PacketPlayOutSpawnEntity(this.tracker, 77, 0, entityleash.getBlockPosition());
} else if (this.tracker instanceof EntityAreaEffectCloud) {
return new PacketPlayOutSpawnEntity(this.tracker, 3);
} else {
throw new IllegalArgumentException("Don\'t know how to add " + this.tracker.getClass() + "!");
}
}
}
public void clear(EntityPlayer entityplayer) {
org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot
if (this.trackedPlayers.contains(entityplayer)) {
this.trackedPlayers.remove(entityplayer);
this.tracker.c(entityplayer);
entityplayer.c(this.tracker);
updatePassengers(entityplayer); // Paper
}
}
public Entity b() {
return this.tracker;
}
public void a(int i) {
this.f = i;
}
public void c() {
this.isMoving = false;
}
}

View 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);
}
}
}
}

View 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);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,9 @@
package net.minecraft.server;
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.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
@@ -31,11 +32,11 @@ import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Add volatile to fields<br>
* 2) Expose private members<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add volatile to fields (nsc)
* 2) Expose private members (nsc)
* 3) Changes lock type to updatable lock (compatibility)
* 4) Removes unneed array creation (performance)
*/
public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
@@ -75,7 +76,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
};
private final EnumProtocolDirection h;
private final Queue<NetworkManager.QueuedPacket> i = new CheckedConcurrentLinkedQueue<NetworkManager.QueuedPacket>(); private final Queue<NetworkManager.QueuedPacket> getPacketQueue() { return this.i; } // Paper - Anti-Xray - OBFHELPER // Akarin
private final ReentrantReadWriteLock j = new ReentrantReadWriteLock();
private final ReentrantReadWriteUpdateLock j = new ReentrantReadWriteUpdateLock(); // Akarin - use update lock
public Channel channel;
// Spigot Start // PAIL
public SocketAddress l;
@@ -165,7 +166,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
this.j.writeLock().lock();
try {
this.i.add(new NetworkManager.QueuedPacket(packet, new GenericFutureListener[0]));
this.i.add(new NetworkManager.QueuedPacket(packet)); // Akarin - remove fake listener creation
} finally {
this.j.writeLock().unlock();
}

View File

@@ -0,0 +1,193 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
// Paper start
import com.destroystokyo.paper.antixray.PacketPlayOutMapChunkInfo; // Anti-Xray
// Paper end
/**
* Akarin Changes Note
* 1) WrappedByteBuf -> ByteBuf (compatibility)
*/
public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
private int a;
private int b;
private int c;
private ByteBuf d; // Akarin - byte[] -> ByteBuf
private List<NBTTagCompound> e;
private boolean f;
private volatile boolean ready = false; // Paper - Async-Anti-Xray - Ready flag for the network manager
// Paper start - Async-Anti-Xray - Set the ready flag to true
public PacketPlayOutMapChunk() {
this.ready = true;
}
// Paper end
public PacketPlayOutMapChunk(Chunk chunk, int i) {
PacketPlayOutMapChunkInfo packetPlayOutMapChunkInfo = chunk.world.chunkPacketBlockController.getPacketPlayOutMapChunkInfo(this, chunk, i); // Paper - Anti-Xray - Add chunk packet info
this.a = chunk.locX;
this.b = chunk.locZ;
this.f = i == '\uffff';
boolean flag = chunk.getWorld().worldProvider.m();
this.d = allocateBuffer(this.a(chunk, flag, i)); // Akarin
// Paper start - Anti-Xray - Add chunk packet info
if (packetPlayOutMapChunkInfo != null) {
packetPlayOutMapChunkInfo.setData(this.d);
}
// Paper end
this.c = this.writeChunk(new PacketDataSerializer(this.d), chunk, flag, i, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Add chunk packet info // Akarin
this.e = Lists.newArrayList();
Iterator iterator = chunk.getTileEntities().entrySet().iterator();
while (iterator.hasNext()) {
Entry entry = (Entry) iterator.next();
BlockPosition blockposition = (BlockPosition) entry.getKey();
TileEntity tileentity = (TileEntity) entry.getValue();
int j = blockposition.getY() >> 4;
if (this.e() || (i & 1 << j) != 0) {
NBTTagCompound nbttagcompound = tileentity.d();
this.e.add(nbttagcompound);
}
}
chunk.world.chunkPacketBlockController.modifyBlocks(this, packetPlayOutMapChunkInfo); // Paper - Anti-Xray - Modify blocks
}
// Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
public boolean isReady() {
return this.ready;
}
public void setReady(boolean ready) {
this.ready = ready;
}
// Paper end
public void a(PacketDataSerializer packetdataserializer) throws IOException {
this.a = packetdataserializer.readInt();
this.b = packetdataserializer.readInt();
this.f = packetdataserializer.readBoolean();
this.c = packetdataserializer.g();
int i = packetdataserializer.g();
if (i > 2097152) {
throw new RuntimeException("Chunk Packet trying to allocate too much memory on read.");
} else {
this.d = Unpooled.buffer(i); // Akarin
packetdataserializer.readBytes(this.d);
int j = packetdataserializer.g();
this.e = Lists.newArrayList();
for (int k = 0; k < j; ++k) {
this.e.add(packetdataserializer.j());
}
}
}
public void b(PacketDataSerializer packetdataserializer) throws IOException {
packetdataserializer.writeInt(this.a);
packetdataserializer.writeInt(this.b);
packetdataserializer.writeBoolean(this.f);
packetdataserializer.d(this.c);
packetdataserializer.d(this.d.array().length); // Akarin
packetdataserializer.writeBytes(this.d.array());
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;
}
}

View 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;
}
}

View File

@@ -3,7 +3,7 @@ package net.minecraft.server;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import io.akarin.api.Akari;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
@@ -62,12 +62,11 @@ import co.aikar.timings.MinecraftTimings; // Paper
// CraftBukkit end
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Add volatile to fields<br>
* 2) Expose private members<br>
* 3) Migrated keep alive packet handling to service thread<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add volatile to fields (slack service)
* 2) Expose private members (slack service)
* 3) Removed keep-alive codes (slack service)
* 4) Accessible keep-alive limit (feature, compatibility)
*/
public class PlayerConnection implements PacketListenerPlayIn, ITickable {
@@ -146,7 +145,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.
// CraftBukkit end
@Override
public void e() {
this.syncPosition();
this.player.playerTick();
@@ -189,7 +187,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
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
// This should effectively place the keepalive handling back to "as it was" before 1.12.2
long currentTime = this.getCurrentMillis();
@@ -209,7 +207,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
// Paper end
*/ // Akarin end
*/ // Akarin
this.minecraftServer.methodProfiler.b();
// CraftBukkit start
@@ -225,7 +223,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
--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.disconnect(new ChatMessage("multiplayer.disconnect.idling", new Object[0]));
}
@@ -275,7 +273,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
final ChatComponentText chatcomponenttext = new ChatComponentText(s);
this.networkManager.sendPacket(new PacketPlayOutKickDisconnect(chatcomponenttext), new GenericFutureListener() {
@Override
public void operationComplete(Future future) throws Exception { // CraftBukkit - decompile error
PlayerConnection.this.networkManager.close(chatcomponenttext);
}
@@ -284,14 +281,12 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
this.networkManager.stopReading();
// CraftBukkit - Don't wait
this.minecraftServer.postToMainThread(new Runnable() {
@Override
public void run() {
PlayerConnection.this.networkManager.handleDisconnection();
}
});
}
@Override
public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) {
PlayerConnectionUtils.ensureMainThread(packetplayinsteervehicle, this, this.player.x());
this.player.a(packetplayinsteervehicle.a(), packetplayinsteervehicle.b(), packetplayinsteervehicle.c(), packetplayinsteervehicle.d());
@@ -305,7 +300,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());
}
@Override
public void a(PacketPlayInVehicleMove packetplayinvehiclemove) {
PlayerConnectionUtils.ensureMainThread(packetplayinvehiclemove, this, this.player.x());
if (b(packetplayinvehiclemove)) {
@@ -355,7 +349,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
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
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));
@@ -473,7 +467,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
@Override
public void a(PacketPlayInTeleportAccept packetplayinteleportaccept) {
PlayerConnectionUtils.ensureMainThread(packetplayinteleportaccept, this, this.player.x());
if (packetplayinteleportaccept.a() == this.teleportAwait && this.teleportPos != null) { // CraftBukkit
@@ -490,7 +483,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInRecipeDisplayed packetplayinrecipedisplayed) {
PlayerConnectionUtils.ensureMainThread(packetplayinrecipedisplayed, this, this.player.x());
if (packetplayinrecipedisplayed.a() == PacketPlayInRecipeDisplayed.Status.SHOWN) {
@@ -502,7 +494,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInAdvancements packetplayinadvancements) {
PlayerConnectionUtils.ensureMainThread(packetplayinadvancements, this, this.player.x());
if (packetplayinadvancements.b() == PacketPlayInAdvancements.Status.OPENED_TAB) {
@@ -516,7 +507,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInFlying packetplayinflying) {
PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.x());
if (b(packetplayinflying)) {
@@ -598,7 +588,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (!this.player.L() && (!this.player.x().getGameRules().getBoolean("disableElytraMovementCheck") || !this.player.cP())) {
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
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);
@@ -855,7 +845,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
this.player.playerConnection.sendPacket(new PacketPlayOutPosition(d0, d1, d2, f, f1, set, this.teleportAwait));
}
@Override
public void a(PacketPlayInBlockDig packetplayinblockdig) {
PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -918,12 +907,13 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
case START_DESTROY_BLOCK:
case ABORT_DESTROY_BLOCK:
case STOP_DESTROY_BLOCK:
double d0 = this.player.locX - (blockposition.getX() + 0.5D);
double d1 = this.player.locY - (blockposition.getY() + 0.5D) + 1.5D;
double d2 = this.player.locZ - (blockposition.getZ() + 0.5D);
double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D);
double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D;
double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D);
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
if (d3 > 36.0D) {
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;
} else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight()) {
@@ -984,7 +974,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
// Spigot end
@Override
public void a(PacketPlayInUseItem packetplayinuseitem) {
PlayerConnectionUtils.ensureMainThread(packetplayinuseitem, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -1001,7 +990,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
chatmessage.getChatModifier().setColor(EnumChatFormat.RED);
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
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());
@@ -1016,7 +1005,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition.shift(enumdirection)));
}
@Override
public void a(PacketPlayInBlockPlace packetplayinblockplace) {
PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -1032,7 +1020,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
float f1 = this.player.pitch;
float f2 = this.player.yaw;
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;
Vec3D vec3d = new Vec3D(d0, d1, d2);
@@ -1043,7 +1031,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
float f7 = f4 * f5;
float f8 = f3 * f5;
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);
boolean cancelled;
@@ -1069,7 +1057,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
@Override
public void a(PacketPlayInSpectate packetplayinspectate) {
PlayerConnectionUtils.ensureMainThread(packetplayinspectate, this, this.player.x());
if (this.player.isSpectator()) {
@@ -1127,7 +1114,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
// CraftBukkit start
@Override
public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {
PlayerConnectionUtils.ensureMainThread(packetplayinresourcepackstatus, this, this.player.x());
// Paper start
@@ -1139,7 +1125,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
// CraftBukkit end
@Override
public void a(PacketPlayInBoatMove packetplayinboatmove) {
PlayerConnectionUtils.ensureMainThread(packetplayinboatmove, this, this.player.x());
Entity entity = this.player.bJ();
@@ -1150,7 +1135,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(IChatBaseComponent ichatbasecomponent) {
// CraftBukkit start - Rarely it would send a disconnect line twice
if (this.processedDisconnect) {
@@ -1216,7 +1200,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
return packet.getClass().getCanonicalName();
}
@Override
public Object call() throws Exception {
return this.a();
}
@@ -1225,7 +1208,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
@Override
public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) {
PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -1246,7 +1228,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
@Override
public void a(PacketPlayInChat packetplayinchat) {
// CraftBukkit start - async chat
// SPIGOT-3638
@@ -1387,6 +1368,8 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
// Paper Start
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Akari.isPrimaryThread()) { // Akarin
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, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());
Waitable wait = new Waitable() {
@@ -1405,6 +1388,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
} catch (Exception e) {
throw new RuntimeException("Exception processing chat command", e.getCause());
}
*/ // Akarin
}
// Paper End
this.handleCommand(s);
@@ -1513,7 +1497,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
// CraftBukkit end
}
@Override
public void a(PacketPlayInArmAnimation packetplayinarmanimation) {
PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -1522,7 +1505,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
float f1 = this.player.pitch;
float f2 = this.player.yaw;
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;
Vec3D vec3d = new Vec3D(d0, d1, d2);
@@ -1533,7 +1516,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
float f7 = f4 * f5;
float f8 = f3 * f5;
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);
if (movingobjectposition == null || movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.BLOCK) {
@@ -1549,7 +1532,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
this.player.a(packetplayinarmanimation.a());
}
@Override
public void a(PacketPlayInEntityAction packetplayinentityaction) {
PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.x());
// CraftBukkit start
@@ -1651,12 +1633,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInUseEntity packetplayinuseentity) {
PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension);
Entity entity = packetplayinuseentity.a(worldserver);
Entity entity = packetplayinuseentity.a((World) worldserver);
// Spigot Start
if ( entity == player && !player.isSpectator() )
{
@@ -1686,10 +1667,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
Item origItem = this.player.inventory.getItemInHand() == null ? null : this.player.inventory.getItemInHand().getItem();
PlayerInteractEntityEvent event;
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 {
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);
@@ -1704,6 +1685,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
if (event.isCancelled()) {
this.player.updateInventory(this.player.activeContainer); // Paper - Refresh player inventory
return;
}
// CraftBukkit end
@@ -1755,7 +1737,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInClientCommand packetplayinclientcommand) {
PlayerConnectionUtils.ensureMainThread(packetplayinclientcommand, this, this.player.x());
this.player.resetIdleTimer();
@@ -1787,17 +1768,15 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInCloseWindow packetplayinclosewindow) {
PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.x());
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();
}
@Override
public void a(PacketPlayInWindowClick packetplayinwindowclick) {
PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -1808,7 +1787,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
NonNullList nonnulllist = NonNullList.a();
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);
@@ -2094,7 +2073,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
NonNullList nonnulllist1 = NonNullList.a();
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;
nonnulllist1.add(itemstack2);
@@ -2107,7 +2086,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInAutoRecipe packetplayinautorecipe) {
PlayerConnectionUtils.ensureMainThread(packetplayinautorecipe, this, this.player.x());
this.player.resetIdleTimer();
@@ -2116,7 +2094,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
}
@Override
public void a(PacketPlayInEnchantItem packetplayinenchantitem) {
PlayerConnectionUtils.ensureMainThread(packetplayinenchantitem, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -2128,7 +2105,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInSetCreativeSlot packetplayinsetcreativeslot) {
PlayerConnectionUtils.ensureMainThread(packetplayinsetcreativeslot, this, this.player.x());
if (this.player.playerInteractManager.isCreative()) {
@@ -2148,7 +2124,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
nbttagcompound1.remove("x");
nbttagcompound1.remove("y");
nbttagcompound1.remove("z");
itemstack.a("BlockEntityTag", nbttagcompound1);
itemstack.a("BlockEntityTag", (NBTBase) nbttagcompound1);
}
}
}
@@ -2214,11 +2190,10 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInTransaction packetplayintransaction) {
PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.x());
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()) {
this.player.activeContainer.a(this.player, true);
@@ -2226,7 +2201,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInUpdateSign packetplayinupdatesign) {
PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.x());
if (this.player.isFrozen()) return; // CraftBukkit
@@ -2262,7 +2236,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
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.
}
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);
if (!event.isCancelled()) {
@@ -2277,7 +2251,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
@Override
public void a(PacketPlayInKeepAlive packetplayinkeepalive) {
//PlayerConnectionUtils.ensureMainThread(packetplayinkeepalive, this, this.player.x()); // CraftBukkit // Paper - This shouldn't be on the main thread
if (this.g && packetplayinkeepalive.a() == this.h) {
@@ -2302,7 +2275,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
return System.nanoTime() / 1000000L;
}
@Override
public void a(PacketPlayInAbilities packetplayinabilities) {
PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.x());
// CraftBukkit start
@@ -2319,7 +2291,6 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
// Paper start - async tab completion
@Override
public void a(PacketPlayInTabComplete packet) {
// CraftBukkit start
if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
@@ -2366,13 +2337,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
// Paper end
}
@Override
public void a(PacketPlayInSettings packetplayinsettings) {
PlayerConnectionUtils.ensureMainThread(packetplayinsettings, this, this.player.x());
this.player.a(packetplayinsettings);
}
@Override
public void a(PacketPlayInCustomPayload packetplayincustompayload) {
PlayerConnectionUtils.ensureMainThread(packetplayincustompayload, this, this.player.x());
String s = packetplayincustompayload.a();
@@ -2407,7 +2376,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) {
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
}
} catch (Exception exception) {
@@ -2444,19 +2413,19 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) {
ItemStack itemstack2 = new ItemStack(Items.WRITTEN_BOOK);
itemstack2.a("author", (new NBTTagString(this.player.getName())));
itemstack2.a("title", (new NBTTagString(itemstack.getTag().getString("title"))));
itemstack2.a("author", (NBTBase) (new NBTTagString(this.player.getName())));
itemstack2.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title"))));
NBTTagList nbttaglist = itemstack.getTag().getList("pages", 8);
for (int i = 0; i < nbttaglist.size(); ++i) {
s1 = nbttaglist.getString(i);
ChatComponentText chatcomponenttext = new ChatComponentText(s1);
s1 = IChatBaseComponent.ChatSerializer.a(chatcomponenttext);
s1 = IChatBaseComponent.ChatSerializer.a((IChatBaseComponent) chatcomponenttext);
nbttaglist.a(i, new NBTTagString(s1));
}
itemstack2.a("pages", nbttaglist);
itemstack2.a("pages", (NBTBase) nbttaglist);
CraftEventFactory.handleEditBookEvent(player, itemstack2); // CraftBukkit
}
} catch (Exception exception1) {
@@ -2554,7 +2523,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
boolean flag3 = packetdataserializer.readBoolean();
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;
switch (tileentitycommand_type) {
@@ -2674,23 +2643,23 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (b1 == 2) {
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 {
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) {
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()) {
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 {
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) {
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 {
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);
}
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View File

@@ -0,0 +1,161 @@
package net.minecraft.server;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterators;
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 java.util.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;
}
}

View 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);
}
}
}

View File

@@ -12,10 +12,8 @@ import org.bukkit.event.weather.WeatherChangeEvent;
// CraftBukkit end
/**
* <b>Akarin Changes Note</b><br>
* <br>
* 1) Add volatile to fields<br>
* @author cakoyo
* Akarin Changes Note
* 1) Add volatile to fields (slack service)
*/
public class WorldData {
@@ -29,8 +27,8 @@ public class WorldData {
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 volatile long k; // Akarin - volatile - OBFHELPER: time
private volatile long l; // Akarin - volatile - OBFHELPER: dayTime
private long m;
private long n;
private NBTTagCompound o;
@@ -47,7 +45,7 @@ public class WorldData {
private boolean z;
private boolean A;
private boolean B;
private EnumDifficulty C;
private volatile EnumDifficulty C; // Akarin - volatile
private boolean D;
private double E;
private double F;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,37 @@
# This is the main configuration file for Bukkit.
# As you can see, there's actually not that much to configure without any plugins.
# For a reference for any variable inside this file, check out the Bukkit Wiki at
# http://wiki.bukkit.org/Bukkit.yml
#
# If you need help on this file, feel free to join us on irc or leave a message
# on the forums asking for advice.
#
# IRC: #spigot @ irc.spi.gt
# (If this means nothing to you, just go to http://www.spigotmc.org/pages/irc/ )
# Forums: http://www.spigotmc.org/
# Bug tracker: http://www.spigotmc.org/go/bugs
settings:
allow-end: true
warn-on-overload: true
permissions-file: permissions.yml
update-folder: update
plugin-profiling: false
connection-throttle: 4000
query-plugins: false
deprecated-verbose: default
shutdown-message: "Server closed"
spawn-limits:
monsters: 70
animals: 15
water-animals: 5
ambient: 15
chunk-gc:
period-in-ticks: 600
load-threshold: 0
ticks-per:
animal-spawns: 400
monster-spawns: 1
autosave: 6000
aliases: now-in-commands.yml

View File

@@ -1,6 +1,6 @@
{
"required": true,
"minVersion": "0.7.8",
"minVersion": "0.7.10",
"package": "io.akarin.server.mixin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
@@ -11,33 +11,34 @@
"bootstrap.MixinMetrics",
"bootstrap.ParallelRegistry",
"bootstrap.MetricsBootstrap",
"bootstrap.MixinRestartCommand",
"core.MixinWorld",
"core.MixinMCUtil",
"core.MixinCommandBan",
"core.MixinCommandKick",
"core.MixinCraftServer",
"core.MixinWorldServer",
"core.MixinWorldManager",
"core.MixinCommandBanIp",
"core.MixinChunkSection",
"core.MixinAsyncCatcher",
"core.MixinTimingHandler",
"core.MonsterEggGuardian",
"core.MixinVersionCommand",
"core.MixinItemMonsterEgg",
"core.MixinMinecraftServer",
"core.MixinChunkIOExecutor",
"core.MixinPlayerConnectionUtils",
"cps.MixinChunk",
"cps.MixinCraftWorld",
"cps.MixinChunkProviderServer",
"nsc.MixinPlayerConnection",
"nsc.OptimisticNetworkManager",
"nsc.NonblockingServerConnection",
"lighting.MixinChunk",
"lighting.MixinWorld",
"lighting.MixinWorldServer",
"lighting.MixinChunkProviderServer",
"optimization.MixinEntity",
"optimization.WeakBigTree",
"optimization.WeakEnchantmentManager",
"optimization.MixinEntityHorseAbstract",
"optimization.MixinEntityTameableAnimal",
"optimization.MixinPersistentCollection",
"optimization.MixinTileEntityEnchantTable"
]
}

Some files were not shown because too many files have changed in this diff Show More