Compare commits

392 Commits

Author SHA1 Message Date
dependabot[bot]
8a0951bd4c Bump mysql-connector-java from 5.1.45 to 8.0.28 in /sources
Bumps [mysql-connector-java](https://github.com/mysql/mysql-connector-j) from 5.1.45 to 8.0.28.
- [Release notes](https://github.com/mysql/mysql-connector-j/releases)
- [Changelog](https://github.com/mysql/mysql-connector-j/blob/release/8.0/CHANGES)
- [Commits](https://github.com/mysql/mysql-connector-j/compare/5.1.45...8.0.28)

---
updated-dependencies:
- dependency-name: mysql:mysql-connector-java
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-08-28 06:20:18 +00:00
josephworks
7dcfc130d2 Merge pull request #230 from FatSaw/ver/1.12.2
More fixes and optimisations
2022-08-27 23:14:58 -07:00
FatSaw
0e9940771e Fix cow dupe 2022-07-31 09:02:05 +03:00
FatSaw
ee8e09fe58 Cache block break animation packet 2022-07-31 08:56:35 +03:00
FatSaw
b74ec9aa2e Performance improvement 2022-07-31 08:51:45 +03:00
FatSaw
ea8292aaf9 Prevent hook going through portals 2022-07-31 08:45:05 +03:00
FatSaw
7b47710c42 Update IntCache.java 2022-07-31 08:14:28 +03:00
FatSaw
d34531aa84 Update SpawnerCreature.java 2022-07-31 08:08:00 +03:00
FatSaw
30bb7a43fb Update IntCache.java 2022-07-31 08:00:45 +03:00
FatSaw
9a54ba116e Prevent creature spawning in unloaded chunks 2022-07-31 07:47:03 +03:00
FatSaw
a3cc630375 Do not load chunks for light checks 2022-07-31 07:42:57 +03:00
FatSaw
e5081d4575 Don t load chunks for physics 2022-07-31 07:37:30 +03:00
FatSaw
852acc0d65 Thread Local IntCache Fixed 2022-07-31 07:29:05 +03:00
FatSaw
7e756e786d Dont load chunks for chests 2022-07-31 07:16:31 +03:00
FatSaw
62848df6de Optimize Furnace 2022-07-31 07:14:59 +03:00
FatSaw
f5a531a15c Use valid item for enchantment checks on block 2022-07-31 07:13:32 +03:00
FatSaw
ba3d1c9672 Don t allow digging in unloaded chunks 2022-07-31 07:08:23 +03:00
FatSaw
fa08306751 Prevent enderman loading chunks 2022-07-31 07:05:43 +03:00
FatSaw
9a8d4c35a9 Maps shouldn t load chunks 2022-07-31 07:02:23 +03:00
FatSaw
adf961666a Fix MC-10657 2022-07-31 06:59:11 +03:00
FatSaw
947b22892c Use fastutil for passengers 2022-07-31 06:54:09 +03:00
FatSaw
0d9f04a748 Fix MC-158900 2022-07-31 06:53:42 +03:00
FatSaw
26652d43fc Improve fire spread checks 2022-07-31 06:44:57 +03:00
FatSaw
ac24d10a57 collide method Test noclip earlier 2022-07-31 06:37:35 +03:00
FatSaw
f220a6a153 don t tick dead players that are not in the world currently 2022-07-31 06:13:59 +03:00
FatSaw
233aab099e Fix MC-161754 2022-07-31 06:10:28 +03:00
FatSaw
5268e9a55a FlamePaper Fix head rotation packet spam 2022-07-31 06:03:08 +03:00
FatSaw
83c30311d0 FlamePaper Hopper item lookup optimizations 2022-07-31 06:02:19 +03:00
FatSaw
6a7f2b8699 Check if channel is opened before reading packet 2022-07-31 06:00:48 +03:00
FatSaw
cb55572504 Fix MC-103516 2022-07-31 06:00:09 +03:00
FatSaw
9d5c535d18 Fix MC-128547 2022-07-31 05:59:36 +03:00
FatSaw
b46cddd84d TacoSpigot Fix incorrect getNoDamageTicks patch 2022-07-31 05:58:53 +03:00
FatSaw
936cf720f4 Update Explosion.java 2022-07-09 21:43:25 +03:00
FatSaw
9b6412766f Update Explosion.java 2022-07-09 21:28:49 +03:00
FatSaw
d847e87c65 Update Explosion.java 2022-07-09 21:21:09 +03:00
FatSaw
ff8c0cd3be Optimize Explosions 2022-07-09 21:04:44 +03:00
FatSaw
19991463bf Create Explosion.java 2022-07-09 20:38:36 +03:00
FatSaw
c2931e362e Update Chunk.java 2022-07-09 20:27:54 +03:00
josephworks
e912498dac Merge pull request #224 from siyy123/ver/1.12.2
Fix NoSuchMethodError java.nio.ByteBuffer.clear()Ljava/nio/ByteBuffer
2022-07-08 21:24:37 -07:00
josephworks
98a7b4d757 Merge pull request #228 from FatSaw/ver/1.12.2
Some changes from other Paper forks
2022-07-08 21:23:41 -07:00
josephworks
62aa216b48 Update pom.xml 2022-07-08 21:20:53 -07:00
josephworks
1a56f7ac75 Update MathHelper.java 2022-07-08 21:17:58 -07:00
FatSaw
0080380486 Fix Dispenser Shulker Crash 2022-07-09 05:03:05 +03:00
FatSaw
e7419af0de Fixes MC-47080 and simplifies the logic 2022-07-09 04:53:47 +03:00
FatSaw
371817ab85 Update pom.xml 2022-07-09 04:26:16 +03:00
FatSaw
9ad318d4bb Use more fastutil data structures 2022-07-09 04:00:26 +03:00
FatSaw
e066c5e12c CompactSineLUT 2022-07-09 03:34:52 +03:00
josephworks
cfa99ef96e Merge pull request #227 from FatSaw/ver/1.12.2
Upstream paper pom changes with log4j fix
2022-07-03 11:58:18 -07:00
FatSaw
03bcac33d7 Update pom.xml 2022-07-03 18:05:12 +03:00
FatSaw
fcbda069fc Update pom.xml 2022-06-20 23:01:01 +03:00
siyy123
27561af9d8 ((Buffer) header).clear()
Fix java.nio.ByteBuffer.clear()
2022-05-27 09:09:49 +08:00
josephworks
ecb47cc23f Merge pull request #223 from siyy123/ver/1.12.2
Fix Akarin Slack Scheduler Thread.sleep() exception
2022-05-25 21:01:07 -07:00
siyy123
2fd9d75ba7 Fix Thread.sleep() 2022-05-26 10:34:09 +08:00
josephworks
69c5da47a0 Merge pull request #217 from FatSaw/ver/1.12.2
Fix build
2022-04-27 23:34:15 -07:00
FatSaw
c46b459f08 Fix timings 2022-04-28 01:40:19 +03:00
FatSaw
a540b150aa Return WeakEnchantmentManager 2022-04-27 22:04:16 +03:00
FatSaw
16eb219b60 Return OptimisticNetworkManager 2022-04-27 22:04:02 +03:00
FatSaw
3dac5cebbf Update Paper 2022-04-27 21:28:18 +03:00
FatSaw
78a3fb3aad Update Paper 2022-04-27 21:23:09 +03:00
FatSaw
df97aca45f Build fix 2022-04-27 20:23:30 +03:00
FatSaw
a0daa4f40e Update log4j and netty 2022-04-27 20:19:37 +03:00
josephworks
ebf0968fd3 Merge pull request #216 from siyy123/ver/1.12.2
Update Log4j for 1.12.2
2022-03-18 23:47:28 -10:00
siyy123
a0d41a8dea Update Log4j 2022-03-19 14:17:06 +08:00
LegacyGamerHD
8101d7b1a7 Update pom.xml 2021-12-13 21:08:28 +01:00
LegacyGamerHD
9a0558d218 Update Jenkinsfile 2021-12-13 21:07:55 +01:00
LegacyGamerHD
6d023d0ff5 Update Jenkinsfile 2021-12-13 20:38:25 +01:00
LegacyGamerHD
26d65b8875 recompile 2021-12-13 20:34:57 +01:00
LegacyGamerHD
b65309abd3 Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2021-04-01 19:50:38 +02:00
LegacyGamerHD
e07860fb92 test 2021-04-01 19:50:33 +02:00
LegacyGamerHD
9491da6b8f Update build.sh 2021-04-01 19:47:10 +02:00
LegacyGamerHD
4d57eb6172 Update build.sh 2021-04-01 19:45:25 +02:00
LegacyGamerHD
2920fa4993 Update Jenkinsfile 2021-04-01 19:41:11 +02:00
LegacyGamerHD
48574c2eb4 Update Jenkinsfile 2021-04-01 19:40:23 +02:00
LegacyGamerHD
233199054e test again 2021-04-01 19:37:14 +02:00
LegacyGamerHD
fe6068d6de testing jenkins again 2021-04-01 19:32:41 +02:00
LegacyGamerHD
a9a9b933f5 clean up 2021-04-01 19:28:05 +02:00
LegacyGamerHD
d082c42898 todo fix those 2021-04-01 19:25:20 +02:00
LegacyGamerHD
d784d03289 getting closer 2021-04-01 19:17:44 +02:00
LegacyGamerHD
3702d83f80 . 2021-04-01 19:13:22 +02:00
LegacyGamerHD
ebd4a75b88 ugh 2021-04-01 19:09:47 +02:00
LegacyGamerHD
d45dde48f1 moment of truth 2021-04-01 19:06:27 +02:00
LegacyGamerHD
55878409ac another test 2021-04-01 18:58:37 +02:00
LegacyGamerHD
5f0eca806a update paper 2021-04-01 18:53:59 +02:00
LegacyGamerHD
5e656d02eb back at it again 2021-04-01 18:51:35 +02:00
LegacyGamerHD
e407443ca9 am getting tired of this 2021-04-01 13:30:42 +02:00
LegacyGamerHD
345faccac7 test 5 2021-04-01 13:28:34 +02:00
LegacyGamerHD
0dcd33f41b est4 2021-04-01 13:26:27 +02:00
LegacyGamerHD
10a0c82475 test 2021-04-01 13:22:53 +02:00
LegacyGamerHD
2871e0dc81 test2 2021-04-01 13:18:24 +02:00
LegacyGamerHD
15ab29a426 test2 2021-04-01 13:04:51 +02:00
LegacyGamerHD
93cac1aeea test 2021-04-01 13:02:30 +02:00
LegacyGamerHD
55bbbdfbf2 lets tsart fress 2021-04-01 12:58:46 +02:00
LegacyGamerHD
01555f49a0 this gets anoying to get paper ready 2021-04-01 12:50:32 +02:00
LegacyGamerHD
4c40903958 bump 2021-04-01 12:47:26 +02:00
LegacyGamerHD
74d0113b46 bump 2021-04-01 12:42:54 +02:00
LegacyGamerHD
1db625034e update paper to the right one 2021-04-01 12:40:36 +02:00
LegacyGamerHD
6fbd890884 readd 2021-04-01 12:39:08 +02:00
LegacyGamerHD
3411916593 pussh 2021-04-01 12:37:59 +02:00
LegacyGamerHD
9e2b072bc6 test 1 2021-04-01 12:37:22 +02:00
LegacyGamerHD
cf03617415 update 2021-04-01 12:32:53 +02:00
LegacyGamerHD
d2c3477e59 Update Jenkinsfile 2021-03-29 19:49:40 +02:00
LegacyGamerHD
6c9c9bef6e Update inst.sh 2021-03-29 19:49:07 +02:00
LegacyGamerHD
4a48e19dcc fix 2021-03-29 19:41:15 +02:00
LegacyGamerHD
0e44c002df Update pom.xml 2021-03-29 19:39:43 +02:00
LegacyGamerHD
cbe1830f1c Update pom.xml 2021-03-29 19:36:02 +02:00
LegacyGamerHD
c7e7d1a40b Update Jenkinsfile 2021-03-29 19:31:45 +02:00
LegacyGamerHD
daf6af0269 Update build.sh 2021-03-29 19:30:38 +02:00
LegacyGamerHD
b510185638 Update build.sh 2021-03-29 19:28:31 +02:00
LegacyGamerHD
0eaeb3b28c Update Jenkinsfile 2021-03-29 19:21:18 +02:00
LegacyGamerHD
a2fad07eb6 Update Jenkinsfile 2021-03-29 19:19:18 +02:00
LegacyGamerHD
f07072102b Update Jenkinsfile 2021-03-29 19:13:51 +02:00
LegacyGamerHD
94264a57a3 Update build.sh 2021-03-29 19:08:38 +02:00
LegacyGamerHD
83f71f673b Update Jenkinsfile 2021-03-29 19:07:17 +02:00
LegacyGamerHD
3794ab48c1 update paper 2021-03-29 19:05:40 +02:00
LegacyGamerHD
643f313789 Update .gitmodules 2021-03-29 18:56:40 +02:00
LegacyGamerHD
573d187e38 Update pom.xml 2021-03-29 18:52:09 +02:00
LegacyGamerHD
2e9075e2d7 update 2021-02-11 12:10:33 +01:00
LegacyGamerHD
db9cf4e5a2 small downgrade 2021-02-11 12:03:49 +01:00
LegacyGamerHD
08c6269f3e Update .gitmodules 2021-02-11 12:58:05 +01:00
LegacyGamerHD
d25176aff1 test3 2021-02-04 10:14:13 +01:00
LegacyGamerHD
f5fc1e6b64 test2 2021-02-04 10:10:19 +01:00
LegacyGamerHD
000ea6409b test 2021-02-04 10:08:04 +01:00
LegacyGamerHD
925e91c99e testing 2021-02-04 10:04:15 +01:00
LegacyGamerHD
b1b3efae8d ... 2021-02-04 09:56:16 +01:00
LegacyGamerHD
a00d3645e1 . 2021-02-04 09:53:37 +01:00
LegacyGamerHD
95d3bc3529 . 2021-02-04 09:49:47 +01:00
LegacyGamerHD
677914bd39 . 2021-02-04 09:45:27 +01:00
LegacyGamerHD
e82e06c760 udpate paper 2021-02-04 09:43:36 +01:00
LegacyGamerHD
e40656e7f2 upda submodule file 2021-02-04 09:32:46 +01:00
LegacyGamerHD
676173300f downgrade paper 2021-02-04 09:31:46 +01:00
LegacyGamerHD
2d45820922 :3 2021-02-04 09:18:14 +01:00
LegacyGamerHD
bdbec60f02 re do that 2021-02-04 09:16:06 +01:00
LegacyGamerHD
433e18e7d2 revert some stuff 2021-02-04 09:14:28 +01:00
LegacyGamerHD
21d24bdfa5 ugh 2021-02-04 09:07:37 +01:00
LegacyGamerHD
4eec855840 . 2021-02-04 09:05:47 +01:00
LegacyGamerHD
dbe68a55d4 whoops 2021-02-04 09:03:57 +01:00
LegacyGamerHD
08d22009b7 try this 2021-02-04 08:58:35 +01:00
LegacyGamerHD
0f32cb2ee9 . 2021-02-04 08:56:33 +01:00
LegacyGamerHD
fc47e66e4c update paper 2021-02-04 08:51:36 +01:00
LegacyGamerHD
8778323b0c test 3 2021-02-04 08:47:05 +01:00
LegacyGamerHD
8751adbf1b . 2021-02-04 08:43:00 +01:00
LegacyGamerHD
742fe4e0a3 well thats because i live on energie drinks 2021-02-04 08:41:19 +01:00
LegacyGamerHD
3d75fe1118 whoops 2021-02-04 08:39:34 +01:00
LegacyGamerHD
a1c3051508 skip f patches fail 2021-02-04 08:37:33 +01:00
LegacyGamerHD
da57bfbf57 test 2 2021-02-04 08:34:36 +01:00
LegacyGamerHD
54e05c84f6 lets try again 2021-02-04 08:28:11 +01:00
LegecyGamerHD
9fd22502b2 work plz 2020-12-17 18:34:43 +01:00
LegecyGamerHD
5551f505a1 Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2020-12-17 18:31:16 +01:00
LegecyGamerHD
8f421ffb2a lets try again 2020-12-17 18:29:47 +01:00
LegacyGamerHD
7fe2637206 Update Jenkinsfile 2020-12-17 18:25:52 +01:00
LegecyGamerHD
362c9561dd . 2020-12-17 18:24:20 +01:00
LegecyGamerHD
fa1cf770f4 test ppaer stuff 2020-12-17 18:19:20 +01:00
LegecyGamerHD
4758e8aeef changes 2020-12-17 18:13:12 +01:00
LegecyGamerHD
998b18d2d8 update paper 2020-12-17 10:38:50 +01:00
LegacyGamerHD
b5b7684c58 Update pom.xml 2020-12-15 20:38:06 +01:00
LegacyGamerHD
48604bc9a6 Update pom.xml 2020-12-15 20:12:12 +01:00
LegacyGamerHD
2f4b0a7a88 Update pom.xml 2020-12-15 20:05:07 +01:00
LegacyGamerHD
16a91e1e31 Update build.sh 2020-12-15 20:01:09 +01:00
LegacyGamerHD
d64d8eb5f8 Update build.sh 2020-12-15 19:57:25 +01:00
LegacyGamerHD
e10f2ae716 Update pom.xml 2020-12-15 19:51:50 +01:00
LegacyGamerHD
e62e662196 Update build.sh 2020-12-15 19:48:34 +01:00
LegacyGamerHD
6c425fa820 Update Jenkinsfile 2020-12-15 19:38:58 +01:00
LegacyGamerHD
29385180ab Update Jenkinsfile 2020-12-15 19:38:48 +01:00
LegacyGamerHD
295d11b87a Update build.sh 2020-12-15 19:38:18 +01:00
LegacyGamerHD
fda0839d62 Update pom.xml 2020-12-15 19:35:00 +01:00
LegacyGamerHD
6002e2e6d2 Update pom.xml 2020-12-15 19:31:27 +01:00
LegacyGamerHD
b23baff1d8 Update Jenkinsfile 2020-12-15 19:26:57 +01:00
LegacyGamerHD
05ea8eda44 Update pom.xml 2020-12-15 19:22:24 +01:00
LegacyGamerHD
84de7a43fc Update pom.xml 2020-12-15 19:19:32 +01:00
LegacyGamerHD
3a53619460 Update pom.xml 2020-12-15 19:15:22 +01:00
LegacyGamerHD
fd6f9ef9f3 correct my mestakes 2020-12-15 19:12:33 +01:00
LegacyGamerHD
bcea435a88 Update Jenkinsfile 2020-12-15 19:09:32 +01:00
LegacyGamerHD
8d669910f6 Update build.sh 2020-12-15 19:09:15 +01:00
LegacyGamerHD
5f3843eb14 Update pom.xml 2020-12-15 19:06:39 +01:00
LegacyGamerHD
c407e63047 Update pom.xml 2020-12-15 18:59:48 +01:00
LegacyGamerHD
c60d1f672e Update pom.xml 2020-12-15 18:52:45 +01:00
LegacyGamerHD
ff6b2d60ab Update pom.xml 2020-12-15 18:47:31 +01:00
LegacyGamerHD
1cde35cfc1 Update pom.xml 2020-12-15 18:36:54 +01:00
LegacyGamerHD
49cfe2d8e6 Update pom.xml 2020-12-15 18:31:26 +01:00
LegacyGamerHD
19f2be2d7d Update pom.xml 2020-12-15 18:29:24 +01:00
LegacyGamerHD
8ce60a6575 Update Jenkinsfile 2020-12-15 18:26:30 +01:00
LegacyGamerHD
1fffe53720 Update build.sh 2020-12-15 18:25:14 +01:00
LegacyGamerHD
c33f5cea3b try use jitpack 2020-12-15 18:21:27 +01:00
LegacyGamerHD
bc27b6b11d Update pom.xml 2020-12-12 20:32:40 +01:00
LegacyGamerHD
ea9589df5c Update Jenkinsfile 2020-12-12 20:31:40 +01:00
LegacyGamerHD
53e8b9cf2a Update pom.xml 2020-12-12 20:30:46 +01:00
LegacyGamerHD
1d0b02d8d3 Update pom.xml 2020-12-12 20:28:58 +01:00
LegacyGamerHD
7436817e08 Update pom.xml 2020-12-12 20:27:19 +01:00
LegacyGamerHD
7b6f58c979 Update Jenkinsfile 2020-12-12 20:17:04 +01:00
LegacyGamerHD
7f12dd9c28 Update pom.xml 2020-12-12 20:14:57 +01:00
LegacyGamerHD
973d4d56c2 Update pom.xml 2020-12-12 20:08:17 +01:00
LegacyGamerHD
db31b8edde Update Jenkinsfile 2020-12-12 20:02:01 +01:00
LegacyGamerHD
2aabcc3bb1 Update pom.xml 2020-12-12 20:00:28 +01:00
LegacyGamerHD
9e83056973 Update Jenkinsfile 2020-12-12 19:50:03 +01:00
Joseph Robinson
7d268c178c Fixed security issue 2020-11-28 13:31:07 -07:00
LegacyGamerHD
4ed193b1dd Update pom.xml 2020-11-27 21:03:25 +01:00
LegacyGamerHD
1445ea5401 Revert "Update pom.xml"
This reverts commit 342f48cf8de0a3c8d18d9d84b89e83fc53662787.
2020-11-27 21:02:08 +01:00
LegacyGamerHD
950662e652 fix build script 2020-11-27 20:18:52 +01:00
LegacyGamerHD
9686940a08 .12.2: Auto stash before revert of ".12.2: Auto stash before merge of "ver/1.12.2" and "origin/ver/1.12.2"" 2020-11-27 20:11:31 +01:00
LegacyGamerHD
46c2fd8790 Revert ".12.2: Auto stash before merge of "ver/1.12.2" and "origin/ver/1.12.2""
This reverts commit 53b7a9095c.
2020-11-27 20:07:08 +01:00
LegacyGamerHD
53b7a9095c .12.2: Auto stash before merge of "ver/1.12.2" and "origin/ver/1.12.2" 2020-11-27 06:13:27 +01:00
Joseph Robinson
1803ed497f updated repositories 2020-11-26 13:27:18 -07:00
Joseph Robinson
9931749781 cleaned pom 2020-11-26 12:54:34 -07:00
LegacyGamerHD
5721388795 Update build.sh 2020-11-26 19:28:03 +01:00
LegacyGamerHD
7e195b8bd9 Update pom.xml 2020-11-26 19:10:11 +01:00
LegacyGamerHD
3929f61633 Update build.sh 2020-11-26 19:06:48 +01:00
LegacyGamerHD
d9b608826a waiting for the repo 2020-11-26 19:00:21 +01:00
LegacyGamerHD
30f216b3c5 fangit 2020-11-26 17:14:21 +01:00
LegacyGamerHD
663953230d fix 2020-11-26 17:03:51 +01:00
LegacyGamerHD
18868c715c Update Jenkinsfile 2020-11-26 16:54:12 +01:00
LegacyGamerHD
26e0546b50 lets try this 2020-11-26 16:53:32 +01:00
LegacyGamerHD
1708888915 well my mestake 2020-11-26 15:29:19 +01:00
LegacyGamerHD
a4c72a2762 update paper 2020-11-26 15:23:41 +01:00
LegacyGamerHD
a096eeadb1 am getting crazy 2020-11-26 15:02:16 +01:00
LegacyGamerHD
47fc205ba4 Update pom.xml 2020-11-26 14:57:32 +01:00
LegacyGamerHD
8a9d3acc05 Update Jenkinsfile 2020-11-26 14:52:34 +01:00
LegacyGamerHD
c4db74f13a Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2020-11-26 14:51:03 +01:00
LegacyGamerHD
044759f197 Update Jenkinsfile 2020-11-26 14:50:51 +01:00
LegacyGamerHD
c120dafe85 update paper 2020-11-26 14:46:12 +01:00
LegacyGamerHD
3cf50c844d update 2020-11-26 14:44:02 +01:00
LegacyGamerHD
fbeb9840ea whoops 2020-11-26 14:37:48 +01:00
LegacyGamerHD
82106ee8b6 Update pom.xml 2020-11-26 14:36:10 +01:00
LegacyGamerHD
a74aa135ae Update pom.xml 2020-11-26 13:01:47 +01:00
LegacyGamerHD
0201722e69 reset the build sh 2020-11-26 12:52:36 +01:00
LegacyGamerHD
cbf71003ff reset the pom 2020-11-26 12:51:06 +01:00
LegacyGamerHD
90f6d0e9ba Update Jenkinsfile 2020-11-26 12:45:12 +01:00
LegacyGamerHD
4290216041 lest try to get latest paper working from 1.12.2 ofc 2020-11-26 12:43:10 +01:00
LegacyGamerHD
33b5d7fb22 Update MixinMinecraftServer.java 2020-11-24 12:13:14 +01:00
LegacyGamerHD
ac1d99be3b Update MixinMinecraftServer.java 2020-11-24 11:52:59 +01:00
LegacyGamerHD
d282f46a5b Update MixinMinecraftServer.java 2020-11-24 11:47:50 +01:00
LegacyGamerHD
a1dee4e1b6 Update pom.xml 2020-11-24 11:41:45 +01:00
LegacyGamerHD
e8e6eb230d Update pom.xml 2020-11-24 11:40:05 +01:00
LegacyGamerHD
5fd8d633cb Update pom.xml 2020-11-24 11:37:38 +01:00
LegacyGamerHD
473190dc17 Update pom.xml 2020-11-24 11:35:51 +01:00
LegacyGamerHD
ce64d00b3a Update Jenkinsfile 2020-11-24 11:25:40 +01:00
LegacyGamerHD
810c8a5d00 Update Jenkinsfile 2020-11-24 11:22:39 +01:00
LegacyGamerHD
768ac2f14c some changes 2020-11-24 11:18:56 +01:00
LegacyGamerHD
183c5fc53d sanity check 2020-11-23 17:41:44 +01:00
LegacyGamerHD
5e51b97d27 lets try the original paper 2020-11-23 16:15:12 +01:00
LegacyGamerHD
dbd6e66ab9 Update pom.xml 2020-11-23 13:23:19 +01:00
LegacyGamerHD
8cb2bc2356 Update README.md 2020-11-23 13:17:52 +01:00
LegacyGamerHD
48ff1679fe Update README.md 2020-11-23 13:17:31 +01:00
LegacyGamerHD
8f77cd21b5 Update README.md 2020-11-23 10:14:29 +01:00
LegacyGamerHD
a825d60641 Update README.md 2020-11-23 10:14:16 +01:00
LegacyGamerHD
64f74e19a0 Fix 2020-11-23 10:10:46 +01:00
LegacyGamerHD
a741a4336c Update pom.xml 2020-11-23 10:08:41 +01:00
LegacyGamerHD
6946210cf9 Update README.md 2020-11-23 06:47:31 +01:00
LegacyGamerHD
e412d13ccc Update README.md 2020-11-23 06:47:12 +01:00
LegacyGamerHD
a74603ce85 Update pom.xml 2020-11-23 06:42:42 +01:00
LegacyGamerHD
b9a14c436f Trigger build 2020-11-23 06:33:22 +01:00
LegacyGamerHD
3ff1a9d072 fix 2020-11-22 20:34:02 +01:00
LegacyGamerHD
0174722008 ugh 2020-11-22 20:28:40 +01:00
LegacyGamerHD
7475c7fb8f Revert "doengrade paper"
This reverts commit 2bc7a3b190.
2020-11-22 20:24:04 +01:00
LegacyGamerHD
41da0735ba Update README.md 2020-11-22 20:21:38 +01:00
LegacyGamerHD
843e612652 trigger build 2020-11-22 20:17:53 +01:00
LegacyGamerHD
739614b286 Update README.md 2020-11-22 20:17:20 +01:00
LegacyGamerHD
04502d021b syntax 2020-11-22 20:15:04 +01:00
LegacyGamerHD
2bc7a3b190 doengrade paper 2020-11-22 20:13:39 +01:00
LegacyGamerHD
1cbd868bdf jenkins fixes 2020-11-22 20:11:55 +01:00
LegacyGamerHD
c54d83cedc Update README.md 2020-11-22 20:05:15 +01:00
LegacyGamerHD
d9040bf6f3 trigger build 2020-11-22 20:04:52 +01:00
LegacyGamerHD
a877b1903f Update README.md 2020-11-22 20:03:02 +01:00
LegacyGamerHD
a768ef41c6 more fixes 2020-11-22 19:58:20 +01:00
LegacyGamerHD
22d100f9f4 fix 2020-11-22 19:50:56 +01:00
LegacyGamerHD
1e57476b41 set paper before i mest with it 2020-11-22 19:41:14 +01:00
LegacyGamerHD
d5e59449fd Update build.sh 2020-11-22 19:38:10 +01:00
LegacyGamerHD
3b20dbd3f8 Update Jenkinsfile 2020-11-22 19:36:11 +01:00
LegacyGamerHD
ec977b85e9 Update build.sh 2020-11-22 19:33:27 +01:00
LegacyGamerHD
c22d51f944 MORE FIXES 2020-11-22 19:29:24 +01:00
LegacyGamerHD
c05abe18b1 Update build.sh 2020-11-22 19:19:12 +01:00
LegacyGamerHD
da6685793d Update build.sh 2020-11-22 19:17:28 +01:00
LegacyGamerHD
55b7a1bfed Update Jenkinsfile 2020-11-22 15:36:02 +01:00
LegacyGamerHD
0f9e51fd0b Update Jenkinsfile 2020-11-22 15:32:18 +01:00
LegacyGamerHD
bfca47a782 Update build.sh 2020-11-22 15:30:50 +01:00
LegacyGamerHD
f2563e1f76 Update Jenkinsfile 2020-11-22 15:23:59 +01:00
LegacyGamerHD
a5d9181450 Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2020-11-22 15:22:43 +01:00
LegacyGamerHD
f1b3273cba lets do this 2020-11-22 15:22:32 +01:00
LegacyGamerHD
1028ea8ff8 Update pom.xml 2020-11-22 15:19:00 +01:00
LegacyGamerHD
3d562bec84 Update pom.xml 2020-11-22 15:07:00 +01:00
LegacyGamerHD
610ec6d52e Update pom.xml 2020-11-22 15:06:16 +01:00
LegacyGamerHD
27a7b2a823 Update pom.xml 2020-11-22 15:03:52 +01:00
LegacyGamerHD
cb3fe4a4c2 Update pom.xml 2020-11-22 15:01:14 +01:00
LegacyGamerHD
b894a5256a Update Jenkinsfile 2020-11-22 14:59:14 +01:00
LegacyGamerHD
b99ec14a99 maby add a sanity check 2020-11-22 11:23:52 +01:00
LegacyGamerHD
4f2510bb51 test again 2020-11-21 18:28:24 +01:00
LegacyGamerHD
24360a8085 downgrade paper 2020-11-21 18:24:12 +01:00
LegacyGamerHD
367f5e2e87 update paper 2020-11-21 18:09:02 +01:00
LegacyGamerHD
91e0140620 remove the remote option as the jenkins is fast anough 2020-11-21 18:01:49 +01:00
LegacyGamerHD
24b26ab846 fix one error 2020-11-21 18:00:53 +01:00
LegacyGamerHD
c0df425445 Revert "test"
This reverts commit 389c787eb9.
2020-11-21 17:57:26 +01:00
LegacyGamerHD
389c787eb9 test 2020-11-20 20:07:05 +01:00
LegacyGamerHD
685b07f51d update source 2020-11-20 19:32:04 +01:00
LegacyGamerHD
43adb03349 paper akarin 2020-11-20 19:29:38 +01:00
LegacyGamerHD
d742c096c6 Update pom.xml 2020-11-20 19:21:59 +01:00
LegacyGamerHD
8071a603ba fix 2020-11-19 19:29:50 +01:00
LegacyGamerHD
889b6ea5ba test 2020-11-19 19:25:32 +01:00
LegacyGamerHD
2df37be2e2 Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2020-11-19 19:13:20 +01:00
LegacyGamerHD
36543cabf5 triggerd 2020-11-19 19:12:20 +01:00
LegacyGamerHD
2173b1e2bc Update Jenkinsfile 2020-11-19 19:09:44 +01:00
LegacyGamerHD
6e9351caaf Revert "let try this"
This reverts commit eb28c5ae38.
2020-11-19 19:07:05 +01:00
LegacyGamerHD
eb28c5ae38 let try this 2020-11-19 19:02:43 +01:00
LegacyGamerHD
ae6de9567f Revert "[test] replace unknown paper jar with bukit and spigot nms"
This reverts commit a6c252130f.
2020-11-19 19:01:44 +01:00
LegacyGamerHD
a6c252130f [test] replace unknown paper jar with bukit and spigot nms 2020-11-19 18:58:23 +01:00
LegacyGamerHD
5b2147cef5 [test] use the paper before upstream josaph 2020-11-19 18:52:00 +01:00
LegacyGamerHD
a4836cf014 add another repo 2020-11-19 15:54:59 +01:00
LegacyGamerHD
a924263f2f paper upstream 2020-11-19 15:42:43 +01:00
LegacyGamerHD
eb4ac897c7 whoops forgot the repo 2020-11-19 15:36:01 +01:00
LegacyGamerHD
090ab24a6b replace paper artifact with spigot as the paper one doesnt excist 2020-11-19 15:32:39 +01:00
LegacyGamerHD
445f6f185d add akarin repo 2020-11-19 15:23:21 +01:00
LegacyGamerHD
3c198efd87 plz compile just compile plz 2020-11-19 10:57:09 +01:00
LegacyGamerHD
8fa3e82671 changes 2020-11-19 10:49:02 +01:00
LegacyGamerHD
7381ae8793 import from akrin 1.13 2020-11-18 21:15:47 +01:00
LegacyGamerHD
ac5291d8e4 remove 2020-11-18 21:09:44 +01:00
LegacyGamerHD
57b84f327b hmm more work tommorow i geush 2020-11-18 21:05:28 +01:00
LegacyGamerHD
4529c0237a Merge remote-tracking branch 'origin/ver/1.12.2' into ver/1.12.2 2020-11-18 20:58:53 +01:00
LegacyGamerHD
0dfabac80f akarin repo is kinda dead 2020-11-18 20:58:39 +01:00
LegacyGamerHD
7c4a81bcb6 trigger people and trigger a build on jenkins 2020-11-18 20:53:21 +01:00
LegacyGamerHD
263cf75f49 Revert "Revert "Revert "use paper instead of 1.13.2 source """
This reverts commit 043b87f4ef.
2020-11-18 20:47:40 +01:00
LegacyGamerHD
005b2d2097 Revert "test commit"
This reverts commit 1bc90004ca.
2020-11-18 20:47:15 +01:00
LegacyGamerHD
13b5b7cc13 another test 2020-11-18 20:46:36 +01:00
LegacyGamerHD
1bc90004ca test commit 2020-11-17 10:10:25 +01:00
LegacyGamerHD
8a41fa0401 am a baka 2020-11-15 13:16:25 +01:00
LegacyGamerHD
043b87f4ef Revert "Revert "use paper instead of 1.13.2 source ""
This reverts commit a3501fb8dd.
2020-11-15 13:02:10 +01:00
LegacyGamerHD
6da2ca1f67 gone it is 2020-11-15 12:27:10 +01:00
LegacyGamerHD
f0044d7d07 Update build.sh 2020-11-15 12:26:03 +01:00
LegacyGamerHD
ea18e73509 well josaph at curl 2020-11-15 12:23:05 +01:00
LegacyGamerHD
f24d488601 add curl 2020-11-15 12:21:25 +01:00
LegacyGamerHD
a3501fb8dd Revert "use paper instead of 1.13.2 source "
This reverts commit abe730886a.
2020-11-15 12:18:50 +01:00
LegacyGamerHD
70b75489f2 add the paper repo 2020-11-15 12:17:47 +01:00
LegacyGamerHD
abe730886a use paper instead of 1.13.2 source 2020-11-15 12:15:37 +01:00
Joseph Robinson
645293ed0e Update README.md 2020-11-10 21:26:50 -07:00
Joseph Robinson
00859a2e18 Updated submodule 2020-07-29 15:31:39 -07:00
Joseph Robinson
d643cae29f updated gitmodules to use 1.12.2 and not 1.13 2020-07-23 11:44:07 -07:00
Joseph Robinson
9aa63c56ab Updated submodule work/Paper 2020-07-23 11:34:32 -07:00
Joseph Robinson
b06e5f3e13 Update Jenkinsfile 2020-05-13 19:44:40 -07:00
Joseph Robinson
f2a81f8a1e Create Jenkinsfile 2020-05-13 19:38:39 -07:00
cakoyo
7487ac4edf Merge pull request #60 from MatrixTunnel/fix/readme
Add new Jenkins by a friend
2018-10-14 17:50:52 +08:00
MatrixTunnel
07a8521999 Update README.md 2018-10-14 00:32:36 -07:00
MatrixTunnel
1879eebd8f Fix readme and add new Jenkins by a friend 2018-10-13 19:39:36 -07:00
Sotr
679236cfb9 Upstream Paper 2018-10-05 04:54:25 +08:00
Sotr
5fe93610c9 Fix a NPE and packet compat 2018-09-02 15:56:10 +08:00
Sotr
f73c20e342 Fixes data sync with anti-xray 2018-08-29 00:56:18 +08:00
Sotr
8e6000c523 Accurate timings 2018-08-28 17:39:19 +08:00
Sotr
2f87a4c54e Upstream Paper 2018-08-28 17:11:43 +08:00
cakoyo
751fcb584d Merge pull request #54 from Akarin-project/parallel-world
Parallel world
2018-08-19 21:41:16 +08:00
Sotr
10d2d285d9 Upstream Paper 2018-08-19 20:31:26 +08:00
Sotr
818f5559f7 Temp disable timings 2018-08-19 20:25:55 +08:00
Sotr
bf7f6fe3bc Upstream Paper w/ cleanup 2018-08-14 19:39:25 +08:00
Sotr
7acc339704 Safety timings w/ Configurable parallel mode 2018-08-14 19:15:54 +08:00
Sotr
f0d17e54e9 Shared random to world w/ thread safety fixes 2018-08-14 19:05:53 +08:00
Sotr
01c40ed0d3 CircleCI 2.0 w/ Removed unneed volatile and atomic 2018-08-11 03:22:00 +08:00
Sotr
4fdc9f0166 Fully parallel world ticking 2018-08-09 15:16:55 +08:00
Sotr
3b4926bed2 Merge branch 'ver/1.12.2' into parallel-world 2018-08-08 01:30:04 +08:00
Sotr
7e0f44f0af [ci skip] Add missing comment 2018-08-08 01:29:19 +08:00
Sotr
b41e4fd8c3 Resize worlds 2018-08-07 16:56:12 +08:00
Sotr
241a8ffefa Fully parallel entities ticking 2018-08-07 16:41:00 +08:00
Sotr
c2748ea2df Corrects check in last change 2018-08-05 20:14:08 +08:00
Sotr
e563233ff3 Fixes spawner modify feature 2018-08-05 20:11:31 +08:00
Sotr
9a5de6cf31 [ci skip] Corrects status branch 2018-08-05 06:47:35 +08:00
Sotr
9f7b490f61 Removed dupe locks 2018-08-05 04:30:47 +08:00
Sotr
6dceb465c3 Upstream Paper 2018-08-05 03:12:31 +08:00
Sotr
17ba164cbc Upstream Paper 2018-08-04 23:27:42 +08:00
Sotr
bb19cf0f7a [CI-SKIP] Update Readme 2018-08-04 23:05:06 +08:00
Sotr
0a59c8ce5f Should fix a NPE 2018-08-03 17:01:43 +08:00
Sotr
5d5bb381c0 Fixes CME cause by plugin 2018-08-02 13:18:21 +08:00
Sotr
05cc09ff51 Threadsafe EntityTracker w/ cleanup 2018-08-02 01:00:50 +08:00
Sotr
2ba4bc2755 Better handle locks for EntityTracker and misc 2018-08-02 00:51:56 +08:00
Sotr
9b5b40c002 Fixes tick rate 2018-08-02 00:10:06 +08:00
Sotr
a0545a756d Remove unneed synchronization 2018-08-02 00:07:48 +08:00
Sotr
6e62515d11 Import nms 2018-08-01 23:55:57 +08:00
Sotr
ff100c348e Multi-threaded chunk saving (not really) 2018-08-01 23:41:33 +08:00
Sotr
a25ff5dd93 Save a bit performance 2018-08-01 20:39:21 +08:00
Sotr
c6dbae3c24 [CI-SKIP] Add demo server 2018-08-01 05:53:52 +08:00
Sotr
b2bafb826f Fixes typo 2018-08-01 05:35:03 +08:00
Sotr
fb20bb3113 Fixup versioning 2018-08-01 05:26:19 +08:00
Sotr
927d946dba That not fast 2018-08-01 04:22:49 +08:00
Sotr
9ceec10f18 [CI-SKIP] Add header and clean comment 2018-08-01 04:11:20 +08:00
Sotr
74353989e4 Paper 1.13 Packport: World EntityHuman Lookup Optimizations 2018-08-01 04:01:17 +08:00
Sotr
c2705d4722 Upstream Paper w/ Removes async lighting 2018-08-01 04:00:46 +08:00
Sotr
b743d7dc4c Upstream Paper 2018-07-30 21:56:14 +08:00
Sotr
601ec9e90d This must be handle manually 2018-07-30 01:47:52 +08:00
Sotr
6dcd61f2c7 🤔 2018-07-30 01:33:52 +08:00
Sotr
c42528f4f1 Acutally fixed it 2018-07-30 00:54:50 +08:00
Sotr
402d9d2536 Brand new async lighting system w/ Harmless chunk unload optimization 2018-07-30 00:40:58 +08:00
Sotr
6ba139a83e Removes async lighting - Close GH-33 2018-07-29 23:24:01 +08:00
Sotr
0690cb1c9f FastBitSet 2018-07-29 03:04:37 +08:00
Sotr
b8879db641 Upstream Paper missing 2018-07-29 03:01:38 +08:00
Sotr
a3cc6062b6 Upstream Paper 2018-07-29 02:10:25 +08:00
Sotr
dba9fad220 Make PlayerChunkMap threadsafe 2018-07-29 01:32:34 +08:00
Sotr
af7444df09 Upstream Paper 2018-07-28 22:38:20 +08:00
Sotr
2def9e628a [CI-SKIP] Cleanup 2018-07-28 03:00:42 +08:00
Sotr
1641f2767f Done world load a bit more safer 2018-07-28 02:57:20 +08:00
89 changed files with 18556 additions and 4535 deletions

50
.circleci/config.yml Normal file
View File

@@ -0,0 +1,50 @@
version: 2
jobs:
build:
working_directory: ~/Akarin-project/Akarin
parallelism: 1
shell: /bin/bash --login
environment:
CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
docker:
- image: circleci/build-image:ubuntu-14.04-XXL-upstart-1189-5614f37
command: /sbin/init
steps:
# Machine Setup
- checkout
# Prepare for artifact
- run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
- run:
working_directory: ~/Akarin-project/Akarin
command: sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java; sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac; echo -e "export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64" >> $BASH_ENV
# Dependencies
# Restore the dependency cache
- restore_cache:
keys:
# This branch if available
- v1-dep-{{ .Branch }}-
# Default branch if not
- v1-dep-ver/1.12.2-
# Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
- v1-dep-
- run: git config --global user.email "circle@circleci.com"
- run: git config --global user.name "CircleCI"
- run: chmod +x scripts/inst.sh
- run: ./scripts/inst.sh --setup --remote
# Save dependency cache
- save_cache:
key: v1-dep-{{ .Branch }}-{{ epoch }}
paths:
- ~/.m2
# Test
- run: yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS
# Teardown
# Save test results
- store_test_results:
path: /tmp/circleci-test-results
# Save artifacts
- store_artifacts:
path: /tmp/circleci-artifacts
- store_artifacts:
path: /tmp/circleci-test-results

2
.gitmodules vendored
View File

@@ -1,3 +1,3 @@
[submodule "work/Paper"]
path = work/Paper
url = https://github.com/Akarin-project/Paper.git
url = https://github.com/Akarin-project/Paper.git

24
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,24 @@
pipeline {
agent any
stages {
stage('Init Submodules') {
steps {
sh 'git submodule update --init --recursive'
}
}
stage('Build') {
steps {
sh 'chmod +x scripts/inst.sh'
sh './scripts/inst.sh --setup --fast --remote'
}
}
stage('Archive') {
steps {
archiveArtifacts(artifacts: '*.jar', fingerprint: true)
}
}
}
}

View File

@@ -1,31 +1,29 @@
# <img src="https://i.loli.net/2018/05/17/5afd869c443ef.png" alt="Akarin Face" align="right">Akarin
[![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)
[![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/fw2pJAj)
[![bStats](https://img.shields.io/badge/bStats-Torch-0099ff.svg?style=flat)](https://bstats.org/plugin/bukkit/Torch)
Akarin is currently **under heavy development** and contributions are welcome!
Introduction
---
> Akarin is a powerful server software from the 'new dimension', formerly known as Torch.
> Akarin is a powerful server software from the 'new dimension', formerly known as Torch.
As a [Paper](https://github.com/PaperMC/Paper) fork, it should support almost all plugins that work on [Spigot](https://hub.spigotmc.org/stash/projects/SPIGOT/repos/spigot/browse).
Our project has a few key goals:
* **Open Access** - Make more game mechanics configurable.
* **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
+ [**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
+ [**Jenkins**](http://ci.josephworks.net/job/Akarin/job/ver%252F1.12.2/) - Kudos to [JosephWorks](https://github.com/josephworks)
*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*
@@ -40,13 +38,14 @@ Get Akarin
```
**Notes**
* You must use `--setup` at least once to deploy necessary dependencies otherwise some imports cannot be organized.
* 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` (official)
* `omc.hk` (auth required)
*Open an [Issue](https://github.com/Akarin-project/Akarin/issues) or a [Pull Request](https://github.com/Akarin-project/Akarin/pulls) if you want to add your website here*
@@ -54,12 +53,6 @@ Contributing
---
* Akarin uses [Mixin](https://github.com/SpongePowered/Mixin) to modify the code. You can checkout the `sources` folder to see more.
* 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`
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can [send](mailto://kira@kira.moe) us an email with your experience and necessary information.
![Akarin project](https://i.loli.net/2018/05/13/5af7fbbfbcddf.png)

View File

@@ -1,16 +0,0 @@
machine:
java:
version: openjdk8
dependencies:
cache-directories:
- "/home/ubuntu/Akarin/work/Paper/work/Minecraft"
override:
- git config --global user.email "circle@circleci.com"
- git config --global user.name "CircleCI"
- chmod +x scripts/inst.sh
- ./scripts/inst.sh --setup --remote
test:
post:
- yes|cp -rf ./akarin-*.jar $CIRCLE_ARTIFACTS

View File

@@ -0,0 +1,62 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
public class DataBitsReader {
private ByteBuf dataBits; // Akarin
private int bitsPerValue;
private int mask;
private int longInDataBitsIndex;
private int bitInLongIndex;
private long current;
public void setDataBits(ByteBuf dataBits) { // Akarin
this.dataBits = dataBits;
}
public void setBitsPerValue(int bitsPerValue) {
this.bitsPerValue = bitsPerValue;
mask = (1 << bitsPerValue) - 1;
}
public void setIndex(int index) {
this.longInDataBitsIndex = index;
bitInLongIndex = 0;
init();
}
private void init() {
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
dataBits.getLong(longInDataBitsIndex);
/*
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
*/ // Akarin end
}
}
public int read() {
int value = (int) (current >>> bitInLongIndex) & mask;
bitInLongIndex += bitsPerValue;
if (bitInLongIndex > 63) {
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
if (bitInLongIndex > 0) {
value |= current << bitsPerValue - bitInLongIndex & mask;
}
}
return value;
}
}

View File

@@ -0,0 +1,94 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
public class DataBitsWriter {
private ByteBuf dataBits; // Akarin
private int bitsPerValue;
private long mask;
private int longInDataBitsIndex;
private int bitInLongIndex;
private long current;
private boolean dirty;
public void setDataBits(ByteBuf dataBits) { // Akarin
this.dataBits = dataBits;
}
public void setBitsPerValue(int bitsPerValue) {
this.bitsPerValue = bitsPerValue;
mask = (1 << bitsPerValue) - 1;
}
public void setIndex(int index) {
this.longInDataBitsIndex = index;
bitInLongIndex = 0;
init();
}
private void init() {
if (dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
current = dataBits.getLong(longInDataBitsIndex);
/*
current = ((((long) dataBits[longInDataBitsIndex]) << 56)
| (((long) dataBits[longInDataBitsIndex + 1] & 0xff) << 48)
| (((long) dataBits[longInDataBitsIndex + 2] & 0xff) << 40)
| (((long) dataBits[longInDataBitsIndex + 3] & 0xff) << 32)
| (((long) dataBits[longInDataBitsIndex + 4] & 0xff) << 24)
| (((long) dataBits[longInDataBitsIndex + 5] & 0xff) << 16)
| (((long) dataBits[longInDataBitsIndex + 6] & 0xff) << 8)
| (((long) dataBits[longInDataBitsIndex + 7] & 0xff)));
*/ // Akarin end
}
dirty = false;
}
public void finish() {
if (dirty && dataBits.capacity() > longInDataBitsIndex + 7) { // Akarin
// Akarin start
dataBits.setLong(longInDataBitsIndex, current);
/*
dataBits[longInDataBitsIndex] = (byte) (current >> 56 & 0xff);
dataBits[longInDataBitsIndex + 1] = (byte) (current >> 48 & 0xff);
dataBits[longInDataBitsIndex + 2] = (byte) (current >> 40 & 0xff);
dataBits[longInDataBitsIndex + 3] = (byte) (current >> 32 & 0xff);
dataBits[longInDataBitsIndex + 4] = (byte) (current >> 24 & 0xff);
dataBits[longInDataBitsIndex + 5] = (byte) (current >> 16 & 0xff);
dataBits[longInDataBitsIndex + 6] = (byte) (current >> 8 & 0xff);
dataBits[longInDataBitsIndex + 7] = (byte) (current & 0xff);
*/ // Akarin end
}
}
public void write(int value) {
current = current & ~(mask << bitInLongIndex) | (value & mask) << bitInLongIndex;
dirty = true;
bitInLongIndex += bitsPerValue;
if (bitInLongIndex > 63) {
finish();
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
if (bitInLongIndex > 0) {
current = current & ~(mask >>> bitsPerValue - bitInLongIndex) | (value & mask) >>> bitsPerValue - bitInLongIndex;
dirty = true;
}
}
}
public void skip() {
bitInLongIndex += bitsPerValue;
if (bitInLongIndex > 63) {
finish();
bitInLongIndex -= 64;
longInDataBitsIndex += 8;
init();
}
}
}

View File

@@ -15,7 +15,7 @@ public class PacketPlayOutMapChunkInfo {
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
private final Chunk chunk;
private final int chunkSectionSelector;
private ByteBuf data; // Akarin - byte[] -> ByteBuf
private ByteBuf data; // Akarin
private final int[] bitsPerValue = new int[16];
private final DataPalette[] dataPalettes = new DataPalette[16];
private final int[] dataBitsIndexes = new int[16];
@@ -39,11 +39,11 @@ public class PacketPlayOutMapChunkInfo {
return chunkSectionSelector;
}
public byte[] getData() {
return data.array(); // Akarin
public ByteBuf getData() { // Akarin
return data;
}
public void setData(ByteBuf data) { // Akarin - byte[] -> ByteBuf
public void setData(ByteBuf data) { // Akarin
this.data = data;
}

View File

@@ -105,8 +105,8 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
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.d.capacity()); // Akarin
packetdataserializer.writeBytes(this.d.array()); // Akarin
packetdataserializer.d(this.e.size());
Iterator iterator = this.e.iterator();
@@ -122,9 +122,9 @@ public class PacketPlayOutMapChunk implements Packet<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
private ByteBuf g() { return allocateBuffer(-1); } // Akarin
private ByteBuf allocateBuffer(int expectedCapacity) { // Akarin - added argument
ByteBuf bytebuf = expectedCapacity == -1 ? Unpooled.buffer() : Unpooled.buffer(expectedCapacity); // Akarin
bytebuf.writerIndex(0);
return bytebuf;

View File

@@ -15,7 +15,7 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
if [ -d "Minecraft" ]; then
rm Minecraft/ -r
fi
git clone https://github.com/Akarin-project/Minecraft.git
git clone https://github.com/LegacyGamerHD/Minecraft.git
fi
cd "$paperbasedir"
@@ -51,4 +51,4 @@ echo "[Akarin] Ready to build"
echo "[Akarin] Migrated final jar to $basedir/akarin-$minecraftversion.jar"
)
)
)

View File

@@ -4,9 +4,9 @@
set -e
basedir="$pwd"
(git submodule update --init --remote && chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
(chmod +x scripts/build.sh && ./scripts/build.sh "$basedir" "$1" "$2" "$3") || (
echo "Failed to build Akarin"
exit 1
) || exit 1
)
)

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<artifactId>akarin</artifactId>
<packaging>jar</packaging>
<version>1.12.2-R0.4-RELEASE</version>
<version>1.12.2-R0.4-SNAPSHOT</version>
<name>Akarin</name>
<url>https://github.com/Akarin-project/Akarin</url>
@@ -30,7 +30,7 @@
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.24.Final</version>
<version>4.1.78.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
@@ -45,6 +45,12 @@
<version>${minecraft.version}-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>minecraft-server</artifactId>
<version>${minecraft.version}-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId>
@@ -60,7 +66,7 @@
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
<version>8.0.28</version>
<scope>runtime</scope>
</dependency>
<dependency>
@@ -69,7 +75,6 @@
<version>3.0.3</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.minecrell</groupId>
<artifactId>terminalconsoleappender</artifactId>
@@ -91,7 +96,7 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.1</version>
<version>2.17.2</version>
<scope>compile</scope>
</dependency>
@@ -99,20 +104,20 @@
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.1</version>
<version>2.17.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-iostreams</artifactId>
<version>2.8.1</version>
<version>2.17.2</version>
</dependency>
<!-- Paper - Async loggers -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
<version>3.4.4</version>
<scope>runtime</scope>
</dependency>
@@ -129,12 +134,12 @@
<version>1.3</version>
<scope>test</scope>
</dependency>
<!-- Akarin -->
<dependency>
<groupId>io.akarin</groupId>
<artifactId>legacylauncher</artifactId>
<version>1.25</version>
<version>1.26</version>
</dependency>
<dependency>
<groupId>org.spongepowered</groupId>
@@ -149,18 +154,14 @@
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.6.3-SNAPSHOT</version>
<version>2.9.3</version>
</dependency>
</dependencies>
<repositories>
<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>
<id>elmakers-repo</id>
<url>http://maven.elmakers.com/repository/</url>
</repository>
<repository>
<id>spongepowered-repo</id>
@@ -170,12 +171,36 @@
<id>nallar-repo</id>
<url>http://repo.nallar.me/</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>josephworks</id>
<url>http://repo.josephworks.net/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype-nexusg</id>
<url>https://oss.sonatype.org/content/repositories</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spigotmc-public</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
<id>paper</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</pluginRepository>
</pluginRepositories>
@@ -216,6 +241,7 @@
<Specification-Title>Bukkit</Specification-Title>
<Specification-Version>${api.version}</Specification-Version>
<Specification-Vendor>Bukkit Team</Specification-Vendor>
<Multi-Release>true</Multi-Release> <!-- Paper start - update log4j -->
</manifestEntries>
<manifestSections>
<manifestSection>
@@ -243,7 +269,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
@@ -252,6 +278,25 @@
</goals>
<configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
<filters>
<filter>
<artifact>org.spigotmc:minecraft-server:**</artifact>
<excludes>
<exclude>io/netty/**</exclude>
<exclude>org/apache/logging/log4j/**</exclude>
</excludes>
</filter>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<relocations>
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
<!--<relocation>-->
@@ -283,27 +328,16 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/java.sql.Driver</resource>
</transformer>
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" />
<transformer implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
</transformers>
<!-- Akarin - Avoid signature failure -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.github.edwgiz</groupId>
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId>
<version>2.8.1</version>
<groupId>io.github.edwgiz</groupId>
<artifactId>log4j-maven-shade-plugin-extensions</artifactId>
<version>2.17.2</version>
</dependency>
</dependencies>
</plugin>
@@ -311,6 +345,12 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<excludes>
<exclude>org/spigotmc/CaseInsensitiveHashingStrategy.java</exclude>
<exclude>org/spigotmc/CaseInsensitiveMap.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

@@ -0,0 +1,128 @@
package co.aikar.timings;
import com.google.common.collect.MapMaker;
import net.minecraft.server.*;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.craftbukkit.scheduler.CraftTask;
import java.util.Map;
public final class MinecraftTimings {
public static final Timing playerListTimer = Timings.ofSafe("Player List");
public static final Timing commandFunctionsTimer = Timings.ofSafe("Command Functions");
public static final Timing connectionTimer = Timings.ofSafe("Connection Handler");
public static final Timing tickablesTimer = Timings.ofSafe("Tickables");
public static final Timing minecraftSchedulerTimer = Timings.ofSafe("Minecraft Scheduler");
public static final Timing bukkitSchedulerTimer = Timings.ofSafe("Bukkit Scheduler");
public static final Timing bukkitSchedulerPendingTimer = Timings.ofSafe("Bukkit Scheduler - Pending");
public static final Timing bukkitSchedulerFinishTimer = Timings.ofSafe("Bukkit Scheduler - Finishing");
public static final Timing chunkIOTickTimer = Timings.ofSafe("ChunkIOTick");
public static final Timing timeUpdateTimer = Timings.ofSafe("Time Update");
public static final Timing serverCommandTimer = Timings.ofSafe("Server Command");
public static final Timing savePlayers = Timings.ofSafe("Save Players");
public static final Timing tickEntityTimer = Timings.ofSafe("## tickEntity");
public static final Timing tickTileEntityTimer = Timings.ofSafe("## tickTileEntity");
public static final Timing packetProcessTimer = Timings.ofSafe("## Packet Processing");
public static final Timing scheduledBlocksTimer = Timings.ofSafe("## Scheduled Blocks");
public static final Timing structureGenerationTimer = Timings.ofSafe("Structure Generation");
public static final Timing processQueueTimer = Timings.ofSafe("processQueue");
public static final Timing playerCommandTimer = Timings.ofSafe("playerCommand");
public static final Timing entityActivationCheckTimer = Timings.ofSafe("entityActivationCheck");
public static final Timing antiXrayUpdateTimer = Timings.ofSafe("anti-xray - update");
public static final Timing antiXrayObfuscateTimer = Timings.ofSafe("anti-xray - obfuscate");
private static final Map<Class<? extends Runnable>, String> taskNameCache = new MapMaker().weakKeys().makeMap();
private MinecraftTimings() {}
/**
* Gets a timer associated with a plugins tasks.
* @param bukkitTask
* @param period
* @return
*/
public static Timing getPluginTaskTimings(BukkitTask bukkitTask, long period) {
if (!bukkitTask.isSync()) {
return NullTimingHandler.NULL;
}
Plugin plugin;
Runnable task = ((CraftTask) bukkitTask).task;
final Class<? extends Runnable> taskClass = task.getClass();
if (bukkitTask.getOwner() != null) {
plugin = bukkitTask.getOwner();
} else {
plugin = TimingsManager.getPluginByClassloader(taskClass);
}
final String taskname = taskNameCache.computeIfAbsent(taskClass, clazz ->
clazz.isAnonymousClass() || clazz.isLocalClass()
? clazz.getName()
: clazz.getCanonicalName());
StringBuilder name = new StringBuilder(64);
name.append("Task: ").append(taskname);
if (period > 0) {
name.append(" (interval:").append(period).append(")");
} else {
name.append(" (Single)");
}
if (plugin == null) {
return Timings.ofSafe(null, name.toString());
}
return Timings.ofSafe(plugin, name.toString());
}
/**
* Get a named timer for the specified entity type to track type specific timings.
* @param entity
* @return
*/
public static Timing getEntityTimings(Entity entity) {
String entityType = entity.getClass().getName();
return Timings.ofSafe("Minecraft", "## tickEntity - " + entityType, tickEntityTimer);
}
/**
* Get a named timer for the specified tile entity type to track type specific timings.
* @param entity
* @return
*/
public static Timing getTileEntityTimings(TileEntity entity) {
String entityType = entity.getClass().getName();
return Timings.ofSafe("Minecraft", "## tickTileEntity - " + entityType, tickTileEntityTimer);
}
public static Timing getCancelTasksTimer() {
return Timings.ofSafe("Cancel Tasks");
}
public static Timing getCancelTasksTimer(Plugin plugin) {
return Timings.ofSafe(plugin, "Cancel Tasks");
}
public static void stopServer() {
TimingsManager.stopServer();
}
public static Timing getBlockTiming(Block block) {
return Timings.ofSafe("## Scheduled Block: " + block.getName(), scheduledBlocksTimer);
}
public static Timing getStructureTiming(StructureGenerator structureGenerator) {
return Timings.ofSafe("Structure Generator - " + structureGenerator.getName(), structureGenerationTimer);
}
public static Timing getPacketTiming(Packet packet) {
return Timings.ofSafe("## Packet - " + packet.getClass().getSimpleName(), packetProcessTimer);
}
}

View File

@@ -0,0 +1,131 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014-2016 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import net.minecraft.server.BiomeBase.BiomeMeta;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumCreatureType;
import net.minecraft.server.World;
import net.minecraft.server.WorldServer;
import org.bukkit.Location;
import org.bukkit.craftbukkit.generator.InternalChunkGenerator;
import org.bukkit.generator.BlockPopulator;
import javax.annotation.Nullable;
import java.util.List;
import java.util.Random;
public class TimedChunkGenerator extends InternalChunkGenerator {
private final WorldServer world;
private final InternalChunkGenerator timedGenerator;
public TimedChunkGenerator(WorldServer worldServer, InternalChunkGenerator gen) {
world = worldServer;
timedGenerator = gen;
}
@Override
@Deprecated
public byte[] generate(org.bukkit.World world, Random random, int x, int z) {
return timedGenerator.generate(world, random, x, z);
}
@Override
@Deprecated
public short[][] generateExtBlockSections(org.bukkit.World world, Random random, int x, int z,
BiomeGrid biomes) {
return timedGenerator.generateExtBlockSections(world, random, x, z, biomes);
}
@Override
@Deprecated
public byte[][] generateBlockSections(org.bukkit.World world, Random random, int x, int z,
BiomeGrid biomes) {
return timedGenerator.generateBlockSections(world, random, x, z, biomes);
}
@Override
public ChunkData generateChunkData(org.bukkit.World world, Random random, int x, int z, BiomeGrid biome) {
return timedGenerator.generateChunkData(world, random, x, z, biome);
}
@Override
public boolean canSpawn(org.bukkit.World world, int x, int z) {
return timedGenerator.canSpawn(world, x, z);
}
@Override
public List<BlockPopulator> getDefaultPopulators(org.bukkit.World world) {
return timedGenerator.getDefaultPopulators(world);
}
@Override
public Location getFixedSpawnLocation(org.bukkit.World world, Random random) {
return timedGenerator.getFixedSpawnLocation(world, random);
}
@Override
public Chunk getOrCreateChunk(int i, int j) {
try (Timing ignored = world.timings.chunkGeneration.startTiming()) {
return timedGenerator.getOrCreateChunk(i, j);
}
}
@Override
public void recreateStructures(int i, int j) {
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
timedGenerator.recreateStructures(i, j);
}
}
@Override
public boolean a(Chunk chunk, int i, int j) {
return timedGenerator.a(chunk, i, j);
}
@Override
public List<BiomeMeta> getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
return timedGenerator.getMobsFor(enumcreaturetype, blockposition);
}
@Override
@Nullable
public BlockPosition findNearestMapFeature(World world, String s, BlockPosition blockposition, boolean flag) {
return timedGenerator.findNearestMapFeature(world, s, blockposition, flag);
}
@Override
public void recreateStructures(Chunk chunk, int i, int j) {
try (Timing ignored = world.timings.syncChunkLoadStructuresTimer.startTiming()) {
timedGenerator.recreateStructures(chunk, i, j);
}
}
@Override
public boolean a(World world, String s, BlockPosition blockPosition) {
return timedGenerator.a(world, s, blockPosition);
}
}

View File

@@ -24,21 +24,26 @@
package co.aikar.timings;
import co.aikar.util.LoadingIntMap;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableThread;
import io.akarin.server.core.AkarinGlobalConfig;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import net.minecraft.server.MinecraftServer;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
/**
* Akarin Changes Note
* 1) Add volatile to fields (safety issue)
*/
import org.bukkit.Bukkit;
class TimingHandler implements Timing {
String name;
private static AtomicInteger idPool = new AtomicInteger(1);
static Deque<TimingHandler> TIMING_STACK = new ArrayDeque<>();
final int id = idPool.getAndIncrement();
private static int idPool = 1;
final int id = idPool++;
final String name;
final TimingIdentifier identifier;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
@@ -46,22 +51,15 @@ class TimingHandler implements Timing {
final TimingData record;
private final TimingHandler groupHandler;
private volatile long start = 0; // Akarin - volatile
private volatile int timingDepth = 0; // Akarin - volatile
private long start = 0;
private int timingDepth = 0;
private boolean added;
private boolean timed;
private boolean enabled;
private TimingHandler parent;
TimingHandler(TimingIdentifier id) {
if (id.name.startsWith("##")) {
verbose = true;
this.name = id.name.substring(3);
} else {
this.name = id.name;
verbose = false;
}
this.identifier = id;
this.verbose = id.name.startsWith("##");
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
@@ -86,59 +84,61 @@ class TimingHandler implements Timing {
}
}
@Override
public Timing startTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
startTiming();
}
return this;
startTiming();
return (Timing) this;
}
@Override
public void stopTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
stopTiming();
if (Akari.isPrimaryThread(false)) {
stopTiming(true); // Avoid twice thread check
}
}
@Override
public Timing startTiming() {
if (enabled && ++timingDepth == 1) {
if (enabled && Bukkit.isPrimaryThread() && ++timingDepth == 1) {
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
TIMING_STACK.addLast(this);
}
return this;
}
@Override
public void stopTiming() {
if (enabled && --timingDepth == 0 && start != 0) {
if (!Bukkit.isPrimaryThread()) {
stopTiming(false);
}
public void stopTiming(long start) {
if (enabled) addDiff(System.nanoTime() - start);
}
public void stopTiming(boolean alreadySync) {
if (!enabled) return;
if (!alreadySync) {
Thread curThread = Thread.currentThread();
if (curThread.getClass() == AssignableThread.class) return;
if (curThread != MinecraftServer.getServer().primaryThread) {
if (AkarinGlobalConfig.silentAsyncTimings) return;
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
new Throwable().printStackTrace();
start = 0;
return;
Thread.dumpStack();
}
}
// Main thread ensured
if (--timingDepth == 0 && start != 0) {
addDiff(System.nanoTime() - start);
start = 0;
}
}
@Override
public void abort() {
if (enabled && timingDepth > 0) {
start = 0;
}
}
public final void abort() {
}
void addDiff(long diff) {
if (TimingsManager.CURRENT == this) {
TimingsManager.CURRENT = parent;
if (parent != null) {
parent.children.get(id).add(diff);
}
if (this != null) {
this.children.get(id).add(diff);
}
record.add(diff);
if (!added) {
added = true;
@@ -146,15 +146,29 @@ class TimingHandler implements Timing {
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff);
groupHandler.addDiff(diff, this);
groupHandler.children.get(id).add(diff);
}
}
void addDiff(long diff, TimingHandler parent) {
if (parent != null) {
parent.children.get(id).add(diff);
}
record.add(diff);
if (!added) {
added = true;
timed = true;
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff, parent);
groupHandler.children.get(id).add(diff);
}
}
/**
* Reset this timer, setting all values to zero.
*
* @param full
*/
void reset(boolean full) {
record.reset();
@@ -184,8 +198,7 @@ class TimingHandler implements Timing {
}
/**
* This is simply for the Closeable interface so it can be used with
* try-with-resources ()
* This is simply for the Closeable interface so it can be used with try-with-resources ()
*/
@Override
public void close() {

View File

@@ -0,0 +1,103 @@
package co.aikar.timings;
import net.minecraft.server.World;
/**
* Set of timers per world, to track world specific timings.
*/
public class WorldTimingsHandler {
public final Timing mobSpawn;
public final Timing doChunkUnload;
public final Timing doPortalForcer;
public final Timing scheduledBlocks;
public final Timing scheduledBlocksCleanup;
public final Timing scheduledBlocksTicking;
public final Timing chunkTicks;
public final Timing lightChunk;
public final Timing chunkTicksBlocks;
public final Timing doVillages;
public final Timing doChunkMap;
public final Timing doChunkMapUpdate;
public final Timing doChunkMapToUpdate;
public final Timing doChunkMapSortMissing;
public final Timing doChunkMapSortSendToPlayers;
public final Timing doChunkMapPlayersNeedingChunks;
public final Timing doChunkMapPendingSendToPlayers;
public final Timing doChunkMapUnloadChunks;
public final Timing doChunkGC;
public final Timing doSounds;
public final Timing entityRemoval;
public final Timing entityTick;
public final Timing tileEntityTick;
public final Timing tileEntityPending;
public final Timing tracker1;
public final Timing tracker2;
public final Timing doTick;
public final Timing tickEntities;
public final Timing syncChunkLoadTimer;
public final Timing syncChunkLoadDataTimer;
public final Timing syncChunkLoadStructuresTimer;
public final Timing syncChunkLoadPostTimer;
public final Timing syncChunkLoadNBTTimer;
public final Timing syncChunkLoadPopulateNeighbors;
public final Timing chunkGeneration;
public final Timing chunkIOStage1;
public final Timing chunkIOStage2;
public final Timing worldSave;
public final Timing worldSaveChunks;
public final Timing worldSaveLevel;
public final Timing chunkSaveData;
public final Timing lightingQueueTimer;
public WorldTimingsHandler(World server) {
String name = server.worldData.getName() +" - ";
mobSpawn = Timings.ofSafe(name + "mobSpawn");
doChunkUnload = Timings.ofSafe(name + "doChunkUnload");
scheduledBlocks = Timings.ofSafe(name + "Scheduled Blocks");
scheduledBlocksCleanup = Timings.ofSafe(name + "Scheduled Blocks - Cleanup");
scheduledBlocksTicking = Timings.ofSafe(name + "Scheduled Blocks - Ticking");
chunkTicks = Timings.ofSafe(name + "Chunk Ticks");
lightChunk = Timings.ofSafe(name + "Light Chunk");
chunkTicksBlocks = Timings.ofSafe(name + "Chunk Ticks - Blocks");
doVillages = Timings.ofSafe(name + "doVillages");
doChunkMap = Timings.ofSafe(name + "doChunkMap");
doChunkMapUpdate = Timings.ofSafe(name + "doChunkMap - Update");
doChunkMapToUpdate = Timings.ofSafe(name + "doChunkMap - To Update");
doChunkMapSortMissing = Timings.ofSafe(name + "doChunkMap - Sort Missing");
doChunkMapSortSendToPlayers = Timings.ofSafe(name + "doChunkMap - Sort Send To Players");
doChunkMapPlayersNeedingChunks = Timings.ofSafe(name + "doChunkMap - Players Needing Chunks");
doChunkMapPendingSendToPlayers = Timings.ofSafe(name + "doChunkMap - Pending Send To Players");
doChunkMapUnloadChunks = Timings.ofSafe(name + "doChunkMap - Unload Chunks");
doSounds = Timings.ofSafe(name + "doSounds");
doChunkGC = Timings.ofSafe(name + "doChunkGC");
doPortalForcer = Timings.ofSafe(name + "doPortalForcer");
entityTick = Timings.ofSafe(name + "entityTick");
entityRemoval = Timings.ofSafe(name + "entityRemoval");
tileEntityTick = Timings.ofSafe(name + "tileEntityTick");
tileEntityPending = Timings.ofSafe(name + "tileEntityPending");
syncChunkLoadTimer = Timings.ofSafe(name + "syncChunkLoad");
syncChunkLoadDataTimer = Timings.ofSafe(name + "syncChunkLoad - Data");
syncChunkLoadStructuresTimer = Timings.ofSafe(name + "chunkLoad - recreateStructures");
syncChunkLoadPostTimer = Timings.ofSafe(name + "chunkLoad - Post");
syncChunkLoadNBTTimer = Timings.ofSafe(name + "chunkLoad - NBT");
syncChunkLoadPopulateNeighbors = Timings.ofSafe(name + "chunkLoad - Populate Neighbors");
chunkGeneration = Timings.ofSafe(name + "chunkGeneration");
chunkIOStage1 = Timings.ofSafe(name + "ChunkIO Stage 1 - DiskIO");
chunkIOStage2 = Timings.ofSafe(name + "ChunkIO Stage 2 - Post Load");
worldSave = Timings.ofSafe(name + "World Save");
worldSaveLevel = Timings.ofSafe(name + "World Save - Level");
worldSaveChunks = Timings.ofSafe(name + "World Save - Chunks");
chunkSaveData = Timings.ofSafe(name + "Chunk Save - Data");
tracker1 = Timings.ofSafe(name + "tracker stage 1");
tracker2 = Timings.ofSafe(name + "tracker stage 2");
doTick = Timings.ofSafe(name + "doTick");
tickEntities = Timings.ofSafe(name + "tickEntities");
lightingQueueTimer = Timings.ofSafe(name + "Lighting Queue");
}
}

View File

@@ -4,9 +4,12 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.Queues;
@@ -14,8 +17,15 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.Akari.TimingSignal;
import io.akarin.api.internal.utils.ReentrantSpinningLock;
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
import net.minecraft.server.WorldServer;
@SuppressWarnings("restriction")
public abstract class Akari {
@@ -44,19 +54,59 @@ public abstract class Akari {
}
public static class AssignableFactory implements ThreadFactory {
private final String threadName;
private int threadNumber;
public AssignableFactory(String name) {
threadName = name;
}
@Override
public Thread newThread(Runnable run) {
Thread thread = new AssignableThread(run);
thread.setName("Akarin Parallel Schedule Thread");
thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
return thread;
}
}
/**
* A common tick pool
*/
public static final ExecutorCompletionService<?> STAGE_TICK = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor(new AssignableFactory()));
public static class TimingSignal {
public final World tickedWorld;
public final boolean isEntities;
public TimingSignal(World world, boolean entities) {
tickedWorld = world;
isEntities = entities;
}
}
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
static {
resizeTickExecutors(3);
}
public static void resizeTickExecutors(int worlds) {
int parallelism;
switch (AkarinGlobalConfig.parallelMode) {
case -1:
return;
case 0:
parallelism = 2;
break;
case 1:
parallelism = worlds + 1;
break;
case 2:
default:
parallelism = worlds * 2;
break;
}
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
}
public static boolean isPrimaryThread() {
return isPrimaryThread(true);
@@ -64,7 +114,7 @@ public abstract class Akari {
public static boolean isPrimaryThread(boolean assign) {
Thread current = Thread.currentThread();
return current == MinecraftServer.getServer().primaryThread || (assign ? current instanceof AssignableThread : false);
return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
}
public static final String EMPTY_STRING = "";
@@ -96,9 +146,7 @@ public abstract class Akari {
*/
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
public final static Timing entityCallbackTiming = getTiming("Akarin - Entity Callback");
public final static Timing callbackTiming = getTiming("Akarin - Callback");
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
private static Timing getTiming(String name) {
try {

View File

@@ -1,28 +0,0 @@
package io.akarin.api.internal.mixin;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumSkyBlock;
public interface IMixinChunk {
AtomicInteger getPendingLightUpdates();
long getLightUpdateTime();
boolean areNeighborsLoaded();
@Nullable Chunk getNeighborChunk(int index);
CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type);
List<Chunk> getNeighbors();
void setNeighborChunk(int index, @Nullable Chunk chunk);
void setLightUpdateTime(long time);
}

View File

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

View File

@@ -1,16 +1,8 @@
package io.akarin.api.internal.mixin;
import java.util.List;
import java.util.concurrent.ExecutorService;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumSkyBlock;
import java.util.Random;
public interface IMixinWorldServer {
boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk chunk);
boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors);
ExecutorService getLightingExecutor();
public Object lock();
public Random rand();
}

View File

@@ -0,0 +1,102 @@
package io.akarin.api.internal.utils;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class ReentrantSpinningLock {
/*
* Impl Note:
* A write lock can reentrant as a read lock, while a
* read lock is not allowed to reentrant as a write lock.
* READ LOCK IS UNTESTED, USE WITH CATION.
*/
private final AtomicBoolean writeLocked = new AtomicBoolean(false);
// --------- Thread local restricted fields ---------
private long heldThreadId = 0;
private int reentrantLocks = 0;
/**
* Lock as a typical reentrant write lock
*/
public void lock() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
reentrantLocks++;
} else {
while (!writeLocked.compareAndSet(false, true)) ; // In case acquire one lock concurrently
heldThreadId = currentThreadId;
}
}
public void unlock() {
if (reentrantLocks == 0) {
heldThreadId = 0;
//if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract
writeLocked.set(false);
//}
} else {
--reentrantLocks;
}
}
private final AtomicInteger readerThreads = new AtomicInteger(0);
/**
* Lock as a typical reentrant read lock
*/
@Deprecated
public void lockWeak() {
long currentThreadId = Thread.currentThread().getId();
if (heldThreadId == currentThreadId) {
reentrantLocks++;
} else {
if (readerThreads.get() == 0) {
while (!writeLocked.compareAndSet(false, true)) ; // Block future write lock
}
heldThreadId = currentThreadId;
readerThreads.getAndIncrement(); // Micro-optimization: this saves one plus
}
}
@Deprecated
public void unlockWeak() {
if (reentrantLocks == 0) {
heldThreadId = 0;
writeLocked.set(false);
} else {
--reentrantLocks;
}
}
// --------- Wrappers to allow typical usages ---------
private SpinningWriteLock wrappedWriteLock = new SpinningWriteLock();
private SpinningReadLock wrappedReadLock = new SpinningReadLock();
public class SpinningWriteLock {
public void lock() {
lock();
}
public void unlock() {
unlock();
}
}
@Deprecated
public class SpinningReadLock {
public void lock() {
lockWeak();
}
public void unlock() {
unlockWeak();
}
}
public SpinningWriteLock writeLock() {
return wrappedWriteLock;
}
public SpinningReadLock readLock() {
return wrappedReadLock;
}
}

View File

@@ -0,0 +1,7 @@
package io.akarin.api.internal.utils.thread;
import java.util.concurrent.ExecutionException;
public class OpenExecutionException extends ExecutionException {
private static final long serialVersionUID = 7830266012832686185L;
}

View File

@@ -0,0 +1,142 @@
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
package io.akarin.api.internal.utils.thread;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
public class SuspendableExecutorCompletionService<V> implements CompletionService<V> {
private final SuspendableThreadPoolExecutor executor;
private final BlockingQueue<Future<V>> completionQueue;
public void suspend() {
executor.suspend();
}
public void resume() {
executor.resume();
}
/**
* FutureTask extension to enqueue upon completion
*/
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
private RunnableFuture<V> newTaskFor(Callable<V> task) {
return new FutureTask<V>(task);
}
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
return new FutureTask<V>(task, result);
}
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and a
* {@link LinkedBlockingQueue} as a completion queue.
*
* @param executor the executor to use
* @throws NullPointerException if executor is {@code null}
*/
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
/**
* Creates an ExecutorCompletionService using the supplied
* executor for base task execution and the supplied queue as its
* completion queue.
*
* @param executor the executor to use
* @param completionQueue the queue to use as the completion queue
* normally one dedicated for use by this service. This
* queue is treated as unbounded -- failed attempted
* {@code Queue.add} operations for completed tasks cause
* them not to be retrievable.
* @throws NullPointerException if executor or completionQueue are {@code null}
*/
public SuspendableExecutorCompletionService(SuspendableThreadPoolExecutor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.completionQueue = completionQueue;
}
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
public Future<V> poll() {
return completionQueue.poll();
}
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
}

View File

@@ -165,11 +165,6 @@ public class AkarinGlobalConfig {
silentAsyncTimings = getBoolean("core.always-silent-async-timing", false);
}
public static boolean legacyWorldTimings;
private static void legacyWorldTimings() {
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
}
public static long timeUpdateInterval;
private static void timeUpdateInterval() {
timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
@@ -185,16 +180,6 @@ public class AkarinGlobalConfig {
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
}
public static int asyncLightingThreads;
private static void asyncLightingThreads() {
asyncLightingThreads = getInt("core.async-lighting.executor-threads", 4);
}
public static boolean asyncLightingWorkStealing;
private static void asyncLightingWorkStealing() {
asyncLightingWorkStealing = getBoolean("core.async-lighting.use-work-stealing", false);
}
public static boolean throwOnAsyncCaught;
private static void throwOnAsyncCaught() {
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
@@ -211,52 +196,25 @@ public class AkarinGlobalConfig {
}
public static String messageKick;
private static void messageKick() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
}
public static String messageBan;
private static void messageBan() {
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
}
public static String messageBanReason;
private static void messageBanReason() {
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
}
public static String messageBanExpires;
private static void messageBanExpires() {
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
}
public static String messageBanIp;
private static void messageBanIp() {
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
}
public static String messageDupLogin;
private static void messageDupLogin() {
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
}
public static String messageJoin;
private static void messageJoin() {
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
}
public static String messageJoinRenamed;
private static void messageJoinRenamed() {
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
}
public static String messageKickKeepAlive;
private static void messagekickKeepAlive() {
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
}
public static String messagePlayerQuit;
private static void messagePlayerQuit() {
private static void messagekickKeepAlive() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
}
@@ -294,4 +252,14 @@ public class AkarinGlobalConfig {
private static void forceHardcoreDifficulty() {
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
}
public static int fileIOThreads;
private static void fileIOThreads() {
fileIOThreads = getInt("core.chunk-save-threads", 2);
}
public static int parallelMode;
private static void parallelMode() {
parallelMode = getInt("core.parallel-mode", 1);
}
}

View File

@@ -40,6 +40,7 @@ public class AkarinSlackScheduler extends Thread {
MinecraftServer server = MinecraftServer.getServer();
while (server.isRunning()) {
long startProcessTiming = System.currentTimeMillis();
// Send time updates to everyone, it will get the right time from the world the player is in.
// Time update, from MinecraftServer#D
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
@@ -97,10 +98,10 @@ public class AkarinSlackScheduler extends Thread {
}
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!");
ex.printStackTrace();
long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming);
if (sleepFixed > 0) Thread.sleep(sleepFixed);
} catch (InterruptedException interrupted) {
continue;
}
}
}

View File

@@ -6,7 +6,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.spigotmc.RestartCommand;
import org.spigotmc.WatchdogThread;
import org.spongepowered.asm.mixin.Final;
@@ -23,6 +22,10 @@ import net.minecraft.server.MinecraftServer;
public abstract class Watchcat extends Thread {
@Shadow private static WatchdogThread instance;
@Shadow private @Final long timeoutTime;
@Shadow private @Final long earlyWarningEvery; // Paper - Timeout time for just printing a dump but not restarting
@Shadow private @Final long earlyWarningDelay; // Paper
@Shadow public static volatile boolean hasStarted; // Paper
@Shadow private long lastEarlyWarning; // Paper - Keep track of short dump times to avoid spamming console with short dumps
@Shadow private @Final boolean restart;
@Shadow private volatile long lastTick;
@Shadow private volatile boolean stopping;
@@ -38,48 +41,73 @@ public abstract class Watchcat extends Thread {
@Overwrite
public void run() {
while (!stopping) {
//
if (lastTick != 0 && System.currentTimeMillis() > lastTick + timeoutTime && !Boolean.getBoolean("disable.watchdog")) { // Paper - Add property to disable
Logger log = Bukkit.getServer().getLogger();
log.log(Level.SEVERE, "Server has stopped responding!");
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues");
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion());
//
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
}
// Paper start - Warn in watchdog if an excessive velocity was ever set
if (CraftServer.excessiveVelEx != null) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
log.log(Level.SEVERE, CraftServer.excessiveVelEx.getMessage());
for (StackTraceElement stack : CraftServer.excessiveVelEx.getStackTrace()) {
log.log(Level.SEVERE, "\t\t" + stack);
}
}
// Paper start
long currentTime = System.currentTimeMillis();
if ( lastTick != 0 && currentTime > lastTick + earlyWarningEvery && !Boolean.getBoolean("disable.watchdog") )
{
boolean isLongTimeout = currentTime > lastTick + timeoutTime;
// Don't spam early warning dumps
if (!isLongTimeout && (earlyWarningEvery <= 0 || !hasStarted || currentTime < lastEarlyWarning + earlyWarningEvery || currentTime < lastTick + earlyWarningDelay))
continue;
lastEarlyWarning = currentTime;
// Paper end
Logger log = Bukkit.getServer().getLogger();
// Paper start - Different message when it's a short timeout
if (isLongTimeout) {
log.log(Level.SEVERE, "The server has stopped responding!");
log.log(Level.SEVERE, "Please report this to https://github.com/Akarin-project/Akarin/issues"); // Akarin
log.log(Level.SEVERE, "Be sure to include ALL relevant console errors and Minecraft crash reports");
log.log(Level.SEVERE, "Akarin version: " + Bukkit.getServer().getVersion()); // Akarin
//
if (net.minecraft.server.World.haveWeSilencedAPhysicsCrash) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a physics stackoverflow was supressed");
log.log(Level.SEVERE, "near " + net.minecraft.server.World.blockLocation);
}
// Paper start - Warn in watchdog if an excessive velocity was ever set
if (org.bukkit.craftbukkit.CraftServer.excessiveVelEx != null) {
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "During the run of the server, a plugin set an excessive velocity on an entity");
log.log(Level.SEVERE, "This may be the cause of the issue, or it may be entirely unrelated");
log.log(Level.SEVERE, org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getMessage());
for (StackTraceElement stack : org.bukkit.craftbukkit.CraftServer.excessiveVelEx.getStackTrace()) {
log.log(Level.SEVERE, "\t\t" + stack);
}
}
// Paper end
} else {
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
log.log(Level.SEVERE, "The server has not responded for " + (currentTime - lastTick) / 1000 + " seconds! Creating thread dump");
}
// Paper end - Different message for short timeout
log.log(Level.SEVERE, "------------------------------");
log.log(Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Akarin!):");
dumpThread(ManagementFactory.getThreadMXBean().getThreadInfo(MinecraftServer.getServer().primaryThread.getId(), Integer.MAX_VALUE), log);
log.log(Level.SEVERE, "------------------------------");
//
log.log(Level.SEVERE, "Entire Thread Dump:");
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for (ThreadInfo thread : threads) {
dumpThread(thread, log);
// Paper start - Only print full dump on long timeouts
if (isLongTimeout) {
log.log(Level.SEVERE, "Entire Thread Dump:");
ThreadInfo[] threads = ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
for (ThreadInfo thread : threads) {
dumpThread(thread, log);
}
} else {
// log.log(Level.SEVERE, "--- DO NOT REPORT THIS TO PAPER - THIS IS NOT A BUG OR A CRASH ---"); // Akarin
}
log.log(Level.SEVERE, "------------------------------");
if (restart) RestartCommand.restart(); // GC Inlined
break;
log.log(Level.SEVERE, "------------------------------");
if ( isLongTimeout )
{
if (restart) {
RestartCommand.restart();
}
break;
} // Paper end
}
try {
sleep(9000); // Akarin
sleep(1000); // Paper - Reduce check time to every second instead of every ten seconds, more consistent and allows for short timeout
} catch (InterruptedException ex) {
interrupt();
}

View File

@@ -0,0 +1,56 @@
package io.akarin.server.mixin.core;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.destroystokyo.paper.PaperConfig;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.FileIOThread;
import net.minecraft.server.IAsyncChunkSaver;
@Mixin(value = FileIOThread.class, remap = false)
public abstract class MixinFileIOThread {
private final Executor executor = Executors.newFixedThreadPool(AkarinGlobalConfig.fileIOThreads, new ThreadFactoryBuilder().setNameFormat("Akarin File IO Thread - %1$d").setPriority(1).build());
private final AtomicInteger queuedChunkCounter = new AtomicInteger(0);
@Shadow(aliases = "e") private volatile boolean isAwaitFinish;
@Overwrite // OBFHELPER: saveChunk
public void a(IAsyncChunkSaver iasyncchunksaver) {
queuedChunkCounter.incrementAndGet();
executor.execute(() -> writeChunk(iasyncchunksaver));
}
/**
* Process a chunk, re-add to the queue if unsuccessful
*/
private void writeChunk(IAsyncChunkSaver iasyncchunksaver) {
if (!iasyncchunksaver.a()) { // PAIL: WriteNextIO() -> Returns if the write was unsuccessful
queuedChunkCounter.decrementAndGet();
if (PaperConfig.enableFileIOThreadSleep) {
try {
Thread.sleep(isAwaitFinish ? 0L : 2L);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
} else {
writeChunk(iasyncchunksaver);
}
}
@Overwrite // OBFHELPER: waitForFinish
public void b() throws InterruptedException {
isAwaitFinish = true;
while (queuedChunkCounter.get() != 0) Thread.sleep(9L);
isAwaitFinish = false;
}
}

View File

@@ -1,23 +0,0 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.Block;
import net.minecraft.server.Blocks;
import net.minecraft.server.ItemMonsterEgg;
@Mixin(value = ItemMonsterEgg.class, remap = false)
public abstract class MixinItemMonsterEgg {
@Redirect(method = "a*", at = @At(
value = "FIELD",
target = "net/minecraft/server/Blocks.MOB_SPAWNER:Lnet/minecraft/server/Block;",
opcode = Opcodes.GETSTATIC
))
private boolean configurable(Block target) {
return target == Blocks.MOB_SPAWNER && AkarinGlobalConfig.allowSpawnerModify;
}
}

View File

@@ -2,10 +2,13 @@ package io.akarin.server.mixin.core;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import org.apache.commons.lang.WordUtils;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
@@ -23,7 +26,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import co.aikar.timings.MinecraftTimings;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.mixin.IMixinLockProvider;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.AkarinSlackScheduler;
import net.minecraft.server.BlockPosition;
@@ -42,6 +45,8 @@ import net.minecraft.server.WorldServer;
@Mixin(value = MinecraftServer.class, remap = false)
public abstract class MixinMinecraftServer {
@Shadow @Final public Thread primaryThread;
private boolean tickedPrimaryEntities;
private int cachedWorldSize;
@Overwrite
public String getServerModName() {
@@ -56,10 +61,11 @@ public abstract class MixinMinecraftServer {
private void prerun(CallbackInfo info) {
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
//TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
}
AkarinSlackScheduler.get().boot();
}
@@ -109,7 +115,7 @@ public abstract class MixinMinecraftServer {
@Overwrite
protected void l() throws InterruptedException {
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory()));
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
for (int index = 0; index < worlds.size(); index++) {
WorldServer world = this.worlds.get(index);
@@ -121,7 +127,11 @@ public abstract class MixinMinecraftServer {
for (WorldServer world : this.worlds) {
if (world.getWorld().getKeepSpawnInMemory()) executor.take();
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
}
if (WorldLoadEvent.getHandlerList().getRegisteredListeners().length != 0) {
for (WorldServer world : this.worlds) {
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
}
}
enablePluginsPostWorld();
@@ -144,7 +154,11 @@ public abstract class MixinMinecraftServer {
private boolean tickEntities(WorldServer world) {
try {
world.timings.tickEntities.startTiming();
world.tickEntities();
world.timings.tickEntities.stopTiming();
world.getTracker().updatePlayers();
world.explosionDensityCache.clear(); // Paper - Optimize explosions
} catch (Throwable throwable) {
CrashReport crashreport;
try {
@@ -160,7 +174,9 @@ public abstract class MixinMinecraftServer {
private void tickWorld(WorldServer world) {
try {
world.timings.doTick.startTiming();
world.doTick();
world.timings.doTick.stopTiming();
} catch (Throwable throwable) {
CrashReport crashreport;
try {
@@ -174,8 +190,12 @@ public abstract class MixinMinecraftServer {
}
@Overwrite
public void D() throws InterruptedException {
public void D() throws InterruptedException, ExecutionException, CancellationException {
Runnable runnable;
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
MinecraftTimings.bukkitSchedulerTimer.startTiming();
this.server.getScheduler().mainThreadHeartbeat(this.ticks);
MinecraftTimings.bukkitSchedulerTimer.stopTiming();
@@ -196,54 +216,92 @@ public abstract class MixinMinecraftServer {
ChunkIOExecutor.tick();
MinecraftTimings.chunkIOTickTimer.stopTiming();
Akari.worldTiming.startTiming();
if (AkarinGlobalConfig.legacyWorldTimings) {
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
world.timings.tickEntities.startTiming();
world.timings.doTick.startTiming();
}
}
Akari.STAGE_TICK.submit(() -> {
// Never tick one world concurrently!
for (int i = 1; i <= worlds.size(); ++i) {
WorldServer world = worlds.get(i < worlds.size() ? i : 0);
synchronized (((IMixinLockProvider) world).lock()) {
if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
switch (AkarinGlobalConfig.parallelMode) {
case 1:
case 2:
default:
// Never tick one world concurrently!
for (int i = 0; i < cachedWorldSize; i++) {
// Impl Note:
// Entities ticking: index 1 -> ... -> 0 (parallel)
// World ticking: index 0 -> ... (parallel)
int interlace = i + 1;
WorldServer entityWorld = worlds.get(interlace < cachedWorldSize ? interlace : 0);
Akari.STAGE_TICK.submit(() -> {
synchronized (((IMixinWorldServer) entityWorld).lock()) {
tickEntities(entityWorld);
}
}, null/*new TimingSignal(entityWorld, true)*/);
if (AkarinGlobalConfig.parallelMode != 1) {
int fi = i;
Akari.STAGE_TICK.submit(() -> {
WorldServer world = worlds.get(fi);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
}
}, null);
}
}
if (AkarinGlobalConfig.parallelMode == 1)
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; i++) {
WorldServer world = worlds.get(i);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
}
}
}, null);
for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize + 1 : cachedWorldSize * 2); i --> 0 ;) {
Akari.STAGE_TICK.take();
}
/* for (int i = (AkarinGlobalConfig.parallelMode == 1 ? cachedWorldSize : cachedWorldSize * 2); i --> 0 ;) {
long startTiming = System.nanoTime();
TimingSignal signal = Akari.STAGE_TICK.take().get();
IMixinTimingHandler timing = (IMixinTimingHandler) (signal.isEntities ? signal.tickedWorld.timings.tickEntities : signal.tickedWorld.timings.doTick);
timing.stopTiming(startTiming); // The overlap will be ignored
} */
break;
case 0:
Akari.STAGE_TICK.submit(() -> {
for (int i = 1; i <= cachedWorldSize; ++i) {
WorldServer world = worlds.get(i < cachedWorldSize ? i : 0);
synchronized (((IMixinWorldServer) world).lock()) {
tickEntities(world);
}
}
}, null);
Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world);
}
}
}, null);
Akari.STAGE_TICK.take();
Akari.STAGE_TICK.take();
break;
case -1:
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i);
tickWorld(world);
tickEntities(world);
}
}
}, null);
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
synchronized (((IMixinLockProvider) world).lock()) {
tickWorld(world);
world.explosionDensityCache.clear(); // Paper - Optimize explosions
}
}
Akari.entityCallbackTiming.startTiming();
Akari.STAGE_TICK.take();
Akari.entityCallbackTiming.stopTiming();
Akari.worldTiming.stopTiming();
if (AkarinGlobalConfig.legacyWorldTimings) {
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
world.timings.tickEntities.stopTiming();
world.timings.doTick.stopTiming();
}
break;
}
Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming();
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
tickUnsafeSync(world);
}
MinecraftTimings.connectionTimer.startTiming();
serverConnection().c();
MinecraftTimings.connectionTimer.stopTiming();
@@ -262,12 +320,4 @@ public abstract class MixinMinecraftServer {
}
MinecraftTimings.tickablesTimer.stopTiming();
}
public void tickUnsafeSync(WorldServer world) {
world.timings.doChunkMap.startTiming();
world.manager.flush();
world.timings.doChunkMap.stopTiming();
world.getTracker().updatePlayers();
}
}

View File

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

View File

@@ -1,21 +1,58 @@
package io.akarin.server.mixin.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.VersionCommand;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.google.common.base.Charsets;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MCUtil;
@Mixin(value = VersionCommand.class, remap = false)
public abstract class MixinVersionCommand {
@Shadow private static int getFromRepo(String repo, String hash) { return 0; }
@Overwrite
private static int getFromRepo(String repo, String hash) {
try {
HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/ver/1.12.2..." + hash).openConnection();
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))
) {
JSONObject obj = (JSONObject) new JSONParser().parse(reader);
String status = (String) obj.get("status");
switch (status) {
case "identical":
return 0;
case "behind":
return ((Number) obj.get("behind_by")).intValue();
default:
return -1;
}
} catch (ParseException | NumberFormatException e) {
e.printStackTrace();
return -1;
}
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}
/**
* Match current version with repository and calculate the distance

View File

@@ -16,9 +16,9 @@ public abstract class MixinWorldManager {
@Overwrite
public void a(Entity entity) {
this.world.getTracker().entriesLock.lock(); // Akarin
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
this.world.getTracker().track(entity);
this.world.getTracker().entriesLock.unlock(); // Akarin
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity);

View File

@@ -1,24 +1,37 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import java.util.Random;
import io.akarin.api.internal.mixin.IMixinLockProvider;
import org.apache.logging.log4j.LogManager;
import org.spongepowered.asm.mixin.Mixin;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.WorldServer;
@Mixin(value = WorldServer.class, remap = false)
public abstract class MixinWorldServer implements IMixinLockProvider {
@Redirect(method = "doTick()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/PlayerChunkMap.flush()V"
))
public void onFlush() {} // Migrated to main thread
public abstract class MixinWorldServer implements IMixinWorldServer {
private final Object tickLock = new Object();
@Override
public Object lock() {
return tickLock;
}
private final Random sharedRandom = new io.akarin.api.internal.utils.random.LightRandom() {
private static final long serialVersionUID = 1L;
private boolean locked = false;
@Override
public synchronized void setSeed(long seed) {
if (locked) {
LogManager.getLogger().error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
} else {
super.setSeed(seed);
locked = true;
}
}
};
@Override
public Random rand() {
return sharedRandom;
}
}

View File

@@ -1,130 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.cps;
import java.util.List;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.common.collect.Lists;
import io.akarin.api.internal.mixin.IMixinChunk;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.MCUtil;
import net.minecraft.server.World;
@Mixin(value = Chunk.class, remap = false)
public abstract class MixinChunk implements IMixinChunk {
private Chunk[] neighborChunks = new Chunk[4];
private static final EnumDirection[] CARDINAL_DIRECTIONS = new EnumDirection[] {EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.EAST, EnumDirection.WEST};
@Shadow @Final public World world;
@Shadow @Final public int locX;
@Shadow @Final public int locZ;
@Override
public Chunk getNeighborChunk(int index) {
return this.neighborChunks[index];
}
@Override
public void setNeighborChunk(int index, @Nullable Chunk chunk) {
this.neighborChunks[index] = chunk;
}
@Override
public List<Chunk> getNeighbors() {
List<Chunk> neighborList = Lists.newArrayList();
for (Chunk neighbor : this.neighborChunks) {
if (neighbor != null) {
neighborList.add(neighbor);
}
}
return neighborList;
}
@Override
public boolean areNeighborsLoaded() {
for (int i = 0; i < 4; i++) {
if (this.neighborChunks[i] == null) {
return false;
}
}
return true;
}
private static int directionToIndex(EnumDirection direction) {
switch (direction) {
case NORTH:
return 0;
case SOUTH:
return 1;
case EAST:
return 2;
case WEST:
return 3;
default:
throw new IllegalArgumentException("Unexpected direction");
}
}
@Inject(method = "addEntities", at = @At("RETURN"))
public void onLoadReturn(CallbackInfo ci) {
BlockPosition origin = new BlockPosition(locX, 0, locZ);
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
BlockPosition shift = origin.shift(direction);
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
if (neighbor != null) {
int neighborIndex = directionToIndex(direction);
int oppositeNeighborIndex = directionToIndex(direction.opposite());
this.setNeighborChunk(neighborIndex, neighbor);
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, (Chunk) (Object) this);
}
}
}
@Inject(method = "removeEntities", at = @At("RETURN"))
public void onUnload(CallbackInfo ci) {
BlockPosition origin = new BlockPosition(locX, 0, locZ);
for (EnumDirection direction : CARDINAL_DIRECTIONS) {
BlockPosition shift = origin.shift(direction);
Chunk neighbor = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), shift.getX(), shift.getZ());
if (neighbor != null) {
int neighborIndex = directionToIndex(direction);
int oppositeNeighborIndex = directionToIndex(direction.opposite());
this.setNeighborChunk(neighborIndex, null);
((IMixinChunk) neighbor).setNeighborChunk(oppositeNeighborIndex, null);
}
}
}
}

View File

@@ -40,6 +40,7 @@ public abstract class MixinChunkProviderServer {
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
int remainingChunks = chunks.size();
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
@@ -47,23 +48,19 @@ public abstract class MixinChunkProviderServer {
while (it.hasNext()) {
Entry<Chunk> entry = it.next();
Chunk chunk = entry.getValue();
if (chunk == null) continue;
if (chunk.isUnloading()) {
if (chunk != null && chunk.isUnloading()) {
if (chunk.scheduledForUnload != null) {
if (now - chunk.scheduledForUnload > unloadAfter) {
chunk.scheduledForUnload = null;
} else continue;
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
}
if (!unloadChunk(chunk, true)) { // Event cancelled
// If a plugin cancelled it, we shouldn't trying unload it for a while
chunk.setShouldUnload(false);
continue;
if (unloadChunk(chunk, true)) {
it.remove();
}
chunk.setShouldUnload(false);
chunk.scheduledForUnload = null;
it.remove();
if (--remainingChunks <= targetSize || activityAccountant.activityTimeIsExhausted()) break; // more slack since the target size not work as intended
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
}
}
activityAccountant.endActivity();

View File

@@ -1,671 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.lighting;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Blocks;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkSection;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.EnumSkyBlock;
import net.minecraft.server.IBlockData;
import net.minecraft.server.MCUtil;
import net.minecraft.server.TileEntity;
import net.minecraft.server.World;
import net.minecraft.server.BlockPosition.MutableBlockPosition;
@Mixin(value = Chunk.class, remap = false, priority = 1001)
public abstract class MixinChunk implements IMixinChunk {
// Keeps track of block positions in this chunk currently queued for sky light update
private CopyOnWriteArrayList<Short> queuedSkyLightingUpdates = new CopyOnWriteArrayList<>();
// Keeps track of block positions in this chunk currently queued for block light update
private CopyOnWriteArrayList<Short> queuedBlockLightingUpdates = new CopyOnWriteArrayList<>();
private AtomicInteger pendingLightUpdates = new AtomicInteger();
private long lightUpdateTime;
private static ExecutorService lightExecutorService;
@Shadow(aliases = "m") private boolean isGapLightingUpdated;
@Shadow(aliases = "r") private boolean ticked;
@Shadow @Final private ChunkSection[] sections;
@Shadow @Final public int locX;
@Shadow @Final public int locZ;
@Shadow @Final public World world;
@Shadow @Final public int[] heightMap;
/** Which columns need their skylightMaps updated. */
@Shadow(aliases = "i") @Final private boolean[] updateSkylightColumns;
/** Queue containing the BlockPosition of tile entities queued for creation */
@Shadow(aliases = "y") @Final private ConcurrentLinkedQueue<BlockPosition> tileEntityPosQueue;
/** Boolean value indicating if the terrain is populated. */
@Shadow(aliases = "done") private boolean isTerrainPopulated;
@Shadow(aliases = "lit") private boolean isLightPopulated;
/** Lowest value in the heightmap. */
@Shadow(aliases = "v") private int heightMapMinimum;
@Shadow(aliases = "b") public abstract int getHeightValue(int x, int z);
@Shadow(aliases = "g") @Nullable public abstract TileEntity createNewTileEntity(BlockPosition pos);
@Shadow(aliases = "a") @Nullable public abstract TileEntity getTileEntity(BlockPosition pos, Chunk.EnumTileEntityState state);
@Shadow @Final public abstract IBlockData getBlockData(BlockPosition pos);
@Shadow @Final public abstract IBlockData getBlockData(int x, int y, int z);
@Shadow public abstract boolean isUnloading();
/** Checks the height of a block next to a sky-visible block and schedules a lighting update as necessary */
@Shadow(aliases = "b") public abstract void checkSkylightNeighborHeight(int x, int z, int maxValue);
@Shadow(aliases = "a") public abstract void updateSkylightNeighborHeight(int x, int z, int startY, int endY);
@Shadow(aliases = "z") public abstract void setSkylightUpdated();
@Shadow(aliases = "g") public abstract int getTopFilledSegment();
@Shadow public abstract void markDirty();
@Inject(method = "<init>", at = @At("RETURN"))
public void onConstruct(World worldIn, int x, int z, CallbackInfo ci) {
lightExecutorService = ((IMixinWorldServer) worldIn).getLightingExecutor();
}
@Override
public AtomicInteger getPendingLightUpdates() {
return this.pendingLightUpdates;
}
@Override
public long getLightUpdateTime() {
return this.lightUpdateTime;
}
@Override
public void setLightUpdateTime(long time) {
this.lightUpdateTime = time;
}
@Inject(method = "b(Z)V", at = @At("HEAD"), cancellable = true)
private void onTickHead(boolean skipRecheckGaps, CallbackInfo ci) {
final List<Chunk> neighbors = this.getSurroundingChunks();
if (this.isGapLightingUpdated && this.world.worldProvider.m() && !skipRecheckGaps && !neighbors.isEmpty()) { // OBFHELPER: hasSkyLight
lightExecutorService.execute(() -> {
this.recheckGapsAsync(neighbors);
});
this.isGapLightingUpdated = false;
}
this.ticked = true;
if ((true || !this.isLightPopulated) && this.isTerrainPopulated && !neighbors.isEmpty()) {
lightExecutorService.execute(() -> {
this.checkLightAsync(neighbors);
});
// set to true to avoid requeuing the same task when not finished
this.isLightPopulated = true;
}
while (!this.tileEntityPosQueue.isEmpty()) {
BlockPosition blockpos = this.tileEntityPosQueue.poll();
if (this.getTileEntity(blockpos, Chunk.EnumTileEntityState.CHECK) == null && this.getBlockData(blockpos).getBlock().isTileEntity()) { // OBFHELPER: getTileEntity
TileEntity tileentity = this.createNewTileEntity(blockpos);
this.world.setTileEntity(blockpos, tileentity);
this.world.b(blockpos, blockpos); // OBFHELPER: markBlockRangeForRenderUpdate
}
}
ci.cancel();
}
@Redirect(method = "b(III)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getHighestBlockYAt(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/BlockPosition;"))
private BlockPosition onCheckSkylightGetHeight(World world, BlockPosition pos) {
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
if (chunk == null) {
return BlockPosition.ZERO;
}
return new BlockPosition(pos.getX(), chunk.b(pos.getX() & 15, pos.getZ() & 15), pos.getZ()); // OBFHELPER: getHeightValue
}
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.areChunksLoaded(Lnet/minecraft/server/BlockPosition;I)Z"))
private boolean onAreaLoadedSkyLightNeighbor(World world, BlockPosition pos, int radius) {
return this.isAreaLoaded();
}
@Redirect(method = "a(IIII)V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.c(Lnet/minecraft/server/EnumSkyBlock;Lnet/minecraft/server/BlockPosition;)Z"))
private boolean onCheckLightForSkylightNeighbor(World world, EnumSkyBlock enumSkyBlock, BlockPosition pos) {
return this.checkWorldLightFor(enumSkyBlock, pos);
}
@Inject(method = "h(Z)V", at = @At("HEAD"), cancellable = true)
private void onRecheckGaps(CallbackInfo ci) {
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return;
}
if (this.isUnloading()) {
return;
}
final List<Chunk> neighborChunks = this.getSurroundingChunks();
if (neighborChunks.isEmpty()) {
this.isGapLightingUpdated = true;
return;
}
if (Akari.isPrimaryThread()) {
try {
lightExecutorService.execute(() -> {
this.recheckGapsAsync(neighborChunks);
});
} catch (RejectedExecutionException ex) {
// This could happen if ServerHangWatchdog kills the server
// between the start of the method and the execute() call.
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
throw ex;
}
}
} else {
this.recheckGapsAsync(neighborChunks);
}
ci.cancel();
}
/**
* Rechecks chunk gaps async.
*
* @param neighbors A thread-safe list of surrounding neighbor chunks
*/
private void recheckGapsAsync(List<Chunk> neighbors) {
this.isLightPopulated = false;
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (this.updateSkylightColumns[i + j * 16]) {
this.updateSkylightColumns[i + j * 16] = false;
int k = this.getHeightValue(i, j);
int l = this.locX * 16 + i;
int i1 = this.locZ * 16 + j;
int j1 = Integer.MAX_VALUE;
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
final Chunk chunk = this.getLightChunk((l + enumfacing.getAdjacentX()) >> 4, (i1 + enumfacing.getAdjacentZ()) >> 4, neighbors);
if (chunk == null || chunk.isUnloading()) {
continue;
}
j1 = Math.min(j1, chunk.w()); // OBFHELPER: getLowestHeight
}
this.checkSkylightNeighborHeight(l, i1, j1);
for (EnumDirection enumfacing1 : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
this.checkSkylightNeighborHeight(l + enumfacing1.getAdjacentX(), i1 + enumfacing1.getAdjacentZ(), k);
}
}
}
this.isGapLightingUpdated = false;
}
}
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.getType(Lnet/minecraft/server/BlockPosition;)Lnet/minecraft/server/IBlockData;"))
private IBlockData onRelightChecksGetBlockData(World world, BlockPosition pos) {
Chunk chunk = MCUtil.getLoadedChunkWithoutMarkingActive(world.getChunkProvider(), pos.getX() >> 4, pos.getZ() >> 4);
final IMixinChunk spongeChunk = (IMixinChunk) chunk;
if (chunk == null || chunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
return Blocks.AIR.getBlockData();
}
return chunk.getBlockData(pos);
}
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
private boolean onRelightChecksCheckLight(World world, BlockPosition pos) {
return this.checkWorldLight(pos);
}
// Avoids grabbing chunk async during light check
@Redirect(method = "e(II)Z", at = @At(value = "INVOKE", target = "net/minecraft/server/World.w(Lnet/minecraft/server/BlockPosition;)Z"))
private boolean onCheckLightWorld(World world, BlockPosition pos) {
return this.checkWorldLight(pos);
}
@Inject(method = "o()V", at = @At("HEAD"), cancellable = true)
private void checkLightHead(CallbackInfo ci) {
if (this.world.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return;
}
if (this.isUnloading()) {
return;
}
final List<Chunk> neighborChunks = this.getSurroundingChunks();
if (neighborChunks.isEmpty()) {
this.isLightPopulated = false;
return;
}
if (Akari.isPrimaryThread()) {
try {
lightExecutorService.execute(() -> {
this.checkLightAsync(neighborChunks);
});
} catch (RejectedExecutionException ex) {
// This could happen if ServerHangWatchdog kills the server
// between the start of the method and the execute() call.
if (!this.world.getMinecraftServer().isStopped() && !lightExecutorService.isShutdown()) {
throw ex;
}
}
} else {
this.checkLightAsync(neighborChunks);
}
ci.cancel();
}
/**
* Checks light async.
*
* @param neighbors A thread-safe list of surrounding neighbor chunks
*/
private void checkLightAsync(List<Chunk> neighbors) {
this.isTerrainPopulated = true;
this.isLightPopulated = true;
BlockPosition blockpos = new BlockPosition(this.locX << 4, 0, this.locZ << 4);
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
CHECK_LIGHT:
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (!this.checkLightAsync(i, j, neighbors)) {
this.isLightPopulated = false;
break CHECK_LIGHT;
}
}
}
if (this.isLightPopulated) {
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
int k = enumfacing.c() == EnumDirection.EnumAxisDirection.POSITIVE ? 16 : 1; // OBFHELPER: getAxisDirection
final BlockPosition pos = blockpos.shift(enumfacing, k);
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
if (chunk == null) {
continue;
}
chunk.a(enumfacing.opposite()); // OBFHELPER: checkLightSide
}
this.setSkylightUpdated();
}
}
}
/**
* Checks light async.
*
* @param x The x position of chunk
* @param z The z position of chunk
* @param neighbors A thread-safe list of surrounding neighbor chunks
* @return True if light update was successful, false if not
*/
private boolean checkLightAsync(int x, int z, List<Chunk> neighbors) {
int i = this.getTopFilledSegment();
boolean flag = false;
boolean flag1 = false;
MutableBlockPosition blockpos$mutableblockpos = new MutableBlockPosition((this.locX << 4) + x, 0, (this.locZ << 4) + z);
for (int j = i + 16 - 1; j > this.world.getSeaLevel() || j > 0 && !flag1; --j) {
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), j, blockpos$mutableblockpos.getZ());
int k = this.getBlockData(blockpos$mutableblockpos).c(); // OBFHELPER: getLightOpacity
if (k == 255 && blockpos$mutableblockpos.getY() < this.world.getSeaLevel()) {
flag1 = true;
}
if (!flag && k > 0) {
flag = true;
} else if (flag && k == 0 && !this.checkWorldLight(blockpos$mutableblockpos, neighbors)) {
return false;
}
}
for (int l = blockpos$mutableblockpos.getY(); l > 0; --l) {
blockpos$mutableblockpos.setValues(blockpos$mutableblockpos.getX(), l, blockpos$mutableblockpos.getZ());
if (this.getBlockData(blockpos$mutableblockpos).d() > 0) { // getLightValue
this.checkWorldLight(blockpos$mutableblockpos, neighbors);
}
}
return true;
}
/**
* Thread-safe method to retrieve a chunk during async light updates.
*
* @param chunkX The x position of chunk.
* @param chunkZ The z position of chunk.
* @param neighbors A thread-safe list of surrounding neighbor chunks
* @return The chunk if available, null if not
*/
private Chunk getLightChunk(int chunkX, int chunkZ, List<Chunk> neighbors) {
final Chunk currentChunk = (Chunk) (Object) this;
if (currentChunk.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
if (currentChunk.isUnloading()) {
return null;
}
return currentChunk;
}
if (neighbors == null) {
neighbors = this.getSurroundingChunks();
if (neighbors.isEmpty()) {
return null;
}
}
for (Chunk neighbor : neighbors) {
if (neighbor.a(chunkX, chunkZ)) { // OBFHELPER: isAtLocation
if (neighbor.isUnloading()) {
return null;
}
return neighbor;
}
}
return null;
}
/**
* Checks if surrounding chunks are loaded thread-safe.
*
* @return True if surrounded chunks are loaded, false if not
*/
private boolean isAreaLoaded() {
if (!this.areNeighborsLoaded()) {
return false;
}
// add diagonal chunks
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
if (southEastChunk == null) {
return false;
}
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
if (southWestChunk == null) {
return false;
}
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
if (northEastChunk == null) {
return false;
}
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
if (northWestChunk == null) {
return false;
}
return true;
}
/**
* Gets surrounding chunks thread-safe.
*
* @return The list of surrounding chunks, empty list if not loaded
*/
private List<Chunk> getSurroundingChunks() {
if (!this.areNeighborsLoaded()) {
return Collections.emptyList();
}
// add diagonal chunks
final Chunk southEastChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(2);
if (southEastChunk == null) {
return Collections.emptyList();
}
final Chunk southWestChunk = ((IMixinChunk) this.getNeighborChunk(0)).getNeighborChunk(3);
if (southWestChunk == null) {
return Collections.emptyList();
}
final Chunk northEastChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(2);
if (northEastChunk == null) {
return Collections.emptyList();
}
final Chunk northWestChunk = ((IMixinChunk) this.getNeighborChunk(1)).getNeighborChunk(3);
if (northWestChunk == null) {
return Collections.emptyList();
}
List<Chunk> chunkList = this.getNeighbors();
chunkList.add(southEastChunk);
chunkList.add(southWestChunk);
chunkList.add(northEastChunk);
chunkList.add(northWestChunk);
return chunkList;
}
@Inject(method = "c(III)V", at = @At("HEAD"), cancellable = true)
private void onRelightBlock(int x, int y, int z, CallbackInfo ci) {
lightExecutorService.execute(() -> {
this.relightBlockAsync(x, y, z);
});
ci.cancel();
}
/**
* Relight's a block async.
*
* @param x The x position
* @param y The y position
* @param z The z position
*/
private void relightBlockAsync(int x, int y, int z) {
int i = this.heightMap[z << 4 | x] & 255;
int j = i;
if (y > i) {
j = y;
}
while (j > 0 && this.getBlockData(x, j - 1, z).c() == 0) { // OBFHELPER: getLightOpacity
--j;
}
if (j != i) {
this.markBlocksDirtyVerticalAsync(x + this.locX * 16, z + this.locZ * 16, j, i);
this.heightMap[z << 4 | x] = j;
int k = this.locX * 16 + x;
int l = this.locZ * 16 + z;
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
if (j < i) {
for (int j1 = j; j1 < i; ++j1) {
ChunkSection extendedblockstorage2 = this.sections[j1 >> 4];
if (extendedblockstorage2 != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage2.a(x, j1 & 15, z, 15); // OBFHELPER: setSkyLight
// this.world.m(new BlockPosition((this.locX << 4) + x, j1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
}
}
} else {
for (int i1 = i; i1 < j; ++i1) {
ChunkSection extendedblockstorage = this.sections[i1 >> 4];
if (extendedblockstorage != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage.a(x, i1 & 15, z, 0); // OBFHELPER: setSkyLight
// this.world.m(new BlockPosition((this.locX << 4) + x, i1, (this.locZ << 4) + z)); // OBFHELPER: notifyLightSet - client side
}
}
}
int k1 = 15;
while (j > 0 && k1 > 0) {
--j;
int i2 = this.getBlockData(x, j, z).c();
if (i2 == 0) {
i2 = 1;
}
k1 -= i2;
if (k1 < 0) {
k1 = 0;
}
ChunkSection extendedblockstorage1 = this.sections[j >> 4];
if (extendedblockstorage1 != Chunk.EMPTY_CHUNK_SECTION) {
extendedblockstorage1.a(x, j & 15, z, k1); // OBFHELPER: setSkyLight
}
}
}
int l1 = this.heightMap[z << 4 | x];
int j2 = i;
int k2 = l1;
if (l1 < i) {
j2 = l1;
k2 = i;
}
if (l1 < this.heightMapMinimum) {
this.heightMapMinimum = l1;
}
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
this.updateSkylightNeighborHeight(k + enumfacing.getAdjacentX(), l + enumfacing.getAdjacentZ(), j2, k2); // OBFHELPER: updateSkylightNeighborHeight
}
this.updateSkylightNeighborHeight(k, l, j2, k2);
}
this.markDirty();
}
}
/**
* Marks a vertical line of blocks as dirty async.
* Instead of calling world directly, we pass chunk safely for async light method.
*
* @param x1
* @param z1
* @param x2
* @param z2
*/
private void markBlocksDirtyVerticalAsync(int x1, int z1, int x2, int z2) {
if (x2 > z2) {
int i = z2;
z2 = x2;
x2 = i;
}
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
for (int j = x2; j <= z2; ++j) {
final BlockPosition pos = new BlockPosition(x1, j, z1);
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
if (chunk == null) {
continue;
}
((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, new BlockPosition(x1, j, z1), chunk);
}
}
this.world.b(x1, x2, z1, x1, z2, z1); // OBFHELPER: markBlockRangeForRenderUpdate
}
/**
* Checks world light thread-safe.
*
* @param lightType The type of light to check
* @param pos The block position
* @return True if light update was successful, false if not
*/
private boolean checkWorldLightFor(EnumSkyBlock lightType, BlockPosition pos) {
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, null);
if (chunk == null) {
return false;
}
return ((IMixinWorldServer) this.world).updateLightAsync(lightType, pos, chunk);
}
private boolean checkWorldLight(BlockPosition pos) {
return this.checkWorldLight(pos, null);
}
/**
* Checks world light async.
*
* @param pos The block position
* @param neighbors A thread-safe list of surrounding neighbor chunks
* @return True if light update was successful, false if not
*/
private boolean checkWorldLight(BlockPosition pos, List<Chunk> neighbors) {
boolean flag = false;
final Chunk chunk = this.getLightChunk(pos.getX() >> 4, pos.getZ() >> 4, neighbors);
if (chunk == null) {
return false;
}
if (this.world.worldProvider.m()) { // OBFHELPER: hasSkyLight
flag |= ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.SKY, pos, chunk);
}
flag = flag | ((IMixinWorldServer) this.world).updateLightAsync(EnumSkyBlock.BLOCK, pos, chunk);
return flag;
}
/**
* Gets the list of block positions currently queued for lighting updates.
*
* @param type The light type
* @return The list of queued block positions, empty if none
*/
@Override
public CopyOnWriteArrayList<Short> getQueuedLightingUpdates(EnumSkyBlock type) {
if (type == EnumSkyBlock.SKY) {
return this.queuedSkyLightingUpdates;
}
return this.queuedBlockLightingUpdates;
}
}

View File

@@ -1,51 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.lighting;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinChunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.WorldServer;
@Mixin(value = ChunkProviderServer.class, remap = false, priority = 1001)
public abstract class MixinChunkProviderServer {
@Shadow @Final public WorldServer world;
@Redirect(method = "unloadChunks", at = @At(
value = "INVOKE",
target = "Lnet/minecraft/server/Chunk;isUnloading()Z"
))
public boolean shouldUnload(IMixinChunk chunk) {
if (chunk.getPendingLightUpdates().get() > 0 || this.world.getTime() - chunk.getLightUpdateTime() < 20) {
return false;
}
return true;
}
}

View File

@@ -1,45 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.lighting;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumSkyBlock;
import net.minecraft.server.IChunkProvider;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
@Mixin(value = World.class, remap = false, priority = 1002)
public abstract class MixinWorld {
@Shadow protected IChunkProvider chunkProvider;
@Shadow int[] J; // OBFHELPER: lightUpdateBlockList
@Shadow(aliases = "c") public abstract boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos);
@Shadow public abstract MinecraftServer getMinecraftServer();
@Shadow public abstract boolean areChunksLoaded(BlockPosition center, int radius, boolean allowEmpty);
@Shadow public abstract Chunk getChunkIfLoaded(int x, int z);
}

View File

@@ -1,375 +0,0 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.lighting;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.mixin.IMixinChunk;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.Chunk;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.EnumSkyBlock;
import net.minecraft.server.IBlockData;
import net.minecraft.server.MCUtil;
import net.minecraft.server.MathHelper;
import net.minecraft.server.WorldServer;
import net.minecraft.server.BlockPosition.PooledBlockPosition;
@Mixin(value = WorldServer.class, remap = false, priority = 1002)
public abstract class MixinWorldServer extends MixinWorld implements IMixinWorldServer {
private static final int NUM_XZ_BITS = 4;
private static final int NUM_SHORT_Y_BITS = 8;
private static final short XZ_MASK = 0xF;
private static final short Y_SHORT_MASK = 0xFF;
private final static ExecutorService lightExecutorService = getExecutorService();
private static ExecutorService getExecutorService() {
return AkarinGlobalConfig.asyncLightingWorkStealing ?
Executors.newFixedThreadPool(AkarinGlobalConfig.asyncLightingThreads, new ThreadFactoryBuilder().setNameFormat("Akarin Async Light Thread").build())
:
Executors.newWorkStealingPool(AkarinGlobalConfig.asyncLightingThreads);
}
@Override
public boolean checkLightFor(EnumSkyBlock lightType, BlockPosition pos) { // OBFHELPER: checkLightFor
return updateLightAsync(lightType, pos, null);
}
@Override
public boolean checkLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
// Sponge - This check is not needed as neighbors are checked in updateLightAsync
if (false && !this.areChunksLoaded(pos, 17, false)) {
return false;
} else {
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
int recheckIndex = 0;
int blockIndex = 0;
int current = this.getLightForAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int rawLight = this.getRawBlockLightAsync(lightType, pos, currentChunk, neighbors); // Sponge - use thread safe method
int x = pos.getX();
int y = pos.getY();
int z = pos.getZ();
if (rawLight > current) {
this.J[blockIndex++] = 133152; // OBFHELPER: lightUpdateBlockList
} else if (rawLight < current) {
this.J[blockIndex++] = 133152 | current << 18; // OBFHELPER: lightUpdateBlockList
while (recheckIndex < blockIndex) {
int blockData = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
int i2 = (blockData & 63) - 32 + x;
int j2 = (blockData >> 6 & 63) - 32 + y;
int k2 = (blockData >> 12 & 63) - 32 + z;
int l2 = blockData >> 18 & 15;
BlockPosition blockpos = new BlockPosition(i2, j2, k2);
int lightLevel = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors); // Sponge - use thread safe method
if (lightLevel == l2) {
this.setLightForAsync(lightType, blockpos, 0, currentChunk, neighbors); // Sponge - use thread safe method
if (l2 > 0) {
int j3 = MathHelper.a(i2 - x); // abs
int k3 = MathHelper.a(j2 - y);
int l3 = MathHelper.a(k2 - z);
if (j3 + k3 + l3 < 17) {
PooledBlockPosition mutableBlockpos = PooledBlockPosition.aquire();
for (EnumDirection enumfacing : EnumDirection.values()) {
int i4 = i2 + enumfacing.getAdjacentX();
int j4 = j2 + enumfacing.getAdjacentX();
int k4 = k2 + enumfacing.getAdjacentX();
mutableBlockpos.setValues(i4, j4, k4);
// Sponge start - get chunk safely
final Chunk pooledChunk = this.getLightChunk(mutableBlockpos, currentChunk, neighbors);
if (pooledChunk == null) {
continue;
}
int opacity = Math.max(1, pooledChunk.getBlockData(mutableBlockpos).c()); // OBFHELPER: getLightOpacity
lightLevel = this.getLightForAsync(lightType, mutableBlockpos, currentChunk, neighbors);
// Sponge end
if (lightLevel == l2 - opacity && blockIndex < this.J.length) { // OBFHELPER: lightUpdateBlockList
this.J[blockIndex++] = i4 - x + 32 | j4 - y + 32 << 6 | k4 - z + 32 << 12 | l2 - opacity << 18; // OBFHELPER: lightUpdateBlockList
}
}
mutableBlockpos.free();
}
}
}
}
recheckIndex = 0;
}
while (recheckIndex < blockIndex) {
int i5 = this.J[recheckIndex++]; // OBFHELPER: lightUpdateBlockList
int j5 = (i5 & 63) - 32 + x;
int k5 = (i5 >> 6 & 63) - 32 + y;
int l5 = (i5 >> 12 & 63) - 32 + z;
BlockPosition blockpos1 = new BlockPosition(j5, k5, l5);
int i6 = this.getLightForAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
int j6 = this.getRawBlockLightAsync(lightType, blockpos1, currentChunk, neighbors); // Sponge - use thread safe method
if (j6 != i6) {
this.setLightForAsync(lightType, blockpos1, j6, currentChunk, neighbors); // Sponge - use thread safe method
if (j6 > i6) {
int k6 = Math.abs(j5 - x);
int l6 = Math.abs(k5 - y);
int i7 = Math.abs(l5 - z);
boolean flag = blockIndex < this.J.length - 6; // OBFHELPER: lightUpdateBlockList
if (k6 + l6 + i7 < 17 && flag) {
// Sponge start - use thread safe method getLightForAsync
if (this.getLightForAsync(lightType, blockpos1.west(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 - 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.east(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 + 1 - x + 32 + (k5 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.down(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 - x + 32 + (k5 - 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.up(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 - x + 32 + (k5 + 1 - y + 32 << 6) + (l5 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.north(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 - 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
if (this.getLightForAsync(lightType, blockpos1.south(), currentChunk, neighbors) < j6) {
this.J[blockIndex++] = j5 - x + 32 + (k5 - y + 32 << 6) + (l5 + 1 - z + 32 << 12); // OBFHELPER: lightUpdateBlockList
}
// Sponge end
}
}
}
}
// Sponge start - Asynchronous light updates
spongeChunk.getQueuedLightingUpdates(lightType).remove((Short) this.blockPosToShort(pos));
spongeChunk.getPendingLightUpdates().decrementAndGet();
for (Chunk neighborChunk : neighbors) {
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
neighbor.getPendingLightUpdates().decrementAndGet();
}
// Sponge end
return true;
}
}
@Override
public boolean updateLightAsync(EnumSkyBlock lightType, BlockPosition pos, @Nullable Chunk currentChunk) {
if (this.getMinecraftServer().isStopped() || lightExecutorService.isShutdown()) {
return false;
}
if (currentChunk == null) {
currentChunk = MCUtil.getLoadedChunkWithoutMarkingActive(chunkProvider, pos.getX() >> 4, pos.getZ() >> 4);
}
final IMixinChunk spongeChunk = (IMixinChunk) currentChunk;
if (currentChunk == null || currentChunk.isUnloading() || !spongeChunk.areNeighborsLoaded()) {
return false;
}
final short shortPos = this.blockPosToShort(pos);
if (spongeChunk.getQueuedLightingUpdates(lightType).contains(shortPos)) {
return false;
}
final Chunk chunk = currentChunk;
spongeChunk.getQueuedLightingUpdates(lightType).add(shortPos);
spongeChunk.getPendingLightUpdates().incrementAndGet();
spongeChunk.setLightUpdateTime(chunk.getWorld().getTime());
List<Chunk> neighbors = spongeChunk.getNeighbors();
// add diagonal chunks
Chunk southEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(2);
Chunk southWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(0)).getNeighborChunk(3);
Chunk northEastChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(2);
Chunk northWestChunk = ((IMixinChunk) spongeChunk.getNeighborChunk(1)).getNeighborChunk(3);
if (southEastChunk != null) {
neighbors.add(southEastChunk);
}
if (southWestChunk != null) {
neighbors.add(southWestChunk);
}
if (northEastChunk != null) {
neighbors.add(northEastChunk);
}
if (northWestChunk != null) {
neighbors.add(northWestChunk);
}
for (Chunk neighborChunk : neighbors) {
final IMixinChunk neighbor = (IMixinChunk) neighborChunk;
neighbor.getPendingLightUpdates().incrementAndGet();
neighbor.setLightUpdateTime(chunk.getWorld().getTime());
}
if (Akari.isPrimaryThread()) { // Akarin
lightExecutorService.execute(() -> {
this.checkLightAsync(lightType, pos, chunk, neighbors);
});
} else {
this.checkLightAsync(lightType, pos, chunk, neighbors);
}
return true;
}
@Override
public ExecutorService getLightingExecutor() {
return lightExecutorService;
}
// Thread safe methods to retrieve a chunk during async light updates
// Each method avoids calling getLoadedChunk and instead accesses the passed neighbor chunk list to avoid concurrency issues
public Chunk getLightChunk(BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
if (currentChunk.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
if (currentChunk.isUnloading()) {
return null;
}
return currentChunk;
}
for (Chunk neighbor : neighbors) {
if (neighbor.a(pos.getX() >> 4, pos.getZ() >> 4)) { // OBFHELPER: isAtLocation
if (neighbor.isUnloading()) {
return null;
}
return neighbor;
}
}
return null;
}
private int getLightForAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
if (pos.getY() < 0) {
pos = new BlockPosition(pos.getX(), 0, pos.getZ());
}
if (!pos.isValidLocation()) {
return lightType.c; // OBFHELPER: defaultLightValue
}
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
if (chunk == null || chunk.isUnloading()) {
return lightType.c; // OBFHELPER: defaultLightValue
}
return chunk.getBrightness(lightType, pos);
}
private int getRawBlockLightAsync(EnumSkyBlock lightType, BlockPosition pos, Chunk currentChunk, List<Chunk> neighbors) {
final Chunk chunk = getLightChunk(pos, currentChunk, neighbors);
if (chunk == null || chunk.isUnloading()) {
return lightType.c; // OBFHELPER: defaultLightValue
}
if (lightType == EnumSkyBlock.SKY && chunk.c(pos)) { // OBFHELPER: canSeeSky
return 15;
} else {
IBlockData blockData = chunk.getBlockData(pos);
int blockLight = blockData.d(); // getLightValue
int rawLight = lightType == EnumSkyBlock.SKY ? 0 : blockLight;
int opacity = blockData.c(); // OBFHELPER: getLightOpacity
if (opacity >= 15 && blockLight > 0) {
opacity = 1;
}
if (opacity < 1) {
opacity = 1;
}
if (opacity >= 15) {
return 0;
} else if (rawLight >= 14) {
return rawLight;
} else {
for (EnumDirection facing : EnumDirection.values()) {
BlockPosition blockpos = pos.shift(facing);
int current = this.getLightForAsync(lightType, blockpos, currentChunk, neighbors) - opacity;
if (current > rawLight) {
rawLight = current;
}
if (rawLight >= 14) {
return rawLight;
}
}
return rawLight;
}
}
}
public void setLightForAsync(EnumSkyBlock type, BlockPosition pos, int lightValue, Chunk currentChunk, List<Chunk> neighbors) {
if (pos.isValidLocation()) {
final Chunk chunk = this.getLightChunk(pos, currentChunk, neighbors);
if (chunk != null && !chunk.isUnloading()) {
chunk.a(type, pos, lightValue); // OBFHELPER: setLightFor
// this.notifyLightSet(pos); // client side
}
}
}
private short blockPosToShort(BlockPosition pos) {
short serialized = (short) setNibble(0, pos.getX() & XZ_MASK, 0, NUM_XZ_BITS);
serialized = (short) setNibble(serialized, pos.getY() & Y_SHORT_MASK, 1, NUM_SHORT_Y_BITS);
serialized = (short) setNibble(serialized, pos.getZ() & XZ_MASK, 3, NUM_XZ_BITS);
return serialized;
}
/**
* Modifies bits in an integer.
*
* @param num Integer to modify
* @param data Bits of data to add
* @param which Index of nibble to start at
* @param bitsToReplace The number of bits to replace starting from nibble index
* @return The modified integer
*/
private int setNibble(int num, int data, int which, int bitsToReplace) {
return (num & ~(bitsToReplace << (which * 4)) | (data << (which * 4)));
}
}

View File

@@ -147,6 +147,7 @@ public abstract class PandaRedstoneWire extends Block {
while (!turnOff.isEmpty()) {
BlockPosition pos = turnOff.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;
@@ -185,6 +186,7 @@ public abstract class PandaRedstoneWire extends Block {
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
while (!this.turnOn.isEmpty()) {
BlockPosition pos = this.turnOn.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;

View File

@@ -0,0 +1,479 @@
package net.minecraft.server;
import java.util.Iterator;
import javax.annotation.Nullable;
public class BlockChest extends BlockTileEntity {
public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING;
protected static final AxisAlignedBB b = new AxisAlignedBB(0.0625D, 0.0D, 0.0D, 0.9375D, 0.875D, 0.9375D);
protected static final AxisAlignedBB c = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 1.0D);
protected static final AxisAlignedBB d = new AxisAlignedBB(0.0D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D);
protected static final AxisAlignedBB e = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 1.0D, 0.875D, 0.9375D);
protected static final AxisAlignedBB f = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D);
public final BlockChest.Type g;
protected BlockChest(BlockChest.Type blockchest_type) {
super(Material.WOOD);
this.w(this.blockStateList.getBlockData().set(BlockChest.FACING, EnumDirection.NORTH));
this.g = blockchest_type;
this.a(blockchest_type == BlockChest.Type.TRAP ? CreativeModeTab.d : CreativeModeTab.c);
}
public boolean b(IBlockData iblockdata) {
return false;
}
public boolean c(IBlockData iblockdata) {
return false;
}
public EnumRenderType a(IBlockData iblockdata) {
return EnumRenderType.ENTITYBLOCK_ANIMATED;
}
public AxisAlignedBB b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) {
return iblockaccess.getType(blockposition.north()).getBlock() == this ? BlockChest.b : (iblockaccess.getType(blockposition.south()).getBlock() == this ? BlockChest.c : (iblockaccess.getType(blockposition.west()).getBlock() == this ? BlockChest.d : (iblockaccess.getType(blockposition.east()).getBlock() == this ? BlockChest.e : BlockChest.f)));
}
public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) {
this.e(world, blockposition, iblockdata);
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection enumdirection = (EnumDirection) iterator.next();
BlockPosition blockposition1 = blockposition.shift(enumdirection);
// NeonPaper start - Dont load chunks for chests
final IBlockData iblockdata1 = world.isLoaded(blockposition1) ? world.getType(blockposition1) : null;
if (iblockdata1 == null) {
continue;
}
// NeonPaper end
if (iblockdata1.getBlock() == this) {
this.e(world, blockposition1, iblockdata1);
}
}
}
public IBlockData getPlacedState(World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2, int i, EntityLiving entityliving) {
return this.getBlockData().set(BlockChest.FACING, entityliving.getDirection());
}
public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) {
EnumDirection enumdirection = EnumDirection.fromType2(MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3).opposite();
iblockdata = iblockdata.set(BlockChest.FACING, enumdirection);
BlockPosition blockposition1 = blockposition.north();
BlockPosition blockposition2 = blockposition.south();
BlockPosition blockposition3 = blockposition.west();
BlockPosition blockposition4 = blockposition.east();
boolean flag = this == world.getType(blockposition1).getBlock();
boolean flag1 = this == world.getType(blockposition2).getBlock();
boolean flag2 = this == world.getType(blockposition3).getBlock();
boolean flag3 = this == world.getType(blockposition4).getBlock();
if (!flag && !flag1 && !flag2 && !flag3) {
world.setTypeAndData(blockposition, iblockdata, 3);
} else if (enumdirection.k() == EnumDirection.EnumAxis.X && (flag || flag1)) {
if (flag) {
world.setTypeAndData(blockposition1, iblockdata, 3);
} else {
world.setTypeAndData(blockposition2, iblockdata, 3);
}
world.setTypeAndData(blockposition, iblockdata, 3);
} else if (enumdirection.k() == EnumDirection.EnumAxis.Z && (flag2 || flag3)) {
if (flag2) {
world.setTypeAndData(blockposition3, iblockdata, 3);
} else {
world.setTypeAndData(blockposition4, iblockdata, 3);
}
world.setTypeAndData(blockposition, iblockdata, 3);
}
if (itemstack.hasName()) {
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof TileEntityChest) {
((TileEntityChest) tileentity).setCustomName(itemstack.getName());
}
}
}
public IBlockData e(World world, BlockPosition blockposition, IBlockData iblockdata) {
if (world.isClientSide) {
return iblockdata;
} else {
IBlockData iblockdata1 = world.getType(blockposition.north());
IBlockData iblockdata2 = world.getType(blockposition.south());
IBlockData iblockdata3 = world.getType(blockposition.west());
IBlockData iblockdata4 = world.getType(blockposition.east());
EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING);
if (iblockdata1.getBlock() != this && iblockdata2.getBlock() != this) {
boolean flag = iblockdata1.b();
boolean flag1 = iblockdata2.b();
if (iblockdata3.getBlock() == this || iblockdata4.getBlock() == this) {
BlockPosition blockposition1 = iblockdata3.getBlock() == this ? blockposition.west() : blockposition.east();
IBlockData iblockdata5 = world.getType(blockposition1.north());
IBlockData iblockdata6 = world.getType(blockposition1.south());
enumdirection = EnumDirection.SOUTH;
EnumDirection enumdirection1;
if (iblockdata3.getBlock() == this) {
enumdirection1 = (EnumDirection) iblockdata3.get(BlockChest.FACING);
} else {
enumdirection1 = (EnumDirection) iblockdata4.get(BlockChest.FACING);
}
if (enumdirection1 == EnumDirection.NORTH) {
enumdirection = EnumDirection.NORTH;
}
if ((flag || iblockdata5.b()) && !flag1 && !iblockdata6.b()) {
enumdirection = EnumDirection.SOUTH;
}
if ((flag1 || iblockdata6.b()) && !flag && !iblockdata5.b()) {
enumdirection = EnumDirection.NORTH;
}
}
} else {
BlockPosition blockposition2 = iblockdata1.getBlock() == this ? blockposition.north() : blockposition.south();
IBlockData iblockdata7 = world.getType(blockposition2.west());
IBlockData iblockdata8 = world.getType(blockposition2.east());
enumdirection = EnumDirection.EAST;
EnumDirection enumdirection2;
if (iblockdata1.getBlock() == this) {
enumdirection2 = (EnumDirection) iblockdata1.get(BlockChest.FACING);
} else {
enumdirection2 = (EnumDirection) iblockdata2.get(BlockChest.FACING);
}
if (enumdirection2 == EnumDirection.WEST) {
enumdirection = EnumDirection.WEST;
}
if ((iblockdata3.b() || iblockdata7.b()) && !iblockdata4.b() && !iblockdata8.b()) {
enumdirection = EnumDirection.EAST;
}
if ((iblockdata4.b() || iblockdata8.b()) && !iblockdata3.b() && !iblockdata7.b()) {
enumdirection = EnumDirection.WEST;
}
}
iblockdata = iblockdata.set(BlockChest.FACING, enumdirection);
world.setTypeAndData(blockposition, iblockdata, 3);
return iblockdata;
}
}
public IBlockData f(World world, BlockPosition blockposition, IBlockData iblockdata) {
EnumDirection enumdirection = null;
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection enumdirection1 = (EnumDirection) iterator.next();
IBlockData iblockdata1 = world.getType(blockposition.shift(enumdirection1));
if (iblockdata1.getBlock() == this) {
return iblockdata;
}
if (iblockdata1.b()) {
if (enumdirection != null) {
enumdirection = null;
break;
}
enumdirection = enumdirection1;
}
}
if (enumdirection != null) {
return iblockdata.set(BlockChest.FACING, enumdirection.opposite());
} else {
EnumDirection enumdirection2 = (EnumDirection) iblockdata.get(BlockChest.FACING);
if (world.getType(blockposition.shift(enumdirection2)).b()) {
enumdirection2 = enumdirection2.opposite();
}
if (world.getType(blockposition.shift(enumdirection2)).b()) {
enumdirection2 = enumdirection2.e();
}
if (world.getType(blockposition.shift(enumdirection2)).b()) {
enumdirection2 = enumdirection2.opposite();
}
return iblockdata.set(BlockChest.FACING, enumdirection2);
}
}
public boolean canPlace(World world, BlockPosition blockposition) {
int i = 0;
BlockPosition blockposition1 = blockposition.west();
BlockPosition blockposition2 = blockposition.east();
BlockPosition blockposition3 = blockposition.north();
BlockPosition blockposition4 = blockposition.south();
if (world.getType(blockposition1).getBlock() == this) {
if (this.d(world, blockposition1)) {
return false;
}
++i;
}
if (world.getType(blockposition2).getBlock() == this) {
if (this.d(world, blockposition2)) {
return false;
}
++i;
}
if (world.getType(blockposition3).getBlock() == this) {
if (this.d(world, blockposition3)) {
return false;
}
++i;
}
if (world.getType(blockposition4).getBlock() == this) {
if (this.d(world, blockposition4)) {
return false;
}
++i;
}
return i <= 1;
}
private boolean d(World world, BlockPosition blockposition) {
if (world.getType(blockposition).getBlock() != this) {
return false;
} else {
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
EnumDirection enumdirection;
do {
if (!iterator.hasNext()) {
return false;
}
enumdirection = (EnumDirection) iterator.next();
} while (world.getType(blockposition.shift(enumdirection)).getBlock() != this);
return true;
}
}
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
super.a(iblockdata, world, blockposition, block, blockposition1);
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof TileEntityChest) {
tileentity.invalidateBlockCache();
}
}
public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) {
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof IInventory) {
InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity);
world.updateAdjacentComparators(blockposition, this);
}
super.remove(world, blockposition, iblockdata);
}
public boolean interact(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
if (world.isClientSide) {
return true;
} else {
ITileInventory itileinventory = this.getInventory(world, blockposition);
if (itileinventory != null) {
entityhuman.openContainer(itileinventory);
if (this.g == BlockChest.Type.BASIC) {
entityhuman.b(StatisticList.aa);
} else if (this.g == BlockChest.Type.TRAP) {
entityhuman.b(StatisticList.U);
}
}
return true;
}
}
@Nullable
public ITileInventory getInventory(World world, BlockPosition blockposition) {
return this.a(world, blockposition, false);
}
@Nullable
public ITileInventory a(World world, BlockPosition blockposition, boolean flag) {
TileEntity tileentity = world.getTileEntity(blockposition);
if (!(tileentity instanceof TileEntityChest)) {
return null;
} else {
Object object = (TileEntityChest) tileentity;
if (!flag && this.e(world, blockposition)) {
return null;
} else {
Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator();
while (iterator.hasNext()) {
EnumDirection enumdirection = (EnumDirection) iterator.next();
BlockPosition blockposition1 = blockposition.shift(enumdirection);
// Paper start - don't load chunks if the other side of the chest is in unloaded chunk
final IBlockData type = world.getTypeIfLoaded(blockposition1); // Paper
if (type == null) {
continue;
}
Block block = type.getBlock();
// Paper end
if (block == this) {
if (!flag && this.e(world, blockposition1)) { // Paper - check for allowBlocked flag - MC-99321
return null;
}
TileEntity tileentity1 = world.getTileEntity(blockposition1);
if (tileentity1 instanceof TileEntityChest) {
if (enumdirection != EnumDirection.WEST && enumdirection != EnumDirection.NORTH) {
object = new InventoryLargeChest("container.chestDouble", (ITileInventory) object, (TileEntityChest) tileentity1);
} else {
object = new InventoryLargeChest("container.chestDouble", (TileEntityChest) tileentity1, (ITileInventory) object);
}
}
}
}
return (ITileInventory) object;
}
}
}
public TileEntity a(World world, int i) {
return new TileEntityChest();
}
public boolean isPowerSource(IBlockData iblockdata) {
return this.g == BlockChest.Type.TRAP;
}
public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
if (!iblockdata.m()) {
return 0;
} else {
int i = 0;
TileEntity tileentity = iblockaccess.getTileEntity(blockposition);
if (tileentity instanceof TileEntityChest) {
i = ((TileEntityChest) tileentity).l;
}
return MathHelper.clamp(i, 0, 15);
}
}
public int c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) {
return enumdirection == EnumDirection.UP ? iblockdata.a(iblockaccess, blockposition, enumdirection) : 0;
}
private boolean e(World world, BlockPosition blockposition) {
return this.i(world, blockposition) || this.j(world, blockposition);
}
private boolean i(World world, BlockPosition blockposition) {
return world.getType(blockposition.up()).l();
}
private boolean j(World world, BlockPosition blockposition) {
// Paper start - Option ti dsiable chest cat detection
if (world.paperConfig.disableChestCatDetection) {
return false;
}
// Paper end
Iterator iterator = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1))).iterator();
EntityOcelot entityocelot;
do {
if (!iterator.hasNext()) {
return false;
}
Entity entity = (Entity) iterator.next();
entityocelot = (EntityOcelot) entity;
} while (!entityocelot.isSitting());
return true;
}
public boolean isComplexRedstone(IBlockData iblockdata) {
return true;
}
public int c(IBlockData iblockdata, World world, BlockPosition blockposition) {
return Container.b((IInventory) this.getInventory(world, blockposition));
}
public IBlockData fromLegacyData(int i) {
EnumDirection enumdirection = EnumDirection.fromType1(i);
if (enumdirection.k() == EnumDirection.EnumAxis.Y) {
enumdirection = EnumDirection.NORTH;
}
return this.getBlockData().set(BlockChest.FACING, enumdirection);
}
public int toLegacyData(IBlockData iblockdata) {
return ((EnumDirection) iblockdata.get(BlockChest.FACING)).a();
}
public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) {
return iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
}
public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) {
return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING)));
}
protected BlockStateList getStateList() {
return new BlockStateList(this, new IBlockState[] { BlockChest.FACING});
}
public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) {
return EnumBlockFaceShape.UNDEFINED;
}
public static enum Type {
BASIC, TRAP;
private Type() {}
}
}

View File

@@ -0,0 +1,118 @@
package net.minecraft.server;
import java.util.Random;
import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit
public class BlockStationary extends BlockFluids {
protected BlockStationary(Material material) {
super(material);
this.a(false);
if (material == Material.LAVA) {
this.a(true);
}
}
public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) {
if (!this.e(world, blockposition, iblockdata)) {
this.f(world, blockposition, iblockdata);
}
}
private void f(World world, BlockPosition blockposition, IBlockData iblockdata) {
BlockFlowing blockflowing = a(this.material);
world.setTypeAndData(blockposition, blockflowing.getBlockData().set(BlockStationary.LEVEL, iblockdata.get(BlockStationary.LEVEL)), 2);
world.a(blockposition, (Block) blockflowing, this.a(world));
}
public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) {
if (this.material == Material.LAVA) {
if (world.getGameRules().getBoolean("doFireTick")) {
int i = random.nextInt(3);
if (i > 0) {
BlockPosition blockposition1 = blockposition;
for (int j = 0; j < i; ++j) {
blockposition1 = blockposition1.a(random.nextInt(3) - 1, 1, random.nextInt(3) - 1);
if (blockposition1.getY() >= 0 && blockposition1.getY() < 256 && !world.isLoaded(blockposition1)) {
return;
}
Block block = world.getType(blockposition1).getBlock();
if (block.material == Material.AIR) {
if (this.c(world, blockposition1)) {
// CraftBukkit start - Prevent lava putting something on fire
if (world.getType(blockposition1) != Blocks.FIRE) {
if (CraftEventFactory.callBlockIgniteEvent(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) {
continue;
}
}
// CraftBukkit end
world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData());
return;
}
} else if (block.material.isSolid()) {
return;
}
}
} else {
for (int k = 0; k < 3; ++k) {
BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, 0, random.nextInt(3) - 1);
if (blockposition2.getY() >= 0 && blockposition2.getY() < 256 && !world.isLoaded(blockposition2)) {
return;
}
if (world.isEmpty(blockposition2.up()) && this.d(world, blockposition2)) {
// CraftBukkit start - Prevent lava putting something on fire
BlockPosition up = blockposition2.up();
if (world.getType(up) != Blocks.FIRE) {
if (CraftEventFactory.callBlockIgniteEvent(world, up.getX(), up.getY(), up.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) {
continue;
}
}
// CraftBukkit end
world.setTypeUpdate(blockposition2.up(), Blocks.FIRE.getBlockData());
}
}
}
}
}
}
protected boolean c(World world, BlockPosition blockposition) {
EnumDirection[] aenumdirection = EnumDirection.values();
int i = aenumdirection.length;
for (int j = 0; j < i; ++j) {
EnumDirection enumdirection = aenumdirection[j];
if (this.d(world, blockposition.shift(enumdirection))) {
return true;
}
}
return false;
}
private boolean d(World world, BlockPosition blockposition) {
// Dionysus start - improve fire spread checks
if (blockposition.getY() >= 0 && blockposition.getY() < 256) {
IBlockData blockData = world.getTypeIfLoaded(blockposition);
if (blockData != null) {
return blockData.getMaterial().isBurnable();
}
}
return false;
// Dionysus end
}
}

View File

@@ -1,18 +1,20 @@
package net.minecraft.server;
// Paper start
import com.destroystokyo.paper.PaperWorldConfig.DuplicateUUIDMode;
import java.util.HashMap;
import java.util.UUID;
// Paper end
import com.destroystokyo.paper.exception.ServerInternalException;
import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
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;
@@ -22,11 +24,6 @@ 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();
@@ -54,11 +51,11 @@ public class Chunk {
TileEntity replaced = super.put(key, value);
if (replaced != null) {
replaced.setCurrentChunk(null);
tileEntityCounts.decrement(replaced.tileEntityKeyString);
tileEntityCounts.decrement(replaced.getMinecraftKeyString());
}
if (value != null) {
value.setCurrentChunk(Chunk.this);
tileEntityCounts.increment(value.tileEntityKeyString);
tileEntityCounts.increment(value.getMinecraftKeyString());
}
return replaced;
}
@@ -68,16 +65,16 @@ public class Chunk {
TileEntity removed = super.remove(key);
if (removed != null) {
removed.setCurrentChunk(null);
tileEntityCounts.decrement(removed.tileEntityKeyString);
tileEntityCounts.decrement(removed.getMinecraftKeyString());
}
return removed;
}
}
public final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this); // Akarin - public
final PaperLightingQueue.LightingQueue lightingQueue = new PaperLightingQueue.LightingQueue(this);
// Paper end
private volatile boolean done; // Akarin - volatile
private volatile boolean lit; // Akarin - volatile
private volatile boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER // Akarin - volatile
private boolean done;
private boolean lit;
private boolean r; private boolean isTicked() { return r; }; // Paper - OBFHELPER
private boolean s;
private boolean t;
private long lastSaved;
@@ -277,14 +274,7 @@ public class Chunk {
private void h(boolean flag) {
this.world.methodProfiler.a("recheckGaps");
if (this.world.areChunksLoaded(new BlockPosition(this.locX * 16 + 8, 0, this.locZ * 16 + 8), 16)) {
this.runOrQueueLightUpdate(() -> recheckGaps(flag)); // Paper - Queue light update
}
}
private void recheckGaps(boolean flag) {
if (true) {
// Paper end
if (this.areNeighborsLoaded(1)) { // Paper
for (int i = 0; i < 16; ++i) {
for (int j = 0; j < 16; ++j) {
if (this.i[i + j * 16]) {
@@ -335,7 +325,7 @@ public class Chunk {
}
private void a(int i, int j, int k, int l) {
if (l > k && this.world.areChunksLoaded(new BlockPosition(i, 0, j), 16)) {
if (l > k && this.areNeighborsLoaded(1)) { // Paper
for (int i1 = k; i1 < l; ++i1) {
this.world.c(EnumSkyBlock.SKY, new BlockPosition(i, i1, j));
}
@@ -667,6 +657,7 @@ public class Chunk {
if (i != this.locX || j != this.locZ) {
Chunk.e.warn("Wrong location! ({}, {}) should be ({}, {}), {}", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(this.locX), Integer.valueOf(this.locZ), entity);
entity.die();
return; // Paper
}
int k = MathHelper.floor(entity.locY / 16.0D);
@@ -687,24 +678,18 @@ public class Chunk {
// 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();
List<Entity> currentSlice = entity.entitySlice;
if (inThis || (currentSlice != null && currentSlice.contains(entity))) {
if (currentSlice == entitySlice || inThis) {
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();
currentSlice.remove(entity); // Just incase the above did not remove from this target slice
}
}
entity.entitySlice = entitySlice;
@@ -712,7 +697,7 @@ public class Chunk {
this.markDirty();
entity.setCurrentChunk(this);
entityCounts.increment(entity.entityKeyString);
entityCounts.increment(entity.getMinecraftKeyString());
if (entity instanceof EntityItem) {
itemCounts[k]++;
} else if (entity instanceof IInventory) {
@@ -752,16 +737,13 @@ public class Chunk {
}
// Paper start
if (!this.entitySlices[i].remove(entity)) { return; }
if (entitySlices[i] == entity.entitySlice) {
if (entity.entitySlice == null || !entity.entitySlice.contains(entity) || 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();
}
if (!this.entitySlices[i].remove(entity)) { return; }
this.markDirty();
entity.setCurrentChunk(null);
entityCounts.decrement(entity.entityKeyString);
entityCounts.decrement(entity.getMinecraftKeyString());
if (entity instanceof EntityItem) {
itemCounts[i]--;
} else if (entity instanceof IInventory) {
@@ -890,23 +872,35 @@ public class Chunk {
this.world.b(this.tileEntities.values());
List[] aentityslice = this.entitySlices; // Spigot
int i = aentityslice.length;
List<Entity> toAdd = new java.util.ArrayList<>(32); // Paper
for (int j = 0; j < i; ++j) {
List entityslice = aentityslice[j]; // Spigot
// Paper start
DuplicateUUIDMode mode = world.paperConfig.duplicateUUIDMode;
if (mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.REGEN) {
if (mode == DuplicateUUIDMode.WARN || mode == DuplicateUUIDMode.DELETE || mode == DuplicateUUIDMode.SAFE_REGEN) {
Map<UUID, Entity> thisChunk = new HashMap<>();
for (Iterator<Entity> iterator = ((List<Entity>) entityslice).iterator(); iterator.hasNext(); ) {
Entity entity = iterator.next();
if (entity.dead) continue;
if (entity.dead || entity.valid) continue;
Entity other = ((WorldServer) world).entitiesByUUID.get(entity.uniqueID);
if (other == null) {
if (other == null || other.dead || world.getEntityUnloadQueue().contains(other)) {
other = thisChunk.get(entity.uniqueID);
}
if (mode == DuplicateUUIDMode.SAFE_REGEN && other != null && !other.dead &&
!world.getEntityUnloadQueue().contains(other)
&& java.util.Objects.equals(other.getSaveID(), entity.getSaveID())
&& entity.getBukkitEntity().getLocation().distance(other.getBukkitEntity().getLocation()) < world.paperConfig.duplicateUUIDDeleteRange
) {
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", deleted entity " + entity + " because it was near the duplicate and likely an actual duplicate. See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
entity.die();
iterator.remove();
continue;
}
if (other != null && !other.dead) {
switch (mode) {
case REGEN: {
case SAFE_REGEN: {
entity.setUUID(UUID.randomUUID());
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", regenerated UUID for " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
break;
@@ -917,15 +911,20 @@ public class Chunk {
iterator.remove();
break;
}
default:
logger.warn("[DUPE-UUID] Duplicate UUID found used by " + other + ", doing nothing to " + entity + ". See https://github.com/PaperMC/Paper/issues/1223 for discussion on what this is about.");
break;
}
}
thisChunk.put(entity.uniqueID, entity);
}
}
// Paper end
this.world.a((Collection) entityslice);
//this.world.a((Collection) entityslice); // Move down, add all entities at same time
toAdd.addAll(entityslice);
// Paper end
}
this.world.addChunkEntities(toAdd); // Paper - add all at same time to avoid entities adding to world modifying slice state
}
@@ -972,6 +971,8 @@ public class Chunk {
}
}
// Spigot End
entity.setCurrentChunk(null); // Paper
entity.entitySlice = null; // Paper
// Do not pass along players, as doing so can get them stuck outside of time.
// (which for example disables inventory icon updates and prevents block breaking)
@@ -999,7 +1000,6 @@ public class Chunk {
for (int k = i; k <= j; ++k) {
if (!this.entitySlices[k].isEmpty()) {
Iterator iterator = this.entitySlices[k].iterator();
// Paper start - Don't search for inventories if we have none, and that is all we want
/*
@@ -1010,6 +1010,7 @@ public class Chunk {
*/
if (predicate == IEntitySelector.c && inventoryEntityCounts[k] <= 0) continue;
// Paper end
Iterator iterator = this.entitySlices[k].iterator();
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
@@ -1218,7 +1219,7 @@ public class Chunk {
}
this.r = true;
if (!this.lit && this.done && this.world.spigotConfig.randomLightUpdates) { // Spigot - also use random light updates setting to determine if we should relight
if (!this.lit && this.done) {
this.o();
}
@@ -1405,7 +1406,7 @@ public class Chunk {
this.h(false);
}
public void a(EnumDirection enumdirection) { // Akarin - private -> public
private void a(EnumDirection enumdirection) {
if (this.done) {
int i;
@@ -1547,4 +1548,14 @@ public class Chunk {
private EnumTileEntityState() {}
}
// FlamePaper start - Hopper item lookup optimization
public int getItemCount(BlockPosition blockPosition) {
int k = MathHelper.floor(blockPosition.getY() / 16.0D);
k = Math.max(0, k);
k = Math.min(this.entitySlices.length - 1, k);
return itemCounts[k];
}
// FlamePaper end - Hopper item lookup optimization
}

View File

@@ -0,0 +1,458 @@
package net.minecraft.server;
import com.destroystokyo.paper.PaperConfig;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import it.unimi.dsi.fastutil.longs.LongArraySet;
import it.unimi.dsi.fastutil.longs.LongIterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import com.destroystokyo.paper.exception.ServerInternalException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.world.ChunkUnloadEvent;
// CraftBukkit end
public class ChunkProviderServer implements IChunkProvider {
private static final Logger a = LogManager.getLogger();
public final LongArraySet unloadQueue = new LongArraySet(512); // Dionysus
public final ChunkGenerator chunkGenerator;
private final IChunkLoader chunkLoader;
// Paper start - chunk save stats
private long lastQueuedSaves = 0L; // Paper
private long lastProcessedSaves = 0L; // Paper
private long lastSaveStatPrinted = System.currentTimeMillis();
// Paper end
// Paper start
protected Chunk lastChunkByPos = null;
public Long2ObjectOpenHashMap<Chunk> chunks = new Long2ObjectOpenHashMap<Chunk>(8192) {
@Override
public Chunk get(long key) {
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
return lastChunkByPos;
}
return lastChunkByPos = super.get(key);
}
@Override
public Chunk remove(long key) {
if (lastChunkByPos != null && key == lastChunkByPos.chunkKey) {
lastChunkByPos = null;
}
return super.remove(key);
}
}; // CraftBukkit
// Paper end
public final WorldServer world;
public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, ChunkGenerator chunkgenerator) {
this.world = worldserver;
this.chunkLoader = ichunkloader;
this.chunkGenerator = chunkgenerator;
}
public Collection<Chunk> a() {
return this.chunks.values();
}
public void unload(Chunk chunk) {
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
chunk.d = true;
}
}
public void b() {
ObjectIterator objectiterator = this.chunks.values().iterator();
while (objectiterator.hasNext()) {
Chunk chunk = (Chunk) objectiterator.next();
this.unload(chunk);
}
}
// Paper start
public boolean isChunkGenerated(int x, int z) {
return this.chunks.containsKey(ChunkCoordIntPair.asLong(x, z)) || this.chunkLoader.chunkExists(x, z);
}
// Paper end
@Nullable
public Chunk getLoadedChunkAt(int i, int j) {
long k = ChunkCoordIntPair.a(i, j);
Chunk chunk = (Chunk) this.chunks.get(k);
if (chunk != null) {
chunk.d = false;
}
return chunk;
}
@Nullable
public Chunk getOrLoadChunkAt(int i, int j) {
Chunk chunk = this.getLoadedChunkAt(i, j);
if (chunk == null) {
// CraftBukkit start
ChunkRegionLoader loader = null;
if (this.chunkLoader instanceof ChunkRegionLoader) {
loader = (ChunkRegionLoader) this.chunkLoader;
}
if (loader != null && loader.chunkExists(i, j)) {
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
}
}
return chunk;
}
@Nullable
public Chunk originalGetOrLoadChunkAt(int i, int j) {
// CraftBukkit end
Chunk chunk = this.getLoadedChunkAt(i, j);
if (chunk == null) {
chunk = this.loadChunk(i, j);
if (chunk != null) {
this.chunks.put(ChunkCoordIntPair.a(i, j), chunk);
chunk.addEntities();
chunk.loadNearby(this, this.chunkGenerator, false); // CraftBukkit
}
}
return chunk;
}
// CraftBukkit start
public Chunk getChunkIfLoaded(int x, int z) {
return chunks.get(ChunkCoordIntPair.a(x, z));
}
// CraftBukkit end
public Chunk getChunkAt(int i, int j) {
return getChunkAt(i, j, null);
}
public Chunk getChunkAt(int i, int j, Runnable runnable) {
return getChunkAt(i, j, runnable, true);
}
public Chunk getChunkAt(int i, int j, Runnable runnable, boolean generate) {
Chunk chunk = world.paperConfig.allowPermaChunkLoaders ? getLoadedChunkAt(i, j) : getChunkIfLoaded(i, j); // Paper - Configurable perma chunk loaders
ChunkRegionLoader loader = null;
if (this.chunkLoader instanceof ChunkRegionLoader) {
loader = (ChunkRegionLoader) this.chunkLoader;
}
// We can only use the queue for already generated chunks
if (chunk == null && loader != null && loader.chunkExists(i, j)) {
if (runnable != null) {
ChunkIOExecutor.queueChunkLoad(world, loader, this, i, j, runnable);
return null;
} else {
chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j);
// Paper start - If there was an issue loading the chunk from region, stage1 will fail and stage2 will load it sync
// all we need to do is fetch an instance
if (chunk == null) {
chunk = getChunkIfLoaded(i, j);
}
// Paper end
}
} else if (chunk == null && generate) {
chunk = originalGetChunkAt(i, j);
}
// If we didn't load the chunk async and have a callback run it now
if (runnable != null) {
runnable.run();
}
return chunk;
}
public Chunk originalGetChunkAt(int i, int j) {
Chunk chunk = this.originalGetOrLoadChunkAt(i, j);
// CraftBukkit end
if (chunk == null) {
world.timings.syncChunkLoadTimer.startTiming(); // Spigot
long k = ChunkCoordIntPair.a(i, j);
try {
chunk = this.chunkGenerator.getOrCreateChunk(i, j);
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Exception generating new chunk");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated");
crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)}));
crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(k));
crashreportsystemdetails.a("Generator", (Object) this.chunkGenerator);
throw new ReportedException(crashreport);
}
this.chunks.put(k, chunk);
chunk.addEntities();
chunk.loadNearby(this, this.chunkGenerator, true); // CraftBukkit
world.timings.syncChunkLoadTimer.stopTiming(); // Spigot
}
return chunk;
}
@Nullable
public Chunk loadChunk(int i, int j) {
try {
Chunk chunk = this.chunkLoader.a(this.world, i, j);
if (chunk != null) {
chunk.setLastSaved(this.world.getTime());
this.chunkGenerator.recreateStructures(chunk, i, j);
}
return chunk;
} catch (Exception exception) {
// Paper start
String msg = "Couldn\'t load chunk";
ChunkProviderServer.a.error(msg, exception);
ServerInternalException.reportInternalException(exception);
// Paper end
return null;
}
}
public void saveChunkNOP(Chunk chunk) {
try {
// this.chunkLoader.b(this.world, chunk); // Spigot
} catch (Exception exception) {
// Paper start
String msg = "Couldn\'t save entities";
ChunkProviderServer.a.error(msg, exception);
ServerInternalException.reportInternalException(exception);
// Paper end
}
}
public void saveChunk(Chunk chunk, boolean unloaded) { // Spigot
try (co.aikar.timings.Timing timed = world.timings.chunkSaveData.startTiming()) {
chunk.setLastSaved(this.world.getTime());
this.chunkLoader.saveChunk(this.world, chunk, unloaded); // Spigot
} catch (IOException ioexception) {
// Paper start
String msg = "Couldn\'t save chunk";
ChunkProviderServer.a.error(msg, ioexception);
ServerInternalException.reportInternalException(ioexception);
} catch (ExceptionWorldConflict exceptionworldconflict) {
String msg = "Couldn\'t save chunk; already in use by another instance of Minecraft?";
ChunkProviderServer.a.error(msg, exceptionworldconflict);
ServerInternalException.reportInternalException(exceptionworldconflict);
}
}
public boolean a(boolean flag) {
int i = 0;
// CraftBukkit start
// Paper start
final ChunkRegionLoader chunkLoader = (ChunkRegionLoader) world.getChunkProviderServer().chunkLoader;
final int queueSize = chunkLoader.getQueueSize();
final long now = System.currentTimeMillis();
final long timeSince = (now - lastSaveStatPrinted) / 1000;
final Integer printRateSecs = Integer.getInteger("printSaveStats");
if (printRateSecs != null && timeSince >= printRateSecs) {
final String timeStr = "/" + timeSince +"s";
final long queuedSaves = chunkLoader.getQueuedSaves();
long queuedDiff = queuedSaves - lastQueuedSaves;
lastQueuedSaves = queuedSaves;
final long processedSaves = chunkLoader.getProcessedSaves();
long processedDiff = processedSaves - lastProcessedSaves;
lastProcessedSaves = processedSaves;
lastSaveStatPrinted = now;
if (processedDiff > 0 || queueSize > 0 || queuedDiff > 0) {
System.out.println("[Chunk Save Stats] " + world.worldData.getName() +
" - Current: " + queueSize +
" - Queued: " + queuedDiff + timeStr +
" - Processed: " +processedDiff + timeStr
);
}
}
if (queueSize > world.paperConfig.queueSizeAutoSaveThreshold){
return false;
}
final int autoSaveLimit = world.paperConfig.maxAutoSaveChunksPerTick;
// Paper end
Iterator iterator = this.chunks.values().iterator();
while (iterator.hasNext()) {
Chunk chunk = (Chunk) iterator.next();
// CraftBukkit end
if (flag) {
this.saveChunkNOP(chunk);
}
if (chunk.a(flag)) {
this.saveChunk(chunk, false); // Spigot
chunk.f(false);
++i;
if (!flag && i >= autoSaveLimit) { // Spigot - // Paper - Incremental Auto Save - cap max per tick
return false;
}
}
}
return true;
}
public void c() {
this.chunkLoader.c();
}
private static final double UNLOAD_QUEUE_RESIZE_FACTOR = 0.96;
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
if (!this.unloadQueue.isEmpty()) {
// Spigot start
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
int targetSize = Math.min(this.unloadQueue.size() - 100, (int) (this.unloadQueue.size() * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
// Spigot end
LongIterator iterator = this.unloadQueue.iterator();
while (iterator.hasNext()) { // Spigot
Long chunkKey = iterator.nextLong();
iterator.remove(); // Spigot
Chunk chunk = (Chunk) this.chunks.get(chunkKey);
if (chunk != null && chunk.d) {
// CraftBukkit start - move unload logic to own method
chunk.setShouldUnload(false); // Paper
if (!unloadChunk(chunk, true)) {
continue;
}
// CraftBukkit end
// Spigot start
if (this.unloadQueue.size() <= targetSize && activityAccountant.activityTimeIsExhausted()) {
break;
}
// Spigot end
}
}
activityAccountant.endActivity(); // Spigot
}
// Paper start - delayed chunk unloads
long now = System.currentTimeMillis();
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
if (unloadAfter > 0) {
//noinspection Convert2streamapi
for (Chunk chunk : chunks.values()) {
if (chunk.scheduledForUnload != null && now - chunk.scheduledForUnload > unloadAfter) {
chunk.scheduledForUnload = null;
unload(chunk);
}
}
}
// Paper end
this.chunkLoader.b();
}
return false;
}
// CraftBukkit start
public boolean unloadChunk(Chunk chunk, boolean save) {
ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk, save);
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
save = event.isSaveChunk();
chunk.lightingQueue.processUnload(); // Paper
// Update neighbor counts
for (int x = -2; x < 3; x++) {
for (int z = -2; z < 3; z++) {
if (x == 0 && z == 0) {
continue;
}
Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z);
if (neighbor != null) {
neighbor.setNeighborUnloaded(-x, -z);
chunk.setNeighborUnloaded(x, z);
}
}
}
// Moved from unloadChunks above
chunk.removeEntities();
if (save) {
this.saveChunk(chunk, true); // Spigot
this.saveChunkNOP(chunk);
}
this.chunks.remove(chunk.chunkKey);
return true;
}
// CraftBukkit end
public boolean e() {
return !this.world.savingDisabled;
}
public String getName() {
return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size();
}
public List<BiomeBase.BiomeMeta> a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
return this.chunkGenerator.getMobsFor(enumcreaturetype, blockposition);
}
@Nullable
public BlockPosition a(World world, String s, BlockPosition blockposition, boolean flag) {
return this.chunkGenerator.findNearestMapFeature(world, s, blockposition, flag);
}
public boolean a(World world, String s, BlockPosition blockposition) {
return this.chunkGenerator.a(world, s, blockposition);
}
public int g() {
return this.chunks.size();
}
public boolean isLoaded(int i, int j) {
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j));
}
public boolean e(int i, int j) {
return this.chunks.containsKey(ChunkCoordIntPair.a(i, j)) || this.chunkLoader.chunkExists(i, j);
}
}

View File

@@ -0,0 +1,623 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
import java.util.concurrent.ConcurrentLinkedQueue; // Paper
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// Spigot start
import java.util.function.Supplier;
import org.spigotmc.SupplierUtils;
// Spigot end
/**
* Akarin Changes Note
* 1) Removes unneed synchronization (performance)
*/
public class ChunkRegionLoader implements IChunkLoader, IAsyncChunkSaver {
private ConcurrentLinkedQueue<QueuedChunk> queue = new ConcurrentLinkedQueue<>(); // Paper - Chunk queue improvements
private final Object lock = new Object(); // Paper - Chunk queue improvements
private static final Logger a = LogManager.getLogger();
private final Map<ChunkCoordIntPair, Supplier<NBTTagCompound>> b = Maps.newConcurrentMap(); // Spigot
// CraftBukkit
// private final Set<ChunkCoordIntPair> c = Collections.newSetFromMap(Maps.newConcurrentMap());
private final File d;
private final DataConverterManager e;
// private boolean f;
// CraftBukkit
private static final double SAVE_QUEUE_TARGET_SIZE = 625; // Spigot
public ChunkRegionLoader(File file, DataConverterManager dataconvertermanager) {
this.d = file;
this.e = dataconvertermanager;
}
// Paper start
private long queuedSaves = 0;
private final java.util.concurrent.atomic.AtomicLong processedSaves = new java.util.concurrent.atomic.AtomicLong(0L);
public int getQueueSize() { return queue.size(); }
public long getQueuedSaves() { return queuedSaves; }
public long getProcessedSaves() { return processedSaves.longValue(); }
// Paper end
// CraftBukkit start - Add async variant, provide compatibility
@Nullable
public Chunk a(World world, int i, int j) throws IOException {
world.timings.syncChunkLoadDataTimer.startTiming(); // Spigot
Object[] data = loadChunk(world, i, j);
world.timings.syncChunkLoadDataTimer.stopTiming(); // Spigot
if (data != null) {
Chunk chunk = (Chunk) data[0];
NBTTagCompound nbttagcompound = (NBTTagCompound) data[1];
loadEntities(chunk, nbttagcompound.getCompound("Level"), world);
return chunk;
}
return null;
}
public Object[] loadChunk(World world, int i, int j) throws IOException {
// CraftBukkit end
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(this.b.get(chunkcoordintpair)); // Spigot
if (nbttagcompound == null) {
// CraftBukkit start
nbttagcompound = RegionFileCache.d(this.d, i, j);
if (nbttagcompound == null) {
return null;
}
nbttagcompound = this.e.a((DataConverterType) DataConverterTypes.CHUNK, nbttagcompound);
// CraftBukkit end
}
return this.a(world, i, j, nbttagcompound);
}
public boolean chunkExists(int i, int j) {
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j);
Supplier<NBTTagCompound> nbttagcompound = this.b.get(chunkcoordintpair); // Spigot
return nbttagcompound != null ? true : RegionFileCache.chunkExists(this.d, i, j);
}
@Nullable
protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[]
if (!nbttagcompound.hasKeyOfType("Level", 10)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is missing level data, skipping", Integer.valueOf(i), Integer.valueOf(j));
return null;
} else {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
if (!nbttagcompound1.hasKeyOfType("Sections", 9)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is missing block data, skipping", Integer.valueOf(i), Integer.valueOf(j));
return null;
} else {
Chunk chunk = this.a(world, nbttagcompound1);
if (!chunk.a(i, j)) {
ChunkRegionLoader.a.error("Chunk file at {},{} is in the wrong location; relocating. (Expected {}, {}, got {}, {})", Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(i), Integer.valueOf(j), Integer.valueOf(chunk.locX), Integer.valueOf(chunk.locZ));
nbttagcompound1.setInt("xPos", i);
nbttagcompound1.setInt("zPos", j);
// CraftBukkit start - Have to move tile entities since we don't load them at this stage
NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10);
if (tileEntities != null) {
for (int te = 0; te < tileEntities.size(); te++) {
NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te);
int x = tileEntity.getInt("x") - chunk.locX * 16;
int z = tileEntity.getInt("z") - chunk.locZ * 16;
tileEntity.setInt("x", i * 16 + x);
tileEntity.setInt("z", j * 16 + z);
}
}
// CraftBukkit end
chunk = this.a(world, nbttagcompound1);
}
// CraftBukkit start
Object[] data = new Object[2];
data[0] = chunk;
data[1] = nbttagcompound;
return data;
// CraftBukkit end
}
}
}
public void saveChunk(World world, Chunk chunk, boolean unloaded) throws IOException, ExceptionWorldConflict { // Spigot
world.checkSession();
try {
NBTTagCompound nbttagcompound = new NBTTagCompound();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound.set("Level", nbttagcompound1);
nbttagcompound.setInt("DataVersion", 1343);
// Spigot start
final long worldTime = world.getTime();
final boolean worldHasSkyLight = world.worldProvider.m();
saveEntities(nbttagcompound1, chunk, world);
Supplier<NBTTagCompound> completion = new Supplier<NBTTagCompound>() {
public NBTTagCompound get() {
saveBody(nbttagcompound1, chunk, worldTime, worldHasSkyLight);
return nbttagcompound;
}
};
this.a(chunk.k(), SupplierUtils.createUnivaluedSupplier(completion, unloaded && this.b.size() < SAVE_QUEUE_TARGET_SIZE));
// Spigot end
} catch (Exception exception) {
ChunkRegionLoader.a.error("Failed to save chunk", exception);
}
}
protected void a(ChunkCoordIntPair chunkcoordintpair, Supplier<NBTTagCompound> nbttagcompound) { // Spigot
// CraftBukkit
// if (!this.c.contains(chunkcoordintpair))
synchronized (lock) { // Paper - Chunk queue improvements
this.b.put(chunkcoordintpair, nbttagcompound);
}
queuedSaves++; // Paper
queue.add(new QueuedChunk(chunkcoordintpair, nbttagcompound)); // Paper - Chunk queue improvements
FileIOThread.a().a(this);
}
public boolean a() {
// CraftBukkit start
return this.processSaveQueueEntry(false);
}
private /*synchronized*/ boolean processSaveQueueEntry(boolean logCompletion) { // Akarin - remove synchronization
// CraftBukkit start
// Paper start - Chunk queue improvements
QueuedChunk chunk = queue.poll();
if (chunk == null) {
// Paper - end
if (logCompletion) {
// CraftBukkit end
ChunkRegionLoader.a.info("ThreadedAnvilChunkStorage ({}): All chunks are saved", this.d.getName());
}
return false;
} else {
ChunkCoordIntPair chunkcoordintpair = chunk.coords; // Paper - Chunk queue improvements
processedSaves.incrementAndGet(); // Paper
boolean flag;
try {
// this.c.add(chunkcoordintpair);
NBTTagCompound nbttagcompound = SupplierUtils.getIfExists(chunk.compoundSupplier); // Spigot // Paper
// CraftBukkit
if (nbttagcompound != null) {
int attempts = 0; Exception laste = null; while (attempts++ < 5) { // Paper
try {
this.b(chunkcoordintpair, nbttagcompound);
laste = null; break; // Paper
} catch (Exception exception) {
//ChunkRegionLoader.a.error("Failed to save chunk", exception); // Paper
laste = exception; // Paper
}
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} } // Paper
if (laste != null) { com.destroystokyo.paper.exception.ServerInternalException.reportInternalException(laste); MinecraftServer.LOGGER.error("Failed to save chunk", laste); } // Paper
}
synchronized (lock) { if (this.b.get(chunkcoordintpair) == chunk.compoundSupplier) { this.b.remove(chunkcoordintpair); } }// Paper - This will not equal if a newer version is still pending
flag = true;
} finally {
//this.b.remove(chunkcoordintpair, value); // CraftBukkit // Spigot // Paper
}
return flag;
}
}
private void b(ChunkCoordIntPair chunkcoordintpair, NBTTagCompound nbttagcompound) throws IOException {
// CraftBukkit start
RegionFileCache.e(this.d, chunkcoordintpair.x, chunkcoordintpair.z, nbttagcompound);
/*
NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) dataoutputstream);
dataoutputstream.close();
*/
// CraftBukkit end
}
public void b(World world, Chunk chunk) throws IOException {}
public void b() {}
public void c() {
try {
// this.f = true; // CraftBukkit
while (true) {
if (this.processSaveQueueEntry(true)) { // CraftBukkit
continue;
}
break; // CraftBukkit - Fix infinite loop when saving chunks
}
} finally {
// this.f = false; // CraftBukkit
}
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.CHUNK, new DataInspector() {
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("Level", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Level");
NBTTagList nbttaglist;
int j;
if (nbttagcompound1.hasKeyOfType("Entities", 9)) {
nbttaglist = nbttagcompound1.getList("Entities", 10);
for (j = 0; j < nbttaglist.size(); ++j) {
nbttaglist.a(j, dataconverter.a(DataConverterTypes.ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
}
}
if (nbttagcompound1.hasKeyOfType("TileEntities", 9)) {
nbttaglist = nbttagcompound1.getList("TileEntities", 10);
for (j = 0; j < nbttaglist.size(); ++j) {
nbttaglist.a(j, dataconverter.a(DataConverterTypes.BLOCK_ENTITY, (NBTTagCompound) nbttaglist.i(j), i));
}
}
}
return nbttagcompound;
}
});
}
private static void saveBody(NBTTagCompound nbttagcompound, Chunk chunk, long worldTime, boolean worldHasSkyLight) { // Spigot
nbttagcompound.setInt("xPos", chunk.locX);
nbttagcompound.setInt("zPos", chunk.locZ);
nbttagcompound.setLong("LastUpdate", worldTime); // Spigot
nbttagcompound.setIntArray("HeightMap", chunk.r());
nbttagcompound.setBoolean("TerrainPopulated", chunk.isDone());
nbttagcompound.setBoolean("LightPopulated", chunk.v());
nbttagcompound.setLong("InhabitedTime", chunk.x());
ChunkSection[] achunksection = chunk.getSections();
NBTTagList nbttaglist = new NBTTagList();
boolean flag = worldHasSkyLight; // Spigot
ChunkSection[] achunksection1 = achunksection;
int i = achunksection.length;
NBTTagCompound nbttagcompound1;
for (int j = 0; j < i; ++j) {
ChunkSection chunksection = achunksection1[j];
if (chunksection != Chunk.a) {
nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setByte("Y", (byte) (chunksection.getYPosition() >> 4 & 255));
byte[] abyte = new byte[4096];
NibbleArray nibblearray = new NibbleArray();
NibbleArray nibblearray1 = chunksection.getBlocks().exportData(abyte, nibblearray);
nbttagcompound1.setByteArray("Blocks", abyte);
nbttagcompound1.setByteArray("Data", nibblearray.asBytes());
if (nibblearray1 != null) {
nbttagcompound1.setByteArray("Add", nibblearray1.asBytes());
}
nbttagcompound1.setByteArray("BlockLight", chunksection.getEmittedLightArray().asBytes());
if (flag) {
nbttagcompound1.setByteArray("SkyLight", chunksection.getSkyLightArray().asBytes());
} else {
nbttagcompound1.setByteArray("SkyLight", new byte[chunksection.getEmittedLightArray().asBytes().length]);
}
nbttaglist.add(nbttagcompound1);
}
}
nbttagcompound.set("Sections", nbttaglist);
nbttagcompound.setByteArray("Biomes", chunk.getBiomeIndex());
// Spigot start - End this method here and split off entity saving to another method
}
private static void saveEntities(NBTTagCompound nbttagcompound, Chunk chunk, World world) {
int i;
NBTTagCompound nbttagcompound1;
// Spigot end
chunk.g(false);
NBTTagList nbttaglist1 = new NBTTagList();
Iterator iterator;
List<Entity> toUpdate = new java.util.ArrayList<>(); // Paper
for (i = 0; i < chunk.getEntitySlices().length; ++i) {
iterator = chunk.getEntitySlices()[i].iterator();
while (iterator.hasNext()) {
Entity entity = (Entity) iterator.next();
// Paper start
if ((int)Math.floor(entity.locX) >> 4 != chunk.locX || (int)Math.floor(entity.locZ) >> 4 != chunk.locZ) {
LogManager.getLogger().warn(entity + " is not in this chunk, skipping save. This a bug fix to a vanilla bug. Do not report this to PaperMC please.");
toUpdate.add(entity);
continue;
}
if (entity.dead) {
continue;
}
// Paper end
nbttagcompound1 = new NBTTagCompound();
if (entity.d(nbttagcompound1)) {
chunk.g(true);
nbttaglist1.add(nbttagcompound1);
}
}
}
// Paper start - move entities to the correct chunk
for (Entity entity : toUpdate) {
world.entityJoinedWorld(entity, false);
}
// Paper end
nbttagcompound.set("Entities", nbttaglist1);
NBTTagList nbttaglist2 = new NBTTagList();
iterator = chunk.getTileEntities().values().iterator();
while (iterator.hasNext()) {
TileEntity tileentity = (TileEntity) iterator.next();
nbttagcompound1 = tileentity.save(new NBTTagCompound());
nbttaglist2.add(nbttagcompound1);
}
nbttagcompound.set("TileEntities", nbttaglist2);
List list = world.a(chunk, false);
if (list != null) {
long k = world.getTime();
NBTTagList nbttaglist3 = new NBTTagList();
Iterator iterator1 = list.iterator();
while (iterator1.hasNext()) {
NextTickListEntry nextticklistentry = (NextTickListEntry) iterator1.next();
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
MinecraftKey minecraftkey = (MinecraftKey) Block.REGISTRY.b(nextticklistentry.a());
nbttagcompound2.setString("i", minecraftkey == null ? "" : minecraftkey.toString());
nbttagcompound2.setInt("x", nextticklistentry.a.getX());
nbttagcompound2.setInt("y", nextticklistentry.a.getY());
nbttagcompound2.setInt("z", nextticklistentry.a.getZ());
nbttagcompound2.setInt("t", (int) (nextticklistentry.b - k));
nbttagcompound2.setInt("p", nextticklistentry.c);
nbttaglist3.add(nbttagcompound2);
}
nbttagcompound.set("TileTicks", nbttaglist3);
}
}
private Chunk a(World world, NBTTagCompound nbttagcompound) {
int i = nbttagcompound.getInt("xPos");
int j = nbttagcompound.getInt("zPos");
Chunk chunk = new Chunk(world, i, j);
chunk.a(nbttagcompound.getIntArray("HeightMap"));
chunk.d(nbttagcompound.getBoolean("TerrainPopulated"));
chunk.e(nbttagcompound.getBoolean("LightPopulated"));
chunk.c(nbttagcompound.getLong("InhabitedTime"));
NBTTagList nbttaglist = nbttagcompound.getList("Sections", 10);
boolean flag = true;
ChunkSection[] achunksection = new ChunkSection[16];
boolean flag1 = world.worldProvider.m();
for (int k = 0; k < nbttaglist.size(); ++k) {
NBTTagCompound nbttagcompound1 = nbttaglist.get(k);
byte b0 = nbttagcompound1.getByte("Y");
ChunkSection chunksection = new ChunkSection(b0 << 4, flag1, world.chunkPacketBlockController.getPredefinedBlockData(chunk, b0)); // Paper - Anti-Xray - Add predefined block data
byte[] abyte = nbttagcompound1.getByteArray("Blocks");
NibbleArray nibblearray = new NibbleArray(nbttagcompound1.getByteArray("Data"));
NibbleArray nibblearray1 = nbttagcompound1.hasKeyOfType("Add", 7) ? new NibbleArray(nbttagcompound1.getByteArray("Add")) : null;
chunksection.getBlocks().a(abyte, nibblearray, nibblearray1);
chunksection.a(new NibbleArray(nbttagcompound1.getByteArray("BlockLight")));
if (flag1) {
chunksection.b(new NibbleArray(nbttagcompound1.getByteArray("SkyLight")));
}
chunksection.recalcBlockCounts();
achunksection[b0] = chunksection;
}
chunk.a(achunksection);
if (nbttagcompound.hasKeyOfType("Biomes", 7)) {
chunk.a(nbttagcompound.getByteArray("Biomes"));
}
// CraftBukkit start - End this method here and split off entity loading to another method
return chunk;
}
public void loadEntities(Chunk chunk, NBTTagCompound nbttagcompound, World world) {
// CraftBukkit end
world.timings.syncChunkLoadNBTTimer.startTiming(); // Spigot
NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10);
for (int l = 0; l < nbttaglist1.size(); ++l) {
NBTTagCompound nbttagcompound2 = nbttaglist1.get(l);
a(nbttagcompound2, world, chunk);
chunk.g(true);
}
NBTTagList nbttaglist2 = nbttagcompound.getList("TileEntities", 10);
for (int i1 = 0; i1 < nbttaglist2.size(); ++i1) {
NBTTagCompound nbttagcompound3 = nbttaglist2.get(i1);
TileEntity tileentity = TileEntity.create(world, nbttagcompound3);
if (tileentity != null) {
chunk.a(tileentity);
}
}
if (nbttagcompound.hasKeyOfType("TileTicks", 9)) {
NBTTagList nbttaglist3 = nbttagcompound.getList("TileTicks", 10);
for (int j1 = 0; j1 < nbttaglist3.size(); ++j1) {
NBTTagCompound nbttagcompound4 = nbttaglist3.get(j1);
Block block;
if (nbttagcompound4.hasKeyOfType("i", 8)) {
block = Block.getByName(nbttagcompound4.getString("i"));
} else {
block = Block.getById(nbttagcompound4.getInt("i"));
}
world.b(new BlockPosition(nbttagcompound4.getInt("x"), nbttagcompound4.getInt("y"), nbttagcompound4.getInt("z")), block, nbttagcompound4.getInt("t"), nbttagcompound4.getInt("p"));
}
}
world.timings.syncChunkLoadNBTTimer.stopTiming(); // Spigot
// return chunk; // CraftBukkit
}
@Nullable
public static Entity a(NBTTagCompound nbttagcompound, World world, Chunk chunk) {
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else {
chunk.a(entity);
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, chunk);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
@Nullable
// CraftBukkit start
public static Entity a(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag) {
return spawnEntity(nbttagcompound, world, d0, d1, d2, flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
}
public static Entity spawnEntity(NBTTagCompound nbttagcompound, World world, double d0, double d1, double d2, boolean flag, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
// CraftBukkit end
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else {
entity.setPositionRotation(d0, d1, d2, entity.yaw, entity.pitch);
if (flag && !world.addEntity(entity, spawnReason)) { // CraftBukkit
return null;
} else {
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, d0, d1, d2, flag);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
}
@Nullable
protected static Entity a(NBTTagCompound nbttagcompound, World world) {
try {
return EntityTypes.a(nbttagcompound, world);
} catch (RuntimeException runtimeexception) {
return null;
}
}
// CraftBukkit start
public static void a(Entity entity, World world) {
a(entity, world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT);
}
public static void a(Entity entity, World world, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason reason) {
if (!entity.valid && world.addEntity(entity, reason) && entity.isVehicle()) { // Paper
// CraftBukkit end
Iterator iterator = entity.bF().iterator();
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
a(entity1, world);
}
}
}
@Nullable
public static Entity a(NBTTagCompound nbttagcompound, World world, boolean flag) {
Entity entity = a(nbttagcompound, world);
if (entity == null) {
return null;
} else if (flag && !world.addEntity(entity)) {
return null;
} else {
if (nbttagcompound.hasKeyOfType("Passengers", 9)) {
NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10);
for (int i = 0; i < nbttaglist.size(); ++i) {
Entity entity1 = a(nbttaglist.get(i), world, flag);
if (entity1 != null) {
entity1.a(entity, true);
}
}
}
return entity;
}
}
// Paper start - Chunk queue improvements
private static class QueuedChunk {
public ChunkCoordIntPair coords;
public Supplier<NBTTagCompound> compoundSupplier;
public QueuedChunk(ChunkCoordIntPair coords, Supplier<NBTTagCompound> compoundSupplier) {
this.coords = coords;
this.compoundSupplier = compoundSupplier;
}
}
// Paper end
}

View File

@@ -0,0 +1,115 @@
package net.minecraft.server;
// CraftBukkit start
import org.bukkit.craftbukkit.inventory.CraftInventoryView;
import org.bukkit.inventory.InventoryView;
// CraftBukkit end
public class ContainerHorse extends Container {
private final IInventory a;
private final EntityHorseAbstract f;
// CraftBukkit start
org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity;
PlayerInventory player;
@Override
public InventoryView getBukkitView() {
if (bukkitEntity != null) {
return bukkitEntity;
}
return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), a.getOwner().getInventory(), this);
}
public ContainerHorse(IInventory iinventory, final IInventory iinventory1, final EntityHorseAbstract entityhorseabstract, EntityHuman entityhuman) {
player = (PlayerInventory) iinventory;
// CraftBukkit end
this.a = iinventory1;
this.f = entityhorseabstract;
boolean flag = true;
iinventory1.startOpen(entityhuman);
boolean flag1 = true;
this.a(new Slot(iinventory1, 0, 8, 18) {
public boolean isAllowed(ItemStack itemstack) {
return itemstack.getItem() == Items.SADDLE && !this.hasItem() && entityhorseabstract.dF();
}
});
this.a(new Slot(iinventory1, 1, 8, 36) {
public boolean isAllowed(ItemStack itemstack) {
return entityhorseabstract.f(itemstack);
}
public int getMaxStackSize() {
return 1;
}
});
int i;
int j;
if (entityhorseabstract instanceof EntityHorseChestedAbstract && ((EntityHorseChestedAbstract) entityhorseabstract).isCarryingChest()) {
for (i = 0; i < 3; ++i) {
for (j = 0; j < ((EntityHorseChestedAbstract) entityhorseabstract).dt(); ++j) {
this.a(new Slot(iinventory1, 2 + j + i * ((EntityHorseChestedAbstract) entityhorseabstract).dt(), 80 + j * 18, 18 + i * 18));
}
}
}
for (i = 0; i < 3; ++i) {
for (j = 0; j < 9; ++j) {
this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18));
}
}
for (i = 0; i < 9; ++i) {
this.a(new Slot(iinventory, i, 8 + i * 18, 142));
}
}
public boolean canUse(EntityHuman entityhuman) {
return this.a.a(entityhuman) && this.f.isAlive() && this.f.valid && this.f.g((Entity) entityhuman) < 8.0F; // NeonPaper! - Fix MC-161754
}
public ItemStack shiftClick(EntityHuman entityhuman, int i) {
ItemStack itemstack = ItemStack.a;
Slot slot = (Slot) this.slots.get(i);
if (slot != null && slot.hasItem()) {
ItemStack itemstack1 = slot.getItem();
itemstack = itemstack1.cloneItemStack();
if (i < this.a.getSize()) {
if (!this.a(itemstack1, this.a.getSize(), this.slots.size(), true)) {
return ItemStack.a;
}
} else if (this.getSlot(1).isAllowed(itemstack1) && !this.getSlot(1).hasItem()) {
if (!this.a(itemstack1, 1, 2, false)) {
return ItemStack.a;
}
} else if (this.getSlot(0).isAllowed(itemstack1)) {
if (!this.a(itemstack1, 0, 1, false)) {
return ItemStack.a;
}
} else if (this.a.getSize() <= 2 || !this.a(itemstack1, 2, this.a.getSize(), false)) {
return ItemStack.a;
}
if (itemstack1.isEmpty()) {
slot.set(ItemStack.a);
} else {
slot.f();
}
}
return itemstack;
}
public void b(EntityHuman entityhuman) {
super.b(entityhuman);
this.a.closeContainer(entityhuman);
}
}

View File

@@ -0,0 +1,868 @@
package net.minecraft.server;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.PrintStream;
import java.util.Random;
import java.util.UUID;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
// CraftBukkit start
import java.util.List;
import org.bukkit.Location;
import org.bukkit.TreeType;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.event.block.BlockDispenseEvent;
import org.bukkit.event.world.StructureGrowEvent;
// CraftBukkit end
public class DispenserRegistry {
public static final PrintStream a = System.out;
private static boolean c;
public static boolean b;
private static final Logger d = LogManager.getLogger();
public static boolean a() {
return DispenserRegistry.c;
}
static void b() {
BlockDispenser.REGISTRY.a(Items.ARROW, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
return entitytippedarrow;
}
});
BlockDispenser.REGISTRY.a(Items.TIPPED_ARROW, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
EntityTippedArrow entitytippedarrow = new EntityTippedArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
entitytippedarrow.a(itemstack);
entitytippedarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
return entitytippedarrow;
}
});
BlockDispenser.REGISTRY.a(Items.SPECTRAL_ARROW, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
EntitySpectralArrow entityspectralarrow = new EntitySpectralArrow(world, iposition.getX(), iposition.getY(), iposition.getZ());
entityspectralarrow.fromPlayer = EntityArrow.PickupStatus.ALLOWED;
return entityspectralarrow;
}
});
BlockDispenser.REGISTRY.a(Items.EGG, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
return new EntityEgg(world, iposition.getX(), iposition.getY(), iposition.getZ());
}
});
BlockDispenser.REGISTRY.a(Items.SNOWBALL, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
return new EntitySnowball(world, iposition.getX(), iposition.getY(), iposition.getZ());
}
});
BlockDispenser.REGISTRY.a(Items.EXPERIENCE_BOTTLE, new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack) {
return new EntityThrownExpBottle(world, iposition.getX(), iposition.getY(), iposition.getZ());
}
protected float a() {
return super.a() * 0.5F;
}
protected float getPower() {
return super.getPower() * 1.25F;
}
});
BlockDispenser.REGISTRY.a(Items.SPLASH_POTION, new IDispenseBehavior() {
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
return (new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
}
protected float a() {
return super.a() * 0.5F;
}
protected float getPower() {
return super.getPower() * 1.25F;
}
}).a(isourceblock, itemstack);
}
});
BlockDispenser.REGISTRY.a(Items.LINGERING_POTION, new IDispenseBehavior() {
public ItemStack a(ISourceBlock isourceblock, final ItemStack itemstack) {
return (new DispenseBehaviorProjectile() {
protected IProjectile a(World world, IPosition iposition, ItemStack itemstack1) { // CraftBukkit - decompile issue
return new EntityPotion(world, iposition.getX(), iposition.getY(), iposition.getZ(), itemstack1.cloneItemStack());
}
protected float a() {
return super.a() * 0.5F;
}
protected float getPower() {
return super.getPower() * 1.25F;
}
}).a(isourceblock, itemstack);
}
});
BlockDispenser.REGISTRY.a(Items.SPAWN_EGG, new DispenseBehaviorItem() {
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
double d1 = (double) ((float) (isourceblock.getBlockPosition().getY() + enumdirection.getAdjacentY()) + 0.2F);
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
// Entity entity = ItemMonsterEgg.a(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), d0, d1, d2);
// CraftBukkit start
World world = isourceblock.getWorld();
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
itemstack.add(1);
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
itemstack.add(1);
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
Entity entity = ItemMonsterEgg.spawnCreature(isourceblock.getWorld(), ItemMonsterEgg.h(itemstack), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG);
if (entity instanceof EntityLiving && itemstack.hasName()) {
entity.setCustomName(itemstack.getName());
}
ItemMonsterEgg.a(isourceblock.getWorld(), (EntityHuman) null, itemstack, entity);
// itemstack.subtract(1);// Handled during event processing
// CraftBukkit end
return itemstack;
}
});
BlockDispenser.REGISTRY.a(Items.FIREWORKS, new DispenseBehaviorItem() {
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX();
double d1 = (double) ((float) isourceblock.getBlockPosition().getY() + 0.2F);
double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ();
// CraftBukkit start
World world = isourceblock.getWorld();
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
itemstack.add(1);
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
itemstack.add(1);
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
itemstack1 = CraftItemStack.asNMSCopy(event.getItem());
EntityFireworks entityfireworks = new EntityFireworks(isourceblock.getWorld(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), itemstack1);
isourceblock.getWorld().addEntity(entityfireworks);
// itemstack.subtract(1); // Handled during event processing
// CraftBukkit end
return itemstack;
}
protected void a(ISourceBlock isourceblock) {
isourceblock.getWorld().triggerEffect(1004, isourceblock.getBlockPosition(), 0);
}
});
BlockDispenser.REGISTRY.a(Items.FIRE_CHARGE, new DispenseBehaviorItem() {
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
IPosition iposition = BlockDispenser.a(isourceblock);
double d0 = iposition.getX() + (double) ((float) enumdirection.getAdjacentX() * 0.3F);
double d1 = iposition.getY() + (double) ((float) enumdirection.getAdjacentY() * 0.3F);
double d2 = iposition.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 0.3F);
World world = isourceblock.getWorld();
Random random = world.random;
double d3 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentX();
double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY();
double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ();
// CraftBukkit start
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
itemstack.add(1);
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
itemstack.add(1);
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
EntitySmallFireball fireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
fireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity());
world.addEntity(fireball);
// itemstack.subtract(1); // Handled during event processing
// CraftBukkit end
return itemstack;
}
protected void a(ISourceBlock isourceblock) {
isourceblock.getWorld().triggerEffect(1018, isourceblock.getBlockPosition(), 0);
}
});
BlockDispenser.REGISTRY.a(Items.aH, new DispenserRegistry.a(EntityBoat.EnumBoatType.OAK));
BlockDispenser.REGISTRY.a(Items.aI, new DispenserRegistry.a(EntityBoat.EnumBoatType.SPRUCE));
BlockDispenser.REGISTRY.a(Items.aJ, new DispenserRegistry.a(EntityBoat.EnumBoatType.BIRCH));
BlockDispenser.REGISTRY.a(Items.aK, new DispenserRegistry.a(EntityBoat.EnumBoatType.JUNGLE));
BlockDispenser.REGISTRY.a(Items.aM, new DispenserRegistry.a(EntityBoat.EnumBoatType.DARK_OAK));
BlockDispenser.REGISTRY.a(Items.aL, new DispenserRegistry.a(EntityBoat.EnumBoatType.ACACIA));
DispenseBehaviorItem dispensebehavioritem = new DispenseBehaviorItem() {
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
ItemBucket itembucket = (ItemBucket) itemstack.getItem();
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
// CraftBukkit start
World world = isourceblock.getWorld();
int x = blockposition.getX();
int y = blockposition.getY();
int z = blockposition.getZ();
if (world.isEmpty(blockposition) || !world.getType(blockposition).getMaterial().isBuildable()) {
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem();
}
// CraftBukkit end
if (itembucket.a((EntityHuman) null, isourceblock.getWorld(), blockposition)) {
// CraftBukkit start - Handle stacked buckets
Item item = Items.BUCKET;
itemstack.subtract(1);
if (itemstack.isEmpty()) {
itemstack.setItem(Items.BUCKET);
itemstack.setCount(1);
} else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
this.b.a(isourceblock, new ItemStack(item));
}
// CraftBukkit end
return itemstack;
} else {
return this.b.a(isourceblock, itemstack);
}
}
};
BlockDispenser.REGISTRY.a(Items.LAVA_BUCKET, dispensebehavioritem);
BlockDispenser.REGISTRY.a(Items.WATER_BUCKET, dispensebehavioritem);
BlockDispenser.REGISTRY.a(Items.BUCKET, new DispenseBehaviorItem() {
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
World world = isourceblock.getWorld();
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
IBlockData iblockdata = world.getType(blockposition);
Block block = iblockdata.getBlock();
Material material = iblockdata.getMaterial();
Item item;
if (Material.WATER.equals(material) && block instanceof BlockFluids && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0) {
item = Items.WATER_BUCKET;
} else {
if (!Material.LAVA.equals(material) || !(block instanceof BlockFluids) || ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() != 0) {
return super.b(isourceblock, itemstack);
}
item = Items.LAVA_BUCKET;
}
// CraftBukkit start
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
// CraftBukkit end
world.setAir(blockposition);
itemstack.subtract(1);
if (itemstack.isEmpty()) {
return new ItemStack(item);
} else {
if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) {
this.b.a(isourceblock, new ItemStack(item));
}
return itemstack;
}
}
});
BlockDispenser.REGISTRY.a(Items.FLINT_AND_STEEL, new DispenserRegistry.b() {
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
World world = isourceblock.getWorld();
// CraftBukkit start
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
// CraftBukkit end
this.b = true;
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
if (world.isEmpty(blockposition)) {
// CraftBukkit start - Ignition by dispensing flint and steel
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()).isCancelled()) {
world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
if (itemstack.isDamaged(1, world.random, (EntityPlayer) null)) {
itemstack.setCount(0);
}
}
// CraftBukkit end
} else if (world.getType(blockposition).getBlock() == Blocks.TNT) {
Blocks.TNT.postBreak(world, blockposition, Blocks.TNT.getBlockData().set(BlockTNT.EXPLODE, Boolean.valueOf(true)));
world.setAir(blockposition);
} else {
this.b = false;
}
return itemstack;
}
});
BlockDispenser.REGISTRY.a(Items.DYE, new DispenserRegistry.b() {
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
this.b = true;
if (EnumColor.WHITE == EnumColor.fromInvColorIndex(itemstack.getData())) {
World world = isourceblock.getWorld();
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
// CraftBukkit start
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
world.captureTreeGeneration = true;
// CraftBukkit end
if (ItemDye.a(itemstack, world, blockposition)) {
if (!world.isClientSide) {
world.triggerEffect(2005, blockposition, 0);
}
} else {
this.b = false;
}
// CraftBukkit start
world.captureTreeGeneration = false;
if (world.capturedBlockStates.size() > 0) {
TreeType treeType = BlockSapling.treeType;
BlockSapling.treeType = null;
Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ());
List<org.bukkit.block.BlockState> blocks = (List<org.bukkit.block.BlockState>) world.capturedBlockStates.clone();
world.capturedBlockStates.clear();
StructureGrowEvent structureEvent = null;
if (treeType != null) {
structureEvent = new StructureGrowEvent(location, treeType, false, null, blocks);
org.bukkit.Bukkit.getPluginManager().callEvent(structureEvent);
}
if (structureEvent == null || !structureEvent.isCancelled()) {
for (org.bukkit.block.BlockState blockstate : blocks) {
blockstate.update(true);
}
}
}
// CraftBukkit end
return itemstack;
} else {
return super.b(isourceblock, itemstack);
}
}
});
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.TNT), new DispenseBehaviorItem() {
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
World world = isourceblock.getWorld();
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
// EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null);
// CraftBukkit start
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
itemstack.add(1);
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
itemstack.add(1);
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null);
// CraftBukkit end
world.addEntity(entitytntprimed);
world.a((EntityHuman) null, entitytntprimed.locX, entitytntprimed.locY, entitytntprimed.locZ, SoundEffects.hW, SoundCategory.BLOCKS, 1.0F, 1.0F);
// itemstack.subtract(1); // CraftBukkit - handled above
return itemstack;
}
});
BlockDispenser.REGISTRY.a(Items.SKULL, new DispenserRegistry.b() {
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
World world = isourceblock.getWorld();
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
BlockSkull blockskull = Blocks.SKULL;
// CraftBukkit start
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
// CraftBukkit end
this.b = true;
if (world.isEmpty(blockposition) && blockskull.b(world, blockposition, itemstack)) {
if (!world.isClientSide) {
world.setTypeAndData(blockposition, blockskull.getBlockData().set(BlockSkull.FACING, EnumDirection.UP), 3);
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof TileEntitySkull) {
if (itemstack.getData() == 3) {
GameProfile gameprofile = null;
if (itemstack.hasTag()) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound.hasKeyOfType("SkullOwner", 10)) {
gameprofile = GameProfileSerializer.deserialize(nbttagcompound.getCompound("SkullOwner"));
} else if (nbttagcompound.hasKeyOfType("SkullOwner", 8)) {
String s = nbttagcompound.getString("SkullOwner");
if (!UtilColor.b(s)) {
gameprofile = new GameProfile((UUID) null, s);
}
}
}
((TileEntitySkull) tileentity).setGameProfile(gameprofile);
} else {
((TileEntitySkull) tileentity).setSkullType(itemstack.getData());
}
((TileEntitySkull) tileentity).setRotation(enumdirection.opposite().get2DRotationValue() * 4);
Blocks.SKULL.a(world, blockposition, (TileEntitySkull) tileentity);
}
itemstack.subtract(1);
}
} else if (ItemArmor.a(isourceblock, itemstack).isEmpty()) {
this.b = false;
}
return itemstack;
}
});
BlockDispenser.REGISTRY.a(Item.getItemOf(Blocks.PUMPKIN), new DispenserRegistry.b() {
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
World world = isourceblock.getWorld();
BlockPosition blockposition = isourceblock.getBlockPosition().shift((EnumDirection) isourceblock.e().get(BlockDispenser.FACING));
BlockPumpkin blockpumpkin = (BlockPumpkin) Blocks.PUMPKIN;
// CraftBukkit start
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
// CraftBukkit end
this.b = true;
if (world.isEmpty(blockposition) && blockpumpkin.b(world, blockposition)) {
if (!world.isClientSide) {
world.setTypeAndData(blockposition, blockpumpkin.getBlockData(), 3);
}
itemstack.subtract(1);
} else {
ItemStack itemstack1 = ItemArmor.a(isourceblock, itemstack);
if (itemstack1.isEmpty()) {
this.b = false;
}
}
return itemstack;
}
});
EnumColor[] aenumcolor = EnumColor.values();
int i = aenumcolor.length;
for (int j = 0; j < i; ++j) {
EnumColor enumcolor = aenumcolor[j];
BlockDispenser.REGISTRY.a(Item.getItemOf(BlockShulkerBox.a(enumcolor)), new DispenserRegistry.c(null));
}
}
public static void c() {
if (!DispenserRegistry.c) {
DispenserRegistry.c = true;
d();
SoundEffect.b();
Block.w();
BlockFire.e();
MobEffectList.k();
Enchantment.g();
Item.t();
PotionRegistry.b();
PotionBrewer.a();
EntityTypes.c();
BiomeBase.q();
b();
if (!CraftingManager.init()) {
DispenserRegistry.b = true;
DispenserRegistry.d.error("Errors with built-in recipes!");
}
StatisticList.a();
if (DispenserRegistry.d.isDebugEnabled()) {
if ((new AdvancementDataWorld((File) null)).b()) {
DispenserRegistry.b = true;
DispenserRegistry.d.error("Errors with built-in advancements!");
}
if (!LootTables.b()) {
DispenserRegistry.b = true;
DispenserRegistry.d.error("Errors with built-in loot tables");
}
}
}
}
private static void d() {
if (DispenserRegistry.d.isDebugEnabled()) {
System.setErr(new DebugOutputStream("STDERR", System.err));
System.setOut(new DebugOutputStream("STDOUT", DispenserRegistry.a));
} else {
System.setErr(new RedirectStream("STDERR", System.err));
System.setOut(new RedirectStream("STDOUT", DispenserRegistry.a));
}
}
static class c extends DispenserRegistry.b {
private c() {}
protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
Block block = Block.asBlock(itemstack.getItem());
World world = isourceblock.getWorld();
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
// CraftBukkit start
org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack);
BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ()));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
// CraftBukkit end
this.b = world.a(block, blockposition, false, EnumDirection.DOWN, (Entity) null);
if (this.b) {
EnumDirection enumdirection1 = world.isEmpty(blockposition.down()) ? enumdirection : EnumDirection.UP;
IBlockData iblockdata = block.getBlockData().set(BlockShulkerBox.a, enumdirection1);
// Dionysus start - fix Dispenser crashes
boolean wasPlaced = world.setTypeUpdate(blockposition, iblockdata);
if (!wasPlaced) {
return itemstack;
}
// Dionysus end
TileEntity tileentity = world.getTileEntity(blockposition);
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
if (itemstack1.hasTag()) {
((TileEntityShulkerBox) tileentity).e(itemstack1.getTag().getCompound("BlockEntityTag"));
}
if (itemstack1.hasName()) {
((TileEntityShulkerBox) tileentity).setCustomName(itemstack1.getName());
}
world.updateAdjacentComparators(blockposition, iblockdata.getBlock());
}
return itemstack;
}
c(Object object) {
this();
}
}
public abstract static class b extends DispenseBehaviorItem {
protected boolean b = true;
public b() {}
protected void a(ISourceBlock isourceblock) {
isourceblock.getWorld().triggerEffect(this.b ? 1000 : 1001, isourceblock.getBlockPosition(), 0);
}
}
public static class a extends DispenseBehaviorItem {
private final DispenseBehaviorItem b = new DispenseBehaviorItem();
private final EntityBoat.EnumBoatType c;
public a(EntityBoat.EnumBoatType entityboat_enumboattype) {
this.c = entityboat_enumboattype;
}
public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) {
EnumDirection enumdirection = (EnumDirection) isourceblock.e().get(BlockDispenser.FACING);
World world = isourceblock.getWorld();
double d0 = isourceblock.getX() + (double) ((float) enumdirection.getAdjacentX() * 1.125F);
double d1 = isourceblock.getY() + (double) ((float) enumdirection.getAdjacentY() * 1.125F);
double d2 = isourceblock.getZ() + (double) ((float) enumdirection.getAdjacentZ() * 1.125F);
BlockPosition blockposition = isourceblock.getBlockPosition().shift(enumdirection);
Material material = world.getType(blockposition).getMaterial();
double d3;
if (Material.WATER.equals(material)) {
d3 = 1.0D;
} else {
if (!Material.AIR.equals(material) || !Material.WATER.equals(world.getType(blockposition.down()).getMaterial())) {
return this.b.a(isourceblock, itemstack);
}
d3 = 0.0D;
}
// EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2);
// CraftBukkit start
ItemStack itemstack1 = itemstack.cloneAndSubtract(1);
org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ());
CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1);
BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2));
if (!BlockDispenser.eventFired) {
world.getServer().getPluginManager().callEvent(event);
}
if (event.isCancelled()) {
itemstack.add(1);
return itemstack;
}
if (!event.getItem().equals(craftItem)) {
itemstack.add(1);
// Chain to handler for new item
ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem());
IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.REGISTRY.get(eventStack.getItem());
if (idispensebehavior != IDispenseBehavior.NONE && idispensebehavior != this) {
idispensebehavior.a(isourceblock, eventStack);
return itemstack;
}
}
EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ());
// CraftBukkit end
entityboat.setType(this.c);
entityboat.yaw = enumdirection.l();
if (!world.addEntity(entityboat)) itemstack.add(1); // CraftBukkit
// itemstack.subtract(1); // CraftBukkit - handled during event processing
return itemstack;
}
protected void a(ISourceBlock isourceblock) {
isourceblock.getWorld().triggerEffect(1000, isourceblock.getBlockPosition(), 0);
}
}
}

View File

@@ -118,6 +118,7 @@ public class EnchantmentManager {
EnchantmentManager.a.a = 0;
EnchantmentManager.a.b = damagesource;
a(EnchantmentManager.a, iterable);
EnchantmentManager.a.b = null; // Reaper - Fix MC-128547
return EnchantmentManager.a.a;
}
@@ -144,6 +145,11 @@ public class EnchantmentManager {
if (entity instanceof EntityHuman) {
a(EnchantmentManager.c, entityliving.getItemInMainHand());
}
// Reaper start - Fix MC-128547
EnchantmentManager.c.b = null;
EnchantmentManager.c.a = null;
// Reaper end
}
@@ -157,7 +163,10 @@ public class EnchantmentManager {
if (entityliving instanceof EntityHuman) {
a(EnchantmentManager.d, entityliving.getItemInMainHand());
}
// Reaper start - Fix MC-128547
EnchantmentManager.d.b = null;
EnchantmentManager.d.a = null;
// Reaper end
}
public static int a(Enchantment enchantment, EntityLiving entityliving) {

View File

@@ -27,6 +27,9 @@ import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Vehicle;
import co.aikar.timings.MinecraftTimings; // Paper
import co.aikar.timings.Timing; // Paper
import io.akarin.api.internal.Akari;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
@@ -42,6 +45,10 @@ import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.plugin.PluginManager;
// CraftBukkit end
// Dionysus start
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
// Dionysus end
/**
* Akarin Changes Note
* 1) Random -> LightRandom (performance)
@@ -51,7 +58,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
// Paper start
public static Random SHARED_RANDOM = new io.akarin.api.internal.utils.random.LightRandom() { // Akarin - LightRNG
public static Random SHARED_RANDOM = new java.util.Random() {
private boolean locked = false;
@Override
public synchronized void setSeed(long seed) {
@@ -63,7 +70,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
}
}
};
Object entitySlice = null;
List<Entity> entitySlice = null;
// Paper end
static boolean isLevelAtLeast(NBTTagCompound tag, int level) {
return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
@@ -85,10 +92,10 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
private static final List<ItemStack> b = Collections.emptyList();
private static final AxisAlignedBB c = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
private static double f = 1.0D;
private static int entityCount;
private static int entityCount = 1; // Paper - MC-111480 - ID 0 is treated as special for DataWatchers, start 1
private int id;
public boolean i; public boolean blocksEntitySpawning() { return i; } // Paper - OBFHELPER
public final List<Entity> passengers;
public final ObjectArrayList<Entity> passengers; // Dionysus
protected int j;
private Entity au;public void setVehicle(Entity entity) { this.au = entity; } // Paper // OBFHELPER
public boolean attachedToPlayer;
@@ -128,6 +135,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
protected boolean E;
private boolean aw;
public boolean dead;
public boolean shouldBeRemoved; // Paper
public float width;
public float length;
public float I;
@@ -201,13 +209,13 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
public Entity(World world) {
this.id = Entity.entityCount++;
this.passengers = Lists.newArrayList();
this.passengers = new ObjectArrayList<>(); // Dionysus
this.boundingBox = Entity.c;
this.width = 0.6F;
this.length = 1.8F;
this.ax = 1;
this.ay = 1.0F;
this.random = SHARED_RANDOM; // Paper
this.random = world == null ? SHARED_RANDOM : ((IMixinWorldServer) world).rand(); // Paper // Akarin
this.fireTicks = -this.getMaxFireTicks();
this.justCreated = true;
this.uniqueID = MathHelper.a(this.random);
@@ -344,6 +352,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
this.locX = d0;
this.locY = d1;
this.locZ = d2;
if (valid) world.entityJoinedWorld(this, false); // Paper - ensure Entity is moved to its proper chunk
float f = this.width / 2.0F;
float f1 = this.length;
@@ -989,6 +998,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
this.locX = (axisalignedbb.a + axisalignedbb.d) / 2.0D;
this.locY = axisalignedbb.b;
this.locZ = (axisalignedbb.c + axisalignedbb.f) / 2.0D;
if (valid) world.entityJoinedWorld(this, false); // Paper - ensure Entity is moved to its proper chunk
}
protected SoundEffect ae() {
@@ -1310,6 +1320,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
this.lastYaw -= 360.0F;
}
world.getChunkAt((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); // Paper - ensure chunk is always loaded
this.setPosition(this.locX, this.locY, this.locZ);
this.setYawPitch(f, f1);
}
@@ -1376,37 +1387,33 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
public void d(EntityHuman entityhuman) {}
public void collide(Entity entity) {
if (!this.x(entity)) {
if (!entity.noclip && !this.noclip) {
double d0 = entity.locX - this.locX;
double d1 = entity.locZ - this.locZ;
double d2 = MathHelper.a(d0, d1);
if (entity.noclip || this.noclip || this.x(entity)) return; // NeonPaper - Test this earlier
double d0 = entity.locX - this.locX;
double d1 = entity.locZ - this.locZ;
double d2 = MathHelper.a(d0, d1);
if (d2 >= 0.009999999776482582D) {
d2 = (double) MathHelper.sqrt(d2);
d0 /= d2;
d1 /= d2;
double d3 = 1.0D / d2;
if (d2 >= 0.009999999776482582D) {
d2 = (double) MathHelper.sqrt(d2);
d0 /= d2;
d1 /= d2;
double d3 = 1.0D / d2;
if (d3 > 1.0D) {
d3 = 1.0D;
}
if (d3 > 1.0D) {
d3 = 1.0D;
}
d0 *= d3;
d1 *= d3;
d0 *= 0.05000000074505806D;
d1 *= 0.05000000074505806D;
d0 *= (double) (1.0F - this.R);
d1 *= (double) (1.0F - this.R);
if (!this.isVehicle()) {
this.f(-d0, 0.0D, -d1);
}
if (!entity.isVehicle()) {
entity.f(d0, 0.0D, d1);
}
}
d0 *= d3;
d1 *= d3;
d0 *= 0.05000000074505806D;
d1 *= 0.05000000074505806D;
d0 *= (double) (1.0F - this.R);
d1 *= (double) (1.0F - this.R);
if (!this.isVehicle()) {
this.f(-d0, 0.0D, -d1);
}
if (!entity.isVehicle()) {
entity.f(d0, 0.0D, d1);
}
}
}
@@ -1471,6 +1478,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
return false;
}
public void runKillTrigger(Entity entity, int kills, DamageSource damageSource) { this.a(entity, kills, damageSource); } // Paper - OBFHELPER
public void a(Entity entity, int i, DamageSource damagesource) {
if (entity instanceof EntityPlayer) {
CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource);
@@ -1791,7 +1799,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
*/
public Chunk getCurrentChunk() {
final Chunk chunk = currentChunk != null ? currentChunk.get() : null;
return chunk != null && chunk.isLoaded() ? chunk : null;
return chunk != null && chunk.isLoaded() ? chunk : (isAddedToChunk() ? world.getChunkIfLoaded(getChunkX(), getChunkZ()) : null);
}
/**
* Returns the chunk at the location, using the entities local cache if avail
@@ -1813,21 +1821,26 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
public Chunk getChunkAtLocation() {
return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4);
}
public final MinecraftKey entityKey = EntityTypes.getKey(this);
public final String entityKeyString = entityKey != null ? entityKey.toString() : null;
private String entityKeyString = null;
private MinecraftKey entityKey = getMinecraftKey();
@Override
public MinecraftKey getMinecraftKey() {
if (entityKey == null) {
entityKey = EntityTypes.getKey(this);
entityKeyString = entityKey != null ? entityKey.toString() : null;
}
return entityKey;
}
@Override
public String getMinecraftKeyString() {
getMinecraftKey(); // Try to load if it doesn't exists. see: https://github.com/PaperMC/Paper/issues/1280
return entityKeyString;
}
@Nullable
public final String getSaveID() {
return entityKeyString;
return getMinecraftKeyString();
// Paper end
}
@@ -2262,6 +2275,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
}
public void onKill(EntityLiving entityLiving) { this.b(entityLiving); } // Paper - OBFHELPER
public void b(EntityLiving entityliving) {}
protected boolean i(double d0, double d1, double d2) {
@@ -2867,7 +2881,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
}
public List<Entity> bF() {
return (List) (this.passengers.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.passengers));
return (List) (this.passengers.isEmpty() ? Collections.emptyList() : new ObjectArrayList<>(this.passengers)); // Dionysus
}
public boolean w(Entity entity) {
@@ -2960,6 +2974,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper
return EnumPistonReaction.NORMAL;
}
public SoundCategory getDeathSoundCategory() { return bK();} // Paper - OBFHELPER
public SoundCategory bK() {
return SoundCategory.NEUTRAL;
}

View File

@@ -0,0 +1,476 @@
package net.minecraft.server;
import com.destroystokyo.paper.event.entity.EndermanEscapeEvent;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Sets;
import org.bukkit.event.entity.EntityTargetEvent;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
public class EntityEnderman extends EntityMonster {
private static final UUID a = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0");
private static final AttributeModifier b = (new AttributeModifier(EntityEnderman.a, "Attacking speed boost", 0.15000000596046448D, 0)).a(false);
private static final Set<Block> c = Sets.newIdentityHashSet();
private static final DataWatcherObject<Optional<IBlockData>> bx = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.g);
private static final DataWatcherObject<Boolean> by = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.h);
private int bz;
private int bA;
public EntityEnderman(World world) {
super(world);
this.setSize(0.6F, 2.9F);
this.P = 1.0F;
this.a(PathType.WATER, -1.0F);
}
protected void r() {
this.goalSelector.a(0, new PathfinderGoalFloat(this));
this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, 1.0D, false));
this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D, 0.0F));
this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F));
this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this));
this.goalSelector.a(10, new EntityEnderman.PathfinderGoalEndermanPlaceBlock(this));
this.goalSelector.a(11, new EntityEnderman.PathfinderGoalEndermanPickupBlock(this));
this.targetSelector.a(1, new EntityEnderman.PathfinderGoalPlayerWhoLookedAtTarget(this));
this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0]));
this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityEndermite.class, 10, true, false, new Predicate() {
public boolean a(@Nullable EntityEndermite entityendermite) {
return entityendermite.p();
}
public boolean apply(@Nullable Object object) {
return this.a((EntityEndermite) object);
}
}));
}
protected void initAttributes() {
super.initAttributes();
this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D);
this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D);
this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(7.0D);
this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(64.0D);
}
public void setGoalTarget(@Nullable EntityLiving entityliving) {
// CraftBukkit start - fire event
setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true);
}
// Paper start
private boolean tryEscape(EndermanEscapeEvent.Reason reason) {
return new EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent();
}
// Paper end
@Override
public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) {
if (!super.setGoalTarget(entityliving, reason, fireEvent)) {
return false;
}
entityliving = getGoalTarget();
// CraftBukkit end
AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED);
if (entityliving == null) {
this.bA = 0;
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(false));
attributeinstance.c(EntityEnderman.b);
} else {
this.bA = this.ticksLived;
this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(true));
if (!attributeinstance.a(EntityEnderman.b)) {
attributeinstance.b(EntityEnderman.b);
}
}
return true;
}
protected void i() {
super.i();
this.datawatcher.register(EntityEnderman.bx, Optional.absent());
this.datawatcher.register(EntityEnderman.by, Boolean.valueOf(false));
}
public void p() {
if (this.ticksLived >= this.bz + 400) {
this.bz = this.ticksLived;
if (!this.isSilent()) {
this.world.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, SoundEffects.bh, this.bK(), 2.5F, 1.0F, false);
}
}
}
public void a(DataWatcherObject<?> datawatcherobject) {
if (EntityEnderman.by.equals(datawatcherobject) && this.do_() && this.world.isClientSide) {
this.p();
}
super.a(datawatcherobject);
}
public static void a(DataConverterManager dataconvertermanager) {
EntityInsentient.a(dataconvertermanager, EntityEnderman.class);
}
public void b(NBTTagCompound nbttagcompound) {
super.b(nbttagcompound);
IBlockData iblockdata = this.getCarried();
if (iblockdata != null) {
nbttagcompound.setShort("carried", (short) Block.getId(iblockdata.getBlock()));
nbttagcompound.setShort("carriedData", (short) iblockdata.getBlock().toLegacyData(iblockdata));
}
}
public void a(NBTTagCompound nbttagcompound) {
super.a(nbttagcompound);
IBlockData iblockdata;
if (nbttagcompound.hasKeyOfType("carried", 8)) {
iblockdata = Block.getByName(nbttagcompound.getString("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
} else {
iblockdata = Block.getById(nbttagcompound.getShort("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff');
}
if (iblockdata == null || iblockdata.getBlock() == null || iblockdata.getMaterial() == Material.AIR) {
iblockdata = null;
}
this.setCarried(iblockdata);
}
// Paper start - OBFHELPER - ok not really, but verify this on updates
private boolean f(EntityHuman entityhuman) {
boolean shouldAttack = f_real(entityhuman);
com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) entityhuman.getBukkitEntity());
event.setCancelled(!shouldAttack);
return event.callEvent();
}
private boolean f_real(EntityHuman entityhuman) {
// Paper end
ItemStack itemstack = (ItemStack) entityhuman.inventory.armor.get(3);
if (itemstack.getItem() == Item.getItemOf(Blocks.PUMPKIN)) {
return false;
} else {
Vec3D vec3d = entityhuman.e(1.0F).a();
Vec3D vec3d1 = new Vec3D(this.locX - entityhuman.locX, this.getBoundingBox().b + (double) this.getHeadHeight() - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ);
double d0 = vec3d1.b();
vec3d1 = vec3d1.a();
double d1 = vec3d.b(vec3d1);
return d1 > 1.0D - 0.025D / d0 ? entityhuman.hasLineOfSight(this) : false;
}
}
public float getHeadHeight() {
return 2.55F;
}
public void n() {
if (this.world.isClientSide) {
for (int i = 0; i < 2; ++i) {
this.world.addParticle(EnumParticle.PORTAL, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D, new int[0]);
}
}
this.bd = false;
super.n();
}
protected void M() {
if (this.an()) {
this.damageEntity(DamageSource.DROWN, 1.0F);
}
if (this.world.D() && this.ticksLived >= this.bA + 600) {
float f = this.aw();
if (f > 0.5F && this.world.h(new BlockPosition(this)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && tryEscape(EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper
this.setGoalTarget((EntityLiving) null);
this.dm();
}
}
super.M();
}
public boolean teleportRandomly() { return dm(); } // Paper - OBFHELPER
protected boolean dm() {
double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D;
double d1 = this.locY + (double) (this.random.nextInt(64) - 32);
double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D;
return this.k(d0, d1, d2);
}
protected boolean a(Entity entity) {
Vec3D vec3d = new Vec3D(this.locX - entity.locX, this.getBoundingBox().b + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ);
vec3d = vec3d.a();
double d0 = 16.0D;
double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D;
double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D;
double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D;
return this.k(d1, d2, d3);
}
private boolean k(double d0, double d1, double d2) {
boolean flag = this.j(d0, d1, d2);
if (flag) {
this.world.a((EntityHuman) null, this.lastX, this.lastY, this.lastZ, SoundEffects.bi, this.bK(), 1.0F, 1.0F);
this.a(SoundEffects.bi, 1.0F, 1.0F);
}
return flag;
}
protected SoundEffect F() {
return this.do_() ? SoundEffects.bg : SoundEffects.bd;
}
protected SoundEffect d(DamageSource damagesource) {
return SoundEffects.bf;
}
protected SoundEffect cf() {
return SoundEffects.be;
}
protected void dropEquipment(boolean flag, int i) {
super.dropEquipment(flag, i);
IBlockData iblockdata = this.getCarried();
if (iblockdata != null) {
Item item = Item.getItemOf(iblockdata.getBlock());
int j = item.k() ? iblockdata.getBlock().toLegacyData(iblockdata) : 0;
this.a(new ItemStack(item, 1, j), 0.0F);
}
}
@Nullable
protected MinecraftKey J() {
return LootTables.w;
}
public void setCarried(@Nullable IBlockData iblockdata) {
this.datawatcher.set(EntityEnderman.bx, Optional.fromNullable(iblockdata));
}
@Nullable
public IBlockData getCarried() {
return (IBlockData) ((Optional) this.datawatcher.get(EntityEnderman.bx)).orNull();
}
public boolean damageEntity(DamageSource damagesource, float f) {
if (this.isInvulnerable(damagesource)) {
return false;
} else if (damagesource instanceof EntityDamageSourceIndirect && tryEscape(EndermanEscapeEvent.Reason.INDIRECT)) { // Paper
for (int i = 0; i < 64; ++i) {
if (this.dm()) {
return true;
}
}
return false;
} else {
boolean flag = super.damageEntity(damagesource, f);
if (damagesource.ignoresArmor() && this.random.nextInt(10) != 0 && tryEscape(damagesource == DamageSource.DROWN ? EndermanEscapeEvent.Reason.DROWN : EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper
this.dm();
}
return flag;
}
}
public boolean do_() {
return ((Boolean) this.datawatcher.get(EntityEnderman.by)).booleanValue();
}
static {
EntityEnderman.c.add(Blocks.GRASS);
EntityEnderman.c.add(Blocks.DIRT);
EntityEnderman.c.add(Blocks.SAND);
EntityEnderman.c.add(Blocks.GRAVEL);
EntityEnderman.c.add(Blocks.YELLOW_FLOWER);
EntityEnderman.c.add(Blocks.RED_FLOWER);
EntityEnderman.c.add(Blocks.BROWN_MUSHROOM);
EntityEnderman.c.add(Blocks.RED_MUSHROOM);
EntityEnderman.c.add(Blocks.TNT);
EntityEnderman.c.add(Blocks.CACTUS);
EntityEnderman.c.add(Blocks.CLAY);
EntityEnderman.c.add(Blocks.PUMPKIN);
EntityEnderman.c.add(Blocks.MELON_BLOCK);
EntityEnderman.c.add(Blocks.MYCELIUM);
EntityEnderman.c.add(Blocks.NETHERRACK);
}
static class PathfinderGoalEndermanPickupBlock extends PathfinderGoal {
private final EntityEnderman enderman;
public PathfinderGoalEndermanPickupBlock(EntityEnderman entityenderman) {
this.enderman = entityenderman;
}
public boolean a() {
return this.enderman.getCarried() != null ? false : (!this.enderman.world.getGameRules().getBoolean("mobGriefing") ? false : this.enderman.getRandom().nextInt(20) == 0);
}
public void e() {
Random random = this.enderman.getRandom();
World world = this.enderman.world;
int i = MathHelper.floor(this.enderman.locX - 2.0D + random.nextDouble() * 4.0D);
int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D);
int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D);
BlockPosition blockposition = new BlockPosition(i, j, k);
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
if (iblockdata == null) return; // NeonPaper
Block block = iblockdata.getBlock();
MovingObjectPosition movingobjectposition = world.rayTrace(new Vec3D((double) ((float) MathHelper.floor(this.enderman.locX) + 0.5F), (double) ((float) j + 0.5F), (double) ((float) MathHelper.floor(this.enderman.locZ) + 0.5F)), new Vec3D((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)), false, true, false);
boolean flag = movingobjectposition != null && movingobjectposition.a().equals(blockposition);
if (EntityEnderman.c.contains(block) && flag) {
// CraftBukkit start - Pickup event
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, this.enderman.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), org.bukkit.Material.AIR).isCancelled()) {
this.enderman.setCarried(iblockdata);
world.setAir(blockposition);
}
// CraftBukkit end
}
}
}
static class PathfinderGoalEndermanPlaceBlock extends PathfinderGoal {
private final EntityEnderman a;
public PathfinderGoalEndermanPlaceBlock(EntityEnderman entityenderman) {
this.a = entityenderman;
}
public boolean a() {
return this.a.getCarried() == null ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : this.a.getRandom().nextInt(2000) == 0);
}
public void e() {
Random random = this.a.getRandom();
World world = this.a.world;
int i = MathHelper.floor(this.a.locX - 1.0D + random.nextDouble() * 2.0D);
int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D);
int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D);
BlockPosition blockposition = new BlockPosition(i, j, k);
IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper
if (iblockdata == null) return; // NeonPaper
IBlockData iblockdata1 = world.getType(blockposition.down());
IBlockData iblockdata2 = this.a.getCarried();
if (iblockdata2 != null && this.a(world, blockposition, iblockdata2.getBlock(), iblockdata, iblockdata1)) {
// CraftBukkit start - Place event
if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, this.a.getCarried().getBlock(), this.a.getCarried().getBlock().toLegacyData(this.a.getCarried())).isCancelled()) {
world.setTypeAndData(blockposition, iblockdata2, 3);
this.a.setCarried((IBlockData) null);
}
// CraftBukkit end
}
}
private boolean a(World world, BlockPosition blockposition, Block block, IBlockData iblockdata, IBlockData iblockdata1) {
return !block.canPlace(world, blockposition) ? false : (iblockdata.getMaterial() != Material.AIR ? false : (iblockdata1.getMaterial() == Material.AIR ? false : iblockdata1.g()));
}
}
static class PathfinderGoalPlayerWhoLookedAtTarget extends PathfinderGoalNearestAttackableTarget<EntityHuman> {
private final EntityEnderman i; public EntityEnderman getEnderman() { return i; } // Paper - OBFHELPER
private EntityHuman j;
private int k;
private int l;
public PathfinderGoalPlayerWhoLookedAtTarget(EntityEnderman entityenderman) {
super(entityenderman, EntityHuman.class, false);
this.i = entityenderman;
}
public boolean a() {
double d0 = this.i();
this.j = this.i.world.a(this.i.locX, this.i.locY, this.i.locZ, d0, d0, (Function) null, new Predicate() {
public boolean a(@Nullable EntityHuman entityhuman) {
return entityhuman != null && PathfinderGoalPlayerWhoLookedAtTarget.this.i.f(entityhuman);
}
public boolean apply(@Nullable Object object) {
return this.a((EntityHuman) object);
}
});
return this.j != null;
}
public void c() {
this.k = 5;
this.l = 0;
}
public void d() {
this.j = null;
super.d();
}
public boolean b() {
if (this.j != null) {
if (!this.i.f(this.j)) {
return false;
} else {
this.i.a((Entity) this.j, 10.0F, 10.0F);
return true;
}
} else {
return this.d != null && ((EntityHuman) this.d).isAlive() ? true : super.b();
}
}
public void e() {
if (this.j != null) {
if (--this.k <= 0) {
this.d = this.j;
this.j = null;
super.c();
}
} else {
if (this.d != null) {
if (this.i.f((EntityHuman) this.d)) {
if (((EntityHuman) this.d).h(this.i) < 16.0D && this.getEnderman().tryEscape(EndermanEscapeEvent.Reason.STARE)) { // Paper
this.i.dm();
}
this.l = 0;
} else if (((EntityHuman) this.d).h(this.i) > 256.0D && this.l++ >= 30 && this.i.a((Entity) this.d)) {
this.l = 0;
}
}
super.e();
}
}
}
}

View File

@@ -0,0 +1,538 @@
package net.minecraft.server;
import java.util.Iterator;
import java.util.List;
// CraftBukkit start
import org.bukkit.entity.Player;
import org.bukkit.entity.Fish;
import org.bukkit.event.player.PlayerFishEvent;
// CraftBukkit end
public class EntityFishingHook extends Entity {
private static final DataWatcherObject<Integer> b = DataWatcher.a(EntityFishingHook.class, DataWatcherRegistry.b);
private boolean isInGround;
private int d;
public EntityHuman owner;
private int f;
private int g;
private int h;
private int at;
private float au;
public Entity hooked;
private EntityFishingHook.HookState av;
private int aw;
private int ax;
public EntityFishingHook(World world, EntityHuman entityhuman) {
super(world);
this.av = EntityFishingHook.HookState.FLYING;
this.a(entityhuman);
this.n();
}
private void a(EntityHuman entityhuman) {
this.setSize(0.25F, 0.25F);
this.ah = true;
this.owner = entityhuman;
this.owner.hookedFish = this;
}
public void a(int i) {
this.ax = i;
}
public void c(int i) {
this.aw = i;
}
private void n() {
float f = this.owner.lastPitch + (this.owner.pitch - this.owner.lastPitch);
float f1 = this.owner.lastYaw + (this.owner.yaw - this.owner.lastYaw);
float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F);
float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F);
float f4 = -MathHelper.cos(-f * 0.017453292F);
float f5 = MathHelper.sin(-f * 0.017453292F);
double d0 = this.owner.lastX + (this.owner.locX - this.owner.lastX) - (double) f3 * 0.3D;
double d1 = this.owner.lastY + (this.owner.locY - this.owner.lastY) + (double) this.owner.getHeadHeight();
double d2 = this.owner.lastZ + (this.owner.locZ - this.owner.lastZ) - (double) f2 * 0.3D;
this.setPositionRotation(d0, d1, d2, f1, f);
this.motX = (double) (-f3);
this.motY = (double) MathHelper.a(-(f5 / f4), -5.0F, 5.0F);
this.motZ = (double) (-f2);
float f6 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ);
this.motX *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
this.motY *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
this.motZ *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D;
float f7 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
this.pitch = (float) (MathHelper.c(this.motY, (double) f7) * 57.2957763671875D);
this.lastYaw = this.yaw;
this.lastPitch = this.pitch;
}
protected void i() {
this.getDataWatcher().register(EntityFishingHook.b, Integer.valueOf(0));
}
public void a(DataWatcherObject<?> datawatcherobject) {
if (EntityFishingHook.b.equals(datawatcherobject)) {
int i = ((Integer) this.getDataWatcher().get(EntityFishingHook.b)).intValue();
this.hooked = i > 0 ? this.world.getEntity(i - 1) : null;
}
super.a(datawatcherobject);
}
public void B_() {
super.B_();
if (this.owner == null) {
this.die();
} else if (this.world.isClientSide || !this.p()) {
if (this.isInGround) {
++this.d;
if (this.d >= 1200) {
this.die();
return;
}
}
float f = 0.0F;
BlockPosition blockposition = new BlockPosition(this);
IBlockData iblockdata = this.world.getType(blockposition);
if (iblockdata.getMaterial() == Material.WATER) {
f = BlockFluids.g(iblockdata, this.world, blockposition);
}
double d0;
if (this.av == EntityFishingHook.HookState.FLYING) {
if (this.hooked != null) {
this.motX = 0.0D;
this.motY = 0.0D;
this.motZ = 0.0D;
this.av = EntityFishingHook.HookState.HOOKED_IN_ENTITY;
return;
}
if (f > 0.0F) {
this.motX *= 0.3D;
this.motY *= 0.2D;
this.motZ *= 0.3D;
this.av = EntityFishingHook.HookState.BOBBING;
return;
}
if (!this.world.isClientSide) {
this.r();
}
if (!this.isInGround && !this.onGround && !this.positionChanged) {
++this.f;
} else {
this.f = 0;
this.motX = 0.0D;
this.motY = 0.0D;
this.motZ = 0.0D;
}
} else {
if (this.av == EntityFishingHook.HookState.HOOKED_IN_ENTITY) {
if (this.hooked != null) {
if (this.hooked.dead) {
this.hooked = null;
this.av = EntityFishingHook.HookState.FLYING;
} else {
this.locX = this.hooked.locX;
double d1 = (double) this.hooked.length;
this.locY = this.hooked.getBoundingBox().b + d1 * 0.8D;
this.locZ = this.hooked.locZ;
this.setPosition(this.locX, this.locY, this.locZ);
if (this.ak) this.die(); // NeonPaper - Prevent going through portals
}
}
return;
}
if (this.av == EntityFishingHook.HookState.BOBBING) {
this.motX *= 0.9D;
this.motZ *= 0.9D;
d0 = this.locY + this.motY - (double) blockposition.getY() - (double) f;
if (Math.abs(d0) < 0.01D) {
d0 += Math.signum(d0) * 0.1D;
}
this.motY -= d0 * (double) this.random.nextFloat() * 0.2D;
if (!this.world.isClientSide && f > 0.0F) {
this.a(blockposition);
}
}
}
if (iblockdata.getMaterial() != Material.WATER) {
this.motY -= 0.03D;
}
this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ);
this.q();
d0 = 0.92D;
this.motX *= 0.92D;
this.motY *= 0.92D;
this.motZ *= 0.92D;
this.setPosition(this.locX, this.locY, this.locZ);
// Paper start - These shouldn't be going through portals
if (this.inPortal()) {
this.die();
}
// Paper end
}
}
private boolean p() {
ItemStack itemstack = this.owner.getItemInMainHand();
ItemStack itemstack1 = this.owner.getItemInOffHand();
boolean flag = itemstack.getItem() == Items.FISHING_ROD;
boolean flag1 = itemstack1.getItem() == Items.FISHING_ROD;
if (!this.owner.dead && this.owner.isAlive() && (flag || flag1) && this.h(this.owner) <= 1024.0D) {
return false;
} else {
this.die();
return true;
}
}
private void q() {
float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ);
this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D);
for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) {
;
}
while (this.pitch - this.lastPitch >= 180.0F) {
this.lastPitch += 360.0F;
}
while (this.yaw - this.lastYaw < -180.0F) {
this.lastYaw -= 360.0F;
}
while (this.yaw - this.lastYaw >= 180.0F) {
this.lastYaw += 360.0F;
}
this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F;
this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F;
}
private void r() {
Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ);
Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, false, true, false);
vec3d = new Vec3D(this.locX, this.locY, this.locZ);
vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ);
// Paper start - Call ProjectileCollideEvent
if (movingobjectposition != null && movingobjectposition.entity != null) {
com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition);
if (event.isCancelled()) {
movingobjectposition = null;
}
}
// Paper end
if (movingobjectposition != null) {
vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z);
}
Entity entity = null;
List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D));
double d0 = 0.0D;
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Entity entity1 = (Entity) iterator.next();
if (this.a(entity1) && (entity1 != this.owner || this.f >= 5)) {
AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D);
MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1);
if (movingobjectposition1 != null) {
double d1 = vec3d.distanceSquared(movingobjectposition1.pos);
if (d1 < d0 || d0 == 0.0D) {
entity = entity1;
d0 = d1;
}
}
}
}
if (entity != null) {
movingobjectposition = new MovingObjectPosition(entity);
}
if (movingobjectposition != null && movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.MISS) {
org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event
if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.ENTITY) {
this.hooked = movingobjectposition.entity;
this.s();
} else {
this.isInGround = true;
}
}
}
private void s() {
this.getDataWatcher().set(EntityFishingHook.b, Integer.valueOf(this.hooked.getId() + 1));
}
private void a(BlockPosition blockposition) {
WorldServer worldserver = (WorldServer) this.world;
int i = 1;
BlockPosition blockposition1 = blockposition.up();
if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(blockposition1)) {
++i;
}
if (this.random.nextFloat() < 0.5F && !this.world.h(blockposition1)) {
--i;
}
if (this.g > 0) {
--this.g;
if (this.g <= 0) {
this.h = 0;
this.at = 0;
// CraftBukkit start
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
// CraftBukkit end
} else {
this.motY -= 0.2D * (double) this.random.nextFloat() * (double) this.random.nextFloat();
}
} else {
float f;
float f1;
float f2;
double d0;
double d1;
double d2;
Block block;
if (this.at > 0) {
this.at -= i;
if (this.at > 0) {
this.au = (float) ((double) this.au + this.random.nextGaussian() * 4.0D);
f = this.au * 0.017453292F;
f1 = MathHelper.sin(f);
f2 = MathHelper.cos(f);
d0 = this.locX + (double) (f1 * (float) this.at * 0.1F);
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
d2 = this.locZ + (double) (f2 * (float) this.at * 0.1F);
block = worldserver.getType(new BlockPosition(d0, d1 - 1.0D, d2)).getBlock();
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
if (this.random.nextFloat() < 0.15F) {
worldserver.a(EnumParticle.WATER_BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D, new int[0]);
}
float f3 = f1 * 0.04F;
float f4 = f2 * 0.04F;
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D, new int[0]);
worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D, new int[0]);
}
} else {
// CraftBukkit start
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.BITE);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
if (playerFishEvent.isCancelled()) {
return;
}
// CraftBukkit end
this.motY = (double) (-0.4F * MathHelper.a(this.random, 0.6F, 1.0F));
this.a(SoundEffects.K, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
double d3 = this.getBoundingBox().b + 0.5D;
worldserver.a(EnumParticle.WATER_BUBBLE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
worldserver.a(EnumParticle.WATER_WAKE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]);
this.g = MathHelper.nextInt(this.random, 20, 40);
}
} else if (this.h > 0) {
this.h -= i;
f = 0.15F;
if (this.h < 20) {
f = (float) ((double) f + (double) (20 - this.h) * 0.05D);
} else if (this.h < 40) {
f = (float) ((double) f + (double) (40 - this.h) * 0.02D);
} else if (this.h < 60) {
f = (float) ((double) f + (double) (60 - this.h) * 0.01D);
}
if (this.random.nextFloat() < f) {
f1 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F;
f2 = MathHelper.a(this.random, 25.0F, 60.0F);
d0 = this.locX + (double) (MathHelper.sin(f1) * f2 * 0.1F);
d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F);
d2 = this.locZ + (double) (MathHelper.cos(f1) * f2 * 0.1F);
block = worldserver.getType(new BlockPosition((int) d0, (int) d1 - 1, (int) d2)).getBlock();
if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) {
worldserver.a(EnumParticle.WATER_SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]);
}
}
if (this.h <= 0) {
this.au = MathHelper.a(this.random, 0.0F, 360.0F);
this.at = MathHelper.nextInt(this.random, 20, 80);
}
} else {
this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper
this.h -= this.ax * 20 * 5;
}
}
}
protected boolean a(Entity entity) {
return entity.isInteractable() || entity instanceof EntityItem;
}
public void b(NBTTagCompound nbttagcompound) {}
public void a(NBTTagCompound nbttagcompound) {}
public int j() {
if (!this.world.isClientSide && this.owner != null) {
int i = 0;
if (this.hooked != null) {
// CraftBukkit start
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
if (playerFishEvent.isCancelled()) {
return 0;
}
// CraftBukkit end
this.k();
this.world.broadcastEntityEffect(this, (byte) 31);
i = this.hooked instanceof EntityItem ? 3 : 5;
} else if (this.g > 0) {
LootTableInfo.a loottableinfo_a = new LootTableInfo.a((WorldServer) this.world);
loottableinfo_a.a((float) this.aw + this.owner.du());
Iterator iterator = this.world.getLootTableRegistry().a(LootTables.aA).a(this.random, loottableinfo_a.a()).iterator();
while (iterator.hasNext()) {
ItemStack itemstack = (ItemStack) iterator.next();
EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack);
// CraftBukkit start
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH);
playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
if (playerFishEvent.isCancelled()) {
return 0;
}
// CraftBukkit end
double d0 = this.owner.locX - this.locX;
double d1 = this.owner.locY - this.locY;
double d2 = this.owner.locZ - this.locZ;
double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
double d4 = 0.1D;
entityitem.motX = d0 * 0.1D;
entityitem.motY = d1 * 0.1D + (double) MathHelper.sqrt(d3) * 0.08D;
entityitem.motZ = d2 * 0.1D;
this.world.addEntity(entityitem);
// CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop()
if (playerFishEvent.getExpToDrop() > 0) {
this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper
}
// CraftBukkit end
Item item = itemstack.getItem();
if (item == Items.FISH || item == Items.COOKED_FISH) {
this.owner.a(StatisticList.E, 1);
}
}
i = 1;
}
if (this.isInGround) {
// CraftBukkit start
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
if (playerFishEvent.isCancelled()) {
return 0;
}
// CraftBukkit end
i = 2;
}
// CraftBukkit start
if (i == 0) {
PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT);
this.world.getServer().getPluginManager().callEvent(playerFishEvent);
if (playerFishEvent.isCancelled()) {
return 0;
}
}
// CraftBukkit end
this.die();
return i;
} else {
return 0;
}
}
protected void k() {
if (this.owner != null) {
double d0 = this.owner.locX - this.locX;
double d1 = this.owner.locY - this.locY;
double d2 = this.owner.locZ - this.locZ;
double d3 = 0.1D;
this.hooked.motX += d0 * 0.1D;
this.hooked.motY += d1 * 0.1D;
this.hooked.motZ += d2 * 0.1D;
}
}
protected boolean playStepSound() {
return false;
}
public void die() {
super.die();
if (this.owner != null) {
this.owner.hookedFish = null;
}
}
public EntityHuman l() {
return this.owner;
}
static enum HookState {
FLYING, HOOKED_IN_ENTITY, BOBBING;
private HookState() {}
}
}

View File

@@ -0,0 +1,84 @@
package net.minecraft.server;
import javax.annotation.Nullable;
import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit
public class EntityMushroomCow extends EntityCow {
public EntityMushroomCow(World world) {
super(world);
this.setSize(0.9F, 1.4F);
this.bA = Blocks.MYCELIUM;
}
public static void c(DataConverterManager dataconvertermanager) {
EntityInsentient.a(dataconvertermanager, EntityMushroomCow.class);
}
public boolean a(EntityHuman entityhuman, EnumHand enumhand) {
ItemStack itemstack = entityhuman.b(enumhand);
if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
if (itemstack.isEmpty()) {
entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW));
} else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) {
entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false);
}
return true;
} else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) {
if (this.dead) return false; // Reaper - Fix cow dupe
// CraftBukkit start
PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity());
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// CraftBukkit end
this.die();
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D, new int[0]);
if (!this.world.isClientSide) {
EntityCow entitycow = new EntityCow(this.world);
entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
entitycow.setHealth(this.getHealth());
entitycow.aN = this.aN;
if (this.hasCustomName()) {
entitycow.setCustomName(this.getCustomName());
}
this.world.addEntity(entitycow);
for (int i = 0; i < 5; ++i) {
this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM)));
}
itemstack.damage(1, entityhuman);
this.a(SoundEffects.ei, 1.0F, 1.0F);
}
return true;
} else {
return super.a(entityhuman, enumhand);
}
}
public EntityMushroomCow c(EntityAgeable entityageable) {
return new EntityMushroomCow(this.world);
}
@Nullable
protected MinecraftKey J() {
return LootTables.M;
}
public EntityCow b(EntityAgeable entityageable) {
return this.c(entityageable);
}
public EntityAgeable createChild(EntityAgeable entityageable) {
return this.c(entityageable);
}
}

View File

@@ -33,7 +33,6 @@ import org.bukkit.inventory.MainHand;
/**
* 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 {
@@ -85,6 +84,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// Paper end
private int containerUpdateDelay; // Paper
// Paper start - cancellable death event
public boolean queueHealthUpdatePacket = false;
public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
// Paper end
// CraftBukkit start
public String displayName;
@@ -442,9 +445,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public void die(DamageSource damagesource) {
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag));
//this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
// CraftBukkit start - fire PlayerDeathEvent
if (this.dead) {
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
return;
}
java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.inventory.getSize());
@@ -462,6 +466,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
String deathmessage = chatmessage.toPlainText();
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
// Paper start - cancellable death event
if (event.isCancelled()) {
// make compatible with plugins that might have already set the health in an event listener
if (this.getHealth() <= 0) {
this.setHealth((float) event.getReviveHealth());
}
return;
}
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag));
// Paper end
String deathMessage = event.getDeathMessage();
@@ -615,7 +629,17 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
}
return super.damageEntity(damagesource, f);
// Paper start - cancellable death events
//return super.damageEntity(damagesource, f);
this.queueHealthUpdatePacket = true;
boolean damaged = super.damageEntity(damagesource, f);
this.queueHealthUpdatePacket = false;
if (this.queuedHealthUpdatePacket != null) {
this.playerConnection.sendPacket(this.queuedHealthUpdatePacket);
this.queuedHealthUpdatePacket = null;
}
return damaged;
// Paper end
}
}
}
@@ -745,9 +769,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (entity instanceof EntityPlayer) {
WorldServer worldServer = (WorldServer) entity.getWorld();
worldServer.tracker.untrackEntity(this);
worldServer.tracker.entriesLock.lock(); // Akarin
worldServer.tracker.entriesLock.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
worldServer.tracker.track(this);
worldServer.tracker.entriesLock.unlock(); // Akarin
worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
}
// Paper end
@@ -760,7 +784,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
protected void b(BlockPosition blockposition) {
if (!this.isSpectator()) {
if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Dionysus - don't tick dead players that are not in the world currently (pending respawn)
super.b(blockposition);
}
@@ -1132,6 +1156,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.getDataWatcher().set(EntityPlayer.br, entityplayer.getDataWatcher().get(EntityPlayer.br));
this.lastSentExp = -1;
this.lastHealthSent = -1.0F;
setSneaking(false); // NeonPaper - fix MC-10657
this.ch = -1;
// this.cr.a((RecipeBook) entityplayer.cr); // CraftBukkit
// Paper start - Optimize remove queue
@@ -1422,8 +1447,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}
// CraftBukkit start - Add per-player time and weather.
public volatile long timeOffset = 0; // Akarin - volatile
public volatile boolean relativeTime = true; // Akarin - volatile
public long timeOffset = 0;
public boolean relativeTime = true;
public long getPlayerTime() {
if (this.relativeTime) {

View File

@@ -2,25 +2,28 @@ package net.minecraft.server;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.googlecode.concurentlocks.ReentrantReadWriteUpdateLock;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.annotation.concurrent.ThreadSafe;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Akarin Changes Note
* 1) Add lock for entries set operations (safety issue)
* 1) Made collections and entry access thread-safe (safety issue)
*/
@ThreadSafe // Akarin
public class EntityTracker {
private static final Logger a = LogManager.getLogger();
private final WorldServer world;
private final Set<EntityTrackerEntry> c = Sets.newHashSet();
public final ReentrantLock entriesLock = new ReentrantLock(); // Akarin - add lock
public final ReentrantReadWriteUpdateLock entriesLock = new ReentrantReadWriteUpdateLock(); // Akarin - add lock
public final IntHashMap<EntityTrackerEntry> trackedEntities = new IntHashMap();
private int e;
@@ -38,7 +41,7 @@ public class EntityTracker {
this.addEntity(entity, 512, 2);
EntityPlayer entityplayer = (EntityPlayer) entity;
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
// entriesLock.writeLock().lock(); // Akarin - locked in EntityPlayer
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -47,7 +50,7 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer);
}
}
entriesLock.unlock(); // Akarin
// entriesLock.writeLock().unlock(); // Akarin - locked in EntityPlayer
} else if (entity instanceof EntityFishingHook) {
this.addEntity(entity, 64, 5, true);
} else if (entity instanceof EntityArrow) {
@@ -118,18 +121,18 @@ public class EntityTracker {
org.spigotmc.AsyncCatcher.catchOp( "entity track"); // Spigot
i = org.spigotmc.TrackingRange.getEntityTrackingRange(entity, i); // Spigot
try {
// entriesLock.writeLock().lock(); // Akarin - locked from track method
if (this.trackedEntities.b(entity.getId())) {
throw new IllegalStateException("Entity is already tracked!");
}
EntityTrackerEntry entitytrackerentry = new EntityTrackerEntry(entity, i, this.e, j, flag);
entriesLock.lock(); // Akarin
this.c.add(entitytrackerentry);
this.trackedEntities.a(entity.getId(), entitytrackerentry);
entitytrackerentry.scanPlayers(this.world.players);
entriesLock.unlock(); // Akarin
// entriesLock.writeLock().unlock(); // Akarin - locked from track method
} catch (Throwable throwable) {
CrashReport crashreport = CrashReport.a(throwable, "Adding entity to track");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity To Track");
@@ -166,34 +169,32 @@ 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();
Iterator iterator = this.c.iterator();
world.timings.tracker1.startTiming(); // Spigot
entriesLock.lock(); // Akarin
entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -221,14 +222,14 @@ public class EntityTracker {
}
}
}
entriesLock.unlock(); // Akarin
entriesLock.writeLock().unlock(); // Akarin
world.timings.tracker2.stopTiming(); // Spigot
}
public void a(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -239,11 +240,13 @@ public class EntityTracker {
entitytrackerentry.updatePlayer(entityplayer);
}
}
entriesLock.unlock(); // Akarin
entriesLock.writeLock().unlock(); // Akarin
}
public void a(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
if (entitytrackerentry != null) {
entitytrackerentry.broadcast(packet);
@@ -252,7 +255,9 @@ public class EntityTracker {
}
public void sendPacketToEntity(Entity entity, Packet<?> packet) {
entriesLock.readLock().lock(); // Akarin
EntityTrackerEntry entitytrackerentry = this.trackedEntities.get(entity.getId());
entriesLock.readLock().unlock(); // Akarin
if (entitytrackerentry != null) {
entitytrackerentry.broadcastIncludingSelf(packet);
@@ -262,21 +267,21 @@ public class EntityTracker {
public void untrackPlayer(EntityPlayer entityplayer) {
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
entriesLock.writeLock().lock();
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.clear(entityplayer);
}
entriesLock.unlock(); // Akarin
entriesLock.writeLock().unlock();
}
public void a(EntityPlayer entityplayer, Chunk chunk) {
ArrayList arraylist = Lists.newArrayList();
ArrayList arraylist1 = Lists.newArrayList();
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
entriesLock.writeLock().lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
@@ -293,7 +298,7 @@ public class EntityTracker {
}
}
}
entriesLock.unlock(); // Akarin
entriesLock.writeLock().unlock(); // Akarin
Entity entity1;
@@ -320,13 +325,13 @@ public class EntityTracker {
public void a(int i) {
this.e = (i - 1) * 16;
Iterator iterator = this.c.iterator();
entriesLock.lock(); // Akarin
entriesLock.readLock().lock(); // Akarin
while (iterator.hasNext()) {
EntityTrackerEntry entitytrackerentry = (EntityTrackerEntry) iterator.next();
entitytrackerentry.a(this.e);
}
entriesLock.unlock(); // Akarin
entriesLock.readLock().unlock(); // Akarin
}
}

View File

@@ -1,5 +1,6 @@
package net.minecraft.server;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -13,10 +14,6 @@ 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();
@@ -47,7 +44,7 @@ public class EntityTrackerEntry {
// 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 java.util.Map<EntityPlayer, Boolean> trackedPlayerMap = new java.util.HashMap<EntityPlayer, Boolean>();
public Set<EntityPlayer> trackedPlayers = trackedPlayerMap.keySet();
// Paper end
@@ -424,12 +421,11 @@ public class EntityTrackerEntry {
}
}
// 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) {
// CraftBukkit start - Fix for nonsensical head yaw
this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F);
this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw));
// CraftBukkit end
EntityLiving entityliving = (EntityLiving) this.tracker;
Iterator iterator = entityliving.getEffects().iterator();

View File

@@ -0,0 +1,504 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
// CraftBukkit start
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.Location;
import org.bukkit.event.block.BlockExplodeEvent;
// CraftBukkit end
public class Explosion {
private final boolean a;
private final boolean b;
private final Random c = new Random();
private final World world;
private final double posX;
private final double posY;
private final double posZ;
public final Entity source;
private final float size;
private final ArrayList<BlockPosition> blocks = Lists.newArrayList();
private final Map<EntityHuman, Vec3D> k = Maps.newHashMap();
public boolean wasCanceled = false; // CraftBukkit - add field
// Dionysus start
private final BlockPosition.MutableBlockPosition cachedPos = new BlockPosition.MutableBlockPosition();
// The chunk coordinate of the most recently stepped through block.
private int prevChunkX = Integer.MIN_VALUE;
private int prevChunkZ = Integer.MIN_VALUE;
// The chunk belonging to prevChunkPos.
private Chunk prevChunk;
private static final com.google.common.base.Predicate<Entity> hitPredicate = entity -> IEntitySelector.d.apply(entity) && !entity.dead; // Dionysus - Paper - don't hit dead entities
// Dionysus end
public Explosion(World world, Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) {
this.world = world;
this.source = entity;
this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values
this.posX = d0;
this.posY = d1;
this.posZ = d2;
this.a = flag;
this.b = flag1;
}
public void a() {
// CraftBukkit start
if (this.size < 0.1F) {
return;
}
// CraftBukkit end
// Dionysus start - optimize memory usage from explosions
// CaffeineMC optimized raytracing loop
// @author JellySquid
// @author nopjmp
// https://github.com/CaffeineMC/lithium-fabric
// Using integer encoding for the block positions provides a massive speedup and prevents us from needing to
// allocate a block position for every step we make along each ray, eliminating essentially all the memory
// allocations of this function. The overhead of packing block positions into integer format is negligible
// compared to a memory allocation and associated overhead of hashing real objects in a set.
final LongOpenHashSet touched = new LongOpenHashSet(0);
final Random random = this.world.random;
for (int rayX = 0; rayX < 16; ++rayX) {
boolean xPlane = rayX == 0 || rayX == 15;
double vecX = (((float) rayX / 15.0F) * 2.0F) - 1.0F;
for (int rayY = 0; rayY < 16; ++rayY) {
boolean yPlane = rayY == 0 || rayY == 15;
double vecY = (((float) rayY / 15.0F) * 2.0F) - 1.0F;
for (int rayZ = 0; rayZ < 16; ++rayZ) {
boolean zPlane = rayZ == 0 || rayZ == 15;
// We only fire rays from the surface of our origin volume
if (xPlane || yPlane || zPlane) {
double vecZ = (((float) rayZ / 15.0F) * 2.0F) - 1.0F;
this.performRayCast(random, vecX, vecY, vecZ, touched);
}
}
}
}
// We can now iterate back over the set of positions we modified and re-build BlockPos objects from them
// This will only allocate as many objects as there are in the set, where otherwise we would allocate them
// each step of a every ray.
blocks.ensureCapacity(touched.size());
for (Long longPos : touched) {
blocks.add(BlockPosition.fromLong(longPos));
}
float f3 = this.size * 2.0F;
int i = MathHelper.floor(this.posX - (double) f3 - 1.0D);
int j = MathHelper.floor(this.posX + (double) f3 + 1.0D);
int l = MathHelper.floor(this.posY - (double) f3 - 1.0D);
int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D);
int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D);
int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D);
// Paper start - Fix lag from explosions processing dead entities
List<Entity> list = this.world.getEntities(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), hitPredicate);
// Paper end
Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ);
for (Entity entity : list) {
if (!entity.bB()) {
double d7 = entity.e(this.posX, this.posY, this.posZ) / (double) f3;
if (d7 <= 1.0D) {
double d8 = entity.locX - this.posX;
double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY;
double d10 = entity.locZ - this.posZ;
double d11 = MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10);
if (d11 != 0.0D) {
d8 /= d11;
d9 /= d11;
d10 /= d11;
double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // Paper - Optimize explosions
double d13 = (1.0D - d7) * d12;
// CraftBukkit start
// entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
CraftEventFactory.entityDamage = source;
entity.forceExplosionKnockback = false;
boolean wasDamaged = entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D)));
CraftEventFactory.entityDamage = null;
if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) {
continue;
}
// CraftBukkit end
double d14 = d13;
if (entity instanceof EntityLiving) {
d14 = entity instanceof EntityHuman && world.paperConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a((EntityLiving) entity, d13); // Paper - Disable explosion knockback
}
entity.motX += d8 * d14;
entity.motY += d9 * d14;
entity.motZ += d10 * d14;
if (entity instanceof EntityHuman) {
EntityHuman entityhuman = (EntityHuman) entity;
if (!entityhuman.isSpectator() && (!entityhuman.z() && !world.paperConfig.disableExplosionKnockback || !entityhuman.abilities.isFlying)) { // Paper - Disable explosion knockback
this.k.put(entityhuman, new Vec3D(d8 * d13, d9 * d13, d10 * d13));
}
}
}
}
}
}
}
private void performRayCast(Random random, double vecX, double vecY, double vecZ, LongOpenHashSet touched) {
double dist = Math.sqrt((vecX * vecX) + (vecY * vecY) + (vecZ * vecZ));
double normX = (vecX / dist) * 0.3D;
double normY = (vecY / dist) * 0.3D;
double normZ = (vecZ / dist) * 0.3D;
float strength = this.size * (0.7F + (random.nextFloat() * 0.6F));
double stepX = this.posX;
double stepY = this.posY;
double stepZ = this.posZ;
int prevX = Integer.MIN_VALUE;
int prevY = Integer.MIN_VALUE;
int prevZ = Integer.MIN_VALUE;
float prevResistance = 0.0F;
// Step through the ray until it is finally stopped
while (strength > 0.0F) {
int blockX = MathHelper.floor(stepX);
int blockY = MathHelper.floor(stepY);
int blockZ = MathHelper.floor(stepZ);
float resistance;
// Check whether or not we have actually moved into a new block this step. Due to how rays are stepped through,
// over-sampling of the same block positions will occur. Changing this behaviour would introduce differences in
// aliasing and sampling, which is unacceptable for our purposes. As a band-aid, we can simply re-use the
// previous result and get a decent boost.
if (prevX != blockX || prevY != blockY || prevZ != blockZ) {
resistance = this.traverseBlock(strength, blockX, blockY, blockZ, touched);
prevX = blockX;
prevY = blockY;
prevZ = blockZ;
prevResistance = resistance;
} else {
resistance = prevResistance;
}
strength -= resistance;
// Apply a constant fall-off
strength -= 0.22500001F;
stepX += normX;
stepY += normY;
stepZ += normZ;
}
}
/**
* Called for every step made by a ray being cast by an explosion.
*
* @param strength The strength of the ray during this step
* @param blockX The x-coordinate of the block the ray is inside of
* @param blockY The y-coordinate of the block the ray is inside of
* @param blockZ The z-coordinate of the block the ray is inside of
* @return The resistance of the current block space to the ray
*/
private float traverseBlock(float strength, int blockX, int blockY, int blockZ, LongOpenHashSet touched) {
cachedPos.c(blockX, blockY, blockZ);
IBlockData iblockdata = this.world.getType(cachedPos);
// Early-exit if the y-coordinate is out of bounds.
if (cachedPos.isInvalidYLocation()) {
if (iblockdata.getMaterial() != Material.AIR) {
float blastResistance = this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null);
return (blastResistance + 0.3F) * 0.3F;
}
return 0.0F;
}
int chunkX = blockX >> 4;
int chunkZ = blockZ >> 4;
// Avoid calling into the chunk manager as much as possible through managing chunks locally
if (this.prevChunkX != chunkX || this.prevChunkZ != chunkZ) {
this.prevChunk = this.world.getChunkAt(chunkX, chunkZ);
this.prevChunkX = chunkX;
this.prevChunkZ = chunkZ;
}
final Chunk chunk = this.prevChunk;
float totalResistance = 0.0F;
Optional<Float> blastResistance = Optional.empty();
// If the chunk is missing or out of bounds, assume that it is air
if (chunk != null) {
// We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much
// performance as possible here
ChunkSection section = chunk.getSections()[blockY >> 4];
// If the section doesn't exist or it's empty, assume that the block is air
if (section != null && !section.a()) {
// Retrieve the block state from the chunk section directly to avoid associated overhead
IBlockData blockState = section.getType(blockX & 15, blockY & 15, blockZ & 15);
// If the block state is air, it cannot have fluid or any kind of resistance, so just leave
if (blockState.getBlock() != Blocks.AIR) {
// Get the explosion resistance like vanilla
blastResistance = Optional.of(this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null));
}
}
}
// Calculate how much this block will resist an explosion's ray
if (blastResistance.isPresent()) {
totalResistance = (blastResistance.get() + 0.3F) * 0.3F;
}
// Check if this ray is still strong enough to break blocks, and if so, add this position to the set
// of positions to destroy
float reducedStrength = strength - totalResistance;
if (reducedStrength > 0.0F && (this.a || iblockdata.getMaterial() != Material.AIR)) {
if ((this.source == null || this.source.a(this, this.world, cachedPos, iblockdata, reducedStrength)) && cachedPos.getY() < 256 && cachedPos.getY() >= 0) {
touched.add(cachedPos.asLong());
}
}
return totalResistance;
}
// Dionysus end
public void a(boolean flag) {
this.world.a((EntityHuman) null, this.posX, this.posY, this.posZ, SoundEffects.bV, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F);
if (this.size >= 2.0F && this.b) {
this.world.addParticle(EnumParticle.EXPLOSION_HUGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
} else {
this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D);
}
Iterator iterator;
BlockPosition blockposition;
if (this.b) {
// CraftBukkit start
org.bukkit.World bworld = this.world.getWorld();
org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity();
Location location = new Location(bworld, this.posX, this.posY, this.posZ);
List<org.bukkit.block.Block> blockList = Lists.newArrayList();
for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) {
BlockPosition cpos = (BlockPosition) this.blocks.get(i1);
org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ());
if (bblock.getType() != org.bukkit.Material.AIR) {
blockList.add(bblock);
}
}
boolean cancelled;
List<org.bukkit.block.Block> bukkitBlocks;
float yield;
if (explode != null) {
EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 1.0F / this.size);
this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled();
bukkitBlocks = event.blockList();
yield = event.getYield();
} else {
BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 1.0F / this.size);
this.world.getServer().getPluginManager().callEvent(event);
cancelled = event.isCancelled();
bukkitBlocks = event.blockList();
yield = event.getYield();
}
this.blocks.clear();
this.blocks.ensureCapacity(bukkitBlocks.size());
for (org.bukkit.block.Block bblock : bukkitBlocks) {
BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ());
blocks.add(coords);
}
if (cancelled) {
this.wasCanceled = true;
return;
}
// CraftBukkit end
iterator = this.blocks.iterator();
while (iterator.hasNext()) {
blockposition = (BlockPosition) iterator.next();
IBlockData iblockdata = this.world.getType(blockposition);
Block block = iblockdata.getBlock();
this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray
if (flag) {
double d0 = (double) ((float) blockposition.getX() + this.world.random.nextFloat());
double d1 = (double) ((float) blockposition.getY() + this.world.random.nextFloat());
double d2 = (double) ((float) blockposition.getZ() + this.world.random.nextFloat());
double d3 = d0 - this.posX;
double d4 = d1 - this.posY;
double d5 = d2 - this.posZ;
double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5);
d3 /= d6;
d4 /= d6;
d5 /= d6;
double d7 = 0.5D / (d6 / (double) this.size + 0.1D);
d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F);
d3 *= d7;
d4 *= d7;
d5 *= d7;
this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5);
this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5);
}
if (iblockdata.getMaterial() != Material.AIR) {
if (block.a(this)) {
// CraftBukkit - add yield
block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), yield, 0);
}
this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3);
block.wasExploded(this.world, blockposition, this);
}
}
}
if (this.a) {
iterator = this.blocks.iterator();
while (iterator.hasNext()) {
blockposition = (BlockPosition) iterator.next();
if (this.world.getType(blockposition).getMaterial() == Material.AIR && this.world.getType(blockposition.down()).b() && this.c.nextInt(3) == 0) {
// CraftBukkit start - Ignition by explosion
if (!CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) {
this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData());
}
// CraftBukkit end
}
}
}
}
public Map<EntityHuman, Vec3D> b() {
return this.k;
}
@Nullable
public EntityLiving getSource() {
// CraftBukkit start - obtain Fireball shooter for explosion tracking
return this.source == null ? null : (this.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) this.source).getSource() : (this.source instanceof EntityLiving ? (EntityLiving) this.source : (this.source instanceof EntityFireball ? ((EntityFireball) this.source).shooter : null)));
// CraftBukkit end
}
public void clearBlocks() {
this.blocks.clear();
}
public List<BlockPosition> getBlocks() {
return this.blocks;
}
// Paper start - Optimize explosions
private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) {
if (!this.world.paperConfig.optimizeExplosions) {
return this.world.a(vec3d, aabb);
}
CacheKey key = new CacheKey(this, aabb);
return this.world.explosionDensityCache.computeIfAbsent(key, k1 -> this.world.a(vec3d, aabb));
}
static class CacheKey {
private final World world;
private final double posX, posY, posZ;
private final double minX, minY, minZ;
private final double maxX, maxY, maxZ;
public CacheKey(Explosion explosion, AxisAlignedBB aabb) {
this.world = explosion.world;
this.posX = explosion.posX;
this.posY = explosion.posY;
this.posZ = explosion.posZ;
this.minX = aabb.a;
this.minY = aabb.b;
this.minZ = aabb.c;
this.maxX = aabb.d;
this.maxY = aabb.e;
this.maxZ = aabb.f;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CacheKey cacheKey = (CacheKey) o;
if (Double.compare(cacheKey.posX, posX) != 0) return false;
if (Double.compare(cacheKey.posY, posY) != 0) return false;
if (Double.compare(cacheKey.posZ, posZ) != 0) return false;
if (Double.compare(cacheKey.minX, minX) != 0) return false;
if (Double.compare(cacheKey.minY, minY) != 0) return false;
if (Double.compare(cacheKey.minZ, minZ) != 0) return false;
if (Double.compare(cacheKey.maxX, maxX) != 0) return false;
if (Double.compare(cacheKey.maxY, maxY) != 0) return false;
if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false;
return world.equals(cacheKey.world);
}
@Override
public int hashCode() {
int result;
long temp;
result = world.hashCode();
temp = Double.doubleToLongBits(posX);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(posY);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(posZ);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(minX);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(minY);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(minZ);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxX);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxY);
result = 31 * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(maxZ);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
// Paper end
}

View File

@@ -0,0 +1,81 @@
package net.minecraft.server;
import java.util.List;
/**
* Akarin Changes Note
* 1) Multi-threaded chunk saving (performance)
*/
public class FileIOThread implements Runnable {
private static final FileIOThread a = new FileIOThread();
private final List<IAsyncChunkSaver> b = /*Collections.synchronizedList(Lists.newArrayList())*/ null; // Akarin - I don't think any plugin rely on this
private volatile long c;
private volatile long d;
private volatile boolean e;
private FileIOThread() {
// Thread thread = new Thread(this, "File IO Thread"); // Akarin
// thread.setPriority(1); // Akarin
// thread.start(); // Akarin
}
public static FileIOThread a() {
return FileIOThread.a;
}
public void run() {
while (true) {
this.c();
}
}
private void c() {
for (int i = 0; i < this.b.size(); ++i) {
IAsyncChunkSaver iasyncchunksaver = (IAsyncChunkSaver) this.b.get(i);
boolean flag = iasyncchunksaver.a();
if (!flag) {
this.b.remove(i--);
++this.d;
}
// Paper start - Add toggle
if (com.destroystokyo.paper.PaperConfig.enableFileIOThreadSleep) {
try {
Thread.sleep(this.e ? 0L : 2L);
} catch (InterruptedException interruptedexception) {
interruptedexception.printStackTrace();
}
}
// Paper end
}
if (this.b.isEmpty()) {
try {
Thread.sleep(25L);
} catch (InterruptedException interruptedexception1) {
interruptedexception1.printStackTrace();
}
}
}
public void a(IAsyncChunkSaver iasyncchunksaver) {
if (!this.b.contains(iasyncchunksaver)) {
++this.c;
this.b.add(iasyncchunksaver);
}
}
public void b() throws InterruptedException {
this.e = true;
while (this.c != this.d) {
Thread.sleep(10L);
}
this.e = false;
}
}

View File

@@ -0,0 +1,138 @@
package net.minecraft.server;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
// CraftBukkit start
import java.net.InetAddress;
import java.util.Iterator;
import java.util.Map;
// CraftBukkit end
public class HandshakeListener implements PacketHandshakingInListener {
private static final com.google.gson.Gson gson = new com.google.gson.Gson(); // Spigot
// CraftBukkit start - add fields
private static final Object2LongOpenHashMap<InetAddress> throttleTracker = new Object2LongOpenHashMap<>();
private static int throttleCounter = 0;
// CraftBukkit end
private final MinecraftServer a;
private final NetworkManager b;
private NetworkManager getNetworkManager() { return b; } // Paper - OBFHELPER
public HandshakeListener(MinecraftServer minecraftserver, NetworkManager networkmanager) {
this.a = minecraftserver;
this.b = networkmanager;
}
public void a(PacketHandshakingInSetProtocol packethandshakinginsetprotocol) {
switch (packethandshakinginsetprotocol.a()) {
case LOGIN:
this.b.setProtocol(EnumProtocol.LOGIN);
ChatMessage chatmessage;
// CraftBukkit start - Connection throttle
try {
long currentTime = System.currentTimeMillis();
long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle();
InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress();
synchronized (throttleTracker) {
if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.getLong(address) < connectionThrottle) {
throttleTracker.put(address, currentTime);
chatmessage = new ChatMessage("Connection throttled! Please wait before reconnecting.");
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
this.b.close(chatmessage);
return;
}
throttleTracker.put(address, currentTime);
throttleCounter++;
if (throttleCounter > 200) {
throttleCounter = 0;
// Cleanup stale entries
throttleTracker.object2LongEntrySet().removeIf(entry -> entry.getLongValue() > connectionThrottle); // Dionysus
}
}
} catch (Throwable t) {
org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t);
}
// CraftBukkit end
if (packethandshakinginsetprotocol.b() > 340) {
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
this.b.close(chatmessage);
} else if (packethandshakinginsetprotocol.b() < 340) {
chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), "1.12.2" ) ); // Spigot
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
this.b.close(chatmessage);
} else {
this.b.setPacketListener(new LoginListener(this.a, this.b));
// Paper start - handshake event
boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee;
boolean handledByEvent = false;
// Try and handle the handshake through the event
if (com.destroystokyo.paper.event.player.PlayerHandshakeEvent.getHandlerList().getRegisteredListeners().length != 0) { // Hello? Can you hear me?
com.destroystokyo.paper.event.player.PlayerHandshakeEvent event = new com.destroystokyo.paper.event.player.PlayerHandshakeEvent(packethandshakinginsetprotocol.hostname, !proxyLogicEnabled);
if (event.callEvent()) {
// If we've failed somehow, let the client know so and go no further.
if (event.isFailed()) {
chatmessage = new ChatMessage(event.getFailMessage());
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
this.b.close(chatmessage);
return;
}
packethandshakinginsetprotocol.hostname = event.getServerHostname();
this.b.l = new java.net.InetSocketAddress(event.getSocketAddressHostname(), ((java.net.InetSocketAddress) this.b.getSocketAddress()).getPort());
this.b.spoofedUUID = event.getUniqueId();
this.b.spoofedProfile = gson.fromJson(event.getPropertiesJson(), com.mojang.authlib.properties.Property[].class);
handledByEvent = true; // Hooray, we did it!
}
}
// Don't try and handle default logic if it's been handled by the event.
if (!handledByEvent && proxyLogicEnabled) {
// Paper end
// Spigot Start
//if (org.spigotmc.SpigotConfig.bungee) { // Paper - comment out, we check above!
String[] split = packethandshakinginsetprotocol.hostname.split("\00");
if ( split.length == 3 || split.length == 4 ) {
packethandshakinginsetprotocol.hostname = split[0];
b.l = new java.net.InetSocketAddress(split[1], ((java.net.InetSocketAddress) b.getSocketAddress()).getPort());
b.spoofedUUID = com.mojang.util.UUIDTypeAdapter.fromString( split[2] );
} else
{
chatmessage = new ChatMessage("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage));
this.b.close(chatmessage);
return;
}
if ( split.length == 4 )
{
b.spoofedProfile = gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class);
}
}
// Spigot End
((LoginListener) this.b.i()).hostname = packethandshakinginsetprotocol.hostname + ":" + packethandshakinginsetprotocol.port; // CraftBukkit - set hostname
}
break;
case STATUS:
this.b.setProtocol(EnumProtocol.STATUS);
this.b.setPacketListener(new PacketStatusListener(this.a, this.b));
break;
default:
throw new UnsupportedOperationException("Invalid intention " + packethandshakinginsetprotocol.a());
}
// Paper start - NetworkClient implementation
this.getNetworkManager().protocolVersion = packethandshakinginsetprotocol.getProtocolVersion();
this.getNetworkManager().virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packethandshakinginsetprotocol.hostname, packethandshakinginsetprotocol.port);
// Paper end
}
public void a(IChatBaseComponent ichatbasecomponent) {}
}

View File

@@ -0,0 +1,113 @@
package net.minecraft.server;
// NeonPaper start
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
// NeonPaper end
public class IntCache {
// NeonPaper start - Refactored IntCache to be thread local instead of static
private static final ThreadLocal<IntCache> caches = new ThreadLocal<IntCache>() {
@Override
protected IntCache initialValue() {
IntCache cache = new IntCache();
synchronized (ALL_CACHES) {
ALL_CACHES.add(new WeakReference<>(cache));
}
return new IntCache();
}
};
private static final List<WeakReference<IntCache>> ALL_CACHES = new ObjectArrayList<>();
private int a = 256;
private final List<int[]> b = new ObjectArrayList<>();
private final List<int[]> c = new ObjectArrayList<>();
private final List<int[]> d = new ObjectArrayList<>();
private final List<int[]> e = new ObjectArrayList<>();
private final int cacheLimit = org.spigotmc.SpigotConfig.intCacheLimit;
public static int[] a(int i) {
return caches.get().aNonStatic(i);
}
public int[] aNonStatic(int i) {
int[] aint;
if (i <= 256) {
if (this.b.isEmpty()) {
aint = new int[256];
if (c.size() < cacheLimit) this.c.add(aint);
return aint;
} else {
aint = this.b.remove(this.b.size() - 1);
if (c.size() < cacheLimit) this.c.add(aint);
return aint;
}
} else if (i > this.a) {
this.a = i;
this.d.clear();
this.e.clear();
aint = new int[this.a];
if (e.size() < cacheLimit) this.e.add(aint);
return aint;
} else if (this.d.isEmpty()) {
aint = new int[this.a];
if (e.size() < cacheLimit) this.e.add(aint);
return aint;
} else {
aint = this.d.remove(this.d.size() - 1);
if (e.size() < cacheLimit) this.e.add(aint);
return aint;
}
}
public static void a() {
caches.get().aNonStatic();
}
public void aNonStatic() {
if (!this.d.isEmpty()) {
this.d.remove(this.d.size() - 1);
}
if (!this.b.isEmpty()) {
this.b.remove(this.b.size() - 1);
}
this.d.addAll(this.e);
this.b.addAll(this.c);
this.e.clear();
this.c.clear();
}
public static String b() {
int cache = 0;
int tcache = 0;
int allocated = 0;
int tallocated = 0;
int numberOfCaches;
synchronized (ALL_CACHES) {
numberOfCaches = ALL_CACHES.size();
Iterator<WeakReference<IntCache>> iter = ALL_CACHES.iterator();
while (iter.hasNext()) {
WeakReference<IntCache> reference = iter.next();
IntCache intcache = reference.get();
if (intcache != null) {
cache += intcache.d.size();
tcache += intcache.b.size();
allocated += intcache.e.size();
tallocated += intcache.c.size();
} else {
iter.remove();
}
}
}
return numberOfCaches + " IntCaches. In Total => cache: " + cache + ", tcache: " + tcache + ", allocated: " + allocated + ", tallocated: " + tallocated;
}
// NeonPaper end
}

View File

@@ -0,0 +1,243 @@
package net.minecraft.server;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import javax.annotation.Nullable;
import io.akarin.server.core.AkarinGlobalConfig;
/**
* Akarin Changes Note
* 1) Restricted spawner modify (feature)
*/
public class ItemMonsterEgg extends Item {
public ItemMonsterEgg() {
this.b(CreativeModeTab.f);
}
public String b(ItemStack itemstack) {
String s = ("" + LocaleI18n.get(this.getName() + ".name")).trim();
String s1 = EntityTypes.a(h(itemstack));
if (s1 != null) {
s = s + " " + LocaleI18n.get("entity." + s1 + ".name");
}
return s;
}
public EnumInteractionResult a(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) {
ItemStack itemstack = entityhuman.b(enumhand);
if (world.isClientSide) {
return EnumInteractionResult.SUCCESS;
} else if (!entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack)) {
return EnumInteractionResult.FAIL;
} else {
IBlockData iblockdata = world.getType(blockposition);
Block block = iblockdata.getBlock();
if (block == Blocks.MOB_SPAWNER && (AkarinGlobalConfig.allowSpawnerModify || entityhuman.isCreativeAndOp())) { // Akarin
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof TileEntityMobSpawner) {
MobSpawnerAbstract mobspawnerabstract = ((TileEntityMobSpawner) tileentity).getSpawner();
mobspawnerabstract.setMobName(h(itemstack));
tileentity.update();
world.notify(blockposition, iblockdata, iblockdata, 3);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
return EnumInteractionResult.SUCCESS;
}
}
BlockPosition blockposition1 = blockposition.shift(enumdirection);
double d0 = this.a(world, blockposition1);
Entity entity = a(world, h(itemstack), (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + d0, (double) blockposition1.getZ() + 0.5D);
if (entity != null) {
if (entity instanceof EntityLiving && itemstack.hasName()) {
entity.setCustomName(itemstack.getName());
}
a(world, entityhuman, itemstack, entity);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
}
return EnumInteractionResult.SUCCESS;
}
}
protected double a(World world, BlockPosition blockposition) {
AxisAlignedBB axisalignedbb = (new AxisAlignedBB(blockposition)).b(0.0D, -1.0D, 0.0D);
List list = world.getCubes((Entity) null, axisalignedbb);
if (list.isEmpty()) {
return 0.0D;
} else {
double d0 = axisalignedbb.b;
AxisAlignedBB axisalignedbb1;
for (Iterator iterator = list.iterator(); iterator.hasNext(); d0 = Math.max(axisalignedbb1.e, d0)) {
axisalignedbb1 = (AxisAlignedBB) iterator.next();
}
return d0 - (double) blockposition.getY();
}
}
public static void a(World world, @Nullable EntityHuman entityhuman, ItemStack itemstack, @Nullable Entity entity) {
MinecraftServer minecraftserver = world.getMinecraftServer();
if (minecraftserver != null && entity != null) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound != null && nbttagcompound.hasKeyOfType("EntityTag", 10)) {
if (!world.isClientSide && entity.bC() && (entityhuman == null || !minecraftserver.getPlayerList().isOp(entityhuman.getProfile()))) {
return;
}
NBTTagCompound nbttagcompound1 = entity.save(new NBTTagCompound());
UUID uuid = entity.getUniqueID();
nbttagcompound1.a(nbttagcompound.getCompound("EntityTag"));
entity.a(uuid);
entity.f(nbttagcompound1);
}
}
}
public InteractionResultWrapper<ItemStack> a(World world, EntityHuman entityhuman, EnumHand enumhand) {
ItemStack itemstack = entityhuman.b(enumhand);
if (world.isClientSide) {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
} else {
MovingObjectPosition movingobjectposition = this.a(world, entityhuman, true);
if (movingobjectposition != null && movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.BLOCK) {
BlockPosition blockposition = movingobjectposition.a();
if (!(world.getType(blockposition).getBlock() instanceof BlockFluids)) {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
} else if (world.a(entityhuman, blockposition) && entityhuman.a(blockposition, movingobjectposition.direction, itemstack)) {
Entity entity = a(world, h(itemstack), (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D);
if (entity == null) {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
} else {
if (entity instanceof EntityLiving && itemstack.hasName()) {
entity.setCustomName(itemstack.getName());
}
a(world, entityhuman, itemstack, entity);
if (!entityhuman.abilities.canInstantlyBuild) {
itemstack.subtract(1);
}
entityhuman.b(StatisticList.b((Item) this));
return new InteractionResultWrapper(EnumInteractionResult.SUCCESS, itemstack);
}
} else {
return new InteractionResultWrapper(EnumInteractionResult.FAIL, itemstack);
}
} else {
return new InteractionResultWrapper(EnumInteractionResult.PASS, itemstack);
}
}
}
@Nullable
public static Entity a(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2) {
return spawnCreature(world, minecraftkey, d0, d1, d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG);
}
@Nullable
public static Entity spawnCreature(World world, @Nullable MinecraftKey minecraftkey, double d0, double d1, double d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
if (minecraftkey != null && EntityTypes.eggInfo.containsKey(minecraftkey)) {
Entity entity = null;
for (int i = 0; i < 1; ++i) {
entity = EntityTypes.a(minecraftkey, world);
if (entity instanceof EntityInsentient) {
EntityInsentient entityinsentient = (EntityInsentient) entity;
entity.setPositionRotation(d0, d1, d2, MathHelper.g(world.random.nextFloat() * 360.0F), 0.0F);
entityinsentient.aP = entityinsentient.yaw;
entityinsentient.aN = entityinsentient.yaw;
entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), (GroupDataEntity) null);
// CraftBukkit start - don't return an entity when CreatureSpawnEvent is canceled
if (!world.addEntity(entity, spawnReason)) {
entity = null;
} else {
entityinsentient.D();
}
// CraftBukkit end
}
}
return entity;
} else {
return null;
}
}
public void a(CreativeModeTab creativemodetab, NonNullList<ItemStack> nonnulllist) {
if (this.a(creativemodetab)) {
Iterator iterator = EntityTypes.eggInfo.values().iterator();
while (iterator.hasNext()) {
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = (EntityTypes.MonsterEggInfo) iterator.next();
ItemStack itemstack = new ItemStack(this, 1);
a(itemstack, entitytypes_monsteregginfo.a);
nonnulllist.add(itemstack);
}
}
}
public static void a(ItemStack itemstack, MinecraftKey minecraftkey) {
NBTTagCompound nbttagcompound = itemstack.hasTag() ? itemstack.getTag() : new NBTTagCompound();
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
nbttagcompound1.setString("id", minecraftkey.toString());
nbttagcompound.set("EntityTag", nbttagcompound1);
itemstack.setTag(nbttagcompound);
}
@Nullable
public static MinecraftKey h(ItemStack itemstack) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound == null) {
return null;
} else if (!nbttagcompound.hasKeyOfType("EntityTag", 10)) {
return null;
} else {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("EntityTag");
if (!nbttagcompound1.hasKeyOfType("id", 8)) {
return null;
} else {
String s = nbttagcompound1.getString("id");
MinecraftKey minecraftkey = new MinecraftKey(s);
if (!s.contains(":")) {
nbttagcompound1.setString("id", minecraftkey.toString());
}
return minecraftkey;
}
}
}
}

View File

@@ -0,0 +1,367 @@
package net.minecraft.server;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multisets;
import javax.annotation.Nullable;
// CraftBukkit start
import org.bukkit.Bukkit;
import org.bukkit.event.server.MapInitializeEvent;
// CraftBukkit end
public class ItemWorldMap extends ItemWorldMapBase {
protected ItemWorldMap() {
this.a(true);
}
public static ItemStack a(World world, double d0, double d1, byte b0, boolean flag, boolean flag1) {
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
ItemStack itemstack = new ItemStack(Items.FILLED_MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps
String s = "map_" + itemstack.getData();
WorldMap worldmap = new WorldMap(s);
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit
worldmap.scale = b0;
worldmap.a(d0, d1, worldmap.scale);
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension
worldmap.track = flag;
worldmap.unlimitedTracking = flag1;
worldmap.c();
org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit
return itemstack;
}
@Nullable
public WorldMap getSavedMap(ItemStack itemstack, World world) {
World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world
String s = "map_" + itemstack.getData();
WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps
if (worldmap == null && !world.isClientSide) {
itemstack.setData(worldMain.b("map")); // CraftBukkit - use primary world for maps
s = "map_" + itemstack.getData();
worldmap = new WorldMap(s);
worldmap.scale = 3;
worldmap.a((double) world.getWorldData().b(), (double) world.getWorldData().d(), worldmap.scale);
worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps
worldmap.c();
worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps
// CraftBukkit start
MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView);
Bukkit.getServer().getPluginManager().callEvent(event);
// CraftBukkit end
}
return worldmap;
}
public void a(World world, Entity entity, WorldMap worldmap) {
// CraftBukkit - world.worldProvider -> ((WorldServer) world)
if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) {
int i = 1 << worldmap.scale;
int j = worldmap.centerX;
int k = worldmap.centerZ;
int l = MathHelper.floor(entity.locX - (double) j) / i + 64;
int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64;
int j1 = 128 / i;
if (world.worldProvider.n()) {
j1 /= 2;
}
WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = worldmap.a((EntityHuman) entity);
++worldmap_worldmaphumantracker.b;
boolean flag = false;
for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) {
if ((k1 & 15) == (worldmap_worldmaphumantracker.b & 15) || flag) {
flag = false;
double d0 = 0.0D;
for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) {
if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) {
int i2 = k1 - l;
int j2 = l1 - i1;
boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2);
int k2 = (j / i + k1 - 64) * i;
int l2 = (k / i + l1 - 64) * i;
HashMultiset hashmultiset = HashMultiset.create();
Chunk chunk = world.getChunkIfLoaded(new BlockPosition(k2, 0, l2)); // NeonPaper - Maps shouldn't load chunks
if (chunk != null && !chunk.isEmpty()) { // NeonPaper - Maps shouldn't load chunks
int i3 = k2 & 15;
int j3 = l2 & 15;
int k3 = 0;
double d1 = 0.0D;
if (world.worldProvider.n()) {
int l3 = k2 + l2 * 231871;
l3 = l3 * l3 * 31287121 + l3 * 11;
if ((l3 >> 20 & 1) == 0) {
hashmultiset.add(Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, BlockDirt.EnumDirtVariant.DIRT).a((IBlockAccess) world, BlockPosition.ZERO), 10);
} else {
hashmultiset.add(Blocks.STONE.getBlockData().set(BlockStone.VARIANT, BlockStone.EnumStoneVariant.STONE).a((IBlockAccess) world, BlockPosition.ZERO), 100);
}
d1 = 100.0D;
} else {
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
for (int i4 = 0; i4 < i; ++i4) {
for (int j4 = 0; j4 < i; ++j4) {
int k4 = chunk.b(i4 + i3, j4 + j3) + 1;
IBlockData iblockdata = Blocks.AIR.getBlockData();
if (k4 > 1) {
do {
--k4;
iblockdata = chunk.a(i4 + i3, k4, j4 + j3);
blockposition_mutableblockposition.c((chunk.locX << 4) + i4 + i3, k4, (chunk.locZ << 4) + j4 + j3);
} while (iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition) == MaterialMapColor.c && k4 > 0);
if (k4 > 0 && iblockdata.getMaterial().isLiquid()) {
int l4 = k4 - 1;
IBlockData iblockdata1;
do {
iblockdata1 = chunk.a(i4 + i3, l4--, j4 + j3);
++k3;
} while (l4 > 0 && iblockdata1.getMaterial().isLiquid());
}
} else {
iblockdata = Blocks.BEDROCK.getBlockData();
}
d1 += (double) k4 / (double) (i * i);
hashmultiset.add(iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition));
}
}
}
k3 /= i * i;
double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + l1 & 1) - 0.5D) * 0.4D;
byte b0 = 1;
if (d2 > 0.6D) {
b0 = 2;
}
if (d2 < -0.6D) {
b0 = 0;
}
MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MaterialMapColor.c);
if (materialmapcolor == MaterialMapColor.o) {
d2 = (double) k3 * 0.1D + (double) (k1 + l1 & 1) * 0.2D;
b0 = 1;
if (d2 < 0.5D) {
b0 = 2;
}
if (d2 > 0.9D) {
b0 = 0;
}
}
d0 = d1;
if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) {
byte b1 = worldmap.colors[k1 + l1 * 128];
byte b2 = (byte) (materialmapcolor.ad * 4 + b0);
if (b1 != b2) {
worldmap.colors[k1 + l1 * 128] = b2;
worldmap.flagDirty(k1, l1);
flag = true;
}
}
}
}
}
}
}
}
}
public static void a(World world, ItemStack itemstack) {
if (itemstack.getItem() == Items.FILLED_MAP) {
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
if (worldmap != null) {
if (world.worldProvider.getDimensionManager().getDimensionID() == worldmap.map) {
int i = 1 << worldmap.scale;
int j = worldmap.centerX;
int k = worldmap.centerZ;
BiomeBase[] abiomebase = world.getWorldChunkManager().a((BiomeBase[]) null, (j / i - 64) * i, (k / i - 64) * i, 128 * i, 128 * i, false);
for (int l = 0; l < 128; ++l) {
for (int i1 = 0; i1 < 128; ++i1) {
int j1 = l * i;
int k1 = i1 * i;
BiomeBase biomebase = abiomebase[j1 + k1 * 128 * i];
MaterialMapColor materialmapcolor = MaterialMapColor.c;
int l1 = 3;
int i2 = 8;
if (l > 0 && i1 > 0 && l < 127 && i1 < 127) {
if (abiomebase[(l - 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[(l - 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[(l - 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[(l + 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[(l + 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[(l + 1) * i + i1 * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[l * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (abiomebase[l * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) {
--i2;
}
if (biomebase.j() < 0.0F) {
materialmapcolor = MaterialMapColor.r;
if (i2 > 7 && i1 % 2 == 0) {
l1 = (l + (int) (MathHelper.sin((float) i1 + 0.0F) * 7.0F)) / 8 % 5;
if (l1 == 3) {
l1 = 1;
} else if (l1 == 4) {
l1 = 0;
}
} else if (i2 > 7) {
materialmapcolor = MaterialMapColor.c;
} else if (i2 > 5) {
l1 = 1;
} else if (i2 > 3) {
l1 = 0;
} else if (i2 > 1) {
l1 = 0;
}
} else if (i2 > 0) {
materialmapcolor = MaterialMapColor.C;
if (i2 > 3) {
l1 = 1;
} else {
l1 = 3;
}
}
}
if (materialmapcolor != MaterialMapColor.c) {
worldmap.colors[l + i1 * 128] = (byte) (materialmapcolor.ad * 4 + l1);
worldmap.flagDirty(l, i1);
}
}
}
}
}
}
}
public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) {
if (!world.isClientSide) {
WorldMap worldmap = this.getSavedMap(itemstack, world);
if (entity instanceof EntityHuman) {
EntityHuman entityhuman = (EntityHuman) entity;
worldmap.a(entityhuman, itemstack);
}
if (flag || entity instanceof EntityHuman && ((EntityHuman) entity).getItemInOffHand() == itemstack) {
this.a(world, entity, worldmap);
}
}
}
@Nullable
public Packet<?> a(ItemStack itemstack, World world, EntityHuman entityhuman) {
return this.getSavedMap(itemstack, world).a(itemstack, world, entityhuman);
}
public void b(ItemStack itemstack, World world, EntityHuman entityhuman) {
NBTTagCompound nbttagcompound = itemstack.getTag();
if (nbttagcompound != null) {
if (nbttagcompound.hasKeyOfType("map_scale_direction", 99)) {
a(itemstack, world, nbttagcompound.getInt("map_scale_direction"));
nbttagcompound.remove("map_scale_direction");
} else if (nbttagcompound.getBoolean("map_tracking_position")) {
b(itemstack, world);
nbttagcompound.remove("map_tracking_position");
}
}
}
protected static void a(ItemStack itemstack, World world, int i) {
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
itemstack.setData(world.b("map"));
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
if (worldmap != null) {
worldmap1.scale = (byte) MathHelper.clamp(worldmap.scale + i, 0, 4);
worldmap1.track = worldmap.track;
worldmap1.a((double) worldmap.centerX, (double) worldmap.centerZ, worldmap1.scale);
worldmap1.map = worldmap.map;
worldmap1.c();
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
// CraftBukkit start
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
Bukkit.getServer().getPluginManager().callEvent(event);
// CraftBukkit end
}
}
protected static void b(ItemStack itemstack, World world) {
WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world);
world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps
itemstack.setData(world.b("map"));
WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData());
worldmap1.track = true;
if (worldmap != null) {
worldmap1.centerX = worldmap.centerX;
worldmap1.centerZ = worldmap.centerZ;
worldmap1.scale = worldmap.scale;
worldmap1.map = worldmap.map;
worldmap1.c();
world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1);
// CraftBukkit start
MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView);
Bukkit.getServer().getPluginManager().callEvent(event);
// CraftBukkit end
}
}
}

View File

@@ -0,0 +1,370 @@
package net.minecraft.server;
import java.util.Random;
import java.util.UUID;
public class MathHelper {
public static final float a = c(2.0F);
private static final int[] SINE_TABLE_INT = new int[16384 + 1];
private static final float SINE_TABLE_MIDPOINT;
private static final Random c = new Random();
private static final int[] d;
private static final double e;
private static final double[] f;
private static final double[] g;
public static float sin(float f) {
return lookup((int) (f * 10430.38) & 0xFFFF);
}
public static float cos(float f) {
return lookup((int) (f * 10430.38 + 16384.0) & 0xFFFF);
}
private static float lookup(int index) {
if (index == 32768) {
return SINE_TABLE_MIDPOINT;
}
int neg = (index & 0x8000) << 16;
int mask = (index << 17) >> 31;
int pos = (0x8001 & mask) + (index ^ mask);
pos &= 0x7fff;
return Float.intBitsToFloat(SINE_TABLE_INT[pos] ^ neg);
}
public static float c(float f) {
return (float) Math.sqrt((double) f);
}
public static float sqrt(double d0) {
return (float) Math.sqrt(d0);
}
public static int d(float f) {
int i = (int) f;
return f < (float) i ? i - 1 : i;
}
public static int floor(double d0) {
int i = (int) d0;
return d0 < (double) i ? i - 1 : i;
}
public static long d(double d0) {
long i = (long) d0;
return d0 < (double) i ? i - 1L : i;
}
public static float e(float f) {
return f >= 0.0F ? f : -f;
}
public static int a(int i) {
return i >= 0 ? i : -i;
}
public static int f(float f) {
int i = (int) f;
return f > (float) i ? i + 1 : i;
}
public static int f(double d0) {
int i = (int) d0;
return d0 > (double) i ? i + 1 : i;
}
public static int clamp(int i, int j, int k) {
return i < j ? j : (i > k ? k : i);
}
public static float a(float f, float f1, float f2) {
return f < f1 ? f1 : (f > f2 ? f2 : f);
}
public static double a(double d0, double d1, double d2) {
return d0 < d1 ? d1 : (d0 > d2 ? d2 : d0);
}
public static double b(double d0, double d1, double d2) {
return d2 < 0.0D ? d0 : (d2 > 1.0D ? d1 : d0 + (d1 - d0) * d2);
}
public static double a(double d0, double d1) {
if (d0 < 0.0D) {
d0 = -d0;
}
if (d1 < 0.0D) {
d1 = -d1;
}
return d0 > d1 ? d0 : d1;
}
public static int nextInt(Random random, int i, int j) {
return i >= j ? i : random.nextInt(j - i + 1) + i;
}
public static float a(Random random, float f, float f1) {
return f >= f1 ? f : random.nextFloat() * (f1 - f) + f;
}
public static double a(Random random, double d0, double d1) {
return d0 >= d1 ? d0 : random.nextDouble() * (d1 - d0) + d0;
}
public static double a(long[] along) {
long i = 0L;
long[] along1 = along;
int j = along.length;
for (int k = 0; k < j; ++k) {
long l = along1[k];
i += l;
}
return (double) i / (double) along.length;
}
public static float g(float f) {
f %= 360.0F;
if (f >= 180.0F) {
f -= 360.0F;
}
if (f < -180.0F) {
f += 360.0F;
}
return f;
}
public static double g(double d0) {
d0 %= 360.0D;
if (d0 >= 180.0D) {
d0 -= 360.0D;
}
if (d0 < -180.0D) {
d0 += 360.0D;
}
return d0;
}
public static int b(int i) {
i %= 360;
if (i >= 180) {
i -= 360;
}
if (i < -180) {
i += 360;
}
return i;
}
public static int a(String s, int i) {
try {
return Integer.parseInt(s);
} catch (Throwable throwable) {
return i;
}
}
public static int a(String s, int i, int j) {
return Math.max(j, a(s, i));
}
public static double a(String s, double d0) {
try {
return Double.parseDouble(s);
} catch (Throwable throwable) {
return d0;
}
}
public static double a(String s, double d0, double d1) {
return Math.max(d1, a(s, d0));
}
public static int c(int i) {
int j = i - 1;
j |= j >> 1;
j |= j >> 2;
j |= j >> 4;
j |= j >> 8;
j |= j >> 16;
return j + 1;
}
private static boolean g(int i) {
return i != 0 && (i & i - 1) == 0;
}
public static int d(int i) {
i = g(i) ? i : c(i);
return MathHelper.d[(int) ((long) i * 125613361L >> 27) & 31];
}
public static int e(int i) {
return d(i) - (g(i) ? 0 : 1);
}
public static int c(int i, int j) {
if (j == 0) {
return 0;
} else if (i == 0) {
return j;
} else {
if (i < 0) {
j *= -1;
}
int k = i % j;
return k == 0 ? i : i + j - k;
}
}
public static long c(int i, int j, int k) {
long l = (long) (i * 3129871) ^ (long) k * 116129781L ^ (long) j;
l = l * l * 42317861L + l * 11L;
return l;
}
public static UUID a(Random random) {
long i = random.nextLong() & -61441L | 16384L;
long j = random.nextLong() & 4611686018427387903L | Long.MIN_VALUE;
return new UUID(i, j);
}
public static UUID a() {
return a(MathHelper.c);
}
public static double c(double d0, double d1, double d2) {
return (d0 - d1) / (d2 - d1);
}
public static double c(double d0, double d1) {
double d2 = d1 * d1 + d0 * d0;
if (Double.isNaN(d2)) {
return Double.NaN;
} else {
boolean flag = d0 < 0.0D;
if (flag) {
d0 = -d0;
}
boolean flag1 = d1 < 0.0D;
if (flag1) {
d1 = -d1;
}
boolean flag2 = d0 > d1;
double d3;
if (flag2) {
d3 = d1;
d1 = d0;
d0 = d3;
}
d3 = i(d2);
d1 *= d3;
d0 *= d3;
double d4 = MathHelper.e + d0;
int i = (int) Double.doubleToRawLongBits(d4);
double d5 = MathHelper.f[i];
double d6 = MathHelper.g[i];
double d7 = d4 - MathHelper.e;
double d8 = d0 * d6 - d1 * d7;
double d9 = (6.0D + d8 * d8) * d8 * 0.16666666666666666D;
double d10 = d5 + d9;
if (flag2) {
d10 = 1.5707963267948966D - d10;
}
if (flag1) {
d10 = 3.141592653589793D - d10;
}
if (flag) {
d10 = -d10;
}
return d10;
}
}
public static double i(double d0) {
double d1 = 0.5D * d0;
long i = Double.doubleToRawLongBits(d0);
i = 6910469410427058090L - (i >> 1);
d0 = Double.longBitsToDouble(i);
d0 *= 1.5D - d1 * d0 * d0;
return d0;
}
public static int f(int i) {
i ^= i >>> 16;
i *= -2048144789;
i ^= i >>> 13;
i *= -1028477387;
i ^= i >>> 16;
return i;
}
static {
int i;
final float[] SINE_TABLE = new float[65536];
for (i = 0; i < 65536; ++i) {
SINE_TABLE[i] = (float) Math.sin((double) i * 3.141592653589793D * 2.0D / 65536.0D);
}
for (i = 0; i < SINE_TABLE_INT.length; i++) {
SINE_TABLE_INT[i] = Float.floatToRawIntBits(SINE_TABLE[i]);
}
SINE_TABLE_MIDPOINT = SINE_TABLE[SINE_TABLE.length / 2];
for (i = 0; i < SINE_TABLE.length; i++) {
float expected = SINE_TABLE[i];
float value = lookup(i);
if (expected != value) {
throw new IllegalArgumentException(String.format("LUT error at index %d (expected: %s, found: %s)", i, expected, value));
}
}
d = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};
e = Double.longBitsToDouble(4805340802404319232L);
f = new double[257];
g = new double[257];
for (i = 0; i < 257; ++i) {
double d0 = (double) i / 256.0D;
double d1 = Math.asin(d0);
MathHelper.g[i] = Math.cos(d1);
MathHelper.f[i] = d1;
}
}
}

View File

@@ -0,0 +1,164 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.Object2LongMap;
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class MethodProfiler {
public static final boolean ENABLED = Boolean.getBoolean("enableDebugMethodProfiler"); // CraftBukkit - disable unless specified in JVM arguments
private static final Logger b = LogManager.getLogger();
private final ObjectArrayList<String> c = new ObjectArrayList<>(); // Dionysus
private final LongArrayList d = new LongArrayList(); // Dionysus
public boolean a;
private String e = "";
private final Object2LongOpenHashMap<String> f = new Object2LongOpenHashMap<>();
public MethodProfiler() {}
public void a() {
if (!ENABLED) return; // CraftBukkit
this.f.clear();
this.e = "";
this.c.clear();
}
public void a(String s) {
if (!ENABLED) return; // CraftBukkit
if (this.a) {
if (!this.e.isEmpty()) {
this.e = this.e + ".";
}
this.e = this.e + s;
this.c.add(this.e);
this.d.add(Long.valueOf(System.nanoTime()));
}
}
public void a(Supplier<String> supplier) {
if (!ENABLED) return; // CraftBukkit
if (this.a) {
this.a((String) supplier.get());
}
}
public void b() {
if (!ENABLED) return; // CraftBukkit
if (this.a) {
long i = System.nanoTime();
long j = this.d.removeLong(this.d.size() - 1);
this.c.remove(this.c.size() - 1);
long k = i - j;
if (this.f.containsKey(this.e)) {
this.f.put(this.e, this.f.get(this.e) + k);
} else {
this.f.put(this.e, k);
}
if (k > 100000000L) {
MethodProfiler.b.warn("Something\'s taking too long! \'{}\' took aprox {} ms", this.e, Double.valueOf((double) k / 1000000.0D));
}
this.e = this.c.isEmpty() ? "" : (String) this.c.get(this.c.size() - 1);
}
}
public List<MethodProfiler.ProfilerInfo> b(String s) {
if (!ENABLED || !this.a) { // CraftBukkit
return Collections.emptyList();
} else {
long i = this.f.getOrDefault("root", 0L);
long j = this.f.getOrDefault(s, -1L);
ArrayList<MethodProfiler.ProfilerInfo> arraylist = Lists.newArrayList();
if (!s.isEmpty()) {
s = s + ".";
}
long k = 0L;
for (String s1 : this.f.keySet()) {
if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) {
k += this.f.getLong(s1);
}
}
float f = (float) k;
if (k < j) {
k = j;
}
if (i < k) {
i = k;
}
for (Object2LongMap.Entry<String> entry : this.f.object2LongEntrySet()) {
String s2 = entry.getKey();
if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) {
long l = this.f.getLong(s2);
double d0 = (double) l * 100.0D / (double) k;
double d1 = (double) l * 100.0D / (double) i;
String s3 = s2.substring(s.length());
arraylist.add(new MethodProfiler.ProfilerInfo(s3, d0, d1));
}
entry.setValue(entry.getLongValue() * 999L / 1000L);
}
if ((float) k > f) {
arraylist.add(new MethodProfiler.ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i));
}
Collections.sort(arraylist);
arraylist.add(0, new MethodProfiler.ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i));
return arraylist;
}
}
public void c(String s) {
if (!ENABLED) return; // CraftBukkit
this.b();
this.a(s);
}
public String c() {
if (!ENABLED) return "[DISABLED]"; // CraftBukkit
return this.c.isEmpty() ? "[UNKNOWN]" : (String) this.c.get(this.c.size() - 1);
}
public static final class ProfilerInfo implements Comparable<MethodProfiler.ProfilerInfo> {
public double a;
public double b;
public String c;
public ProfilerInfo(String s, double d0, double d1) {
this.c = s;
this.a = d0;
this.b = d1;
}
public int a(MethodProfiler.ProfilerInfo methodprofiler_profilerinfo) {
return methodprofiler_profilerinfo.a < this.a ? -1 : (methodprofiler_profilerinfo.a > this.a ? 1 : methodprofiler_profilerinfo.c.compareTo(this.c));
}
public int compareTo(MethodProfiler.ProfilerInfo object) { // CraftBukkit: decompile error
return this.a((MethodProfiler.ProfilerInfo) object);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,6 @@ import io.netty.util.concurrent.GenericFutureListener;
import java.net.SocketAddress;
import java.util.Queue;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nullable;
import javax.crypto.SecretKey;
import org.apache.commons.lang3.ArrayUtils;
@@ -33,7 +32,6 @@ import org.apache.logging.log4j.MarkerManager;
/**
* 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)
@@ -82,7 +80,7 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
public SocketAddress l;
public java.util.UUID spoofedUUID;
public com.mojang.authlib.properties.Property[] spoofedProfile;
public volatile boolean preparing = true; // Akarin - add volatile
public boolean preparing = true;
// Spigot End
private PacketListener m;
private IChatBaseComponent n;
@@ -367,7 +365,10 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
@Override
protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error
this.a(channelhandlercontext, object);
// FlamePaper - Check if channel is opened before reading packet
if (isConnected()) {
this.a(channelhandlercontext, object);
}
}
public static class QueuedPacket { // Akarin - default -> public

View File

@@ -0,0 +1,601 @@
package net.minecraft.server;
import co.aikar.timings.Timing;
import com.google.common.base.Predicate;
import com.google.common.collect.AbstractIterator;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
// CraftBukkit start
import java.util.LinkedList;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Make whole class thread-safe (safety issue)
*/
@ThreadSafe // Akarin - idk why we need do so!!
public class PlayerChunkMap {
private static final Predicate<EntityPlayer> a = new Predicate() {
public boolean a(@Nullable EntityPlayer entityplayer) {
return entityplayer != null && !entityplayer.isSpectator();
}
public boolean apply(@Nullable Object object) {
return this.a((EntityPlayer) object);
}
};
private static final Predicate<EntityPlayer> b = new Predicate() {
public boolean a(@Nullable EntityPlayer entityplayer) {
return entityplayer != null && (!entityplayer.isSpectator() || entityplayer.x().getGameRules().getBoolean("spectatorsGenerateChunks"));
}
public boolean apply(@Nullable Object object) {
return this.a((EntityPlayer) object);
}
};
private final WorldServer world;
private final List<EntityPlayer> managedPlayers = Lists.newArrayList();
private final ReentrantReadWriteLock managedPlayersLock = new ReentrantReadWriteLock(); // Akarin - add lock
private final Long2ObjectMap<PlayerChunk> e = new Long2ObjectOpenHashMap(4096);
private final Set<PlayerChunk> f = Sets.newHashSet();
private final List<PlayerChunk> g = Lists.newLinkedList();
private final List<PlayerChunk> h = Lists.newLinkedList();
private final List<PlayerChunk> i = Lists.newCopyOnWriteArrayList(); // Akarin - bad plugin will access this
private int j; public int getViewDistance() { return j; } // Paper OBFHELPER
private long k;
private boolean l = true;
private boolean m = true;
private boolean wasNotEmpty; // CraftBukkit - add field
public PlayerChunkMap(WorldServer worldserver) {
this.world = worldserver;
this.a(worldserver.spigotConfig.viewDistance); // Spigot
}
public WorldServer getWorld() {
return this.world;
}
public Iterator<Chunk> b() {
final Iterator iterator = this.i.iterator();
return new AbstractIterator<Chunk>() {
protected Chunk a() {
while (true) {
if (iterator.hasNext()) {
PlayerChunk playerchunk = (PlayerChunk) iterator.next();
Chunk chunk = playerchunk.f();
if (chunk == null) {
continue;
}
if (!chunk.v() && chunk.isDone()) {
return chunk;
}
if (!chunk.j()) {
return chunk;
}
if (!playerchunk.a(128.0D, PlayerChunkMap.a)) {
continue;
}
return chunk;
}
return (Chunk) this.endOfData();
}
}
protected Chunk computeNext() {
return this.a();
}
};
}
public synchronized void flush() { // Akarin - synchronized
long i = this.world.getTime();
int j;
PlayerChunk playerchunk;
if (i - this.k > 8000L) {
try (Timing ignored = world.timings.doChunkMapUpdate.startTiming()) { // Paper
this.k = i;
for (j = 0; j < this.i.size(); ++j) {
playerchunk = (PlayerChunk) this.i.get(j);
playerchunk.d();
playerchunk.c();
}
} // Paper timing
}
if (!this.f.isEmpty()) {
try (Timing ignored = world.timings.doChunkMapToUpdate.startTiming()) { // Paper
Iterator iterator = this.f.iterator();
while (iterator.hasNext()) {
playerchunk = (PlayerChunk) iterator.next();
playerchunk.d();
}
this.f.clear();
} // Paper timing
}
if (this.l && i % 4L == 0L) {
this.l = false;
try (Timing ignored = world.timings.doChunkMapSortMissing.startTiming()) { // Paper
Collections.sort(this.h, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
}
public int compare(Object object, Object object1) {
return this.a((PlayerChunk) object, (PlayerChunk) object1);
}
});
} // Paper timing
}
if (this.m && i % 4L == 2L) {
this.m = false;
try (Timing ignored = world.timings.doChunkMapSortSendToPlayers.startTiming()) { // Paper
Collections.sort(this.g, new Comparator() {
public int a(PlayerChunk playerchunk, PlayerChunk playerchunk1) {
return ComparisonChain.start().compare(playerchunk.g(), playerchunk1.g()).result();
}
public int compare(Object object, Object object1) {
return this.a((PlayerChunk) object, (PlayerChunk) object1);
}
});
} // Paper timing
}
if (!this.h.isEmpty()) {
try (Timing ignored = world.timings.doChunkMapPlayersNeedingChunks.startTiming()) { // Paper
// Spigot start
org.spigotmc.SlackActivityAccountant activityAccountant = this.world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
int chunkGensAllowed = world.paperConfig.maxChunkGensPerTick; // Paper
// Spigot end
Iterator iterator1 = this.h.iterator();
while (iterator1.hasNext()) {
PlayerChunk playerchunk1 = (PlayerChunk) iterator1.next();
if (playerchunk1.f() == null) {
boolean flag = playerchunk1.a(PlayerChunkMap.b);
// Paper start
if (flag && !playerchunk1.chunkExists && chunkGensAllowed-- <= 0) {
continue;
}
// Paper end
if (playerchunk1.a(flag)) {
iterator1.remove();
if (playerchunk1.b()) {
this.g.remove(playerchunk1);
}
if (activityAccountant.activityTimeIsExhausted()) { // Spigot
break;
}
}
// CraftBukkit start - SPIGOT-2891: remove once chunk has been provided
} else {
iterator1.remove();
}
// CraftBukkit end
}
activityAccountant.endActivity(); // Spigot
} // Paper timing
}
if (!this.g.isEmpty()) {
j = world.paperConfig.maxChunkSendsPerTick; // Paper
try (Timing ignored = world.timings.doChunkMapPendingSendToPlayers.startTiming()) { // Paper
Iterator iterator2 = this.g.iterator();
while (iterator2.hasNext()) {
PlayerChunk playerchunk2 = (PlayerChunk) iterator2.next();
if (playerchunk2.b()) {
iterator2.remove();
--j;
if (j < 0) {
break;
}
}
}
} // Paper timing
}
managedPlayersLock.readLock().lock(); // Akarin
if (this.managedPlayers.isEmpty()) {
try (Timing ignored = world.timings.doChunkMapUnloadChunks.startTiming()) { // Paper
WorldProvider worldprovider = this.world.worldProvider;
if (!worldprovider.e() && !this.world.savingDisabled) { // Paper - respect saving disabled setting
this.world.getChunkProviderServer().b();
}
} // Paper timing
}
managedPlayersLock.readLock().unlock(); // Akarin
}
public synchronized boolean a(int i, int j) { // Akarin - synchronized
long k = d(i, j);
return this.e.get(k) != null;
}
@Nullable
public synchronized PlayerChunk getChunk(int i, int j) { // Akarin - synchronized
return (PlayerChunk) this.e.get(d(i, j));
}
private PlayerChunk c(int i, int j) {
long k = d(i, j);
PlayerChunk playerchunk = (PlayerChunk) this.e.get(k);
if (playerchunk == null) {
playerchunk = new PlayerChunk(this, i, j);
this.e.put(k, playerchunk);
this.i.add(playerchunk);
if (playerchunk.f() == null) {
this.h.add(playerchunk);
}
if (!playerchunk.b()) {
this.g.add(playerchunk);
}
}
return playerchunk;
}
// CraftBukkit start - add method
public final boolean isChunkInUse(int x, int z) {
PlayerChunk pi = getChunk(x, z);
if (pi != null) {
return (pi.c.size() > 0);
}
return false;
}
// CraftBukkit end
public void flagDirty(BlockPosition blockposition) {
int i = blockposition.getX() >> 4;
int j = blockposition.getZ() >> 4;
PlayerChunk playerchunk = this.getChunk(i, j);
if (playerchunk != null) {
playerchunk.a(blockposition.getX() & 15, blockposition.getY(), blockposition.getZ() & 15);
}
}
public void addPlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.locX >> 4;
int j = (int) entityplayer.locZ >> 4;
entityplayer.d = entityplayer.locX;
entityplayer.e = entityplayer.locZ;
// CraftBukkit start - Load nearby chunks first
List<ChunkCoordIntPair> chunkList = new LinkedList<ChunkCoordIntPair>();
// Paper start - Player view distance API
int viewDistance = entityplayer.getViewDistance();
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
// Paper end
chunkList.add(new ChunkCoordIntPair(k, l));
}
}
Collections.sort(chunkList, new ChunkCoordComparator(entityplayer));
synchronized (this) { // Akarin - synchronized
for (ChunkCoordIntPair pair : chunkList) {
this.c(pair.x, pair.z).a(entityplayer);
}
} // Akarin
// CraftBukkit end
managedPlayersLock.writeLock().lock(); // Akarin
this.managedPlayers.add(entityplayer);
managedPlayersLock.writeLock().unlock(); // Akarin
this.e();
}
public void removePlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.d >> 4;
int j = (int) entityplayer.e >> 4;
// Paper start - Player view distance API
int viewDistance = entityplayer.getViewDistance();
for (int k = i - viewDistance; k <= i + viewDistance; ++k) {
for (int l = j - viewDistance; l <= j + viewDistance; ++l) {
// Paper end
PlayerChunk playerchunk = this.getChunk(k, l);
if (playerchunk != null) {
playerchunk.b(entityplayer);
}
}
}
managedPlayersLock.writeLock().lock(); // Akarin
this.managedPlayers.remove(entityplayer);
managedPlayersLock.writeLock().unlock(); // Akarin
this.e();
}
private boolean a(int i, int j, int k, int l, int i1) {
int j1 = i - k;
int k1 = j - l;
return j1 >= -i1 && j1 <= i1 ? k1 >= -i1 && k1 <= i1 : false;
}
public void movePlayer(EntityPlayer entityplayer) {
int i = (int) entityplayer.locX >> 4;
int j = (int) entityplayer.locZ >> 4;
double d0 = entityplayer.d - entityplayer.locX;
double d1 = entityplayer.e - entityplayer.locZ;
double d2 = d0 * d0 + d1 * d1;
if (d2 >= 64.0D) {
int k = (int) entityplayer.d >> 4;
int l = (int) entityplayer.e >> 4;
final int viewDistance = entityplayer.getViewDistance(); // Paper - Player view distance API
int i1 = Math.max(getViewDistance(), viewDistance); // Paper - Player view distance API
int j1 = i - k;
int k1 = j - l;
List<ChunkCoordIntPair> chunksToLoad = new LinkedList<ChunkCoordIntPair>(); // CraftBukkit
if (j1 != 0 || k1 != 0) {
for (int l1 = i - i1; l1 <= i + i1; ++l1) {
for (int i2 = j - i1; i2 <= j + i1; ++i2) {
if (!this.a(l1, i2, k, l, viewDistance)) { // Paper - Player view distance API
// this.c(l1, i2).a(entityplayer);
chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit
}
if (!this.a(l1 - j1, i2 - k1, i, j, i1)) {
PlayerChunk playerchunk = this.getChunk(l1 - j1, i2 - k1);
if (playerchunk != null) {
playerchunk.b(entityplayer);
}
}
}
}
entityplayer.d = entityplayer.locX;
entityplayer.e = entityplayer.locZ;
this.e();
// CraftBukkit start - send nearest chunks first
Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer));
synchronized (this) { // Akarin - synchronized
for (ChunkCoordIntPair pair : chunksToLoad) {
this.c(pair.x, pair.z).a(entityplayer);
}
} // Akarin
// CraftBukkit end
}
}
}
public boolean a(EntityPlayer entityplayer, int i, int j) {
PlayerChunk playerchunk = this.getChunk(i, j);
return playerchunk != null && playerchunk.d(entityplayer) && playerchunk.e();
}
public final void setViewDistanceForAll(int viewDistance) { this.a(viewDistance); } // Paper - OBFHELPER
// Paper start - Separate into two methods
public void a(int i) {
i = MathHelper.clamp(i, 3, 32);
if (i != this.j) {
int j = i - this.j;
managedPlayersLock.readLock().lock(); // Akarin
ArrayList arraylist = Lists.newArrayList(this.managedPlayers);
managedPlayersLock.readLock().unlock(); // Akarin
Iterator iterator = arraylist.iterator();
while (iterator.hasNext()) {
EntityPlayer entityplayer = (EntityPlayer) iterator.next();
this.setViewDistance(entityplayer, i, false); // Paper - Split, don't mark sort pending, we'll handle it after
}
this.j = i;
this.e();
}
}
public void setViewDistance(EntityPlayer entityplayer, int i) {
this.setViewDistance(entityplayer, i, true); // Mark sort pending by default so we don't have to remember to do so all the time
}
// Copied from above with minor changes
public void setViewDistance(EntityPlayer entityplayer, int i, boolean markSort) {
i = MathHelper.clamp(i, 3, 32);
int oldViewDistance = entityplayer.getViewDistance();
if (i != oldViewDistance) {
int j = i - oldViewDistance;
int k = (int) entityplayer.locX >> 4;
int l = (int) entityplayer.locZ >> 4;
int i1;
int j1;
if (j > 0) {
synchronized (this) { // Akarin - synchronized
for (i1 = k - i; i1 <= k + i; ++i1) {
for (j1 = l - i; j1 <= l + i; ++j1) {
PlayerChunk playerchunk = this.c(i1, j1);
if (!playerchunk.d(entityplayer)) {
playerchunk.a(entityplayer);
}
}
}
} // Akarin
} else {
synchronized (this) { // Akarin - synchronized
for (i1 = k - oldViewDistance; i1 <= k + oldViewDistance; ++i1) {
for (j1 = l - oldViewDistance; j1 <= l + oldViewDistance; ++j1) {
if (!this.a(i1, j1, k, l, i)) {
this.c(i1, j1).b(entityplayer);
}
}
}
} // Akarin
if (markSort) {
this.e();
}
}
}
}
// Paper end
private void e() {
this.l = true;
this.m = true;
}
public static int getFurthestViewableBlock(int i) {
return i * 16 - 16;
}
private static long d(int i, int j) {
return (long) i + 2147483647L | (long) j + 2147483647L << 32;
}
public synchronized void a(PlayerChunk playerchunk) { // Akarin - synchronized
// org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Add"); // Paper // Akarin
this.f.add(playerchunk);
}
public synchronized void b(PlayerChunk playerchunk) { // Akarin - synchronized
org.spigotmc.AsyncCatcher.catchOp("Async Player Chunk Remove"); // Paper
ChunkCoordIntPair chunkcoordintpair = playerchunk.a();
long i = d(chunkcoordintpair.x, chunkcoordintpair.z);
playerchunk.c();
this.e.remove(i);
this.i.remove(playerchunk);
this.f.remove(playerchunk);
this.g.remove(playerchunk);
this.h.remove(playerchunk);
Chunk chunk = playerchunk.f();
if (chunk != null) {
// Paper start - delay chunk unloads
if (world.paperConfig.delayChunkUnloadsBy <= 0) {
this.getWorld().getChunkProviderServer().unload(chunk);
} else {
chunk.scheduledForUnload = System.currentTimeMillis();
}
// Paper end
}
}
// CraftBukkit start - Sorter to load nearby chunks first
private static class ChunkCoordComparator implements java.util.Comparator<ChunkCoordIntPair> {
private int x;
private int z;
public ChunkCoordComparator (EntityPlayer entityplayer) {
x = (int) entityplayer.locX >> 4;
z = (int) entityplayer.locZ >> 4;
}
public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) {
if (a.equals(b)) {
return 0;
}
// Subtract current position to set center point
int ax = a.x - this.x;
int az = a.z - this.z;
int bx = b.x - this.x;
int bz = b.z - this.z;
int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz));
if (result != 0) {
return result;
}
if (ax < 0) {
if (bx < 0) {
return bz - az;
} else {
return -1;
}
} else {
if (bx < 0) {
return 1;
} else {
return az - bz;
}
}
}
}
// CraftBukkit end
// Paper start - Player view distance API
public void updateViewDistance(EntityPlayer player, int distanceIn) {
final int oldViewDistance = player.getViewDistance();
// This represents the view distance that we will set on the player
// It can exist as a negative value
int playerViewDistance = MathHelper.clamp(distanceIn, 3, 32);
// This value is the one we actually use to update the chunk map
// We don't ever want this to be a negative
int toSet = playerViewDistance;
if (distanceIn < 0) {
playerViewDistance = -1;
toSet = world.getPlayerChunkMap().getViewDistance();
}
if (toSet != oldViewDistance) {
// Order matters
this.setViewDistance(player, toSet);
player.setViewDistance(playerViewDistance);
}
}
// Paper end
}

View File

@@ -1,7 +1,9 @@
package net.minecraft.server;
import com.google.common.collect.Lists;
import com.google.common.primitives.Doubles;
import com.google.common.primitives.Floats;
import com.google.common.util.concurrent.Futures;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
@@ -10,6 +12,8 @@ import io.netty.util.concurrent.GenericFutureListener;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
@@ -75,12 +79,13 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
private final MinecraftServer minecraftServer;
public EntityPlayer player;
private int e;
private volatile long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public - volatile
private volatile boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public - volatile
private volatile long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public - volatile
private long f = getCurrentMillis(); public void setLastPing(long lastPing) { this.f = lastPing;}; public long getLastPing() { return this.f;}; // Paper - OBFHELPER - set ping to delay initial // Akarin - private -> public
private boolean g; public void setPendingPing(boolean isPending) { this.g = isPending;}; public boolean isPendingPing() { return this.g;}; // Paper - OBFHELPER // Akarin - private -> public
private long h; public void setKeepAliveID(long keepAliveID) { this.h = keepAliveID;}; public long getKeepAliveID() {return this.h; }; // Paper - OBFHELPER // Akarin - private -> public
// CraftBukkit start - multithreaded fields
private volatile int chatThrottle;
private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle");
private final java.util.concurrent.atomic.AtomicInteger tabSpamLimiter = new java.util.concurrent.atomic.AtomicInteger(); // Paper - configurable tab spam limits
// CraftBukkit end
private int j;
private final IntHashMap<Short> k = new IntHashMap();
@@ -212,6 +217,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
this.minecraftServer.methodProfiler.b();
// CraftBukkit start
for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ;
if (tabSpamLimiter.get() > 0) tabSpamLimiter.getAndDecrement(); // Paper - split to seperate variable
/* Use thread-safe field access instead
if (this.chatThrottle > 0) {
--this.chatThrottle;
@@ -380,10 +386,13 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
entity.setLocation(d3, d4, d5, f, f1);
Location curPos = getPlayer().getLocation(); // Paper
player.setLocation(d3, d4, d5, f, f1); // Paper
boolean flag2 = worldserver.getCubes(entity, entity.getBoundingBox().shrink(0.0625D)).isEmpty();
if (flag && (flag1 || !flag2)) {
entity.setLocation(d0, d1, d2, f, f1);
player.setLocation(d0, d1, d2, f, f1); // Paper
this.networkManager.sendPacket(new PacketPlayOutVehicleMove(entity));
return;
}
@@ -393,7 +402,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
// Spigot Start
if ( !hasMoved )
{
Location curPos = player.getLocation();
//Location curPos = player.getLocation(); // Paper - move up
lastPosX = curPos.getX();
lastPosY = curPos.getY();
lastPosZ = curPos.getZ();
@@ -907,6 +916,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
case START_DESTROY_BLOCK:
case ABORT_DESTROY_BLOCK:
case STOP_DESTROY_BLOCK:
// NeonPaper start - Don't allow digging in unloaded chunks
if (!worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) {
return;
}
// NeonPaper end - Don't allow digging in unloaded chunks
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);
@@ -2293,7 +2307,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
// Paper start - async tab completion
public void a(PacketPlayInTabComplete packet) {
// CraftBukkit start
if (chatSpamField.addAndGet(this, 10) > 500 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) {
if (tabSpamLimiter.addAndGet(com.destroystokyo.paper.PaperConfig.tabSpamIncrement) > com.destroystokyo.paper.PaperConfig.tabSpamLimit && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { // Paper start - split and make configurable
minecraftServer.postToMainThread(() -> this.disconnect(new ChatMessage("disconnect.spam", new Object[0])));
return;
}
@@ -2308,32 +2322,26 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
buffer, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
event.callEvent();
completions = event.isCancelled() ? com.google.common.collect.ImmutableList.of() : event.getCompletions();
if (event.isCancelled() || event.isHandled()) {
// Still fire sync event with the provided completions, if someone is listening
if (!event.isCancelled() && org.bukkit.event.server.TabCompleteEvent.getHandlerList().getRegisteredListeners().length > 0) {
java.util.List<String> finalCompletions = completions;
Waitable<java.util.List<String>> syncCompletions = new Waitable<java.util.List<String>>() {
@Override
protected java.util.List<String> evaluate() {
org.bukkit.event.server.TabCompleteEvent syncEvent = new org.bukkit.event.server.TabCompleteEvent(PlayerConnection.this.getPlayer(), buffer, finalCompletions, isCommand, blockpos != null ? MCUtil.toLocation(player.world, blockpos) : null);
return syncEvent.callEvent() ? syncEvent.getCompletions() : com.google.common.collect.ImmutableList.of();
}
};
server.getServer().processQueue.add(syncCompletions);
try {
completions = syncCompletions.get();
} catch (InterruptedException | ExecutionException e1) {
e1.printStackTrace();
if (!event.isHandled()) {
// If the event isn't handled, we can assume that we have no completions, and so we'll ask the server
Waitable<java.util.List<String>> syncCompletions = new Waitable<java.util.List<String>>() {
@Override
protected java.util.List<String> evaluate() {
return minecraftServer.tabCompleteCommand(player, buffer, blockpos, isCommand);
}
};
server.getServer().processQueue.add(syncCompletions);
try {
completions = syncCompletions.get();
} catch (InterruptedException | ExecutionException e1) {
e1.printStackTrace();
}
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
return;
} else if (!event.isCancelled()) {
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(completions.toArray(new String[completions.size()])));
}
minecraftServer.postToMainThread(() -> {
java.util.List<String> syncCompletions = this.minecraftServer.tabCompleteCommand(this.player, buffer, blockpos, isCommand);
this.player.playerConnection.sendPacket(new PacketPlayOutTabComplete(syncCompletions.toArray(new String[syncCompletions.size()])));
});
// Paper end
}
@@ -2375,7 +2383,6 @@ 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", (NBTBase) itemstack.getTag().getList("pages", 8));
CraftEventFactory.handleEditBookEvent(player, itemstack1); // CraftBukkit
}

View File

@@ -0,0 +1,568 @@
package net.minecraft.server;
// CraftBukkit start
import java.util.ArrayList;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.event.Event;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
// CraftBukkit end
public class PlayerInteractManager {
public World world;
public EntityPlayer player;
private EnumGamemode gamemode;
private boolean d;
private int lastDigTick;
private BlockPosition f;
private int currentTick;
private boolean h;
private BlockPosition i;
private int j;
private int k;
public PlayerInteractManager(World world) {
this.gamemode = EnumGamemode.NOT_SET;
this.f = BlockPosition.ZERO;
this.i = BlockPosition.ZERO;
this.k = -1;
this.world = world;
}
public void setGameMode(EnumGamemode enumgamemode) {
this.gamemode = enumgamemode;
enumgamemode.a(this.player.abilities);
this.player.updateAbilities();
this.player.server.getPlayerList().sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, new EntityPlayer[] { this.player}), this.player); // CraftBukkit
this.world.everyoneSleeping();
}
public EnumGamemode getGameMode() {
return this.gamemode;
}
public boolean c() {
return this.gamemode.e();
}
public boolean isCreative() {
return this.gamemode.isCreative();
}
public void b(EnumGamemode enumgamemode) {
if (this.gamemode == EnumGamemode.NOT_SET) {
this.gamemode = enumgamemode;
}
this.setGameMode(this.gamemode);
}
public void a() {
this.currentTick = MinecraftServer.currentTick; // CraftBukkit;
float f;
int i;
if (this.h) {
int j = this.currentTick - this.j;
IBlockData iblockdata = this.world.getType(this.i);
if (iblockdata.getMaterial() == Material.AIR) {
this.h = false;
} else {
f = iblockdata.a((EntityHuman) this.player, this.player.world, this.i) * (float) (j + 1);
i = (int) (f * 10.0F);
if (i != this.k) {
this.world.c(this.player.getId(), this.i, i);
this.k = i;
}
if (f >= 1.0F) {
this.h = false;
this.breakBlock(this.i);
}
}
} else if (this.d) {
IBlockData iblockdata1 = this.world.getType(this.f);
if (iblockdata1.getMaterial() == Material.AIR) {
this.world.c(this.player.getId(), this.f, -1);
this.k = -1;
this.d = false;
} else {
int k = this.currentTick - this.lastDigTick;
f = iblockdata1.a((EntityHuman) this.player, this.player.world, this.i) * (float) (k + 1);
i = (int) (f * 10.0F);
if (i != this.k) {
this.world.c(this.player.getId(), this.f, i);
this.k = i;
}
}
}
}
public void a(BlockPosition blockposition, EnumDirection enumdirection) {
// CraftBukkit start
PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND);
if (event.isCancelled()) {
// Let the client know the block still exists
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
cancelBreakBlock(blockposition, this.world.getType(blockposition)); // Paper - Avoid visual issues on the client
// Update any tile entity data for this block
TileEntity tileentity = this.world.getTileEntity(blockposition);
if (tileentity != null) {
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
}
return;
}
// CraftBukkit end
if (this.isCreative()) {
if (!this.world.douseFire((EntityHuman) null, blockposition, enumdirection)) {
this.breakBlock(blockposition);
}
} else {
IBlockData iblockdata = this.world.getType(blockposition);
Block block = iblockdata.getBlock();
if (this.gamemode.c()) {
if (this.gamemode == EnumGamemode.SPECTATOR) {
return;
}
if (!this.player.dk()) {
ItemStack itemstack = this.player.getItemInMainHand();
if (itemstack.isEmpty()) {
return;
}
if (!itemstack.a(block)) {
return;
}
}
}
// this.world.douseFire((EntityHuman) null, blockposition, enumdirection); // CraftBukkit - Moved down
this.lastDigTick = this.currentTick;
float f = 1.0F;
// CraftBukkit start - Swings at air do *NOT* exist.
if (event.useInteractedBlock() == Event.Result.DENY) {
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
IBlockData data = this.world.getType(blockposition);
if (block == Blocks.WOODEN_DOOR) {
// For some reason *BOTH* the bottom/top part have to be marked updated.
boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, bottom ? blockposition.up() : blockposition.down()));
} else if (block == Blocks.TRAPDOOR) {
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
}
} else if (iblockdata.getMaterial() != Material.AIR) {
block.attack(this.world, blockposition, this.player);
f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition);
// Allow fire punching to be blocked
this.world.douseFire((EntityHuman) null, blockposition, enumdirection);
}
if (event.useItemInHand() == Event.Result.DENY) {
// If we 'insta destroyed' then the client needs to be informed.
if (f > 1.0f) {
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
}
return;
}
org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.player.inventory.getItemInHand(), f >= 1.0f);
if (blockEvent.isCancelled()) {
// Let the client know the block still exists
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
return;
}
if (blockEvent.getInstaBreak()) {
f = 2.0f;
}
// CraftBukkit end
if (iblockdata.getMaterial() != Material.AIR && f >= 1.0F) {
this.breakBlock(blockposition);
} else {
this.d = true;
this.f = blockposition;
int i = (int) (f * 10.0F);
this.world.c(this.player.getId(), blockposition, i);
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition)); // Paper - MC-54026 - backport from 1.13
this.k = i;
}
}
this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray
}
public void a(BlockPosition blockposition) {
if (blockposition.equals(this.f)) {
this.currentTick = MinecraftServer.currentTick; // CraftBukkit
int i = this.currentTick - this.lastDigTick;
IBlockData iblockdata = this.world.getType(blockposition);
if (iblockdata.getMaterial() != Material.AIR) {
float f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition) * (float) (i + 1);
if (f >= 0.7F) {
this.d = false;
this.world.c(this.player.getId(), blockposition, -1);
this.breakBlock(blockposition);
} else if (!this.h) {
this.d = false;
this.h = true;
this.i = blockposition;
this.j = this.lastDigTick;
}
}
// CraftBukkit start - Force block reset to client
} else {
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
// CraftBukkit end
}
}
public void e() {
this.d = false;
this.world.c(this.player.getId(), this.f, -1);
}
private boolean c(BlockPosition blockposition) {
IBlockData iblockdata = this.world.getType(blockposition);
iblockdata.getBlock().a(this.world, blockposition, iblockdata, (EntityHuman) this.player);
boolean flag = this.world.setAir(blockposition);
if (flag) {
iblockdata.getBlock().postBreak(this.world, blockposition, iblockdata);
}
return flag;
}
// Paper start - Extra method to avoid visual issues on the client when cancelling block breaks
private void cancelBreakBlock(BlockPosition position, IBlockData data) {
Block block = data.getBlock();
// Send other half of the door
if (block instanceof BlockDoor) {
boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down()));
} else if (block instanceof BlockTallPlant) {
boolean bottom = data.get(BlockTallPlant.HALF) == BlockTallPlant.EnumTallPlantHalf.LOWER;
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down()));
} else if (block instanceof BlockPistonExtension) {
BlockPosition piston = position.shift(data.get(BlockPistonExtension.FACING).opposite());
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, piston));
} else if (block instanceof BlockBed) {
if (data.get(BlockBed.PART) == BlockBed.EnumBedPart.FOOT) {
// Restore head of bed
BlockPosition head = position.shift(data.get(BlockBed.FACING));
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, head));
TileEntity tileentity = this.world.getTileEntity(head);
if (tileentity != null) {
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
}
}
}
}
// Paper end
public boolean breakBlock(BlockPosition blockposition) {
// CraftBukkit start - fire BlockBreakEvent
BlockBreakEvent event = null;
if (this.player instanceof EntityPlayer) {
org.bukkit.block.Block block = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ());
// Sword + Creative mode pre-cancel
boolean isSwordNoBreak = this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword;
// Tell client the block is gone immediately then process events
// Don't tell the client if its a creative sword break because its not broken!
if (world.getTileEntity(blockposition) == null && !isSwordNoBreak) {
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition);
packet.block = Blocks.AIR.getBlockData();
((EntityPlayer) this.player).playerConnection.sendPacket(packet);
}
event = new BlockBreakEvent(block, this.player.getBukkitEntity());
// Sword + Creative mode pre-cancel
event.setCancelled(isSwordNoBreak);
// Calculate default block experience
IBlockData nmsData = this.world.getType(blockposition);
Block nmsBlock = nmsData.getBlock();
ItemStack itemstack = this.player.getEquipment(EnumItemSlot.MAINHAND);
if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.hasBlock(nmsBlock.getBlockData())) {
// Copied from block.a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack)
// PAIL: checkme each update
if (!(nmsBlock.o() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0)) {
int bonusLevel = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack);
event.setExpToDrop(nmsBlock.getExpDrop(this.world, nmsData, bonusLevel));
}
}
this.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
if (isSwordNoBreak) {
return false;
}
// Let the client know the block still exists
((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
cancelBreakBlock(blockposition, nmsData); // Paper - Move cancellation code to extra "cancelBreakBlock" method
// Update any tile entity data for this block
TileEntity tileentity = this.world.getTileEntity(blockposition);
if (tileentity != null) {
this.player.playerConnection.sendPacket(tileentity.getUpdatePacket());
}
return false;
}
}
if (false && this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword) { // CraftBukkit - false
return false;
} else {
IBlockData iblockdata = this.world.getType(blockposition);
if (iblockdata.getBlock() == Blocks.AIR) return false; // CraftBukkit - A plugin set block to air without cancelling
TileEntity tileentity = this.world.getTileEntity(blockposition);
Block block = iblockdata.getBlock();
// CraftBukkit start - Special case skulls, their item data comes from a tile entity (Also check if block should drop items)
if (iblockdata.getBlock() == Blocks.SKULL && !this.isCreative() && event.isDropItems()) {
iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0);
return this.c(blockposition);
}
// And shulker boxes too for duplication on cancel reasons (Also check if block should drop items)
if (iblockdata.getBlock() instanceof BlockShulkerBox && event.isDropItems()) {
iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0);
return this.c(blockposition);
}
// CraftBukkit end
if ((block instanceof BlockCommand || block instanceof BlockStructure) && !this.player.isCreativeAndOp()) {
this.world.notify(blockposition, iblockdata, iblockdata, 3);
return false;
} else {
if (this.gamemode.c()) {
if (this.gamemode == EnumGamemode.SPECTATOR) {
return false;
}
if (!this.player.dk()) {
ItemStack itemstack = this.player.getItemInMainHand();
if (itemstack.isEmpty()) {
return false;
}
if (!itemstack.a(block)) {
return false;
}
}
}
this.world.a(this.player, 2001, blockposition, Block.getCombinedId(iblockdata));
// CraftBukkit start
world.captureDrops = new ArrayList<>();
boolean flag = this.c(blockposition);
if (event.isDropItems()) {
for (EntityItem item : world.captureDrops) {
world.addEntity(item);
}
}
world.captureDrops = null;
// CraftBukkit end
if (this.isCreative()) {
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
} else {
ItemStack itemstack1 = this.player.getItemInMainHand();
//ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); //NeonPaper move up
boolean flag1 = this.player.hasBlock(iblockdata);
ItemStack itemstack2 = flag && flag1 && event.isDropItems() && !itemstack1.isEmpty() ? itemstack1.cloneItemStack() : ItemStack.a; // NeonPaper - clone before use
if (!itemstack1.isEmpty()) {
itemstack1.a(this.world, iblockdata, blockposition, this.player);
}
// CraftBukkit start - Check if block should drop items
if (flag && flag1 && event.isDropItems()) {
iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2);
}
// CraftBukkit end
}
// CraftBukkit start - Drop event experience
if (flag && event != null) {
iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop(), this.player); // Paper
}
// CraftBukkit end
return flag;
}
}
}
public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand) {
if (this.gamemode == EnumGamemode.SPECTATOR) {
return EnumInteractionResult.PASS;
} else if (entityhuman.getCooldownTracker().a(itemstack.getItem())) {
return EnumInteractionResult.PASS;
} else {
int i = itemstack.getCount();
int j = itemstack.getData();
InteractionResultWrapper interactionresultwrapper = itemstack.a(world, entityhuman, enumhand);
ItemStack itemstack1 = (ItemStack) interactionresultwrapper.b();
if (itemstack1 == itemstack && itemstack1.getCount() == i && itemstack1.m() <= 0 && itemstack1.getData() == j) {
return interactionresultwrapper.a();
} else if (interactionresultwrapper.a() == EnumInteractionResult.FAIL && itemstack1.m() > 0 && !entityhuman.isHandRaised()) {
return interactionresultwrapper.a();
} else {
entityhuman.a(enumhand, itemstack1);
if (this.isCreative()) {
itemstack1.setCount(i);
if (itemstack1.f()) {
itemstack1.setData(j);
}
}
if (itemstack1.isEmpty()) {
entityhuman.a(enumhand, ItemStack.a);
}
if (!entityhuman.isHandRaised()) {
((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer);
}
return interactionresultwrapper.a();
}
}
}
// CraftBukkit start - whole method
public boolean interactResult = false;
public boolean firedInteract = false;
public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) {
IBlockData blockdata = world.getType(blockposition);
EnumInteractionResult enuminteractionresult = EnumInteractionResult.FAIL;
if (blockdata.getBlock() != Blocks.AIR) {
boolean cancelledBlock = false;
if (this.gamemode == EnumGamemode.SPECTATOR) {
TileEntity tileentity = world.getTileEntity(blockposition);
cancelledBlock = !(tileentity instanceof ITileInventory || tileentity instanceof IInventory);
}
if (entityhuman.getCooldownTracker().a(itemstack.getItem())) {
cancelledBlock = true;
}
if (itemstack.getItem() instanceof ItemBlock && !entityhuman.isCreativeAndOp()) {
Block block1 = ((ItemBlock) itemstack.getItem()).getBlock();
if (block1 instanceof BlockCommand || block1 instanceof BlockStructure) {
cancelledBlock = true;
}
}
PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack, cancelledBlock, enumhand);
firedInteract = true;
interactResult = event.useItemInHand() == Event.Result.DENY;
if (event.useInteractedBlock() == Event.Result.DENY) {
// If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door.
if (blockdata.getBlock() instanceof BlockDoor) {
boolean bottom = blockdata.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER;
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? blockposition.up() : blockposition.down()));
} else if (blockdata.getBlock() instanceof BlockCake) {
((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake
// Paper start - extend Player Interact cancellation
} else if (blockdata.getBlock() instanceof BlockStructure) {
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow());
} else if (blockdata.getBlock() instanceof BlockCommand) {
((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow());
} else if (blockdata.getBlock() instanceof BlockFlowerPot) {
// Send a block change to air and then send back the correct block, just to make the client happy
PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition);
packet.block = Blocks.AIR.getBlockData();
this.player.playerConnection.sendPacket(packet);
this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition));
TileEntity tileentity = this.world.getTileEntity(blockposition);
if (tileentity != null) {
player.playerConnection.sendPacket(tileentity.getUpdatePacket());
}
// Paper end - extend Player Interact cancellation
}
((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-2867
enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS;
} else if (this.gamemode == EnumGamemode.SPECTATOR) {
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof ITileInventory) {
Block block = world.getType(blockposition).getBlock();
ITileInventory itileinventory = (ITileInventory) tileentity;
if (itileinventory instanceof TileEntityChest && block instanceof BlockChest) {
itileinventory = ((BlockChest) block).getInventory(world, blockposition);
}
if (itileinventory != null) {
entityhuman.openContainer(itileinventory);
return EnumInteractionResult.SUCCESS;
}
} else if (tileentity instanceof IInventory) {
entityhuman.openContainer((IInventory) tileentity);
return EnumInteractionResult.SUCCESS;
}
return EnumInteractionResult.PASS;
} else {
if (!entityhuman.isSneaking() || entityhuman.getItemInMainHand().isEmpty() && entityhuman.getItemInOffHand().isEmpty()) {
IBlockData iblockdata = world.getType(blockposition);
enuminteractionresult = iblockdata.getBlock().interact(world, blockposition, iblockdata, entityhuman, enumhand, enumdirection, f, f1, f2) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS;
}
}
if (!itemstack.isEmpty() && enuminteractionresult != EnumInteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764
int i = itemstack.getData();
int j = itemstack.getCount();
enuminteractionresult = itemstack.placeItem(entityhuman, world, blockposition, enumhand, enumdirection, f, f1, f2);
// The item count should not decrement in Creative mode.
if (this.isCreative()) {
itemstack.setData(i);
itemstack.setCount(j);
}
}
}
return enuminteractionresult;
// CraftBukkit end
}
public void a(WorldServer worldserver) {
this.world = worldserver;
}
}

View File

@@ -79,8 +79,20 @@ public abstract class PlayerList {
private int v;
// CraftBukkit start
private CraftServer cserver;
private final Map<String,EntityPlayer> playersByName = new org.spigotmc.CaseInsensitiveMap<EntityPlayer>();
private final CraftServer cserver;
private final Map<String,EntityPlayer> playersByName = new it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap<String, EntityPlayer>(
new it.unimi.dsi.fastutil.Hash.Strategy<String>() {
@Override
public int hashCode(String o) {
return o.toLowerCase().hashCode();
}
@Override
public boolean equals(String a, String b) {
return a.equalsIgnoreCase(b);
}
}
);
@Nullable String collideRuleTeamName; // Paper - Team name used for collideRule
public PlayerList(MinecraftServer minecraftserver) {
@@ -546,9 +558,10 @@ public abstract class PlayerList {
Player player = entity.getBukkitEntity();
PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress());
String s;
if (getProfileBans().isBanned(gameprofile) && !getProfileBans().get(gameprofile).hasExpired()) {
GameProfileBanEntry gameprofilebanentry = this.k.get(gameprofile);
// NeonPaper start - Fix MC-158900
GameProfileBanEntry gameprofilebanentry;
if (getProfileBans().isBanned(gameprofile) && (gameprofilebanentry = getProfileBans().get(gameprofile)) != null) {
// NeonPaper end
s = LocaleI18n.a(AkarinGlobalConfig.messageBan,
gameprofilebanentry.getReason().equals(Akari.EMPTY_STRING) ? Akari.EMPTY_STRING : AkarinGlobalConfig.messageBanReason + gameprofilebanentry.getReason(),
@@ -1252,8 +1265,25 @@ public abstract class PlayerList {
}
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, Packet<?> packet) {
for (int j = 0; j < this.players.size(); ++j) {
EntityPlayer entityplayer = this.players.get(j);
// Paper start - Use world list instead of server list where preferable
sendPacketNearby(entityhuman, d0, d1, d2, d3, i, null, packet); // Retained for compatibility
}
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, WorldServer world, Packet<?> packet) {
sendPacketNearby(entityhuman, d0, d1, d2, d3, world.dimension, world, packet);
}
public void sendPacketNearby(@Nullable EntityHuman entityhuman, double d0, double d1, double d2, double d3, int i, @Nullable WorldServer world, Packet<?> packet) {
if (world == null && entityhuman != null && entityhuman.world instanceof WorldServer) {
world = (WorldServer) entityhuman.world;
}
List<? extends EntityHuman> players1 = world == null ? players : world.players;
for (int j = 0; j < players1.size(); ++j) {
EntityHuman entity = players1.get(j);
if (!(entity instanceof EntityPlayer)) continue;
EntityPlayer entityplayer = (EntityPlayer) players1.get(j);
// Paper end
// CraftBukkit start - Test if player receiving packet can see the source of the packet
if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
@@ -1261,7 +1291,7 @@ public abstract class PlayerList {
}
// CraftBukkit end
if (entityplayer != entityhuman && entityplayer.dimension == i) {
if (entityplayer != entityhuman && (world != null || entityplayer.dimension == i)) { // Paper
double d4 = d0 - entityplayer.locX;
double d5 = d1 - entityplayer.locY;
double d6 = d2 - entityplayer.locZ;

View File

@@ -0,0 +1,506 @@
package net.minecraft.server;
import com.destroystokyo.paper.exception.ServerInternalException;
import com.google.common.collect.Lists;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.Buffer;
import java.nio.IntBuffer;
import java.util.List;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;
import javax.annotation.Nullable;
public class RegionFile {
// Spigot start
// Minecraft is limited to 256 sections per chunk. So 1MB. This can easily be overriden.
// So we extend this to use the REAL size when the count is maxed by seeking to that section and reading the length.
private static final boolean ENABLE_EXTENDED_SAVE = Boolean.parseBoolean(System.getProperty("net.minecraft.server.RegionFile.enableExtendedSave", "true"));
// Spigot end
private static final byte[] a = new byte[4096];
private final File b;private File getFile() { return b; } // Paper - OBFHELPER
private RandomAccessFile c;private RandomAccessFile getDataFile() { return c; } // Paper - OBFHELPER
private final int[] d = new int[1024];private int[] offsets = d; // Paper - OBFHELPER
private final int[] e = new int[1024];private int[] timestamps = e; // Paper - OBFHELPER
private List<Boolean> f;
private int g;
private long h;
public RegionFile(File file) {
this.b = file;
this.g = 0;
try {
if (file.exists()) {
this.h = file.lastModified();
}
this.c = new RandomAccessFile(file, "rw");
if (this.c.length() < 8192L) { // Paper - headers should be 8192
this.c.write(a);
this.c.write(a);
this.g += 8192;
}
int i;
if ((this.c.length() & 4095L) != 0L) {
for (i = 0; (long) i < (this.c.length() & 4095L); ++i) {
this.c.write(0);
}
}
i = (int) this.c.length() / 4096;
this.f = Lists.newArrayListWithCapacity(i);
int j;
for (j = 0; j < i; ++j) {
this.f.add(Boolean.valueOf(true));
}
this.f.set(0, Boolean.valueOf(false));
this.f.set(1, Boolean.valueOf(false));
this.c.seek(0L);
int k;
// Paper Start
ByteBuffer header = ByteBuffer.allocate(8192);
while (header.hasRemaining()) {
if (this.c.getChannel().read(header) == -1) throw new EOFException();
}
((Buffer) header).clear();
IntBuffer headerAsInts = header.asIntBuffer();
initOversizedState();
// Paper End
for (j = 0; j < 1024; ++j) {
k = headerAsInts.get(); // Paper
this.d[j] = k;
// Spigot start
int length = k & 255;
if (length == 255) {
if ((k >> 8) <= this.f.size()) {
// We're maxed out, so we need to read the proper length from the section
this.c.seek((k >> 8) * 4096);
length = (this.c.readInt() + 4) / 4096 + 1;
this.c.seek(j * 4 + 4); // Go back to where we were
}
}
if (k > 0 && (k >> 8) > 1 && (k >> 8) + (length) <= this.f.size()) { // Paper >= 1 as 0/1 are the headers, and negative isnt valid
for (int l = 0; l < (length); ++l) {
// Spigot end
this.f.set((k >> 8) + l, Boolean.valueOf(false));
}
}
// Spigot start
else if (k != 0) { // Paper
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Length: {3} runs off end file. {4}", new Object[]{j % 32, (int) (j / 32), k >> 8, length, file});
deleteChunk(j); // Paper
}
// Spigot end
}
for (j = 0; j < 1024; ++j) {
k = headerAsInts.get(); // Paper
if (offsets[j] != 0) this.timestamps[j] = k; // Paper - don't set timestamp if it got 0'd above due to corruption
}
} catch (IOException ioexception) {
ioexception.printStackTrace();
ServerInternalException.reportInternalException(ioexception); // Paper
}
}
@Nullable
public synchronized DataInputStream getReadStream(int i, int j) { return a(i, j); } @Nullable public synchronized DataInputStream a(int i, int j) { // Paper - OBFHELPER
if (this.d(i, j)) {
return null;
} else {
try {
int k = this.getOffset(i, j);
if (k == 0) {
return null;
} else {
int l = k >> 8;
int i1 = k & 255;
// Spigot start
if (i1 == 255) {
this.c.seek(l * 4096);
i1 = (this.c.readInt() + 4) / 4096 + 1;
}
// Spigot end
if (l + i1 > this.f.size()) {
return null;
} else {
this.c.seek((long) (l * 4096));
int j1 = this.c.readInt();
if (j1 > 4096 * i1) {
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3}>{4} {5}", new Object[]{i, j, l, j1, i1 * 4096, this.b}); // Spigot
return null;
} else if (j1 <= 0) {
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING, "Invalid chunk: ({0}, {1}) Offset: {2} Invalid Size: {3} {4}", new Object[]{i, j, l, j1, this.b}); // Spigot
return null;
} else {
byte b0 = this.c.readByte();
byte[] abyte;
if (b0 == 1) {
abyte = new byte[j1 - 1];
this.c.read(abyte);
return new DataInputStream(new BufferedInputStream(new GZIPInputStream(new ByteArrayInputStream(abyte))));
} else if (b0 == 2) {
abyte = new byte[j1 - 1];
this.c.read(abyte);
return new DataInputStream(new BufferedInputStream(new InflaterInputStream(new ByteArrayInputStream(abyte))));
} else {
return null;
}
}
}
}
} catch (IOException ioexception) {
return null;
}
}
}
@Nullable
public DataOutputStream getWriteStream(int i, int j) { return b(i, j); } @Nullable public DataOutputStream b(int i, int j) { // Paper - OBFHELPER
return this.d(i, j) ? null : new DataOutputStream(new RegionFile.ChunkBuffer(i, j)); // Paper - remove middleware, move deflate to .close() for dynamic levels
}
protected synchronized void a(int i, int j, byte[] abyte, int k) {
try {
int l = this.getOffset(i, j);
int i1 = l >> 8;
int j1 = l & 255;
// Spigot start
if (j1 == 255) {
this.c.seek(i1 * 4096);
j1 = (this.c.readInt() + 4) / 4096 + 1;
}
// Spigot end
int k1 = (k + 5) / 4096 + 1;
if (k1 >= 256) {
// Spigot start
if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b});
if (!ENABLE_EXTENDED_SAVE) return;
// Spigot end
}
if (i1 != 0 && j1 == k1) {
this.a(i1, abyte, k);
} else {
int l1;
for (l1 = 0; l1 < j1; ++l1) {
this.f.set(i1 + l1, Boolean.valueOf(true));
}
l1 = this.f.indexOf(Boolean.valueOf(true));
int i2 = 0;
int j2;
if (l1 != -1) {
for (j2 = l1; j2 < this.f.size(); ++j2) {
if (i2 != 0) {
if (((Boolean) this.f.get(j2)).booleanValue()) {
++i2;
} else {
i2 = 0;
}
} else if (((Boolean) this.f.get(j2)).booleanValue()) {
l1 = j2;
i2 = 1;
}
if (i2 >= k1) {
break;
}
}
}
if (i2 >= k1) {
i1 = l1;
this.a(i, j, l1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
for (j2 = 0; j2 < k1; ++j2) {
this.f.set(i1 + j2, Boolean.valueOf(false));
}
this.a(i1, abyte, k);
} else {
this.c.seek(this.c.length());
i1 = this.f.size();
for (j2 = 0; j2 < k1; ++j2) {
this.c.write(RegionFile.a);
this.f.add(Boolean.valueOf(false));
}
this.g += 4096 * k1;
this.a(i1, abyte, k);
this.a(i, j, i1 << 8 | (k1 > 255 ? 255 : k1)); // Spigot
}
}
this.b(i, j, (int) (MinecraftServer.aw() / 1000L));
} catch (IOException ioexception) {
org.spigotmc.SneakyThrow.sneaky(ioexception); // Paper - we want the upper try/catch to retry this
}
}
private void a(int i, byte[] abyte, int j) throws IOException {
this.c.seek((long) (i * 4096));
this.c.writeInt(j + 1);
this.c.writeByte(2);
this.c.write(abyte, 0, j);
}
private boolean d(int i, int j) {
return i < 0 || i >= 32 || j < 0 || j >= 32;
}
private synchronized int getOffset(int i, int j) {
return this.d[i + j * 32];
}
public boolean c(int i, int j) {
return this.getOffset(i, j) != 0;
}
private void a(int i, int j, int k) throws IOException {
this.d[i + j * 32] = k;
this.c.seek((long) ((i + j * 32) * 4));
this.c.writeInt(k);
}
private void b(int i, int j, int k) throws IOException {
this.e[i + j * 32] = k;
this.c.seek((long) (4096 + (i + j * 32) * 4));
this.c.writeInt(k);
}
public void c() throws IOException {
if (this.c != null) {
this.c.close();
}
}
// Paper start
public synchronized void deleteChunk(int j1) {
backup();
int k = offsets[j1];
int x = j1 & 1024;
int z = j1 >> 2;
int offset = (k >> 8);
int len = (k & 255);
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
String debug = "idx:" + + j1 + " - " + x + "," + z + " - offset: " + offset + " - len: " + len;
try {
RandomAccessFile file = getDataFile();
file.seek(j1 * 4);
file.writeInt(0);
// clear the timestamp
file.seek(4096 + j1 * 4);
file.writeInt(0);
timestamps[j1] = 0;
offsets[j1] = 0;
logger.error("Deleted corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
} catch (IOException e) {
logger.error("Error deleting corrupt chunk (" + debug + ") " + getFile().getAbsolutePath(), e);
}
}
private boolean backedUp = false;
private synchronized void backup() {
if (backedUp) {
return;
}
backedUp = true;
File file = this.getFile();
java.text.DateFormat formatter = new java.text.SimpleDateFormat("yyyy-MM-dd");
java.util.Date today = new java.util.Date();
File corrupt = new File(file.getParentFile(), file.getName() + "." + formatter.format(today) + ".corrupt");
if (corrupt.exists()) {
return;
}
org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger();
logger.error("Region file " + file.getAbsolutePath() + " was corrupt. Backing up to " + corrupt.getAbsolutePath() + " and repairing");
try {
java.nio.file.Files.copy(file.toPath(), corrupt.toPath());
} catch (IOException e) {
logger.error("Error backing up corrupt file" + file.getAbsolutePath(), e);
}
}
private final byte[] oversized = new byte[1024];
private int oversizedCount = 0;
private synchronized void initOversizedState() throws IOException {
File metaFile = getOversizedMetaFile();
if (metaFile.exists()) {
final byte[] read = java.nio.file.Files.readAllBytes(metaFile.toPath());
System.arraycopy(read, 0, oversized, 0, oversized.length);
for (byte temp : oversized) {
oversizedCount += temp;
}
}
}
private static int getChunkIndex(int x, int z) {
return (x & 31) + (z & 31) * 32;
}
synchronized boolean isOversized(int x, int z) {
return this.oversized[getChunkIndex(x, z)] == 1;
}
synchronized void setOversized(int x, int z, boolean oversized) throws IOException {
final int offset = getChunkIndex(x, z);
boolean previous = this.oversized[offset] == 1;
this.oversized[offset] = (byte) (oversized ? 1 : 0);
if (!previous && oversized) {
oversizedCount++;
} else if (!oversized && previous) {
oversizedCount--;
}
if (previous && !oversized) {
File oversizedFile = getOversizedFile(x, z);
if (oversizedFile.exists()) {
oversizedFile.delete();
}
}
if (oversizedCount > 0) {
if (previous != oversized) {
writeOversizedMeta();
}
} else if (previous) {
File oversizedMetaFile = getOversizedMetaFile();
if (oversizedMetaFile.exists()) {
oversizedMetaFile.delete();
}
}
}
private void writeOversizedMeta() throws IOException {
java.nio.file.Files.write(getOversizedMetaFile().toPath(), oversized);
}
private File getOversizedMetaFile() {
return new File(getFile().getParentFile(), getFile().getName().replaceAll("\\.mca$", "") + ".oversized.nbt");
}
private File getOversizedFile(int x, int z) {
return new File(this.getFile().getParentFile(), this.getFile().getName().replaceAll("\\.mca$", "") + "_oversized_" + x + "_" + z + ".nbt");
}
void writeOversizedData(int x, int z, NBTTagCompound oversizedData) throws IOException {
File file = getOversizedFile(x, z);
try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new DeflaterOutputStream(new java.io.FileOutputStream(file), new java.util.zip.Deflater(java.util.zip.Deflater.BEST_COMPRESSION), 32 * 1024), 32 * 1024))) {
NBTCompressedStreamTools.writeNBT(oversizedData, out);
}
this.setOversized(x, z, true);
}
synchronized NBTTagCompound getOversizedData(int x, int z) throws IOException {
File file = getOversizedFile(x, z);
try (DataInputStream out = new DataInputStream(new BufferedInputStream(new InflaterInputStream(new java.io.FileInputStream(file))))) {
return NBTCompressedStreamTools.readNBT(out);
}
}
private static final boolean USE_SPIGOT_OVERSIZED_METHOD = Boolean.getBoolean("Paper.useSpigotExtendedSaveMethod"); // Paper
static {
if (USE_SPIGOT_OVERSIZED_METHOD) {
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "Using Spigot Oversized Chunk save method. Warning this will result in extremely fragmented chunks, as well as making the entire region file unable to be to used in any other software but Forge or Spigot (not usable in Vanilla or CraftBukkit). Paper's method is highly recommended.");
org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.SEVERE, "====================================");
}
}
public class ChunkTooLargeException extends RuntimeException {
public ChunkTooLargeException(int x, int z, int sectors) {
super("Chunk " + x + "," + z + " of " + getFile().toString() + " is too large (" + sectors + "/256)");
}
}
private static class DirectByteArrayOutputStream extends ByteArrayOutputStream {
public DirectByteArrayOutputStream() {
super();
}
public DirectByteArrayOutputStream(int size) {
super(size);
}
public byte[] getBuffer() {
return this.buf;
}
}
// Paper end
class ChunkBuffer extends ByteArrayOutputStream {
private final int b;
private final int c;
public ChunkBuffer(int i, int j) {
super(8096);
this.b = i;
this.c = j;
}
public void close() throws IOException {
// Paper start - apply dynamic compression
int origLength = this.count;
byte[] buf = this.buf;
DirectByteArrayOutputStream out = compressData(buf, origLength);
byte[] bytes = out.getBuffer();
int length = out.size();
RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length
}
}
private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib
private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater();
// since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable.
private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException {
synchronized (deflater) {
deflater.setInput(buf, 0, length);
deflater.finish();
DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length);
while (!deflater.finished()) {
out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer));
}
out.close();
deflater.reset();
return out;
}
}
// Paper end
}

View File

@@ -1,127 +0,0 @@
package net.minecraft.server;
import com.destroystokyo.paper.exception.ServerInternalException;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import com.destroystokyo.paper.PaperConfig; // Paper
import java.util.LinkedHashMap; // Paper
/**
* Akarin Changes Note
* 1) Removes unneed synchronization (performance)
*/
public class RegionFileCache {
public static final Map<File, RegionFile> a = new LinkedHashMap(PaperConfig.regionFileCacheSize, 0.75f, true); // Spigot - private -> public, Paper - HashMap -> LinkedHashMap
public static synchronized RegionFile a(File file, int i, int j) {
File file1 = new File(file, "region");
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
if (regionfile != null) {
return regionfile;
} else {
if (!file1.exists()) {
file1.mkdirs();
}
if (RegionFileCache.a.size() >= PaperConfig.regionFileCacheSize) { // Paper
trimCache(); // Paper
}
RegionFile regionfile1 = new RegionFile(file2);
RegionFileCache.a.put(file2, regionfile1);
return regionfile1;
}
}
public static synchronized RegionFile b(File file, int i, int j) {
File file1 = new File(file, "region");
File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca");
RegionFile regionfile = (RegionFile) RegionFileCache.a.get(file2);
if (regionfile != null) {
return regionfile;
} else if (file1.exists() && file2.exists()) {
if (RegionFileCache.a.size() >= 256) {
a();
}
RegionFile regionfile1 = new RegionFile(file2);
RegionFileCache.a.put(file2, regionfile1);
return regionfile1;
} else {
return null;
}
}
// Paper Start
private static synchronized void trimCache() {
Iterator<Map.Entry<File, RegionFile>> itr = RegionFileCache.a.entrySet().iterator();
int count = RegionFileCache.a.size() - PaperConfig.regionFileCacheSize;
while (count-- >= 0 && itr.hasNext()) {
try {
itr.next().getValue().c();
} catch (IOException ioexception) {
ioexception.printStackTrace();
ServerInternalException.reportInternalException(ioexception);
}
itr.remove();
}
}
// Paper End
public static synchronized void a() {
Iterator iterator = RegionFileCache.a.values().iterator();
while (iterator.hasNext()) {
RegionFile regionfile = (RegionFile) iterator.next();
try {
if (regionfile != null) {
regionfile.c();
}
} catch (IOException ioexception) {
ioexception.printStackTrace();
ServerInternalException.reportInternalException(ioexception); // Paper
}
}
RegionFileCache.a.clear();
}
// CraftBukkit start - call sites hoisted for synchronization
public static /*synchronized*/ NBTTagCompound d(File file, int i, int j) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: read
RegionFile regionfile = a(file, i, j);
DataInputStream datainputstream = regionfile.a(i & 31, j & 31);
if (datainputstream == null) {
return null;
}
return NBTCompressedStreamTools.a(datainputstream);
}
public static /*synchronized*/ void e(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { // Akarin - 1.13 backport - remove synchronization // OBFHELPER: write
RegionFile regionfile = a(file, i, j);
DataOutputStream dataoutputstream = regionfile.b(i & 31, j & 31);
NBTCompressedStreamTools.a(nbttagcompound, (java.io.DataOutput) dataoutputstream);
dataoutputstream.close();
}
// CraftBukkit end
public static /*synchronized*/ boolean chunkExists(File file, int i, int j) { // Akarin - 1.13 backport - remove synchronization
RegionFile regionfile = b(file, i, j);
return regionfile != null ? regionfile.c(i & 31, j & 31) : false;
}
}

View File

@@ -2,6 +2,8 @@ package net.minecraft.server;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterators;
import java.util.BitSet;
import java.util.Iterator;
import javax.annotation.Nullable;
@@ -24,7 +26,7 @@ public class RegistryID<K> implements Registry {
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
this.usedIds = new BitSet(); // Akarin - 1.13 backport
}
public int getId(@Nullable K k0) {

View File

@@ -0,0 +1,356 @@
package net.minecraft.server;
import com.google.common.collect.Sets;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
// CraftBukkit start
import com.destroystokyo.paper.exception.ServerInternalException;
import org.bukkit.craftbukkit.util.LongHash;
import org.bukkit.craftbukkit.util.LongHashSet;
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
// CraftBukkit end
public final class SpawnerCreature {
private static final int a = (int) Math.pow(17.0D, 2.0D);
private final LongHashSet b = new LongHashSet(); // CraftBukkit
public SpawnerCreature() {}
// Spigot start - get entity count only from chunks being processed in b
private int getEntityCount(WorldServer server, Class oClass)
{
// Paper start - use entire world, not just active chunks. Spigot broke vanilla expectations.
if (true) {
int sum = 0;
for (Chunk c : server.getChunkProviderServer().chunks.values()) {
sum += c.entityCount.get(oClass);
}
return sum;
}
// Paper end
int i = 0;
Iterator<Long> it = this.b.iterator();
while ( it.hasNext() )
{
Long coord = it.next();
int x = LongHash.msw( coord );
int z = LongHash.lsw( coord );
if ( !((ChunkProviderServer)server.chunkProvider).unloadQueue.contains( coord ) && server.isChunkLoaded( x, z, true ) )
{
i += server.getChunkAt( x, z ).entityCount.get( oClass );
}
}
return i;
}
// Spigot end
public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) {
org.spigotmc.AsyncCatcher.catchOp("check for eligible spawn chunks"); // Paper - At least until we figure out what is calling this async
if (!flag && !flag1) {
return 0;
} else {
this.b.clear();
int i = 0;
Iterator iterator = worldserver.players.iterator();
int j;
int k;
while (iterator.hasNext()) {
EntityHuman entityhuman = (EntityHuman) iterator.next();
if (!entityhuman.isSpectator() && entityhuman.affectsSpawning) {
int l = MathHelper.floor(entityhuman.locX / 16.0D);
j = MathHelper.floor(entityhuman.locZ / 16.0D);
boolean flag3 = true;
// Spigot Start
byte b0 = worldserver.spigotConfig.mobSpawnRange;
b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0;
b0 = ( b0 > 8 ) ? 8 : b0;
// Paper start
com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event;
event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent(
(org.bukkit.entity.Player) entityhuman.getBukkitEntity(), b0);
if (!event.callEvent()) {
continue;
}
b0 = event.getSpawnRadius();
// Paperr end
for (int i1 = -b0; i1 <= b0; ++i1) {
for (k = -b0; k <= b0; ++k) {
boolean flag4 = i1 == -b0 || i1 == b0 || k == -b0 || k == b0;
// Spigot End
ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j);
// CraftBukkit start - use LongHash and LongHashSet
long chunkCoords = LongHash.toLong(chunkcoordintpair.x, chunkcoordintpair.z);
if (!this.b.contains(chunkCoords)) {
++i;
if (!flag4 && worldserver.isChunkLoaded(chunkcoordintpair.x, chunkcoordintpair.z, true) && worldserver.getWorldBorder().isInBounds(chunkcoordintpair)) {
PlayerChunk playerchunk = worldserver.getPlayerChunkMap().getChunk(chunkcoordintpair.x, chunkcoordintpair.z);
if (playerchunk != null && playerchunk.e()) {
this.b.add(chunkCoords);
// CraftBukkit end
}
}
}
}
}
}
}
int j1 = 0;
BlockPosition blockposition = worldserver.getSpawn();
EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values();
j = aenumcreaturetype.length;
for (int k1 = 0; k1 < j; ++k1) {
EnumCreatureType enumcreaturetype = aenumcreaturetype[k1];
// CraftBukkit start - Use per-world spawn limits
int limit = enumcreaturetype.b();
switch (enumcreaturetype) {
case MONSTER:
limit = worldserver.getWorld().getMonsterSpawnLimit();
break;
case CREATURE:
limit = worldserver.getWorld().getAnimalSpawnLimit();
break;
case WATER_CREATURE:
limit = worldserver.getWorld().getWaterAnimalSpawnLimit();
break;
case AMBIENT:
limit = worldserver.getWorld().getAmbientSpawnLimit();
break;
}
if (limit == 0) {
continue;
}
int mobcnt = 0; // Spigot
// CraftBukkit end
if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) {
/* Paper start - As far as I can tell neither of these are even used
k = worldserver.a(enumcreaturetype.a());
int l1 = limit * i / a; // CraftBukkit - use per-world limits
*/ // Paper end
if ((mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * i / 289) { // Paper - use 17x17 like vanilla (a at top of file)
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
Iterator iterator1 = this.b.iterator();
int moblimit = (limit * i / 256) - mobcnt + 1; // Spigot - up to 1 more than limit
label120:
while (iterator1.hasNext() && (moblimit > 0)) { // Spigot - while more allowed
// CraftBukkit start = use LongHash and LongObjectHashMap
long key = ((Long) iterator1.next()).longValue();
BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key));
// CraftBukkit
int i2 = blockposition1.getX();
int j2 = blockposition1.getY();
int k2 = blockposition1.getZ();
IBlockData iblockdata = worldserver.getWorldBorder().isInBounds(blockposition1) ? worldserver.getTypeIfLoaded(blockposition1) : null; // Paper
if (iblockdata != null && !iblockdata.l()) { // Paper
int l2 = 0;
int i3 = 0;
while (i3 < 3) {
int j3 = i2;
int k3 = j2;
int l3 = k2;
boolean flag5 = true;
BiomeBase.BiomeMeta biomebase_biomemeta = null;
GroupDataEntity groupdataentity = null;
int i4 = MathHelper.f(Math.random() * 4.0D);
int j4 = 0;
while (true) {
if (j4 < i4) {
label113: {
j3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6);
k3 += worldserver.random.nextInt(1) - worldserver.random.nextInt(1);
l3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6);
blockposition_mutableblockposition.c(j3, k3, l3);
float f = (float) j3 + 0.5F;
float f1 = (float) l3 + 0.5F;
if (worldserver.getWorldBorder().isInBounds(blockposition_mutableblockposition) && worldserver.getChunkIfLoaded(blockposition_mutableblockposition) != null && !worldserver.isPlayerNearby((double) f, (double) k3, (double) f1, 24.0D) && blockposition.distanceSquared((double) f, (double) k3, (double) f1) >= 576.0D) { // Paper - Prevent mob spawning from loading/generating chunks
if (biomebase_biomemeta == null) {
biomebase_biomemeta = worldserver.a(enumcreaturetype, (BlockPosition) blockposition_mutableblockposition);
if (biomebase_biomemeta == null) {
break label113;
}
}
if (worldserver.a(enumcreaturetype, biomebase_biomemeta, (BlockPosition) blockposition_mutableblockposition) && a(EntityPositionTypes.a(biomebase_biomemeta.b), worldserver, blockposition_mutableblockposition)) {
// Paper start
com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event;
Class<? extends EntityInsentient> cls = biomebase_biomemeta.b;
org.bukkit.entity.EntityType type = EntityTypes.clsToTypeMap.get(cls);
if (type != null) {
event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent(
MCUtil.toLocation(worldserver, blockposition_mutableblockposition),
type, SpawnReason.NATURAL
);
if (!event.callEvent()) {
if (event.shouldAbortSpawn()) {
continue label120;
}
j1 += l2;
++j4;
continue;
}
}
// Paper end
EntityInsentient entityinsentient;
try {
entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { worldserver});
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
return j1;
}
entityinsentient.setPositionRotation((double) f, (double) k3, (double) f1, worldserver.random.nextFloat() * 360.0F, 0.0F);
if (entityinsentient.P() && entityinsentient.canSpawn()) {
groupdataentity = entityinsentient.prepare(worldserver.D(new BlockPosition(entityinsentient)), groupdataentity);
if (entityinsentient.canSpawn()) {
// CraftBukkit start
if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) {
++l2;
moblimit--; // Spigot
}
// CraftBukkit end
} else {
entityinsentient.die();
}
// Spigot start
if ( moblimit <= 0 ) {
// If we're past limit, stop spawn
// Spigot end
continue label120;
}
}
j1 += l2;
}
}
++j4;
continue;
}
}
++i3;
break;
}
}
}
}
}
}
}
return j1;
}
}
private static BlockPosition getRandomPosition(World world, int i, int j) {
Chunk chunk = world.getChunkAt(i, j);
int k = i * 16 + world.random.nextInt(16);
int l = j * 16 + world.random.nextInt(16);
int i1 = MathHelper.c(chunk.e(new BlockPosition(k, 0, l)) + 1, 16);
int j1 = world.random.nextInt(i1 > 0 ? i1 : chunk.g() + 16 - 1);
return new BlockPosition(k, j1, l);
}
public static boolean a(IBlockData iblockdata) {
return iblockdata.k() ? false : (iblockdata.m() ? false : (iblockdata.getMaterial().isLiquid() ? false : !BlockMinecartTrackAbstract.i(iblockdata)));
}
public static boolean a(EntityInsentient.EnumEntityPositionType entityinsentient_enumentitypositiontype, World world, BlockPosition blockposition) {
if (!world.getWorldBorder().a(blockposition)) {
return false;
} else {
IBlockData iblockdata = world.getType(blockposition);
if (entityinsentient_enumentitypositiontype == EntityInsentient.EnumEntityPositionType.IN_WATER) {
return iblockdata.getMaterial() == Material.WATER && world.getType(blockposition.down()).getMaterial() == Material.WATER && !world.getType(blockposition.up()).l();
} else {
BlockPosition blockposition1 = blockposition.down();
if (!world.getType(blockposition1).q()) {
return false;
} else {
Block block = world.getType(blockposition1).getBlock();
boolean flag = block != Blocks.BEDROCK && block != Blocks.BARRIER;
return flag && a(iblockdata) && a(world.getType(blockposition.up()));
}
}
}
}
public static void a(World world, BiomeBase biomebase, int i, int j, int k, int l, Random random) {
List list = biomebase.getMobs(EnumCreatureType.CREATURE);
if (!list.isEmpty()) {
while (random.nextFloat() < biomebase.f()) {
BiomeBase.BiomeMeta biomebase_biomemeta = (BiomeBase.BiomeMeta) WeightedRandom.a(world.random, list);
int i1 = biomebase_biomemeta.c + random.nextInt(1 + biomebase_biomemeta.d - biomebase_biomemeta.c);
GroupDataEntity groupdataentity = null;
int j1 = i + random.nextInt(k);
int k1 = j + random.nextInt(l);
int l1 = j1;
int i2 = k1;
for (int j2 = 0; j2 < i1; ++j2) {
boolean flag = false;
for (int k2 = 0; !flag && k2 < 4; ++k2) {
BlockPosition blockposition = world.q(new BlockPosition(j1, 0, k1));
if (a(EntityInsentient.EnumEntityPositionType.ON_GROUND, world, blockposition)) {
EntityInsentient entityinsentient;
try {
entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { world});
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
continue;
}
entityinsentient.setPositionRotation((double) ((float) j1 + 0.5F), (double) blockposition.getY(), (double) ((float) k1 + 0.5F), random.nextFloat() * 360.0F, 0.0F);
// CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.prepare(groupdataentity) up
groupdataentity = entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), groupdataentity);
world.addEntity(entityinsentient, SpawnReason.CHUNK_GEN);
// CraftBukkit end
flag = true;
}
j1 += random.nextInt(5) - random.nextInt(5);
for (k1 += random.nextInt(5) - random.nextInt(5); j1 < i || j1 >= i + k || k1 < j || k1 >= j + k; k1 = i2 + random.nextInt(5) - random.nextInt(5)) {
j1 = l1 + random.nextInt(5) - random.nextInt(5);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,423 @@
package net.minecraft.server;
import java.util.Iterator;
// CraftBukkit start
import java.util.List;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.FurnaceBurnEvent;
import org.bukkit.event.inventory.FurnaceSmeltEvent;
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
// CraftBukkit end
public class TileEntityFurnace extends TileEntityContainer implements ITickable, IWorldInventory {
private static final int[] a = new int[] { 0};
private static final int[] f = new int[] { 2, 1};
private static final int[] g = new int[] { 1};
private NonNullList<ItemStack> items;
private int burnTime;
private int ticksForCurrentFuel;
private int cookTime;
private int cookTimeTotal;
private String m;
// CraftBukkit start - add fields and methods
private int lastTick = MinecraftServer.currentTick;
private int maxStack = MAX_STACK;
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
public List<ItemStack> getContents() {
return this.items;
}
public void onOpen(CraftHumanEntity who) {
transaction.add(who);
}
public void onClose(CraftHumanEntity who) {
transaction.remove(who);
}
public List<HumanEntity> getViewers() {
return transaction;
}
public void setMaxStackSize(int size) {
maxStack = size;
}
// CraftBukkit end
public TileEntityFurnace() {
this.items = NonNullList.a(3, ItemStack.a);
}
public int getSize() {
return this.items.size();
}
public boolean x_() {
Iterator iterator = this.items.iterator();
ItemStack itemstack;
do {
if (!iterator.hasNext()) {
return true;
}
itemstack = (ItemStack) iterator.next();
} while (itemstack.isEmpty());
return false;
}
public ItemStack getItem(int i) {
return (ItemStack) this.items.get(i);
}
public ItemStack splitStack(int i, int j) {
return ContainerUtil.a(this.items, i, j);
}
public ItemStack splitWithoutUpdate(int i) {
return ContainerUtil.a(this.items, i);
}
public void setItem(int i, ItemStack itemstack) {
ItemStack itemstack1 = (ItemStack) this.items.get(i);
boolean flag = !itemstack.isEmpty() && itemstack.doMaterialsMatch(itemstack1) && ItemStack.equals(itemstack, itemstack1);
this.items.set(i, itemstack);
if (itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
if (i == 0 && !flag) {
this.cookTimeTotal = this.a(itemstack);
this.cookTime = 0;
this.update();
}
}
public String getName() {
return this.hasCustomName() ? this.m : "container.furnace";
}
public boolean hasCustomName() {
return this.m != null && !this.m.isEmpty();
}
public void setCustomName(String s) {
this.m = s;
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityFurnace.class, new String[] { "Items"})));
}
public void load(NBTTagCompound nbttagcompound) {
super.load(nbttagcompound);
this.items = NonNullList.a(this.getSize(), ItemStack.a);
ContainerUtil.b(nbttagcompound, this.items);
this.burnTime = nbttagcompound.getShort("BurnTime");
this.cookTime = nbttagcompound.getShort("CookTime");
this.cookTimeTotal = nbttagcompound.getShort("CookTimeTotal");
this.ticksForCurrentFuel = fuelTime((ItemStack) this.items.get(1));
if (nbttagcompound.hasKeyOfType("CustomName", 8)) {
this.m = nbttagcompound.getString("CustomName");
}
}
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
super.save(nbttagcompound);
nbttagcompound.setShort("BurnTime", (short) this.burnTime);
nbttagcompound.setShort("CookTime", (short) this.cookTime);
nbttagcompound.setShort("CookTimeTotal", (short) this.cookTimeTotal);
ContainerUtil.a(nbttagcompound, this.items);
if (this.hasCustomName()) {
nbttagcompound.setString("CustomName", this.m);
}
return nbttagcompound;
}
public int getMaxStackSize() {
return 64;
}
public boolean isBurning() {
return this.burnTime > 0;
}
public void e() {
boolean flag = (this.getBlock() == Blocks.LIT_FURNACE); // CraftBukkit - SPIGOT-844 - Check if furnace block is lit using the block instead of burn time
boolean flag1 = false;
// CraftBukkit start - Use wall time instead of ticks for cooking
int elapsedTicks = MinecraftServer.currentTick - this.lastTick;
this.lastTick = MinecraftServer.currentTick;
// CraftBukkit - moved from below - edited for wall time
if (this.isBurning() && this.canBurn()) {
this.cookTime += elapsedTicks;
if (this.cookTime >= this.cookTimeTotal) {
this.cookTime -= this.cookTimeTotal; // Paper
this.cookTimeTotal = this.a((ItemStack) this.items.get(0));
this.burn();
flag1 = true;
}
} else {
this.cookTime = 0;
}
// CraftBukkit end
if (this.isBurning()) {
this.burnTime -= elapsedTicks; // CraftBukkit - use elapsedTicks in place of constant
}
if (!this.world.isClientSide) {
ItemStack itemstack = (ItemStack) this.items.get(1);
if (!this.isBurning() && (itemstack.isEmpty() || ((ItemStack) this.items.get(0)).isEmpty())) {
if (!this.isBurning() && this.cookTime > 0) {
this.cookTime = MathHelper.clamp(this.cookTime - 2, 0, this.cookTimeTotal);
}
} else if(this.items.get(1) != null && this.items.get(1).getItem() != Items.BUCKET) {
// CraftBukkit start - Handle multiple elapsed ticks
if (this.burnTime <= 0 && this.canBurn()) { // CraftBukkit - == to <=
CraftItemStack fuel = CraftItemStack.asCraftMirror(itemstack);
FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), fuel, fuelTime(itemstack));
this.world.getServer().getPluginManager().callEvent(furnaceBurnEvent);
if (furnaceBurnEvent.isCancelled()) {
return;
}
this.ticksForCurrentFuel = furnaceBurnEvent.getBurnTime();
this.burnTime += this.ticksForCurrentFuel;
if (this.burnTime > 0 && furnaceBurnEvent.isBurning()) {
// CraftBukkit end
flag1 = true;
if (!itemstack.isEmpty()) {
Item item = itemstack.getItem();
itemstack.subtract(1);
if (itemstack.isEmpty()) {
Item item1 = item.q();
this.items.set(1, item1 == null ? ItemStack.a : new ItemStack(item1));
}
}
}
}
/* CraftBukkit start - Moved up
if (this.isBurning() && this.canBurn()) {
++this.cookTime;
if (this.cookTime == this.cookTimeTotal) {
this.cookTime = 0;
this.cookTimeTotal = this.a((ItemStack) this.items.get(0));
this.burn();
flag1 = true;
}
} else {
this.cookTime = 0;
}
*/
}
if (flag != this.isBurning()) {
flag1 = true;
BlockFurnace.a(this.isBurning(), this.world, this.position);
this.invalidateBlockCache(); // CraftBukkit - Invalidate tile entity's cached block type
}
}
if (flag1) {
this.update();
}
}
public int a(ItemStack itemstack) {
return 200;
}
private boolean canBurn() {
if (((ItemStack) this.items.get(0)).isEmpty()) {
return false;
} else {
ItemStack itemstack = RecipesFurnace.getInstance().getResult((ItemStack) this.items.get(0));
if (itemstack.isEmpty()) {
return false;
} else {
ItemStack itemstack1 = (ItemStack) this.items.get(2);
// CraftBukkit - consider resultant count instead of current count
return itemstack1.isEmpty() ? true : (!itemstack1.doMaterialsMatch(itemstack) ? false : (itemstack1.getCount() + itemstack.getCount() <= this.getMaxStackSize() && itemstack1.getCount() + itemstack.getCount() < itemstack1.getMaxStackSize() ? true : itemstack1.getCount() + itemstack.getCount() <= itemstack.getMaxStackSize()));
}
}
}
public void burn() {
if (this.canBurn()) {
ItemStack itemstack = (ItemStack) this.items.get(0);
ItemStack itemstack1 = RecipesFurnace.getInstance().getResult(itemstack);
ItemStack itemstack2 = (ItemStack) this.items.get(2);
// CraftBukkit start - fire FurnaceSmeltEvent
CraftItemStack source = CraftItemStack.asCraftMirror(itemstack);
org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1);
FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), source, result);
this.world.getServer().getPluginManager().callEvent(furnaceSmeltEvent);
if (furnaceSmeltEvent.isCancelled()) {
return;
}
result = furnaceSmeltEvent.getResult();
itemstack1 = CraftItemStack.asNMSCopy(result);
if (!itemstack1.isEmpty()) {
if (itemstack2.isEmpty()) {
this.items.set(2, itemstack1.cloneItemStack());
} else if (CraftItemStack.asCraftMirror(itemstack2).isSimilar(result)) {
itemstack2.add(itemstack1.getCount());
} else {
return;
}
}
/*
if (itemstack2.isEmpty()) {
this.items.set(2, itemstack1.cloneItemStack());
} else if (itemstack2.getItem() == itemstack1.getItem()) {
itemstack2.add(1);
}
*/
// CraftBukkit end
if (itemstack.getItem() == Item.getItemOf(Blocks.SPONGE) && itemstack.getData() == 1 && !((ItemStack) this.items.get(1)).isEmpty() && ((ItemStack) this.items.get(1)).getItem() == Items.BUCKET) {
this.items.set(1, new ItemStack(Items.WATER_BUCKET));
}
itemstack.subtract(1);
}
}
public static int fuelTime(ItemStack itemstack) {
if (itemstack.isEmpty()) {
return 0;
} else {
Item item = itemstack.getItem();
return item == Item.getItemOf(Blocks.WOODEN_SLAB) ? 150 : (item == Item.getItemOf(Blocks.WOOL) ? 100 : (item == Item.getItemOf(Blocks.CARPET) ? 67 : (item == Item.getItemOf(Blocks.LADDER) ? 300 : (item == Item.getItemOf(Blocks.WOODEN_BUTTON) ? 100 : (Block.asBlock(item).getBlockData().getMaterial() == Material.WOOD ? 300 : (item == Item.getItemOf(Blocks.COAL_BLOCK) ? 16000 : (item instanceof ItemTool && "WOOD".equals(((ItemTool) item).h()) ? 200 : (item instanceof ItemSword && "WOOD".equals(((ItemSword) item).h()) ? 200 : (item instanceof ItemHoe && "WOOD".equals(((ItemHoe) item).g()) ? 200 : (item == Items.STICK ? 100 : (item != Items.BOW && item != Items.FISHING_ROD ? (item == Items.SIGN ? 200 : (item == Items.COAL ? 1600 : (item == Items.LAVA_BUCKET ? 20000 : (item != Item.getItemOf(Blocks.SAPLING) && item != Items.BOWL ? (item == Items.BLAZE_ROD ? 2400 : (item instanceof ItemDoor && item != Items.IRON_DOOR ? 200 : (item instanceof ItemBoat ? 400 : 0))) : 100)))) : 300)))))))))));
}
}
public static boolean isFuel(ItemStack itemstack) {
return fuelTime(itemstack) > 0;
}
public boolean a(EntityHuman entityhuman) {
return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D;
}
public void startOpen(EntityHuman entityhuman) {}
public void closeContainer(EntityHuman entityhuman) {}
public boolean b(int i, ItemStack itemstack) {
if (i == 2) {
return false;
} else if (i != 1) {
return true;
} else {
ItemStack itemstack1 = (ItemStack) this.items.get(1);
return isFuel(itemstack) || SlotFurnaceFuel.d_(itemstack) && itemstack1.getItem() != Items.BUCKET;
}
}
public int[] getSlotsForFace(EnumDirection enumdirection) {
return enumdirection == EnumDirection.DOWN ? TileEntityFurnace.f : (enumdirection == EnumDirection.UP ? TileEntityFurnace.a : TileEntityFurnace.g);
}
public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) {
return this.b(i, itemstack);
}
public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) {
if (enumdirection == EnumDirection.DOWN && i == 1) {
Item item = itemstack.getItem();
if (item != Items.WATER_BUCKET && item != Items.BUCKET) {
return false;
}
}
return true;
}
public String getContainerName() {
return "minecraft:furnace";
}
public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) {
return new ContainerFurnace(playerinventory, this);
}
public int getProperty(int i) {
switch (i) {
case 0:
return this.burnTime;
case 1:
return this.ticksForCurrentFuel;
case 2:
return this.cookTime;
case 3:
return this.cookTimeTotal;
default:
return 0;
}
}
public void setProperty(int i, int j) {
switch (i) {
case 0:
this.burnTime = j;
break;
case 1:
this.ticksForCurrentFuel = j;
break;
case 2:
this.cookTime = j;
break;
case 3:
this.cookTimeTotal = j;
}
}
public int h() {
return 4;
}
public void clear() {
this.items.clear();
}
}

View File

@@ -0,0 +1,784 @@
package net.minecraft.server;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nullable;
// CraftBukkit start
import org.bukkit.craftbukkit.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.inventory.InventoryPickupItemEvent;
import org.bukkit.inventory.Inventory;
// CraftBukkit end
public class TileEntityHopper extends TileEntityLootable implements IHopper, ITickable {
private NonNullList<ItemStack> items;
private int f;
private long g;
// CraftBukkit start - add fields and methods
public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>();
private int maxStack = MAX_STACK;
public List<ItemStack> getContents() {
return this.items;
}
public void onOpen(CraftHumanEntity who) {
transaction.add(who);
}
public void onClose(CraftHumanEntity who) {
transaction.remove(who);
}
public List<HumanEntity> getViewers() {
return transaction;
}
public void setMaxStackSize(int size) {
maxStack = size;
}
// CraftBukkit end
public TileEntityHopper() {
this.items = NonNullList.a(5, ItemStack.a);
this.f = -1;
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityHopper.class, new String[] { "Items"})));
}
public void load(NBTTagCompound nbttagcompound) {
super.load(nbttagcompound);
this.items = NonNullList.a(this.getSize(), ItemStack.a);
if (!this.c(nbttagcompound)) {
ContainerUtil.b(nbttagcompound, this.items);
}
if (nbttagcompound.hasKeyOfType("CustomName", 8)) {
this.o = nbttagcompound.getString("CustomName");
}
this.f = nbttagcompound.getInt("TransferCooldown");
}
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
super.save(nbttagcompound);
if (!this.d(nbttagcompound)) {
ContainerUtil.a(nbttagcompound, this.items);
}
nbttagcompound.setInt("TransferCooldown", this.f);
if (this.hasCustomName()) {
nbttagcompound.setString("CustomName", this.o);
}
return nbttagcompound;
}
public int getSize() {
return this.items.size();
}
public ItemStack splitStack(int i, int j) {
this.d((EntityHuman) null);
ItemStack itemstack = ContainerUtil.a(this.q(), i, j);
return itemstack;
}
public void setItem(int i, ItemStack itemstack) {
this.d((EntityHuman) null);
this.q().set(i, itemstack);
if (itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
}
public String getName() {
return this.hasCustomName() ? this.o : "container.hopper";
}
public int getMaxStackSize() {
return maxStack; // CraftBukkit
}
public void e() {
if (this.world != null && !this.world.isClientSide) {
--this.f;
this.g = this.world.getTime();
if (!this.J()) {
this.setCooldown(0);
// Spigot start
if (!this.o() && this.world.spigotConfig.hopperCheck > 1) {
this.setCooldown(this.world.spigotConfig.hopperCheck);
}
// Spigot end
}
}
}
private boolean o() {
mayAcceptItems = false; // Paper - at the beginning of a tick, assume we can't accept items
if (this.world != null && !this.world.isClientSide) {
if (!this.J() && BlockHopper.f(this.v())) {
boolean flag = false;
if (!this.p()) {
flag = this.s();
}
if (!this.r()) {
mayAcceptItems = true; // Paper - flag this hopper to be able to accept items
flag = a((IHopper) this) || flag;
}
if (flag) {
this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
this.update();
return true;
}
}
return false;
} else {
return false;
}
}
// Paper start
private boolean mayAcceptItems = false;
public boolean canAcceptItems() {
return mayAcceptItems;
}
// Paper end
private boolean p() {
Iterator iterator = this.items.iterator();
ItemStack itemstack;
do {
if (!iterator.hasNext()) {
return true;
}
itemstack = (ItemStack) iterator.next();
} while (itemstack.isEmpty());
return false;
}
public boolean x_() {
return this.p();
}
private boolean r() {
Iterator iterator = this.items.iterator();
ItemStack itemstack;
do {
if (!iterator.hasNext()) {
return true;
}
itemstack = (ItemStack) iterator.next();
} while (!itemstack.isEmpty() && itemstack.getCount() == itemstack.getMaxStackSize());
return false;
}
// Paper start - Optimize Hoppers
private static boolean skipPullModeEventFire = false;
private static boolean skipPushModeEventFire = false;
static boolean skipHopperEvents = false;
private boolean hopperPush(IInventory iinventory, EnumDirection enumdirection) {
skipPushModeEventFire = skipHopperEvents;
boolean foundItem = false;
for (int i = 0; i < this.getSize(); ++i) {
if (!this.getItem(i).isEmpty()) {
foundItem = true;
ItemStack origItemStack = this.getItem(i);
ItemStack itemstack = origItemStack;
final int origCount = origItemStack.getCount();
final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
origItemStack.setCount(moved);
// We only need to fire the event once to give protection plugins a chance to cancel this event
// Because nothing uses getItem, every event call should end up the same result.
if (!skipPushModeEventFire) {
itemstack = callPushMoveEvent(iinventory, itemstack);
if (itemstack == null) { // cancelled
origItemStack.setCount(origCount);
return false;
}
}
final ItemStack itemstack2 = addItem(this, iinventory, itemstack, enumdirection);
final int remaining = itemstack2.getCount();
if (remaining != moved) {
origItemStack = origItemStack.cloneItemStack();
origItemStack.setCount(origCount - moved + remaining);
this.setItem(i, origItemStack);
iinventory.update();
return true;
}
origItemStack.setCount(origCount);
}
}
if (foundItem && world.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown
this.setCooldown(world.spigotConfig.hopperTransfer);
}
return false;
}
private static boolean hopperPull(IHopper ihopper, IInventory iinventory, int i) {
ItemStack origItemStack = iinventory.getItem(i);
ItemStack itemstack = origItemStack;
final int origCount = origItemStack.getCount();
final World world = ihopper.getWorld();
final int moved = Math.min(world.spigotConfig.hopperAmount, origCount);
itemstack.setCount(moved);
if (!skipPullModeEventFire) {
itemstack = callPullMoveEvent(ihopper, iinventory, itemstack);
if (itemstack == null) { // cancelled
origItemStack.setCount(origCount);
// Drastically improve performance by returning true.
// No plugin could of relied on the behavior of false as the other call
// site for IMIE did not exhibit the same behavior
return true;
}
}
final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null);
final int remaining = itemstack2.getCount();
if (remaining != moved) {
origItemStack = origItemStack.cloneItemStack();
origItemStack.setCount(origCount - moved + remaining);
IGNORE_TILE_UPDATES = true;
iinventory.setItem(i, origItemStack);
IGNORE_TILE_UPDATES = false;
iinventory.update();
return true;
}
origItemStack.setCount(origCount);
if (world.paperConfig.cooldownHopperWhenFull) {
cooldownHopper(ihopper);
}
return false;
}
private ItemStack callPushMoveEvent(IInventory iinventory, ItemStack itemstack) {
Inventory destinationInventory = getInventory(iinventory);
InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(),
CraftItemStack.asCraftMirror(itemstack), destinationInventory, true);
boolean result = event.callEvent();
if (!event.calledGetItem && !event.calledSetItem) {
skipPushModeEventFire = true;
}
if (!result) {
cooldownHopper(this);
return null;
}
if (event.calledSetItem) {
return CraftItemStack.asNMSCopy(event.getItem());
} else {
return itemstack;
}
}
private static ItemStack callPullMoveEvent(IHopper hopper, IInventory iinventory, ItemStack itemstack) {
Inventory sourceInventory = getInventory(iinventory);
Inventory destination = getInventory(hopper);
InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory,
// Mirror is safe as we no plugins ever use this item
CraftItemStack.asCraftMirror(itemstack), destination, false);
boolean result = event.callEvent();
if (!event.calledGetItem && !event.calledSetItem) {
skipPullModeEventFire = true;
}
if (!result) {
cooldownHopper(hopper);
return null;
}
if (event.calledSetItem) {
return CraftItemStack.asNMSCopy(event.getItem());
} else {
return itemstack;
}
}
private static Inventory getInventory(IInventory iinventory) {
Inventory sourceInventory;// Have to special case large chests as they work oddly
if (iinventory instanceof InventoryLargeChest) {
sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
} else if (iinventory instanceof TileEntity) {
sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory();
} else {
sourceInventory = iinventory.getOwner().getInventory();
}
return sourceInventory;
}
private static void cooldownHopper(IHopper hopper) {
if (hopper instanceof TileEntityHopper) {
((TileEntityHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer);
} else if (hopper instanceof EntityMinecartHopper) {
((EntityMinecartHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer / 2);
}
}
// Paper end
private boolean s() {
IInventory iinventory = this.I();
if (iinventory == null) {
return false;
} else {
EnumDirection enumdirection = BlockHopper.b(this.v()).opposite();
if (this.a(iinventory, enumdirection)) {
return false;
} else {
return hopperPush(iinventory, enumdirection); /* // Paper - disable rest
for (int i = 0; i < this.getSize(); ++i) {
if (!this.getItem(i).isEmpty()) {
ItemStack itemstack = this.getItem(i).cloneItemStack();
// ItemStack itemstack1 = addItem(this, iinventory, this.splitStack(i, 1), enumdirection);
// CraftBukkit start - Call event when pushing items into other inventories
CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(i, world.spigotConfig.hopperAmount)); // Spigot
Inventory destinationInventory;
// Have to special case large chests as they work oddly
if (iinventory instanceof InventoryLargeChest) {
destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
} else {
destinationInventory = iinventory.getOwner().getInventory();
}
InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true);
this.getWorld().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
this.setItem(i, itemstack);
this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot
return false;
}
int origCount = event.getItem().getAmount(); // Spigot
ItemStack itemstack1 = addItem(this, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection);
if (itemstack1.isEmpty()) {
if (event.getItem().equals(oitemstack)) {
iinventory.update();
} else {
this.setItem(i, itemstack);
}
// CraftBukkit end
return true;
}
itemstack.subtract(origCount - itemstack1.getCount()); // Spigot
this.setItem(i, itemstack);
}
}
return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
}
}
private boolean a(IInventory iinventory, EnumDirection enumdirection) {
if (iinventory instanceof IWorldInventory) {
IWorldInventory iworldinventory = (IWorldInventory) iinventory;
int[] aint = iworldinventory.getSlotsForFace(enumdirection);
int[] aint1 = aint;
int i = aint.length;
for (int j = 0; j < i; ++j) {
int k = aint1[j];
ItemStack itemstack = iworldinventory.getItem(k);
if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) {
return false;
}
}
} else {
int l = iinventory.getSize();
for (int i1 = 0; i1 < l; ++i1) {
ItemStack itemstack1 = iinventory.getItem(i1);
if (itemstack1.isEmpty() || itemstack1.getCount() != itemstack1.getMaxStackSize()) {
return false;
}
}
}
return true;
}
private static boolean b(IInventory iinventory, EnumDirection enumdirection) {
if (iinventory instanceof IWorldInventory) {
IWorldInventory iworldinventory = (IWorldInventory) iinventory;
int[] aint = iworldinventory.getSlotsForFace(enumdirection);
int[] aint1 = aint;
int i = aint.length;
for (int j = 0; j < i; ++j) {
int k = aint1[j];
if (!iworldinventory.getItem(k).isEmpty()) {
return false;
}
}
} else {
int l = iinventory.getSize();
for (int i1 = 0; i1 < l; ++i1) {
if (!iinventory.getItem(i1).isEmpty()) {
return false;
}
}
}
return true;
}
// Paper start - split methods, and only do entity lookup if in pull mode
public static boolean a(IHopper ihopper) {
IInventory iinventory = getInventory(ihopper, !(ihopper instanceof TileEntityHopper) || !ihopper.getWorld().paperConfig.isHopperPushBased);
return acceptItem(ihopper, iinventory);
}
public static boolean acceptItem(IHopper ihopper, IInventory iinventory) {
// Paper end
if (iinventory != null) {
EnumDirection enumdirection = EnumDirection.DOWN;
if (b(iinventory, enumdirection)) {
return false;
}
skipPullModeEventFire = skipHopperEvents; // Paper
if (iinventory instanceof IWorldInventory) {
IWorldInventory iworldinventory = (IWorldInventory) iinventory;
int[] aint = iworldinventory.getSlotsForFace(enumdirection);
int[] aint1 = aint;
int i = aint.length;
for (int j = 0; j < i; ++j) {
int k = aint1[j];
if (a(ihopper, iinventory, k, enumdirection)) {
return true;
}
}
} else {
int l = iinventory.getSize();
for (int i1 = 0; i1 < l; ++i1) {
if (a(ihopper, iinventory, i1, enumdirection)) {
return true;
}
}
}
} else if (!ihopper.getWorld().paperConfig.isHopperPushBased || !(ihopper instanceof TileEntityHopper)) { // Paper - only search for entities in 'pull mode'
Iterator iterator = a(ihopper.getWorld(), ihopper.E(), ihopper.F(), ihopper.G()).iterator(); // Change getHopperLookupBoundingBox() if this ever changes
while (iterator.hasNext()) {
EntityItem entityitem = (EntityItem) iterator.next();
if (a((IInventory) null, ihopper, entityitem)) {
return true;
}
}
}
return false;
}
private static boolean a(IHopper ihopper, IInventory iinventory, int i, EnumDirection enumdirection) {
ItemStack itemstack = iinventory.getItem(i);
if (!itemstack.isEmpty() && b(iinventory, itemstack, i, enumdirection)) {
return hopperPull(ihopper, iinventory, i); /* // Paper - disable rest
ItemStack itemstack1 = itemstack.cloneItemStack();
// ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null);
// CraftBukkit start - Call event on collection of items from inventories into the hopper
CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot
Inventory sourceInventory;
// Have to special case large chests as they work oddly
if (iinventory instanceof InventoryLargeChest) {
sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory);
} else {
sourceInventory = iinventory.getOwner().getInventory();
}
InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false);
ihopper.getWorld().getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
iinventory.setItem(i, itemstack1);
if (ihopper instanceof TileEntityHopper) {
((TileEntityHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot
} else if (ihopper instanceof EntityMinecartHopper) {
((EntityMinecartHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot
}
return false;
}
int origCount = event.getItem().getAmount(); // Spigot
ItemStack itemstack2 = addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null);
if (itemstack2.isEmpty()) {
if (event.getItem().equals(oitemstack)) {
iinventory.update();
} else {
iinventory.setItem(i, itemstack1);
}
// CraftBukkit end
return true;
}
itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot
iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations
}
return false;
}
public static boolean putDropInInventory(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { return a(iinventory, iinventory1, entityitem); } // Paper - OBFHELPER
public static boolean a(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) {
boolean flag = false;
if (entityitem == null) {
return false;
} else {
// CraftBukkit start
InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory1), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - avoid snapshot creation
entityitem.world.getServer().getPluginManager().callEvent(event);
if (event.isCancelled()) {
return false;
}
// CraftBukkit end
ItemStack itemstack = entityitem.getItemStack().cloneItemStack();
ItemStack itemstack1 = addItem(iinventory, iinventory1, itemstack, (EnumDirection) null);
if (itemstack1.isEmpty()) {
flag = true;
entityitem.die();
} else {
entityitem.setItemStack(itemstack1);
}
return flag;
}
}
public static ItemStack addItem(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, @Nullable EnumDirection enumdirection) {
if (iinventory1 instanceof IWorldInventory && enumdirection != null) {
IWorldInventory iworldinventory = (IWorldInventory) iinventory1;
int[] aint = iworldinventory.getSlotsForFace(enumdirection);
for (int i = 0; i < aint.length && !itemstack.isEmpty(); ++i) {
itemstack = a(iinventory, iinventory1, itemstack, aint[i], enumdirection);
}
} else {
int j = iinventory1.getSize();
for (int k = 0; k < j && !itemstack.isEmpty(); ++k) {
itemstack = a(iinventory, iinventory1, itemstack, k, enumdirection);
}
}
return itemstack;
}
private static boolean a(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) {
return !iinventory.b(i, itemstack) ? false : !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canPlaceItemThroughFace(i, itemstack, enumdirection);
}
private static boolean b(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) {
return !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canTakeItemThroughFace(i, itemstack, enumdirection);
}
private static ItemStack a(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, int i, EnumDirection enumdirection) {
ItemStack itemstack1 = iinventory1.getItem(i);
if (a(iinventory1, itemstack, i, enumdirection)) {
boolean flag = false;
boolean flag1 = iinventory1.x_();
if (itemstack1.isEmpty()) {
IGNORE_TILE_UPDATES = true; // Paper
iinventory1.setItem(i, itemstack);
IGNORE_TILE_UPDATES = false; // Paper
itemstack = ItemStack.a;
flag = true;
} else if (a(itemstack1, itemstack)) {
int j = itemstack.getMaxStackSize() - itemstack1.getCount();
int k = Math.min(itemstack.getCount(), j);
itemstack.subtract(k);
itemstack1.add(k);
flag = k > 0;
}
if (flag) {
if (flag1 && iinventory1 instanceof TileEntityHopper) {
TileEntityHopper tileentityhopper = (TileEntityHopper) iinventory1;
if (!tileentityhopper.K()) {
byte b0 = 0;
if (iinventory != null && iinventory instanceof TileEntityHopper) {
TileEntityHopper tileentityhopper1 = (TileEntityHopper) iinventory;
if (tileentityhopper.g >= tileentityhopper1.g) {
b0 = 1;
}
}
tileentityhopper.setCooldown(tileentityhopper.world.spigotConfig.hopperTransfer - b0); // Spigot
}
}
iinventory1.update();
}
}
return itemstack;
}
private IInventory I() {
EnumDirection enumdirection = BlockHopper.b(this.v());
// Paper start - don't search for entities in push mode
World world = getWorld();
return getInventory(world, this.E() + (double) enumdirection.getAdjacentX(), this.F() + (double) enumdirection.getAdjacentY(), this.G() + (double) enumdirection.getAdjacentZ(), !world.paperConfig.isHopperPushBased);
// Paper end
}
// Paper start - add option to search for entities
public static IInventory b(IHopper hopper) {
return getInventory(hopper, true);
}
public static IInventory getInventory(IHopper ihopper, boolean searchForEntities) {
return getInventory(ihopper.getWorld(), ihopper.E(), ihopper.F() + 1.0D, ihopper.G(), searchForEntities);
// Paper end
}
public static List<EntityItem> a(World world, double d0, double d1, double d2) {
return world.a(EntityItem.class, new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D), IEntitySelector.a); // Change getHopperLookupBoundingBox(double, double, double) if the bounding box calculation is ever changed
}
// Paper start
public AxisAlignedBB getHopperLookupBoundingBox() {
return getHopperLookupBoundingBox(this.getX(), this.getY(), this.getZ());
}
private static AxisAlignedBB getHopperLookupBoundingBox(double d0, double d1, double d2) {
// Change this if a(World, double, double, double) above ever changes
return new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D);
}
// Paper end
// Paper start - add option to searchForEntities
public static IInventory b(World world, double d0, double d1, double d2) {
return getInventory(world, d0, d1, d2, true);
}
public static IInventory getInventory(World world, double d0, double d1, double d2, boolean searchForEntities) {
// Paper end
Object object = null;
int i = MathHelper.floor(d0);
int j = MathHelper.floor(d1);
int k = MathHelper.floor(d2);
BlockPosition blockposition = new BlockPosition(i, j, k);
if ( !world.isLoaded( blockposition ) ) return null; // Spigot
Block block = world.getType(blockposition).getBlock();
if (block.isTileEntity()) {
TileEntity tileentity = world.getTileEntity(blockposition);
if (tileentity instanceof IInventory) {
object = (IInventory) tileentity;
if (object instanceof TileEntityChest && block instanceof BlockChest) {
object = ((BlockChest) block).a(world, blockposition, true);
}
}
}
net.minecraft.server.Chunk chunk = world.getChunkAtWorldCoords(blockposition);
if (object == null && searchForEntities && !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding() && chunk.getItemCount(blockposition) > 0) { // Paper - only if searchForEntities
List list = world.getEntities((Entity) null, new AxisAlignedBB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), IEntitySelector.c);
if (!list.isEmpty()) {
object = (IInventory) list.get(world.random.nextInt(list.size()));
}
}
return (IInventory) object;
}
private static boolean a(ItemStack itemstack, ItemStack itemstack1) {
return itemstack.getItem() != itemstack1.getItem() ? false : (itemstack.getData() != itemstack1.getData() ? false : (itemstack.getCount() > itemstack.getMaxStackSize() ? false : ItemStack.equals(itemstack, itemstack1)));
}
public double E() {
return (double) this.position.getX() + 0.5D;
}
public double F() {
return (double) this.position.getY() + 0.5D;
}
public double G() {
return (double) this.position.getZ() + 0.5D;
}
private void setCooldown(int i) {
this.f = i;
}
private boolean J() {
return this.f > 0;
}
private boolean K() {
return this.f > 8;
}
public String getContainerName() {
return "minecraft:hopper";
}
public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) {
this.d(entityhuman);
return new ContainerHopper(playerinventory, this, entityhuman);
}
protected NonNullList<ItemStack> q() {
return this.items;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,773 +0,0 @@
package net.minecraft.server;
import com.google.common.collect.Maps;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
// CraftBukkit start
import org.bukkit.Bukkit;
import org.bukkit.event.weather.ThunderChangeEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
// CraftBukkit end
/**
* Akarin Changes Note
* 1) Add volatile to fields (slack service)
*/
public class WorldData {
private String b;
private int c;
private boolean d;
public static final EnumDifficulty a = EnumDifficulty.NORMAL;
private long e;
private WorldType f;
private String g;
private int h;
private int i;
private int j;
private volatile long k; // Akarin - volatile - OBFHELPER: time
private volatile long l; // Akarin - volatile - OBFHELPER: dayTime
private long m;
private long n;
private NBTTagCompound o;
private int p;
private String levelName;
private int r;
private int s;
private boolean t;
private int u;
private boolean v;
private int w;
private EnumGamemode x;
private boolean y;
private boolean z;
private boolean A;
private boolean B;
private volatile EnumDifficulty C; // Akarin - volatile
private boolean D;
private double E;
private double F;
private double G;
private long H;
private double I;
private double J;
private double K;
private int L;
private int M;
private final Map<DimensionManager, NBTTagCompound> N;
private GameRules O;
public WorldServer world; // CraftBukkit
protected WorldData() {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
}
public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.LEVEL, new DataInspector() {
@Override
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("Player", 10)) {
nbttagcompound.set("Player", dataconverter.a(DataConverterTypes.PLAYER, nbttagcompound.getCompound("Player"), i));
}
return nbttagcompound;
}
});
}
public WorldData(NBTTagCompound nbttagcompound) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
NBTTagCompound nbttagcompound1;
if (nbttagcompound.hasKeyOfType("Version", 10)) {
nbttagcompound1 = nbttagcompound.getCompound("Version");
this.b = nbttagcompound1.getString("Name");
this.c = nbttagcompound1.getInt("Id");
this.d = nbttagcompound1.getBoolean("Snapshot");
}
this.e = nbttagcompound.getLong("RandomSeed");
if (nbttagcompound.hasKeyOfType("generatorName", 8)) {
String s = nbttagcompound.getString("generatorName");
this.f = WorldType.getType(s);
if (this.f == null) {
this.f = WorldType.NORMAL;
} else if (this.f.f()) {
int i = 0;
if (nbttagcompound.hasKeyOfType("generatorVersion", 99)) {
i = nbttagcompound.getInt("generatorVersion");
}
this.f = this.f.a(i);
}
if (nbttagcompound.hasKeyOfType("generatorOptions", 8)) {
this.g = nbttagcompound.getString("generatorOptions");
}
}
this.x = EnumGamemode.getById(nbttagcompound.getInt("GameType"));
if (nbttagcompound.hasKeyOfType("MapFeatures", 99)) {
this.y = nbttagcompound.getBoolean("MapFeatures");
} else {
this.y = true;
}
this.h = nbttagcompound.getInt("SpawnX");
this.i = nbttagcompound.getInt("SpawnY");
this.j = nbttagcompound.getInt("SpawnZ");
this.k = nbttagcompound.getLong("Time");
if (nbttagcompound.hasKeyOfType("DayTime", 99)) {
this.l = nbttagcompound.getLong("DayTime");
} else {
this.l = this.k;
}
this.m = nbttagcompound.getLong("LastPlayed");
this.n = nbttagcompound.getLong("SizeOnDisk");
this.levelName = nbttagcompound.getString("LevelName");
this.r = nbttagcompound.getInt("version");
this.s = nbttagcompound.getInt("clearWeatherTime");
this.u = nbttagcompound.getInt("rainTime");
this.t = nbttagcompound.getBoolean("raining");
this.w = nbttagcompound.getInt("thunderTime");
this.v = nbttagcompound.getBoolean("thundering");
this.z = nbttagcompound.getBoolean("hardcore");
if (nbttagcompound.hasKeyOfType("initialized", 99)) {
this.B = nbttagcompound.getBoolean("initialized");
} else {
this.B = true;
}
if (nbttagcompound.hasKeyOfType("allowCommands", 99)) {
this.A = nbttagcompound.getBoolean("allowCommands");
} else {
this.A = this.x == EnumGamemode.CREATIVE;
}
if (nbttagcompound.hasKeyOfType("Player", 10)) {
this.o = nbttagcompound.getCompound("Player");
this.p = this.o.getInt("Dimension");
}
if (nbttagcompound.hasKeyOfType("GameRules", 10)) {
this.O.a(nbttagcompound.getCompound("GameRules"));
}
if (nbttagcompound.hasKeyOfType("Difficulty", 99)) {
this.C = EnumDifficulty.getById(nbttagcompound.getByte("Difficulty"));
}
if (nbttagcompound.hasKeyOfType("DifficultyLocked", 1)) {
this.D = nbttagcompound.getBoolean("DifficultyLocked");
}
if (nbttagcompound.hasKeyOfType("BorderCenterX", 99)) {
this.E = nbttagcompound.getDouble("BorderCenterX");
}
if (nbttagcompound.hasKeyOfType("BorderCenterZ", 99)) {
this.F = nbttagcompound.getDouble("BorderCenterZ");
}
if (nbttagcompound.hasKeyOfType("BorderSize", 99)) {
this.G = nbttagcompound.getDouble("BorderSize");
}
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTime", 99)) {
this.H = nbttagcompound.getLong("BorderSizeLerpTime");
}
if (nbttagcompound.hasKeyOfType("BorderSizeLerpTarget", 99)) {
this.I = nbttagcompound.getDouble("BorderSizeLerpTarget");
}
if (nbttagcompound.hasKeyOfType("BorderSafeZone", 99)) {
this.J = nbttagcompound.getDouble("BorderSafeZone");
}
if (nbttagcompound.hasKeyOfType("BorderDamagePerBlock", 99)) {
this.K = nbttagcompound.getDouble("BorderDamagePerBlock");
}
if (nbttagcompound.hasKeyOfType("BorderWarningBlocks", 99)) {
this.L = nbttagcompound.getInt("BorderWarningBlocks");
}
if (nbttagcompound.hasKeyOfType("BorderWarningTime", 99)) {
this.M = nbttagcompound.getInt("BorderWarningTime");
}
if (nbttagcompound.hasKeyOfType("DimensionData", 10)) {
nbttagcompound1 = nbttagcompound.getCompound("DimensionData");
Iterator iterator = nbttagcompound1.c().iterator();
while (iterator.hasNext()) {
String s1 = (String) iterator.next();
this.N.put(DimensionManager.a(Integer.parseInt(s1)), nbttagcompound1.getCompound(s1));
}
}
}
public WorldData(WorldSettings worldsettings, String s) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
this.a(worldsettings);
this.levelName = s;
this.C = WorldData.a;
this.B = false;
}
public void a(WorldSettings worldsettings) {
this.e = worldsettings.d();
this.x = worldsettings.e();
this.y = worldsettings.g();
this.z = worldsettings.f();
this.f = worldsettings.h();
this.g = worldsettings.j();
this.A = worldsettings.i();
}
public WorldData(WorldData worlddata) {
this.f = WorldType.NORMAL;
this.g = "";
this.G = 6.0E7D;
this.J = 5.0D;
this.K = 0.2D;
this.L = 5;
this.M = 15;
this.N = Maps.newEnumMap(DimensionManager.class);
this.O = new GameRules();
this.e = worlddata.e;
this.f = worlddata.f;
this.g = worlddata.g;
this.x = worlddata.x;
this.y = worlddata.y;
this.h = worlddata.h;
this.i = worlddata.i;
this.j = worlddata.j;
this.k = worlddata.k;
this.l = worlddata.l;
this.m = worlddata.m;
this.n = worlddata.n;
this.o = worlddata.o;
this.p = worlddata.p;
this.levelName = worlddata.levelName;
this.r = worlddata.r;
this.u = worlddata.u;
this.t = worlddata.t;
this.w = worlddata.w;
this.v = worlddata.v;
this.z = worlddata.z;
this.A = worlddata.A;
this.B = worlddata.B;
this.O = worlddata.O;
this.C = worlddata.C;
this.D = worlddata.D;
this.E = worlddata.E;
this.F = worlddata.F;
this.G = worlddata.G;
this.H = worlddata.H;
this.I = worlddata.I;
this.J = worlddata.J;
this.K = worlddata.K;
this.M = worlddata.M;
this.L = worlddata.L;
}
public NBTTagCompound a(@Nullable NBTTagCompound nbttagcompound) {
if (nbttagcompound == null) {
nbttagcompound = this.o;
}
NBTTagCompound nbttagcompound1 = new NBTTagCompound();
this.a(nbttagcompound1, nbttagcompound);
return nbttagcompound1;
}
private void a(NBTTagCompound nbttagcompound, NBTTagCompound nbttagcompound1) {
NBTTagCompound nbttagcompound2 = new NBTTagCompound();
nbttagcompound2.setString("Name", "1.12.2");
nbttagcompound2.setInt("Id", 1343);
nbttagcompound2.setBoolean("Snapshot", false);
nbttagcompound.set("Version", nbttagcompound2);
nbttagcompound.setInt("DataVersion", 1343);
nbttagcompound.setLong("RandomSeed", this.e);
nbttagcompound.setString("generatorName", this.f.name());
nbttagcompound.setInt("generatorVersion", this.f.getVersion());
nbttagcompound.setString("generatorOptions", this.g);
nbttagcompound.setInt("GameType", this.x.getId());
nbttagcompound.setBoolean("MapFeatures", this.y);
nbttagcompound.setInt("SpawnX", this.h);
nbttagcompound.setInt("SpawnY", this.i);
nbttagcompound.setInt("SpawnZ", this.j);
nbttagcompound.setLong("Time", this.k);
nbttagcompound.setLong("DayTime", this.l);
nbttagcompound.setLong("SizeOnDisk", this.n);
nbttagcompound.setLong("LastPlayed", MinecraftServer.aw());
nbttagcompound.setString("LevelName", this.levelName);
nbttagcompound.setInt("version", this.r);
nbttagcompound.setInt("clearWeatherTime", this.s);
nbttagcompound.setInt("rainTime", this.u);
nbttagcompound.setBoolean("raining", this.t);
nbttagcompound.setInt("thunderTime", this.w);
nbttagcompound.setBoolean("thundering", this.v);
nbttagcompound.setBoolean("hardcore", this.z);
nbttagcompound.setBoolean("allowCommands", this.A);
nbttagcompound.setBoolean("initialized", this.B);
nbttagcompound.setDouble("BorderCenterX", this.E);
nbttagcompound.setDouble("BorderCenterZ", this.F);
nbttagcompound.setDouble("BorderSize", this.G);
nbttagcompound.setLong("BorderSizeLerpTime", this.H);
nbttagcompound.setDouble("BorderSafeZone", this.J);
nbttagcompound.setDouble("BorderDamagePerBlock", this.K);
nbttagcompound.setDouble("BorderSizeLerpTarget", this.I);
nbttagcompound.setDouble("BorderWarningBlocks", this.L);
nbttagcompound.setDouble("BorderWarningTime", this.M);
if (this.C != null) {
nbttagcompound.setByte("Difficulty", (byte) this.C.a());
}
nbttagcompound.setBoolean("DifficultyLocked", this.D);
nbttagcompound.set("GameRules", this.O.a());
NBTTagCompound nbttagcompound3 = new NBTTagCompound();
Iterator iterator = this.N.entrySet().iterator();
while (iterator.hasNext()) {
Entry entry = (Entry) iterator.next();
nbttagcompound3.set(String.valueOf(((DimensionManager) entry.getKey()).getDimensionID()), (NBTBase) entry.getValue());
}
nbttagcompound.set("DimensionData", nbttagcompound3);
if (nbttagcompound1 != null) {
nbttagcompound.set("Player", nbttagcompound1);
}
}
public long getSeed() {
return this.e;
}
public int b() {
return this.h;
}
public int c() {
return this.i;
}
public int d() {
return this.j;
}
public long getTime() {
return this.k;
}
public long getDayTime() {
return this.l;
}
public NBTTagCompound h() {
return this.o;
}
public void setTime(long i) {
this.k = i;
}
public void setDayTime(long i) {
this.l = i;
}
public void setSpawn(BlockPosition blockposition) {
this.h = blockposition.getX();
this.i = blockposition.getY();
this.j = blockposition.getZ();
}
public String getName() {
return this.levelName;
}
public void a(String s) {
this.levelName = s;
}
public int k() {
return this.r;
}
public void e(int i) {
this.r = i;
}
public int z() {
return this.s;
}
public void i(int i) {
this.s = i;
}
public boolean isThundering() {
return this.v;
}
public void setThundering(boolean flag) {
// CraftBukkit start
org.bukkit.World world = Bukkit.getWorld(getName());
if (world != null) {
ThunderChangeEvent thunder = new ThunderChangeEvent(world, flag);
Bukkit.getServer().getPluginManager().callEvent(thunder);
if (thunder.isCancelled()) {
return;
}
}
// CraftBukkit end
this.v = flag;
}
public int getThunderDuration() {
return this.w;
}
public void setThunderDuration(int i) {
this.w = i;
}
public boolean hasStorm() {
return this.t;
}
public void setStorm(boolean flag) {
// CraftBukkit start
org.bukkit.World world = Bukkit.getWorld(getName());
if (world != null) {
WeatherChangeEvent weather = new WeatherChangeEvent(world, flag);
Bukkit.getServer().getPluginManager().callEvent(weather);
if (weather.isCancelled()) {
return;
}
}
// CraftBukkit end
this.t = flag;
}
public int getWeatherDuration() {
return this.u;
}
public void setWeatherDuration(int i) {
this.u = i;
}
public EnumGamemode getGameType() {
return this.x;
}
public boolean shouldGenerateMapFeatures() {
return this.y;
}
public void f(boolean flag) {
this.y = flag;
}
public void setGameType(EnumGamemode enumgamemode) {
this.x = enumgamemode;
}
public boolean isHardcore() {
return this.z;
}
public void g(boolean flag) {
this.z = flag;
}
public WorldType getType() {
return this.f;
}
public void a(WorldType worldtype) {
this.f = worldtype;
}
public String getGeneratorOptions() {
return this.g == null ? "" : this.g;
}
public boolean u() {
return this.A;
}
public void c(boolean flag) {
this.A = flag;
}
public boolean v() {
return this.B;
}
public void d(boolean flag) {
this.B = flag;
}
public GameRules w() {
return this.O;
}
public double B() {
return this.E;
}
public double C() {
return this.F;
}
public double D() {
return this.G;
}
public void a(double d0) {
this.G = d0;
}
public long E() {
return this.H;
}
public void e(long i) {
this.H = i;
}
public double F() {
return this.I;
}
public void b(double d0) {
this.I = d0;
}
public void c(double d0) {
this.F = d0;
}
public void d(double d0) {
this.E = d0;
}
public double G() {
return this.J;
}
public void e(double d0) {
this.J = d0;
}
public double H() {
return this.K;
}
public void f(double d0) {
this.K = d0;
}
public int I() {
return this.L;
}
public int J() {
return this.M;
}
public void j(int i) {
this.L = i;
}
public void k(int i) {
this.M = i;
}
public EnumDifficulty getDifficulty() {
return this.C;
}
public void setDifficulty(EnumDifficulty enumdifficulty) {
this.C = enumdifficulty;
// CraftBukkit start
PacketPlayOutServerDifficulty packet = new PacketPlayOutServerDifficulty(this.getDifficulty(), this.isDifficultyLocked());
for (EntityPlayer player : (java.util.List<EntityPlayer>) (java.util.List) world.players) {
player.playerConnection.sendPacket(packet);
}
// CraftBukkit end
}
public boolean isDifficultyLocked() {
return this.D;
}
public void e(boolean flag) {
this.D = flag;
}
public void a(CrashReportSystemDetails crashreportsystemdetails) {
crashreportsystemdetails.a("Level seed", new CrashReportCallable() {
public String a() throws Exception {
return String.valueOf(WorldData.this.getSeed());
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level generator", new CrashReportCallable() {
public String a() throws Exception {
return String.format("ID %02d - %s, ver %d. Features enabled: %b", new Object[] { Integer.valueOf(WorldData.this.f.g()), WorldData.this.f.name(), Integer.valueOf(WorldData.this.f.getVersion()), Boolean.valueOf(WorldData.this.y)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level generator options", new CrashReportCallable() {
public String a() throws Exception {
return WorldData.this.g;
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level spawn location", new CrashReportCallable() {
public String a() throws Exception {
return CrashReportSystemDetails.a(WorldData.this.h, WorldData.this.i, WorldData.this.j);
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level time", new CrashReportCallable() {
public String a() throws Exception {
return String.format("%d game time, %d day time", new Object[] { Long.valueOf(WorldData.this.k), Long.valueOf(WorldData.this.l)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level dimension", new CrashReportCallable() {
public String a() throws Exception {
return String.valueOf(WorldData.this.p);
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level storage version", new CrashReportCallable() {
public String a() throws Exception {
String s = "Unknown?";
try {
switch (WorldData.this.r) {
case 19132:
s = "McRegion";
break;
case 19133:
s = "Anvil";
}
} catch (Throwable throwable) {
;
}
return String.format("0x%05X - %s", new Object[] { Integer.valueOf(WorldData.this.r), s});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level weather", new CrashReportCallable() {
public String a() throws Exception {
return String.format("Rain time: %d (now: %b), thunder time: %d (now: %b)", new Object[] { Integer.valueOf(WorldData.this.u), Boolean.valueOf(WorldData.this.t), Integer.valueOf(WorldData.this.w), Boolean.valueOf(WorldData.this.v)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
crashreportsystemdetails.a("Level game mode", new CrashReportCallable() {
public String a() throws Exception {
return String.format("Game mode: %s (ID %d). Hardcore: %b. Cheats: %b", new Object[] { WorldData.this.x.b(), Integer.valueOf(WorldData.this.x.getId()), Boolean.valueOf(WorldData.this.z), Boolean.valueOf(WorldData.this.A)});
}
@Override
public Object call() throws Exception {
return this.a();
}
});
}
public NBTTagCompound a(DimensionManager dimensionmanager) {
NBTTagCompound nbttagcompound = this.N.get(dimensionmanager);
return nbttagcompound == null ? new NBTTagCompound() : nbttagcompound;
}
public void a(DimensionManager dimensionmanager, NBTTagCompound nbttagcompound) {
this.N.put(dimensionmanager, nbttagcompound);
}
// CraftBukkit start - Check if the name stored in NBT is the correct one
public void checkName( String name ) {
if ( !this.levelName.equals( name ) ) {
this.levelName = name;
}
}
// CraftBukkit end
}

View File

@@ -0,0 +1,101 @@
package net.minecraft.server;
import java.util.Iterator;
import javax.annotation.Nullable;
public class WorldManager implements IWorldAccess {
private final MinecraftServer a;
private final WorldServer world;
public WorldManager(MinecraftServer minecraftserver, WorldServer worldserver) {
this.a = minecraftserver;
this.world = worldserver;
}
public void a(int i, boolean flag, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {}
public void a(int i, boolean flag, boolean flag1, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {}
public void a(Entity entity) {
this.world.getTracker().track(entity);
if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity);
}
}
public void b(Entity entity) {
this.world.getTracker().untrackEntity(entity);
this.world.getScoreboard().a(entity);
if (entity instanceof EntityPlayer) {
this.world.worldProvider.b((EntityPlayer) entity);
}
}
public void a(@Nullable EntityHuman entityhuman, SoundEffect soundeffect, SoundCategory soundcategory, double d0, double d1, double d2, float f, float f1) {
// CraftBukkit - this.world.dimension, // Paper - this.world.dimension -> this.world
this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world, new PacketPlayOutNamedSoundEffect(soundeffect, soundcategory, d0, d1, d2, f, f1));
}
public void a(int i, int j, int k, int l, int i1, int j1) {}
public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) {
this.world.getPlayerChunkMap().flagDirty(blockposition);
}
public void a(BlockPosition blockposition) {}
public void a(SoundEffect soundeffect, BlockPosition blockposition) {}
public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) {
// CraftBukkit - this.world.dimension
this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world, new PacketPlayOutWorldEvent(i, blockposition, j, false));
}
public void a(int i, BlockPosition blockposition, int j) {
this.a.getPlayerList().sendAll(new PacketPlayOutWorldEvent(i, blockposition, j, true));
}
public void b(int i, BlockPosition blockposition, int j) {
// Iterator iterator = this.a.getPlayerList().v().iterator(); // Paper
// CraftBukkit start
EntityHuman entityhuman = null;
Entity entity = world.getEntity(i);
if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity;
// CraftBukkit end
// Paper start
java.util.List<? extends EntityHuman> list = entity != null ? entity.world.players : this.a.getPlayerList().v();
Iterator<? extends EntityHuman> iterator = list.iterator();
PacketPlayOutBlockBreakAnimation packet = null; // NeonPaper - cache packet
while (iterator.hasNext()) {
EntityHuman human = iterator.next();
if (!(human instanceof EntityPlayer)) continue;
EntityPlayer entityplayer = (EntityPlayer) human;
// Paper end
if (entityplayer != null && entityplayer.world == this.world && entityplayer.getId() != i) {
double d0 = (double) blockposition.getX() - entityplayer.locX;
double d1 = (double) blockposition.getY() - entityplayer.locY;
double d2 = (double) blockposition.getZ() - entityplayer.locZ;
// CraftBukkit start
if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) {
continue;
}
// CraftBukkit end
if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) {
// NeonPaper start
if (packet == null) packet = new PacketPlayOutBlockBreakAnimation(i, blockposition, j);
entityplayer.playerConnection.sendPacket(packet);
// NeonPaper end
}
}
}
}
}

View File

@@ -424,33 +424,13 @@ public class WorldServer extends World implements IAsyncTaskHandler {
}
// CraftBukkit end
}
// Dionysus
// Fixes MC-47080 and simplifies the logic
public boolean everyoneDeeplySleeping() {
if (this.Q && !this.isClientSide) {
Iterator iterator = this.players.iterator();
// CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers
boolean foundActualSleepers = false;
EntityHuman entityhuman;
do {
if (!iterator.hasNext()) {
return foundActualSleepers;
}
entityhuman = (EntityHuman) iterator.next();
// CraftBukkit start
if (entityhuman.isDeeplySleeping()) {
foundActualSleepers = true;
}
} while (!entityhuman.isSpectator() || entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping);
// CraftBukkit end
if (this.players.size() == 0 || this.isClientSide || !this.Q) {
return false;
} else {
return false;
return this.players.stream().allMatch(p -> p.isSpectator() || p.isDeeplySleeping() || p.fauxSleeping);
}
}
@@ -1194,7 +1174,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
if (this.entitiesByUUID.containsKey(uuid)) {
Entity entity1 = this.entitiesByUUID.get(uuid);
if (this.f.contains(entity1) || entity1.dead) { // Paper - overwrite the current dead one
if (this.f.contains(entity1) || entity1.dead) { // Paper - if dupe is dead, overwrite
this.f.remove(entity1);
} else {
if (!(entity instanceof EntityHuman)) {
@@ -1291,7 +1271,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
}
// CraftBukkit end
if (super.strikeLightning(entity)) {
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, dimension, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, entity.locX, entity.locY, entity.locZ, 512.0D, this, new PacketPlayOutSpawnEntityWeather(entity)); // CraftBukkit - Use dimension, // Paper - use world instead of dimension
return true;
} else {
return false;
@@ -1369,8 +1349,8 @@ public class WorldServer extends World implements IAsyncTaskHandler {
BlockActionData blockactiondata = (BlockActionData) iterator.next();
if (this.a(blockactiondata)) {
// CraftBukkit - this.worldProvider.dimension -> this.dimension
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, blockactiondata.a().getX(), blockactiondata.a().getY(), blockactiondata.a().getZ(), 64.0D, dimension, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c()));
// CraftBukkit - this.worldProvider.dimension -> this.dimension, // Paper - dimension -> world
this.server.getPlayerList().sendPacketNearby((EntityHuman) null, (double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c()));
}
}

View File

@@ -0,0 +1,317 @@
package org.bukkit.craftbukkit;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Random;
import net.minecraft.server.*;
import org.bukkit.Chunk;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.entity.Entity;
import org.bukkit.ChunkSnapshot;
public class CraftChunk implements Chunk {
private WeakReference<net.minecraft.server.Chunk> weakChunk;
private final WorldServer worldServer;
private final int x;
private final int z;
private static final byte[] emptyData = new byte[2048];
private static final short[] emptyBlockIDs = new short[4096];
private static final byte[] emptySkyLight = new byte[2048];
public CraftChunk(net.minecraft.server.Chunk chunk) {
this.weakChunk = new WeakReference<net.minecraft.server.Chunk>(chunk);
worldServer = (WorldServer) getHandle().world;
x = getHandle().locX;
z = getHandle().locZ;
}
public World getWorld() {
return worldServer.getWorld();
}
public CraftWorld getCraftWorld() {
return (CraftWorld) getWorld();
}
public net.minecraft.server.Chunk getHandle() {
net.minecraft.server.Chunk c = weakChunk.get();
if (c == null) {
c = worldServer.getChunkAt(x, z);
weakChunk = new WeakReference<net.minecraft.server.Chunk>(c);
}
return c;
}
void breakLink() {
weakChunk.clear();
}
public int getX() {
return x;
}
public int getZ() {
return z;
}
@Override
public String toString() {
return "CraftChunk{" + "x=" + getX() + "z=" + getZ() + '}';
}
public Block getBlock(int x, int y, int z) {
return new CraftBlock(this, (getX() << 4) | (x & 0xF), y, (getZ() << 4) | (z & 0xF));
}
public Entity[] getEntities() {
int count = 0, index = 0;
net.minecraft.server.Chunk chunk = getHandle();
for (int i = 0; i < 16; i++) {
count += chunk.entitySlices[i].size();
}
Entity[] entities = new Entity[count];
for (int i = 0; i < 16; i++) {
//for (Object obj : chunk.entitySlices[i].toArray()) {
// if (!(obj instanceof net.minecraft.server.Entity)) {
// NeonPaper start - speed up (was with chunk.entitySlices[i].toArray() and cast checks which costs a lot of performance if called often)
for (net.minecraft.server.Entity entity : chunk.entitySlices[i]) {
if (entity == null) {
continue;
}
//entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity();
entities[index++] = entity.getBukkitEntity();
}
//NeonPaper end
}
return entities;
}
public BlockState[] getTileEntities() {
int index = 0;
net.minecraft.server.Chunk chunk = getHandle();
BlockState[] entities = new BlockState[chunk.tileEntities.size()];
for (Object obj : chunk.tileEntities.keySet().toArray()) {
if (!(obj instanceof BlockPosition)) {
continue;
}
BlockPosition position = (BlockPosition) obj;
entities[index++] = worldServer.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()).getState();
}
return entities;
}
public boolean isLoaded() {
return getWorld().isChunkLoaded(this);
}
public boolean load() {
return getWorld().loadChunk(getX(), getZ(), true);
}
public boolean load(boolean generate) {
return getWorld().loadChunk(getX(), getZ(), generate);
}
public boolean unload() {
return getWorld().unloadChunk(getX(), getZ());
}
@Override
public boolean isSlimeChunk() {
// 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk
return getHandle().a(worldServer.spigotConfig.slimeSeed).nextInt(10) == 0;
}
public boolean unload(boolean save) {
return getWorld().unloadChunk(getX(), getZ(), save);
}
public boolean unload(boolean save, boolean safe) {
return getWorld().unloadChunk(getX(), getZ(), save, safe);
}
public ChunkSnapshot getChunkSnapshot() {
return getChunkSnapshot(true, false, false);
}
public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) {
net.minecraft.server.Chunk chunk = getHandle();
ChunkSection[] cs = chunk.getSections();
short[][] sectionBlockIDs = new short[cs.length][];
byte[][] sectionBlockData = new byte[cs.length][];
byte[][] sectionSkyLights = new byte[cs.length][];
byte[][] sectionEmitLights = new byte[cs.length][];
boolean[] sectionEmpty = new boolean[cs.length];
for (int i = 0; i < cs.length; i++) {
if (cs[i] == null) { // Section is empty?
sectionBlockIDs[i] = emptyBlockIDs;
sectionBlockData[i] = emptyData;
sectionSkyLights[i] = emptySkyLight;
sectionEmitLights[i] = emptyData;
sectionEmpty[i] = true;
} else { // Not empty
short[] blockids = new short[4096];
byte[] rawIds = new byte[4096];
NibbleArray data = new NibbleArray();
cs[i].getBlocks().exportData(rawIds, data);
byte[] dataValues = sectionBlockData[i] = data.asBytes();
// Copy base IDs
for (int j = 0; j < 4096; j++) {
blockids[j] = (short) (rawIds[j] & 0xFF);
}
sectionBlockIDs[i] = blockids;
if (cs[i].getSkyLightArray() == null) {
sectionSkyLights[i] = emptyData;
} else {
sectionSkyLights[i] = new byte[2048];
System.arraycopy(cs[i].getSkyLightArray().asBytes(), 0, sectionSkyLights[i], 0, 2048);
}
sectionEmitLights[i] = new byte[2048];
System.arraycopy(cs[i].getEmittedLightArray().asBytes(), 0, sectionEmitLights[i], 0, 2048);
}
}
int[] hmap = null;
if (includeMaxBlockY) {
hmap = new int[256]; // Get copy of height map
System.arraycopy(chunk.heightMap, 0, hmap, 0, 256);
}
BiomeBase[] biome = null;
double[] biomeTemp = null;
double[] biomeRain = null;
if (includeBiome || includeBiomeTempRain) {
WorldChunkManager wcm = chunk.world.getWorldChunkManager();
if (includeBiome) {
biome = new BiomeBase[256];
for (int i = 0; i < 256; i++) {
biome[i] = chunk.getBiome(new BlockPosition(i & 0xF, 0, i >> 4), wcm);
}
}
if (includeBiomeTempRain) {
biomeTemp = new double[256];
biomeRain = new double[256];
float[] dat = getTemperatures(wcm, getX() << 4, getZ() << 4);
for (int i = 0; i < 256; i++) {
biomeTemp[i] = dat[i];
}
/* Removed 15w46a
dat = wcm.getWetness(null, getX() << 4, getZ() << 4, 16, 16);
for (int i = 0; i < 256; i++) {
biomeRain[i] = dat[i];
}
*/
}
}
World world = getWorld();
return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.getFullTime(), sectionBlockIDs, sectionBlockData, sectionSkyLights, sectionEmitLights, sectionEmpty, hmap, biome, biomeTemp, biomeRain);
}
public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world, boolean includeBiome, boolean includeBiomeTempRain) {
BiomeBase[] biome = null;
double[] biomeTemp = null;
double[] biomeRain = null;
if (includeBiome || includeBiomeTempRain) {
WorldChunkManager wcm = world.getHandle().getWorldChunkManager();
if (includeBiome) {
biome = new BiomeBase[256];
for (int i = 0; i < 256; i++) {
biome[i] = world.getHandle().getBiome(new BlockPosition((x << 4) + (i & 0xF), 0, (z << 4) + (i >> 4)));
}
}
if (includeBiomeTempRain) {
biomeTemp = new double[256];
biomeRain = new double[256];
float[] dat = getTemperatures(wcm, x << 4, z << 4);
for (int i = 0; i < 256; i++) {
biomeTemp[i] = dat[i];
}
/* Removed 15w46a
dat = wcm.getWetness(null, x << 4, z << 4, 16, 16);
for (int i = 0; i < 256; i++) {
biomeRain[i] = dat[i];
}
*/
}
}
/* Fill with empty data */
int hSection = world.getMaxHeight() >> 4;
short[][] blockIDs = new short[hSection][];
byte[][] skyLight = new byte[hSection][];
byte[][] emitLight = new byte[hSection][];
byte[][] blockData = new byte[hSection][];
boolean[] empty = new boolean[hSection];
for (int i = 0; i < hSection; i++) {
blockIDs[i] = emptyBlockIDs;
skyLight[i] = emptySkyLight;
emitLight[i] = emptyData;
blockData[i] = emptyData;
empty[i] = true;
}
return new CraftChunkSnapshot(x, z, world.getName(), world.getFullTime(), blockIDs, blockData, skyLight, emitLight, empty, new int[256], biome, biomeTemp, biomeRain);
}
private static float[] getTemperatures(WorldChunkManager chunkmanager, int chunkX, int chunkZ) {
BiomeBase[] biomes = chunkmanager.getBiomes(null, chunkX, chunkZ, 16, 16);
float[] temps = new float[biomes.length];
for (int i = 0; i < biomes.length; i++) {
float temp = biomes[i].getTemperature(); // Vanilla of olde: ((int) biomes[i].temperature * 65536.0F) / 65536.0F
if (temp > 1F) {
temp = 1F;
}
temps[i] = temp;
}
return temps;
}
static {
Arrays.fill(emptySkyLight, (byte) 0xFF);
}
}

View File

@@ -1922,17 +1922,20 @@ public final class CraftServer implements Server {
@Override
public void reloadPermissions() {
((SimplePluginManager) pluginManager).clearPermissions();
loadCustomPermissions();
pluginManager.clearPermissions();
if (com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
for (Plugin plugin : pluginManager.getPlugins()) {
plugin.getDescription().getPermissions().forEach((perm) -> {
for (Permission perm : plugin.getDescription().getPermissions()) {
try {
pluginManager.addPermission(perm);
} catch (IllegalArgumentException ex) {
getLogger().log(Level.WARNING, "Plugin " + plugin.getDescription().getFullName() + " tried to register permission '" + perm.getName() + "' but it's already registered", ex);
}
});
}
}
if (!com.destroystokyo.paper.PaperConfig.loadPermsBeforePlugins) loadCustomPermissions();
DefaultPermissions.registerCorePermissions();
CraftDefaultPermissions.registerCorePermissions();
}
@Override

View File

@@ -1107,10 +1107,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
EntityTracker tracker = ((WorldServer) entity.world).tracker;
// Paper end
tracker.entriesLock.updateLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null) {
tracker.entriesLock.writeLock().lock(); // Akarin
entry.clear(getHandle());
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
// Remove the hidden player from this player user list, if they're on it
if (other.sentListPacket) {
@@ -1157,12 +1161,14 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
getHandle().playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.ADD_PLAYER, other));
tracker.entriesLock.updateLock().lock(); // Akarin
EntityTrackerEntry entry = tracker.trackedEntities.get(other.getId());
if (entry != null && !entry.trackedPlayers.contains(getHandle())) {
tracker.entriesLock.lock(); // Akarin
tracker.entriesLock.writeLock().lock(); // Akarin
entry.updatePlayer(getHandle());
tracker.entriesLock.unlock(); // Akarin
tracker.entriesLock.writeLock().unlock(); // Akarin
}
tracker.entriesLock.updateLock().unlock(); // Akarin
}
// Paper start
private void reregisterPlayer(EntityPlayer player) {
@@ -1482,11 +1488,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
@Override
public int getNoDamageTicks() {
if (getHandle().invulnerableTicks > 0) {
return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks);
} else {
return getHandle().noDamageTicks;
}
// TacoSpigot start - fix incorrect calculation of getNoDamageTicks
/*
if (getHandle().invulnerableTicks > 0) {
return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks);
} else {
return getHandle().noDamageTicks;
}
*/
return Math.max(getHandle().invulnerableTicks, Math.max(0, getHandle().noDamageTicks - (getHandle().maxNoDamageTicks >> 1)));
// TacoSpigot end
}
@Override
@@ -1616,7 +1627,15 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
}
public void sendHealthUpdate() {
getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
// Paper start - cancellable death event
//getHandle().playerConnection.sendPacket(new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel()));
PacketPlayOutUpdateHealth packet = new PacketPlayOutUpdateHealth(getScaledHealth(), getHandle().getFoodData().getFoodLevel(), getHandle().getFoodData().getSaturationLevel());
if (this.getHandle().queueHealthUpdatePacket) {
this.getHandle().queuedHealthUpdatePacket = packet;
} else {
this.getHandle().playerConnection.sendPacket(packet);
}
// Paper end
}
public void injectScaledMaxHealth(Collection<AttributeInstance> collection, boolean force) {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,791 @@
package org.bukkit.plugin;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.destroystokyo.paper.event.server.ServerExceptionEvent;
import com.destroystokyo.paper.exception.ServerEventException;
import com.destroystokyo.paper.exception.ServerPluginEnableDisableException;
import org.apache.commons.lang.Validate;
import org.bukkit.Server;
import org.bukkit.command.Command;
import org.bukkit.command.PluginCommandYamlParser;
import org.bukkit.command.SimpleCommandMap;
import org.bukkit.event.Event;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.permissions.Permissible;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.util.FileUtil;
import com.google.common.collect.ImmutableSet;
/**
* Akarin Changes Note
* 1) Reverts Paper's changes to ensure event order (safety issue)
*/
/**
* Handles all plugin management from the Server
*/
public final class SimplePluginManager implements PluginManager {
private final Server server;
private final Map<Pattern, PluginLoader> fileAssociations = new HashMap<Pattern, PluginLoader>();
private final List<Plugin> plugins = new ArrayList<Plugin>();
private final Map<String, Plugin> lookupNames = new HashMap<String, Plugin>();
private File updateDirectory;
private final SimpleCommandMap commandMap;
private final Map<String, Permission> permissions = new HashMap<String, Permission>();
private final Map<Boolean, Set<Permission>> defaultPerms = new LinkedHashMap<Boolean, Set<Permission>>();
private final Map<String, Map<Permissible, Boolean>> permSubs = new HashMap<String, Map<Permissible, Boolean>>();
private final Map<Boolean, Map<Permissible, Boolean>> defSubs = new HashMap<Boolean, Map<Permissible, Boolean>>();
private boolean useTimings = false;
public SimplePluginManager(Server instance, SimpleCommandMap commandMap) {
server = instance;
this.commandMap = commandMap;
defaultPerms.put(true, new HashSet<Permission>());
defaultPerms.put(false, new HashSet<Permission>());
}
/**
* Registers the specified plugin loader
*
* @param loader Class name of the PluginLoader to register
* @throws IllegalArgumentException Thrown when the given Class is not a
* valid PluginLoader
*/
public void registerInterface(Class<? extends PluginLoader> loader) throws IllegalArgumentException {
PluginLoader instance;
if (PluginLoader.class.isAssignableFrom(loader)) {
Constructor<? extends PluginLoader> constructor;
try {
constructor = loader.getConstructor(Server.class);
instance = constructor.newInstance(server);
} catch (NoSuchMethodException ex) {
String className = loader.getName();
throw new IllegalArgumentException(String.format("Class %s does not have a public %s(Server) constructor", className, className), ex);
} catch (Exception ex) {
throw new IllegalArgumentException(String.format("Unexpected exception %s while attempting to construct a new instance of %s", ex.getClass().getName(), loader.getName()), ex);
}
} else {
throw new IllegalArgumentException(String.format("Class %s does not implement interface PluginLoader", loader.getName()));
}
Pattern[] patterns = instance.getPluginFileFilters();
synchronized (this) {
for (Pattern pattern : patterns) {
fileAssociations.put(pattern, instance);
}
}
}
/**
* Loads the plugins contained within the specified directory
*
* @param directory Directory to check for plugins
* @return A list of all plugins loaded
*/
public Plugin[] loadPlugins(File directory) {
Validate.notNull(directory, "Directory cannot be null");
Validate.isTrue(directory.isDirectory(), "Directory must be a directory");
List<Plugin> result = new ArrayList<Plugin>();
Set<Pattern> filters = fileAssociations.keySet();
if (!(server.getUpdateFolder().equals(""))) {
updateDirectory = new File(directory, server.getUpdateFolder());
}
Map<String, File> plugins = new HashMap<String, File>();
Set<String> loadedPlugins = new HashSet<String>();
Map<String, Collection<String>> dependencies = new HashMap<String, Collection<String>>();
Map<String, Collection<String>> softDependencies = new HashMap<String, Collection<String>>();
// This is where it figures out all possible plugins
for (File file : directory.listFiles()) {
PluginLoader loader = null;
for (Pattern filter : filters) {
Matcher match = filter.matcher(file.getName());
if (match.find()) {
loader = fileAssociations.get(filter);
}
}
if (loader == null) continue;
PluginDescriptionFile description = null;
try {
description = loader.getPluginDescription(file);
String name = description.getName();
if (name.equalsIgnoreCase("bukkit") || name.equalsIgnoreCase("minecraft") || name.equalsIgnoreCase("mojang")) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': Restricted Name");
continue;
} else if (description.rawName.indexOf(' ') != -1) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': uses the space-character (0x20) in its name");
continue;
}
} catch (InvalidDescriptionException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
continue;
}
File replacedFile = plugins.put(description.getName(), file);
if (replacedFile != null) {
server.getLogger().severe(String.format(
"Ambiguous plugin name `%s' for files `%s' and `%s' in `%s'",
description.getName(),
file.getPath(),
replacedFile.getPath(),
directory.getPath()
));
}
Collection<String> softDependencySet = description.getSoftDepend();
if (softDependencySet != null && !softDependencySet.isEmpty()) {
if (softDependencies.containsKey(description.getName())) {
// Duplicates do not matter, they will be removed together if applicable
softDependencies.get(description.getName()).addAll(softDependencySet);
} else {
softDependencies.put(description.getName(), new LinkedList<String>(softDependencySet));
}
}
Collection<String> dependencySet = description.getDepend();
if (dependencySet != null && !dependencySet.isEmpty()) {
dependencies.put(description.getName(), new LinkedList<String>(dependencySet));
}
Collection<String> loadBeforeSet = description.getLoadBefore();
if (loadBeforeSet != null && !loadBeforeSet.isEmpty()) {
for (String loadBeforeTarget : loadBeforeSet) {
if (softDependencies.containsKey(loadBeforeTarget)) {
softDependencies.get(loadBeforeTarget).add(description.getName());
} else {
// softDependencies is never iterated, so 'ghost' plugins aren't an issue
Collection<String> shortSoftDependency = new LinkedList<String>();
shortSoftDependency.add(description.getName());
softDependencies.put(loadBeforeTarget, shortSoftDependency);
}
}
}
}
while (!plugins.isEmpty()) {
boolean missingDependency = true;
Iterator<Map.Entry<String, File>> pluginIterator = plugins.entrySet().iterator();
while (pluginIterator.hasNext()) {
Map.Entry<String, File> entry = pluginIterator.next();
String plugin = entry.getKey();
if (dependencies.containsKey(plugin)) {
Iterator<String> dependencyIterator = dependencies.get(plugin).iterator();
while (dependencyIterator.hasNext()) {
String dependency = dependencyIterator.next();
// Dependency loaded
if (loadedPlugins.contains(dependency)) {
dependencyIterator.remove();
// We have a dependency not found
} else if (!plugins.containsKey(dependency)) {
missingDependency = false;
pluginIterator.remove();
softDependencies.remove(plugin);
dependencies.remove(plugin);
server.getLogger().log(
Level.SEVERE,
"Could not load '" + entry.getValue().getPath() + "' in folder '" + directory.getPath() + "'",
new UnknownDependencyException(dependency));
break;
}
}
if (dependencies.containsKey(plugin) && dependencies.get(plugin).isEmpty()) {
dependencies.remove(plugin);
}
}
if (softDependencies.containsKey(plugin)) {
Iterator<String> softDependencyIterator = softDependencies.get(plugin).iterator();
while (softDependencyIterator.hasNext()) {
String softDependency = softDependencyIterator.next();
// Soft depend is no longer around
if (!plugins.containsKey(softDependency)) {
softDependencyIterator.remove();
}
}
if (softDependencies.get(plugin).isEmpty()) {
softDependencies.remove(plugin);
}
}
if (!(dependencies.containsKey(plugin) || softDependencies.containsKey(plugin)) && plugins.containsKey(plugin)) {
// We're clear to load, no more soft or hard dependencies left
File file = plugins.get(plugin);
pluginIterator.remove();
missingDependency = false;
try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
continue;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
}
}
}
if (missingDependency) {
// We now iterate over plugins until something loads
// This loop will ignore soft dependencies
pluginIterator = plugins.entrySet().iterator();
while (pluginIterator.hasNext()) {
Map.Entry<String, File> entry = pluginIterator.next();
String plugin = entry.getKey();
if (!dependencies.containsKey(plugin)) {
softDependencies.remove(plugin);
missingDependency = false;
File file = entry.getValue();
pluginIterator.remove();
try {
result.add(loadPlugin(file));
loadedPlugins.add(plugin);
break;
} catch (InvalidPluginException ex) {
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "'", ex);
}
}
}
// We have no plugins left without a depend
if (missingDependency) {
softDependencies.clear();
dependencies.clear();
Iterator<File> failedPluginIterator = plugins.values().iterator();
while (failedPluginIterator.hasNext()) {
File file = failedPluginIterator.next();
failedPluginIterator.remove();
server.getLogger().log(Level.SEVERE, "Could not load '" + file.getPath() + "' in folder '" + directory.getPath() + "': circular dependency detected");
}
}
}
}
return result.toArray(new Plugin[result.size()]);
}
/**
* Loads the plugin in the specified file
* <p>
* File must be valid according to the current enabled Plugin interfaces
*
* @param file File containing the plugin to load
* @return The Plugin loaded, or null if it was invalid
* @throws InvalidPluginException Thrown when the specified file is not a
* valid plugin
* @throws UnknownDependencyException If a required dependency could not
* be found
*/
public synchronized Plugin loadPlugin(File file) throws InvalidPluginException, UnknownDependencyException {
Validate.notNull(file, "File cannot be null");
checkUpdate(file);
Set<Pattern> filters = fileAssociations.keySet();
Plugin result = null;
for (Pattern filter : filters) {
String name = file.getName();
Matcher match = filter.matcher(name);
if (match.find()) {
PluginLoader loader = fileAssociations.get(filter);
result = loader.loadPlugin(file);
}
}
if (result != null) {
plugins.add(result);
lookupNames.put(result.getDescription().getName().toLowerCase(java.util.Locale.ENGLISH), result); // Spigot
}
return result;
}
private void checkUpdate(File file) {
if (updateDirectory == null || !updateDirectory.isDirectory()) {
return;
}
File updateFile = new File(updateDirectory, file.getName());
if (updateFile.isFile() && FileUtil.copy(updateFile, file)) {
updateFile.delete();
}
}
/**
* Checks if the given plugin is loaded and returns it when applicable
* <p>
* Please note that the name of the plugin is case-sensitive
*
* @param name Name of the plugin to check
* @return Plugin if it exists, otherwise null
*/
public synchronized Plugin getPlugin(String name) {
return lookupNames.get(name.replace(' ', '_').toLowerCase(java.util.Locale.ENGLISH)); // Spigot
}
public synchronized Plugin[] getPlugins() {
return plugins.toArray(new Plugin[plugins.size()]);
}
/**
* Checks if the given plugin is enabled or not
* <p>
* Please note that the name of the plugin is case-sensitive.
*
* @param name Name of the plugin to check
* @return true if the plugin is enabled, otherwise false
*/
public boolean isPluginEnabled(String name) {
Plugin plugin = getPlugin(name);
return isPluginEnabled(plugin);
}
/**
* Checks if the given plugin is enabled or not
*
* @param plugin Plugin to check
* @return true if the plugin is enabled, otherwise false
*/
public synchronized boolean isPluginEnabled(Plugin plugin) { // Paper - synchronize
if ((plugin != null) && (plugins.contains(plugin))) {
return plugin.isEnabled();
} else {
return false;
}
}
public synchronized void enablePlugin(final Plugin plugin) { // Paper - synchronize
if (!plugin.isEnabled()) {
List<Command> pluginCommands = PluginCommandYamlParser.parse(plugin);
if (!pluginCommands.isEmpty()) {
commandMap.registerAll(plugin.getDescription().getName(), pluginCommands);
}
try {
plugin.getPluginLoader().enablePlugin(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while enabling "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin);
}
HandlerList.bakeAll();
}
}
// Paper start - close Classloader on disable
public void disablePlugins() {
disablePlugins(false);
}
public void disablePlugins(boolean closeClassloaders) {
// Paper end - close Classloader on disable
Plugin[] plugins = getPlugins();
for (int i = plugins.length - 1; i >= 0; i--) {
disablePlugin(plugins[i], closeClassloaders); // Paper - close Classloader on disable
}
}
// Paper start - close Classloader on disable
public void disablePlugin(Plugin plugin) {
disablePlugin(plugin, false);
}
public synchronized void disablePlugin(final Plugin plugin, boolean closeClassloader) { // Paper - synchronize
// Paper end - close Classloader on disable
if (plugin.isEnabled()) {
try {
plugin.getPluginLoader().disablePlugin(plugin, closeClassloader); // Paper - close Classloader on disable
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while disabling "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getScheduler().cancelTasks(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while cancelling tasks for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getServicesManager().unregisterAll(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering services for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
HandlerList.unregisterAll(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering events for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
try {
server.getMessenger().unregisterIncomingPluginChannel(plugin);
server.getMessenger().unregisterOutgoingPluginChannel(plugin);
} catch (Throwable ex) {
handlePluginException("Error occurred (in the plugin loader) while unregistering plugin channels for "
+ plugin.getDescription().getFullName() + " (Is it up to date?)", ex, plugin); // Paper
}
}
}
// Paper start
private void handlePluginException(String msg, Throwable ex, Plugin plugin) {
server.getLogger().log(Level.SEVERE, msg, ex);
callEvent(new ServerExceptionEvent(new ServerPluginEnableDisableException(msg, ex, plugin)));
}
// Paper end
public void clearPlugins() {
synchronized (this) {
disablePlugins(true); // Paper - close Classloader on disable
plugins.clear();
lookupNames.clear();
HandlerList.unregisterAll();
fileAssociations.clear();
permissions.clear();
defaultPerms.get(true).clear();
defaultPerms.get(false).clear();
}
}
/**
* Calls an event with the given details.
* <p>
* This method only synchronizes when the event is not asynchronous.
*
* @param event Event details
*/
public void callEvent(Event event) {
if (event.isAsynchronous()) {
if (Thread.holdsLock(this)) {
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from inside synchronized code.");
}
if (server.isPrimaryThread()) {
throw new IllegalStateException(event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
}
fireEvent(event);
} else {
synchronized (this) {
fireEvent(event);
}
}
}
private void fireEvent(Event event) {
HandlerList handlers = event.getHandlers();
RegisteredListener[] listeners = handlers.getRegisteredListeners();
for (RegisteredListener registration : listeners) {
if (!registration.getPlugin().isEnabled()) {
continue;
}
try {
registration.callEvent(event);
} catch (AuthorNagException ex) {
Plugin plugin = registration.getPlugin();
if (plugin.isNaggable()) {
plugin.setNaggable(false);
server.getLogger().log(Level.SEVERE, String.format(
"Nag author(s): '%s' of '%s' about the following: %s",
plugin.getDescription().getAuthors(),
plugin.getDescription().getFullName(),
ex.getMessage()
));
}
} catch (Throwable ex) {
// Paper start - error reporting
String msg = "Could not pass event " + event.getEventName() + " to " + registration.getPlugin().getDescription().getFullName();
server.getLogger().log(Level.SEVERE, msg, ex);
if (!(event instanceof ServerExceptionEvent)) { // We don't want to cause an endless event loop
callEvent(new ServerExceptionEvent(new ServerEventException(msg, ex, registration.getPlugin(), registration.getListener(), event)));
}
// Paper end
}
}
}
public void registerEvents(Listener listener, Plugin plugin) {
if (!plugin.isEnabled()) {
throw new IllegalPluginAccessException("Plugin attempted to register " + listener + " while not enabled");
}
for (Map.Entry<Class<? extends Event>, Set<RegisteredListener>> entry : plugin.getPluginLoader().createRegisteredListeners(listener, plugin).entrySet()) {
getEventListeners(getRegistrationClass(entry.getKey())).registerAll(entry.getValue());
}
}
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin) {
registerEvent(event, listener, priority, executor, plugin, false);
}
/**
* Registers the given event to the specified listener using a directly
* passed EventExecutor
*
* @param event Event class to register
* @param listener PlayerListener to register
* @param priority Priority of this event
* @param executor EventExecutor to register
* @param plugin Plugin to register
* @param ignoreCancelled Do not call executor if event was already
* cancelled
*/
public void registerEvent(Class<? extends Event> event, Listener listener, EventPriority priority, EventExecutor executor, Plugin plugin, boolean ignoreCancelled) {
Validate.notNull(listener, "Listener cannot be null");
Validate.notNull(priority, "Priority cannot be null");
Validate.notNull(executor, "Executor cannot be null");
Validate.notNull(plugin, "Plugin cannot be null");
if (!plugin.isEnabled()) {
throw new IllegalPluginAccessException("Plugin attempted to register " + event + " while not enabled");
}
executor = new co.aikar.timings.TimedEventExecutor(executor, plugin, null, event); // Spigot
if (false) { // Spigot - RL handles useTimings check now
getEventListeners(event).register(new TimedRegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
} else {
getEventListeners(event).register(new RegisteredListener(listener, executor, priority, plugin, ignoreCancelled));
}
}
private HandlerList getEventListeners(Class<? extends Event> type) {
try {
Method method = getRegistrationClass(type).getDeclaredMethod("getHandlerList");
method.setAccessible(true);
return (HandlerList) method.invoke(null);
} catch (Exception e) {
throw new IllegalPluginAccessException(e.toString());
}
}
private Class<? extends Event> getRegistrationClass(Class<? extends Event> clazz) {
try {
clazz.getDeclaredMethod("getHandlerList");
return clazz;
} catch (NoSuchMethodException e) {
if (clazz.getSuperclass() != null
&& !clazz.getSuperclass().equals(Event.class)
&& Event.class.isAssignableFrom(clazz.getSuperclass())) {
return getRegistrationClass(clazz.getSuperclass().asSubclass(Event.class));
} else {
throw new IllegalPluginAccessException("Unable to find handler list for event " + clazz.getName() + ". Static getHandlerList method required!");
}
}
}
public Permission getPermission(String name) {
return permissions.get(name.toLowerCase(java.util.Locale.ENGLISH));
}
public void addPermission(Permission perm) {
addPermission(perm, true);
}
@Deprecated
public void addPermission(Permission perm, boolean dirty) {
String name = perm.getName().toLowerCase(java.util.Locale.ENGLISH);
if (permissions.containsKey(name)) {
throw new IllegalArgumentException("The permission " + name + " is already defined!");
}
permissions.put(name, perm);
calculatePermissionDefault(perm, dirty);
}
public Set<Permission> getDefaultPermissions(boolean op) {
return ImmutableSet.copyOf(defaultPerms.get(op));
}
public void removePermission(Permission perm) {
removePermission(perm.getName());
}
public void removePermission(String name) {
permissions.remove(name.toLowerCase(java.util.Locale.ENGLISH));
}
public void recalculatePermissionDefaults(Permission perm) {
if (perm != null && permissions.containsKey(perm.getName().toLowerCase(java.util.Locale.ENGLISH))) {
defaultPerms.get(true).remove(perm);
defaultPerms.get(false).remove(perm);
calculatePermissionDefault(perm, true);
}
}
private void calculatePermissionDefault(Permission perm, boolean dirty) {
if ((perm.getDefault() == PermissionDefault.OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
defaultPerms.get(true).add(perm);
if (dirty) {
dirtyPermissibles(true);
}
}
if ((perm.getDefault() == PermissionDefault.NOT_OP) || (perm.getDefault() == PermissionDefault.TRUE)) {
defaultPerms.get(false).add(perm);
if (dirty) {
dirtyPermissibles(false);
}
}
}
@Deprecated
public void dirtyPermissibles() {
dirtyPermissibles(true);
dirtyPermissibles(false);
}
private void dirtyPermissibles(boolean op) {
Set<Permissible> permissibles = getDefaultPermSubscriptions(op);
for (Permissible p : permissibles) {
p.recalculatePermissions();
}
}
public void subscribeToPermission(String permission, Permissible permissible) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map == null) {
map = new WeakHashMap<Permissible, Boolean>();
permSubs.put(name, map);
}
map.put(permissible, true);
}
public void unsubscribeFromPermission(String permission, Permissible permissible) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map != null) {
map.remove(permissible);
if (map.isEmpty()) {
permSubs.remove(name);
}
}
}
public Set<Permissible> getPermissionSubscriptions(String permission) {
String name = permission.toLowerCase(java.util.Locale.ENGLISH);
Map<Permissible, Boolean> map = permSubs.get(name);
if (map == null) {
return ImmutableSet.of();
} else {
return ImmutableSet.copyOf(map.keySet());
}
}
public void subscribeToDefaultPerms(boolean op, Permissible permissible) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map == null) {
map = new WeakHashMap<Permissible, Boolean>();
defSubs.put(op, map);
}
map.put(permissible, true);
}
public void unsubscribeFromDefaultPerms(boolean op, Permissible permissible) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map != null) {
map.remove(permissible);
if (map.isEmpty()) {
defSubs.remove(op);
}
}
}
public Set<Permissible> getDefaultPermSubscriptions(boolean op) {
Map<Permissible, Boolean> map = defSubs.get(op);
if (map == null) {
return ImmutableSet.of();
} else {
return ImmutableSet.copyOf(map.keySet());
}
}
public Set<Permission> getPermissions() {
return new HashSet<Permission>(permissions.values());
}
public boolean useTimings() {
return co.aikar.timings.Timings.isTimingsEnabled(); // Spigot
}
/**
* Sets whether or not per event timing code should be used
*
* @param use True if per event timing code should be used
*/
public void useTimings(boolean use) {
co.aikar.timings.Timings.setTimingsEnabled(use); // Spigot
}
// Paper start
public void clearPermissions() {
permissions.clear();
defaultPerms.get(true).clear();
defaultPerms.get(false).clear();
}
// Paper end
}

View File

@@ -0,0 +1,405 @@
package org.spigotmc;
import com.google.common.base.Throwables;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import gnu.trove.map.hash.TObjectIntHashMap;
import net.minecraft.server.AttributeRanged;
import net.minecraft.server.GenericAttributes;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.StatisticList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
public class SpigotConfig
{
private static File CONFIG_FILE;
private static final String HEADER = "This is the main configuration file for Spigot.\n"
+ "As you can see, there's tons to configure. Some options may impact gameplay, so use\n"
+ "with caution, and make sure you know what each option does before configuring.\n"
+ "For a reference for any variable inside this file, check out the Spigot wiki at\n"
+ "http://www.spigotmc.org/wiki/spigot-configuration/\n"
+ "\n"
+ "If you need help with the configuration or have any questions related to Spigot,\n"
+ "join us at the IRC or drop by our forums and leave a post.\n"
+ "\n"
+ "IRC: #spigot @ irc.spi.gt ( http://www.spigotmc.org/pages/irc/ )\n"
+ "Forums: http://www.spigotmc.org/\n";
/*========================================================================*/
public static YamlConfiguration config;
static int version;
static Map<String, Command> commands;
/*========================================================================*/
private static Metrics metrics;
public static void init(File configFile)
{
CONFIG_FILE = configFile;
config = new YamlConfiguration();
try
{
config.load( CONFIG_FILE );
} catch ( IOException ex )
{
} catch ( InvalidConfigurationException ex )
{
Bukkit.getLogger().log( Level.SEVERE, "Could not load spigot.yml, please correct your syntax errors", ex );
throw Throwables.propagate( ex );
}
config.options().header( HEADER );
config.options().copyDefaults( true );
commands = new HashMap<String, Command>();
commands.put( "spigot", new SpigotCommand( "spigot" ) );
version = getInt( "config-version", 11 );
set( "config-version", 11 );
readConfig( SpigotConfig.class, null );
}
public static void registerCommands()
{
for ( Map.Entry<String, Command> entry : commands.entrySet() )
{
MinecraftServer.getServer().server.getCommandMap().register( entry.getKey(), "Spigot", entry.getValue() );
}
/* // Paper - Replace with our own
if ( metrics == null )
{
try
{
metrics = new Metrics();
metrics.start();
} catch ( IOException ex )
{
Bukkit.getServer().getLogger().log( Level.SEVERE, "Could not start metrics service", ex );
}
}
*/ // Paper end
}
static void readConfig(Class<?> clazz, Object instance)
{
for ( Method method : clazz.getDeclaredMethods() )
{
if ( Modifier.isPrivate( method.getModifiers() ) )
{
if ( method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE )
{
try
{
method.setAccessible( true );
method.invoke( instance );
} catch ( InvocationTargetException ex )
{
throw Throwables.propagate( ex.getCause() );
} catch ( Exception ex )
{
Bukkit.getLogger().log( Level.SEVERE, "Error invoking " + method, ex );
}
}
}
}
try
{
config.save( CONFIG_FILE );
} catch ( IOException ex )
{
Bukkit.getLogger().log( Level.SEVERE, "Could not save " + CONFIG_FILE, ex );
}
}
private static void set(String path, Object val)
{
config.set( path, val );
}
private static boolean getBoolean(String path, boolean def)
{
config.addDefault( path, def );
return config.getBoolean( path, config.getBoolean( path ) );
}
private static int getInt(String path, int def)
{
config.addDefault( path, def );
return config.getInt( path, config.getInt( path ) );
}
private static <T> List getList(String path, T def)
{
config.addDefault( path, def );
return (List<T>) config.getList( path, config.getList( path ) );
}
private static String getString(String path, String def)
{
config.addDefault( path, def );
return config.getString( path, config.getString( path ) );
}
private static double getDouble(String path, double def)
{
config.addDefault( path, def );
return config.getDouble( path, config.getDouble( path ) );
}
public static boolean logCommands;
private static void logCommands()
{
logCommands = getBoolean( "commands.log", true );
}
public static int tabComplete;
private static void tabComplete()
{
if ( version < 6 )
{
boolean oldValue = getBoolean( "commands.tab-complete", true );
if ( oldValue )
{
set( "commands.tab-complete", 0 );
} else
{
set( "commands.tab-complete", -1 );
}
}
tabComplete = getInt( "commands.tab-complete", 0 );
}
public static String whitelistMessage;
public static String unknownCommandMessage;
public static String serverFullMessage;
public static String outdatedClientMessage = "Outdated client! Please use {0}";
public static String outdatedServerMessage = "Outdated server! I\'m still on {0}";
private static String transform(String s)
{
return ChatColor.translateAlternateColorCodes( '&', s ).replaceAll( "\\\\n", "\n" );
}
private static void messages()
{
if (version < 8)
{
set( "messages.outdated-client", outdatedClientMessage );
set( "messages.outdated-server", outdatedServerMessage );
}
whitelistMessage = transform( getString( "messages.whitelist", "You are not whitelisted on this server!" ) );
unknownCommandMessage = transform( getString( "messages.unknown-command", "Unknown command. Type \"/help\" for help." ) );
serverFullMessage = transform( getString( "messages.server-full", "The server is full!" ) );
outdatedClientMessage = transform( getString( "messages.outdated-client", outdatedClientMessage ) );
outdatedServerMessage = transform( getString( "messages.outdated-server", outdatedServerMessage ) );
}
public static int timeoutTime = 60;
public static boolean restartOnCrash = true;
public static String restartScript = "./start.sh";
public static String restartMessage;
private static void watchdog()
{
timeoutTime = getInt( "settings.timeout-time", timeoutTime );
restartOnCrash = getBoolean( "settings.restart-on-crash", restartOnCrash );
restartScript = getString( "settings.restart-script", restartScript );
restartMessage = transform( getString( "messages.restart", "Server is restarting" ) );
commands.put( "restart", new RestartCommand( "restart" ) );
//WatchdogThread.doStart( timeoutTime, restartOnCrash ); // Paper - moved to PaperConfig
}
public static boolean bungee;
private static void bungee() {
if ( version < 4 )
{
set( "settings.bungeecord", false );
System.out.println( "Oudated config, disabling BungeeCord support!" );
}
bungee = getBoolean( "settings.bungeecord", false );
}
private static void nettyThreads()
{
int count = getInt( "settings.netty-threads", 4 );
System.setProperty( "io.netty.eventLoopThreads", Integer.toString( count ) );
Bukkit.getLogger().log( Level.INFO, "Using {0} threads for Netty based IO", count );
}
public static boolean lateBind;
private static void lateBind() {
lateBind = getBoolean( "settings.late-bind", false );
}
public static boolean disableStatSaving;
public static Object2IntOpenHashMap<String> forcedStats = new Object2IntOpenHashMap<String>();
private static void stats()
{
disableStatSaving = getBoolean( "stats.disable-saving", false );
if ( !config.contains( "stats.forced-stats" ) ) {
config.createSection( "stats.forced-stats" );
}
ConfigurationSection section = config.getConfigurationSection( "stats.forced-stats" );
for ( String name : section.getKeys( true ) )
{
if ( section.isInt( name ) )
{
if ( StatisticList.getStatistic(name) == null )
{
Bukkit.getLogger().log(Level.WARNING, "Ignoring non existent stats.forced-stats " + name);
continue;
}
forcedStats.put( name, section.getInt( name ) );
}
}
}
private static void tpsCommand()
{
commands.put( "tps", new TicksPerSecondCommand( "tps" ) );
}
public static int playerSample;
private static void playerSample()
{
playerSample = Math.max(getInt( "settings.sample-count", 12 ), 0); // Paper - Avoid negative counts
Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger
}
public static int playerShuffle;
private static void playerShuffle()
{
playerShuffle = getInt( "settings.player-shuffle", 0 );
}
public static List<String> spamExclusions;
private static void spamExclusions()
{
spamExclusions = getList( "commands.spam-exclusions", Arrays.asList( new String[]
{
"/skill"
} ) );
}
public static boolean silentCommandBlocks;
private static void silentCommandBlocks()
{
silentCommandBlocks = getBoolean( "commands.silent-commandblock-console", false );
}
public static boolean filterCreativeItems;
private static void filterCreativeItems()
{
filterCreativeItems = getBoolean( "settings.filter-creative-items", true );
}
public static Set<String> replaceCommands;
private static void replaceCommands()
{
if ( config.contains( "replace-commands" ) )
{
set( "commands.replace-commands", config.getStringList( "replace-commands" ) );
config.set( "replace-commands", null );
}
replaceCommands = new HashSet<String>( (List<String>) getList( "commands.replace-commands",
Arrays.asList( "setblock", "summon", "testforblock", "tellraw" ) ) );
}
public static int userCacheCap;
private static void userCacheCap()
{
userCacheCap = getInt( "settings.user-cache-size", 1000 );
}
public static boolean saveUserCacheOnStopOnly;
private static void saveUserCacheOnStopOnly()
{
saveUserCacheOnStopOnly = getBoolean( "settings.save-user-cache-on-stop-only", false );
}
public static int intCacheLimit;
private static void intCacheLimit()
{
intCacheLimit = getInt( "settings.int-cache-limit", 1024 );
}
public static double movedWronglyThreshold;
private static void movedWronglyThreshold()
{
movedWronglyThreshold = getDouble( "settings.moved-wrongly-threshold", 0.0625D );
}
public static double movedTooQuicklyMultiplier;
private static void movedTooQuicklyMultiplier()
{
movedTooQuicklyMultiplier = getDouble( "settings.moved-too-quickly-multiplier", 10.0D );
}
public static double maxHealth = 2048;
public static double movementSpeed = 2048;
public static double attackDamage = 2048;
private static void attributeMaxes()
{
maxHealth = getDouble( "settings.attribute.maxHealth.max", maxHealth );
( (AttributeRanged) GenericAttributes.maxHealth ).b = maxHealth;
movementSpeed = getDouble( "settings.attribute.movementSpeed.max", movementSpeed );
( (AttributeRanged) GenericAttributes.MOVEMENT_SPEED ).b = movementSpeed;
attackDamage = getDouble( "settings.attribute.attackDamage.max", attackDamage );
( (AttributeRanged) GenericAttributes.ATTACK_DAMAGE ).b = attackDamage;
}
public static boolean debug;
private static void debug()
{
debug = getBoolean( "settings.debug", false );
if ( debug && !LogManager.getRootLogger().isTraceEnabled() )
{
// Enable debug logging
LoggerContext ctx = (LoggerContext) LogManager.getContext( false );
Configuration conf = ctx.getConfiguration();
conf.getLoggerConfig( LogManager.ROOT_LOGGER_NAME ).setLevel( org.apache.logging.log4j.Level.ALL );
ctx.updateLoggers( conf );
}
if ( LogManager.getRootLogger().isTraceEnabled() )
{
Bukkit.getLogger().info( "Debug logging is enabled" );
} else
{
Bukkit.getLogger().info( "Debug logging is disabled" );
}
}
public static int itemDirtyTicks;
private static void itemDirtyTicks() {
itemDirtyTicks = getInt("settings.item-dirty-ticks", 20);
}
public static boolean disableAdvancementSaving;
public static List<String> disabledAdvancements;
private static void disabledAdvancements() {
disableAdvancementSaving = getBoolean("advancements.disable-saving", false);
disabledAdvancements = getList("advancements.disabled", Arrays.asList(new String[]{"minecraft:story/disabled"}));
}
}

View File

@@ -19,13 +19,12 @@
"core.MixinCommandKick",
"core.MixinCraftServer",
"core.MixinWorldServer",
"core.MixinFileIOThread",
"core.MixinWorldManager",
"core.MixinCommandBanIp",
"core.MixinChunkSection",
"core.MixinAsyncCatcher",
"core.MixinTimingHandler",
"core.MixinVersionCommand",
"core.MixinItemMonsterEgg",
"core.MixinMinecraftServer",
"core.MixinChunkIOExecutor",
"core.MixinPlayerConnectionUtils",
@@ -41,4 +40,4 @@
"optimization.MixinPersistentCollection",
"optimization.MixinTileEntityEnchantTable"
]
}
}

View File

@@ -1,15 +0,0 @@
{
"required": true,
"minVersion": "0.7.10",
"package": "io.akarin.server.mixin",
"target": "@env(DEFAULT)",
"compatibilityLevel": "JAVA_8",
"server": [
"cps.MixinChunk",
"lighting.MixinChunk",
"lighting.MixinWorld",
"lighting.MixinWorldServer",
"lighting.MixinChunkProviderServer",
]
}