Compare commits

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

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

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

View File

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

@@ -0,0 +1,85 @@
package com.destroystokyo.paper.antixray;
import io.netty.buffer.ByteBuf;
import net.minecraft.server.Chunk;
import net.minecraft.server.DataPalette;
import net.minecraft.server.IBlockData;
import net.minecraft.server.PacketPlayOutMapChunk;
/**
* Akarin Changes Note
* 1) byte[] -> ByteBuf (compatibility)
*/
public class PacketPlayOutMapChunkInfo {
private final PacketPlayOutMapChunk packetPlayOutMapChunk;
private final Chunk chunk;
private final int chunkSectionSelector;
private ByteBuf data; // Akarin
private final int[] bitsPerValue = new int[16];
private final DataPalette[] dataPalettes = new DataPalette[16];
private final int[] dataBitsIndexes = new int[16];
private final IBlockData[][] predefinedBlockData = new IBlockData[16][];
public PacketPlayOutMapChunkInfo(PacketPlayOutMapChunk packetPlayOutMapChunk, Chunk chunk, int chunkSectionSelector) {
this.packetPlayOutMapChunk = packetPlayOutMapChunk;
this.chunk = chunk;
this.chunkSectionSelector = chunkSectionSelector;
}
public PacketPlayOutMapChunk getPacketPlayOutMapChunk() {
return packetPlayOutMapChunk;
}
public Chunk getChunk() {
return chunk;
}
public int getChunkSectionSelector() {
return chunkSectionSelector;
}
public ByteBuf getData() { // Akarin
return data;
}
public void setData(ByteBuf data) { // Akarin
this.data = data;
}
public int getBitsPerValue(int chunkSectionIndex) {
return bitsPerValue[chunkSectionIndex];
}
public void setBitsPerValue(int chunkSectionIndex, int bitsPerValue) {
this.bitsPerValue[chunkSectionIndex] = bitsPerValue;
}
public DataPalette getDataPalette(int chunkSectionIndex) {
return dataPalettes[chunkSectionIndex];
}
public void setDataPalette(int chunkSectionIndex, DataPalette dataPalette) {
dataPalettes[chunkSectionIndex] = dataPalette;
}
public int getDataBitsIndex(int chunkSectionIndex) {
return dataBitsIndexes[chunkSectionIndex];
}
public void setDataBitsIndex(int chunkSectionIndex, int dataBitsIndex) {
dataBitsIndexes[chunkSectionIndex] = dataBitsIndex;
}
public IBlockData[] getPredefinedBlockData(int chunkSectionIndex) {
return predefinedBlockData[chunkSectionIndex];
}
public void setPredefinedBlockData(int chunkSectionIndex, IBlockData[] predefinedBlockData) {
this.predefinedBlockData[chunkSectionIndex] = predefinedBlockData;
}
public boolean isWritten(int chunkSectionIndex) {
return bitsPerValue[chunkSectionIndex] != 0;
}
}

View File

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

View File

@@ -15,25 +15,30 @@ if [ "$2" == "--setup" ] || [ "$3" == "--setup" ] || [ "$4" == "--setup" ]; then
if [ -d "Minecraft" ]; then if [ -d "Minecraft" ]; then
rm Minecraft/ -r rm Minecraft/ -r
fi fi
git clone https://github.com/Akarin-project/Minecraft.git git clone https://github.com/LegacyGamerHD/Minecraft.git
fi fi
cd "$paperbasedir" cd "$paperbasedir"
./paper patch ./paper jar
) )
fi fi
echo "[Akarin] Ready to build" echo "[Akarin] Ready to build"
( (
echo "[Akarin] Touch sources.." echo "[Akarin] Touch sources.."
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
cd "$paperbasedir" cd "$paperbasedir"
if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then
echo "[Akarin] Test has been skipped" echo "[Akarin] Test has been skipped"
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
mvn clean install -DskipTests mvn clean install -DskipTests
else else
rm -rf Paper-API/src
rm -rf Paper-Server/src
./paper patch
\cp -rf "$basedir/sources/src" "$paperbasedir/Paper-Server/"
\cp -rf "$basedir/sources/pom.xml" "$paperbasedir/Paper-Server/"
mvn clean install mvn clean install
fi fi

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

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

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>akarin</artifactId> <artifactId>akarin</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.12.2-R0.1-RELEASE</version> <version>1.12.2-R0.4-SNAPSHOT</version>
<name>Akarin</name> <name>Akarin</name>
<url>https://github.com/Akarin-project/Akarin</url> <url>https://github.com/Akarin-project/Akarin</url>
@@ -30,7 +30,7 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.1.24.Final</version> <version>4.1.78.Final</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -45,6 +45,12 @@
<version>${minecraft.version}-R0.1-SNAPSHOT</version> <version>${minecraft.version}-R0.1-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>minecraft-server</artifactId>
<version>${minecraft.version}-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency> <dependency>
<groupId>net.sf.jopt-simple</groupId> <groupId>net.sf.jopt-simple</groupId>
<artifactId>jopt-simple</artifactId> <artifactId>jopt-simple</artifactId>
@@ -60,7 +66,7 @@
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version> <version>8.0.28</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -69,16 +75,15 @@
<version>3.0.3</version> <version>3.0.3</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.minecrell</groupId> <groupId>net.minecrell</groupId>
<artifactId>terminalconsoleappender</artifactId> <artifactId>terminalconsoleappender</artifactId>
<version>1.0.0</version> <version>1.1.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>net.java.dev.jna</groupId> <groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId> <artifactId>jna</artifactId>
<version>4.4.0</version> <version>4.5.2</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
@@ -91,7 +96,7 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<version>2.8.1</version> <version>2.17.2</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
@@ -99,13 +104,21 @@
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId> <artifactId>log4j-slf4j-impl</artifactId>
<version>2.8.1</version> <version>2.17.2</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-iostreams</artifactId> <artifactId>log4j-iostreams</artifactId>
<version>2.8.1</version> <version>2.17.2</version>
</dependency>
<!-- Paper - Async loggers -->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
<scope>runtime</scope>
</dependency> </dependency>
<!-- testing --> <!-- testing -->
@@ -126,34 +139,68 @@
<dependency> <dependency>
<groupId>io.akarin</groupId> <groupId>io.akarin</groupId>
<artifactId>legacylauncher</artifactId> <artifactId>legacylauncher</artifactId>
<version>1.20</version> <version>1.26</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.spongepowered</groupId> <groupId>org.spongepowered</groupId>
<artifactId>mixin</artifactId> <artifactId>mixin</artifactId>
<version>0.7.8-SNAPSHOT</version> <version>0.7.11-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.googlecode.concurrent-locks</groupId>
<artifactId>concurrent-locks</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version>
</dependency> </dependency>
</dependencies> </dependencies>
<repositories> <repositories>
<repository> <repository>
<id>spigotmc-public</id> <id>elmakers-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url> <url>http://maven.elmakers.com/repository/</url>
</repository>
<repository>
<id>akarin-repo</id>
<url>https://raw.githubusercontent.com/Akarin-project/akarin-repo/master/repository</url>
</repository> </repository>
<repository> <repository>
<id>spongepowered-repo</id> <id>spongepowered-repo</id>
<url>https://repo.spongepowered.org/maven/</url> <url>https://repo.spongepowered.org/maven/</url>
</repository> </repository>
<repository>
<id>nallar-repo</id>
<url>http://repo.nallar.me/</url>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>josephworks</id>
<url>http://repo.josephworks.net/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype-nexusg</id>
<url>https://oss.sonatype.org/content/repositories</url>
</repository>
<repository>
<id>papermc</id>
<url>https://papermc.io/repo/repository/maven-public/</url>
</repository>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories> </repositories>
<pluginRepositories> <pluginRepositories>
<pluginRepository> <pluginRepository>
<id>spigotmc-public</id> <id>paper</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url> <url>https://papermc.io/repo/repository/maven-public/</url>
</pluginRepository> </pluginRepository>
</pluginRepositories> </pluginRepositories>
@@ -168,7 +215,7 @@
<version>1.3</version> <version>1.3</version>
<configuration> <configuration>
<outputPrefix>git-Akarin-</outputPrefix> <outputPrefix>git-Akarin-</outputPrefix>
<scmDirectory>..</scmDirectory> <scmDirectory>../..</scmDirectory> <!-- Akarin -->
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
@@ -194,6 +241,7 @@
<Specification-Title>Bukkit</Specification-Title> <Specification-Title>Bukkit</Specification-Title>
<Specification-Version>${api.version}</Specification-Version> <Specification-Version>${api.version}</Specification-Version>
<Specification-Vendor>Bukkit Team</Specification-Vendor> <Specification-Vendor>Bukkit Team</Specification-Vendor>
<Multi-Release>true</Multi-Release> <!-- Paper start - update log4j -->
</manifestEntries> </manifestEntries>
<manifestSections> <manifestSections>
<manifestSection> <manifestSection>
@@ -221,7 +269,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId> <artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version> <version>3.2.4</version>
<executions> <executions>
<execution> <execution>
<phase>package</phase> <phase>package</phase>
@@ -230,6 +278,25 @@
</goals> </goals>
<configuration> <configuration>
<dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper --> <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation> <!-- Paper -->
<filters>
<filter>
<artifact>org.spigotmc:minecraft-server:**</artifact>
<excludes>
<exclude>io/netty/**</exclude>
<exclude>org/apache/logging/log4j/**</exclude>
</excludes>
</filter>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<relocations> <relocations>
<!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 --> <!-- Paper - Workaround for hardcoded path lookup in dependency, easier than forking it - GH-189 -->
<!--<relocation>--> <!--<relocation>-->
@@ -261,27 +328,16 @@
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/java.sql.Driver</resource> <resource>META-INF/services/java.sql.Driver</resource>
</transformer> </transformer>
<transformer implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer" /> <transformer implementation="io.github.edwgiz.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer" />
</transformers> </transformers>
<!-- Akarin - Avoid signature failure -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.github.edwgiz</groupId> <groupId>io.github.edwgiz</groupId>
<artifactId>maven-shade-plugin.log4j2-cachefile-transformer</artifactId> <artifactId>log4j-maven-shade-plugin-extensions</artifactId>
<version>2.8.1</version> <version>2.17.2</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
@@ -289,6 +345,12 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version> <version>3.7.0</version>
<configuration>
<excludes>
<exclude>org/spigotmc/CaseInsensitiveHashingStrategy.java</exclude>
<exclude>org/spigotmc/CaseInsensitiveMap.java</exclude>
</excludes>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>

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

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

@@ -1,77 +0,0 @@
package io.akarin.api;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.Queues;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
public abstract class Akari {
/**
* A common logger used by mixin classes
*/
public final static Logger logger = LogManager.getLogger("Akarin");
/**
* Temporarily disable desync timings error, moreover it's worthless to trace async operation
*/
public static volatile boolean silentTiming;
/**
* A common thread pool factory
*/
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Schedule Thread - %1$d").build();
/**
* Main thread callback tasks
*/
public static final Queue<Runnable> callbackQueue = Queues.newConcurrentLinkedQueue();
/**
* A common tick pool
*/
public static final ExecutorCompletionService<Void> STAGE_TICK = new ExecutorCompletionService<Void>(Executors.newFixedThreadPool(1, Akari.STAGE_FACTORY));
/*
* The unsafe
*/
public final static sun.misc.Unsafe UNSAFE = getUnsafe();
private static sun.misc.Unsafe getUnsafe() {
try {
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (sun.misc.Unsafe) theUnsafe.get(null);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
/*
* Timings
*/
public final static Timing worldTiming = getTiming("Akarin - World");
public final static Timing callbackTiming = getTiming("Akarin - Callback");
private static Timing getTiming(String name) {
try {
Method ofSafe = Timings.class.getDeclaredMethod("ofSafe", String.class);
ofSafe.setAccessible(true);
return (Timing) ofSafe.invoke(null, name);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
}

View File

@@ -0,0 +1,161 @@
package io.akarin.api.internal;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.google.common.collect.Queues;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import co.aikar.timings.Timing;
import co.aikar.timings.Timings;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.Akari.TimingSignal;
import io.akarin.api.internal.utils.ReentrantSpinningLock;
import io.akarin.api.internal.utils.thread.SuspendableExecutorCompletionService;
import io.akarin.api.internal.utils.thread.SuspendableThreadPoolExecutor;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
import net.minecraft.server.WorldServer;
@SuppressWarnings("restriction")
public abstract class Akari {
/**
* A common logger used by mixin classes
*/
public final static Logger logger = LogManager.getLogger("Akarin");
/**
* A common thread pool factory
*/
public static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Akarin Parallel Registry Thread - %1$d").build();
/**
* Main thread callback tasks
*/
public static final Queue<Runnable> callbackQueue = Queues.newConcurrentLinkedQueue();
public static class AssignableThread extends Thread {
public AssignableThread(Runnable run) {
super(run);
}
public AssignableThread() {
super();
}
}
public static class AssignableFactory implements ThreadFactory {
private final String threadName;
private int threadNumber;
public AssignableFactory(String name) {
threadName = name;
}
@Override
public Thread newThread(Runnable run) {
Thread thread = new AssignableThread(run);
thread.setName(StringUtils.replaceChars(threadName, "$", String.valueOf(threadNumber++)));
thread.setPriority(AkarinGlobalConfig.primaryThreadPriority); // Fair
return thread;
}
}
public static class TimingSignal {
public final World tickedWorld;
public final boolean isEntities;
public TimingSignal(World world, boolean entities) {
tickedWorld = world;
isEntities = entities;
}
}
public static SuspendableExecutorCompletionService<TimingSignal> STAGE_TICK;
static {
resizeTickExecutors(3);
}
public static void resizeTickExecutors(int worlds) {
int parallelism;
switch (AkarinGlobalConfig.parallelMode) {
case -1:
return;
case 0:
parallelism = 2;
break;
case 1:
parallelism = worlds + 1;
break;
case 2:
default:
parallelism = worlds * 2;
break;
}
STAGE_TICK = new SuspendableExecutorCompletionService<>(new SuspendableThreadPoolExecutor(parallelism, parallelism,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
new AssignableFactory("Akarin Parallel Ticking Thread - $")));
}
public static boolean isPrimaryThread() {
return isPrimaryThread(true);
}
public static boolean isPrimaryThread(boolean assign) {
Thread current = Thread.currentThread();
return current == MinecraftServer.getServer().primaryThread || (assign ? (current.getClass() == AssignableThread.class) : false);
}
public static final String EMPTY_STRING = "";
/*
* The unsafe
*/
public final static sun.misc.Unsafe UNSAFE = getUnsafe();
private static sun.misc.Unsafe getUnsafe() {
try {
Field theUnsafe = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
return (sun.misc.Unsafe) theUnsafe.get(null);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
private static final String serverVersion = Akari.class.getPackage().getImplementationVersion();
public static String getServerVersion() {
return serverVersion + " (MC: " + MinecraftServer.getServer().getVersion() + ")";
}
/*
* Timings
*/
public final static Timing worldTiming = getTiming("Akarin - Full World Tick");
public final static Timing callbackTiming = getTiming("Akarin - Callback Queue");
private static Timing getTiming(String name) {
try {
Method ofSafe = Timings.class.getDeclaredMethod("ofSafe", String.class);
ofSafe.setAccessible(true);
return (Timing) ofSafe.invoke(null, name);
} catch (Throwable t) {
t.printStackTrace();
return null;
}
}
}

View File

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

View File

@@ -0,0 +1,30 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.api.internal.mixin;
public interface IMixinRealTimeTicking {
long getRealTimeTicks();
}

View File

@@ -0,0 +1,8 @@
package io.akarin.api.internal.mixin;
import java.util.Random;
public interface IMixinWorldServer {
public Object lock();
public Random rand();
}

View File

@@ -33,7 +33,7 @@
* at http://creativecommons.org/publicdomain/zero/1.0/ * at http://creativecommons.org/publicdomain/zero/1.0/
*/ */
package io.akarin.api; package io.akarin.api.internal.utils;
import java.util.AbstractQueue; import java.util.AbstractQueue;
import java.util.ArrayList; import java.util.ArrayList;
@@ -45,6 +45,8 @@ import java.util.Spliterator;
import java.util.Spliterators; import java.util.Spliterators;
import java.util.function.Consumer; import java.util.function.Consumer;
import io.akarin.api.internal.Akari;
/** /**
* An unbounded thread-safe {@linkplain Queue queue} based on linked nodes. * An unbounded thread-safe {@linkplain Queue queue} based on linked nodes.
* This queue orders elements FIFO (first-in-first-out). * This queue orders elements FIFO (first-in-first-out).

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

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

View File

@@ -0,0 +1,240 @@
/*
Written in 2015 by Sebastiano Vigna (vigna@acm.org)
To the extent possible under law, the author has dedicated all copyright
and related and neighboring rights to this software to the public domain
worldwide. This software is distributed without any warranty.
See <http://creativecommons.org/publicdomain/zero/1.0/>. */
package io.akarin.api.internal.utils.random;
/**
* This is a SplittableRandom-style generator, meant to have a tiny state
* that permits storing many different generators with low overhead.
* It should be rather fast, though no guarantees can be made on all hardware.
* <br>
* Benchmarking on a Windows laptop with an i7-4700MQ processor running OpenJDK 8
* reports generation of 64-bit random long output as 17.8x faster than generating
* an equivalent number of random longs with java.util.Random, and generation of
* 32-bit random int output as 9.8x faster. Specifically, generating 1 billion longs
* took about 1.28 nanoseconds per long (1.277 seconds for the whole group) with
* LightRNG, while java.util.Random (which is meant to produce int, to be fair) took
* about 22.8 nanoseconds per long (22.797 seconds for the whole group). XorRNG
* appears to be occasionally faster on int output than LightRNG, but it isn't clear
* why or what causes that (JIT or GC internals, possibly). XorRNG is slightly
* slower at generating 64-bit random data, including long and double, but not by
* a significant degree (a multiplier between 0.9 and 1.2 times). The only deciding
* factor then is state size, where LightRNG is as small as possible for any JVM
* object with even a single field: 16 bytes (on a 64-bit JVM; 8-byte objects with
* 4 bytes or less of non-static members may be possible on 32-bit JVMs but I can't
* find anything confirming that guess).
* <br>
* So yes, this should be very fast, and with only a single long used per LightRNG,
* it is about as memory-efficient as these generators get.
* <br>
* Written in 2015 by Sebastiano Vigna (vigna@acm.org)
* @author Sebastiano Vigna
* @author Tommy Ettinger
*/
public class LightRNG implements RandomnessSource, StatefulRandomness
{
/** 2 raised to the 53, - 1. */
private static final long DOUBLE_MASK = ( 1L << 53 ) - 1;
/** 2 raised to the -53. */
private static final double NORM_53 = 1. / ( 1L << 53 );
/** 2 raised to the 24, -1. */
private static final long FLOAT_MASK = ( 1L << 24 ) - 1;
/** 2 raised to the -24. */
private static final double NORM_24 = 1. / ( 1L << 24 );
private static final long serialVersionUID = -374415589203474497L;
public long state; /* The state can be seeded with any value. */
/** Creates a new generator seeded using Math.random. */
public LightRNG() {
this((long) Math.floor(Math.random() * Long.MAX_VALUE));
}
public LightRNG( final long seed ) {
setSeed(seed);
}
@Override
public int next( int bits ) {
return (int)( nextLong() & ( 1L << bits ) - 1 );
}
/**
* Can return any long, positive or negative, of any size permissible in a 64-bit signed integer.
* @return any long, all 64 bits are random
*/
@Override
public long nextLong() {
long z = ( state += 0x9E3779B97F4A7C15L );
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L;
z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL;
return z ^ (z >>> 31);
}
/**
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
*
* @return a copy of this RandomnessSource
*/
@Override
public RandomnessSource copy() {
return new LightRNG(state);
}
/**
* Can return any int, positive or negative, of any size permissible in a 32-bit signed integer.
* @return any int, all 32 bits are random
*/
public int nextInt() {
return (int)nextLong();
}
/**
* Exclusive on the upper bound. The lower bound is 0.
* @param bound the upper bound; should be positive
* @return a random int less than n and at least equal to 0
*/
public int nextInt( final int bound ) {
if ( bound <= 0 ) return 0;
int threshold = (0x7fffffff - bound + 1) % bound;
for (;;) {
int bits = (int)(nextLong() & 0x7fffffff);
if (bits >= threshold)
return bits % bound;
}
}
/**
* Inclusive lower, exclusive upper.
* @param lower the lower bound, inclusive, can be positive or negative
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
* @return a random int at least equal to lower and less than upper
*/
public int nextInt( final int lower, final int upper ) {
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
return lower + nextInt(upper - lower);
}
/**
* Exclusive on the upper bound. The lower bound is 0.
* @param bound the upper bound; should be positive
* @return a random long less than n
*/
public long nextLong( final long bound ) {
if ( bound <= 0 ) return 0;
long threshold = (0x7fffffffffffffffL - bound + 1) % bound;
for (;;) {
long bits = nextLong() & 0x7fffffffffffffffL;
if (bits >= threshold)
return bits % bound;
}
}
/**
* Inclusive lower, exclusive upper.
* @param lower the lower bound, inclusive, can be positive or negative
* @param upper the upper bound, exclusive, should be positive, must be greater than lower
* @return a random long at least equal to lower and less than upper
*/
public long nextLong( final long lower, final long upper ) {
if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound");
return lower + nextLong(upper - lower);
}
/**
* Gets a uniform random double in the range [0.0,1.0)
* @return a random double at least equal to 0.0 and less than 1.0
*/
public double nextDouble() {
return ( nextLong() & DOUBLE_MASK ) * NORM_53;
}
/**
* Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer
* is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound.
* @param outer the exclusive outer bound, can be negative
* @return a random double between 0.0 (inclusive) and outer (exclusive)
*/
public double nextDouble(final double outer) {
return nextDouble() * outer;
}
/**
* Gets a uniform random float in the range [0.0,1.0)
* @return a random float at least equal to 0.0 and less than 1.0
*/
public float nextFloat() {
return (float)( ( nextLong() & FLOAT_MASK ) * NORM_24 );
}
/**
* Gets a random value, true or false.
* Calls nextLong() once.
* @return a random true or false value.
*/
public boolean nextBoolean() {
return ( nextLong() & 1 ) != 0L;
}
/**
* Given a byte array as a parameter, this will fill the array with random bytes (modifying it
* in-place). Calls nextLong() {@code Math.ceil(bytes.length / 8.0)} times.
* @param bytes a byte array that will have its contents overwritten with random bytes.
*/
public void nextBytes( final byte[] bytes ) {
int i = bytes.length, n = 0;
while( i != 0 ) {
n = Math.min( i, 8 );
for ( long bits = nextLong(); n-- != 0; bits >>= 8 ) bytes[ --i ] = (byte)bits;
}
}
/**
* Sets the seed of this generator (which is also the current state).
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
*/
public void setSeed( final long seed ) {
state = seed;
}
/**
* Sets the seed (also the current state) of this generator.
* @param seed the seed to use for this LightRNG, as if it was constructed with this seed.
*/
@Override
public void setState( final long seed ) {
state = seed;
}
/**
* Gets the current state of this generator.
* @return the current seed of this LightRNG, changed once per call to nextLong()
*/
@Override
public long getState() {
return state;
}
/**
* Advances or rolls back the LightRNG's state without actually generating numbers. Skip forward
* or backward a number of steps specified by advance, where a step is equal to one call to nextInt().
* @param advance Number of future generations to skip past. Can be negative to backtrack.
* @return the state after skipping.
*/
public long skip(long advance)
{
return state += 0x9E3779B97F4A7C15L * advance;
}
@Override
public String toString() {
return "LightRNG (" + state + ")"; // Akarin
}
}

View File

@@ -0,0 +1,62 @@
package io.akarin.api.internal.utils.random;
import java.util.Random;
/**
* This is a "fake" LightRandom, backed by the LightRNG.
*
* This is useful if you want to quickly replace a Random variable with
* LightRNG without breaking every code.
*/
public class LightRandom extends Random {
public static LightRNG light = new LightRNG(); // LightRNG, static.
private static final long serialVersionUID = 1L;
@Override
public int next(int bits) {
return light.next(bits);
}
@Override
public void nextBytes(byte[] bytes) {
light.nextBytes(bytes);
}
@Override
public int nextInt() {
return light.nextInt();
}
@Override
public int nextInt(int n) {
return light.nextInt(n);
}
@Override
public long nextLong() {
return light.nextLong();
}
@Override
public boolean nextBoolean() {
return light.nextBoolean();
}
@Override
public float nextFloat() {
return light.nextFloat();
}
@Override
public double nextDouble() {
return light.nextDouble();
}
// Akarin start
@Override
public void setSeed(long seed) {
light.setSeed(seed);
}
// Akarin end
}

View File

@@ -0,0 +1,40 @@
package io.akarin.api.internal.utils.random;
import java.io.Serializable;
/**
* This interface defines the interactions required of a random number
* generator. It is a replacement for Java's built-in Random because for
* improved performance.
*
* @author Eben Howard - http://squidpony.com - howard@squidpony.com
*/
public interface RandomnessSource extends Serializable {
/**
* Using this method, any algorithm that might use the built-in Java Random
* can interface with this randomness source.
*
* @param bits the number of bits to be returned
* @return the integer containing the appropriate number of bits
*/
int next(int bits);
/**
*
* Using this method, any algorithm that needs to efficiently generate more
* than 32 bits of random data can interface with this randomness source.
*
* Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive).
* @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive)
*/
long nextLong();
/**
* Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the
* copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to
* copy the state so it isn't shared, usually, and produce a new value with the same exact state.
* @return a copy of this RandomnessSource
*/
RandomnessSource copy();
}

View File

@@ -0,0 +1,20 @@
package io.akarin.api.internal.utils.random;
/**
* A simple interface for RandomnessSources that have the additional property of a state that can be re-set.
* Created by Tommy Ettinger on 9/15/2015.
*/
public interface StatefulRandomness extends RandomnessSource {
/**
* Get the current internal state of the StatefulRandomness as a long.
* @return the current internal state of this object.
*/
long getState();
/**
* Set the current internal state of this StatefulRandomness with a long.
*
* @param state a 64-bit long. You should avoid passing 0, even though some implementations can handle that.
*/
void setState(long state);
}

View File

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

@@ -3,6 +3,8 @@ package io.akarin.server.core;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import io.akarin.api.internal.Akari;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@@ -14,8 +16,8 @@ import java.util.regex.Pattern;
import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.configuration.file.YamlConfiguration;
import io.akarin.api.Akari;
@SuppressWarnings("unused")
public class AkarinGlobalConfig { public class AkarinGlobalConfig {
private static File CONFIG_FILE; private static File CONFIG_FILE;
@@ -153,11 +155,6 @@ public class AkarinGlobalConfig {
legacyVersioningCompat = getBoolean("alternative.legacy-versioning-compat", false); legacyVersioningCompat = getBoolean("alternative.legacy-versioning-compat", false);
} }
public static int registryTerminationSeconds;
private static void registryTerminationSeconds() {
registryTerminationSeconds = getSeconds(getString("bootstrap.parallel-registry-termination", "9s"));
}
public static int playersPerIOThread; public static int playersPerIOThread;
private static void playersPerIOThread() { private static void playersPerIOThread() {
playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50); playersPerIOThread = getInt("core.players-per-chunk-io-thread", 50);
@@ -168,28 +165,101 @@ public class AkarinGlobalConfig {
silentAsyncTimings = getBoolean("core.always-silent-async-timing", false); silentAsyncTimings = getBoolean("core.always-silent-async-timing", false);
} }
public static boolean legacyWorldTimings; public static long timeUpdateInterval;
private static void legacyWorldTimings() {
legacyWorldTimings = getBoolean("alternative.legacy-world-timings-required", false);
}
public static int timeUpdateInterval;
private static void timeUpdateInterval() { private static void timeUpdateInterval() {
timeUpdateInterval = getSeconds(getString("core.world-time-update-interval", "1s")); timeUpdateInterval = getSeconds(getString("core.tick-rate.world-time-update-interval", "1s")) * 10;
} }
public static int keepAliveSendInterval; public static long keepAliveSendInterval;
private static void keepAliveSendInterval() { private static void keepAliveSendInterval() {
keepAliveSendInterval = getSeconds(getString("core.keep-alive-packet-send-interval", "15s")); keepAliveSendInterval = getSeconds(getString("core.tick-rate.keep-alive-packet-send-interval", "15s")) * 1000;
} }
public static int keepAliveTimeout; public static long keepAliveTimeout;
private static void keepAliveTimeout() { private static void keepAliveTimeout() {
keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")); keepAliveTimeout = getSeconds(getString("core.keep-alive-response-timeout", "30s")) * 1000;
}
public static boolean throwOnAsyncCaught;
private static void throwOnAsyncCaught() {
throwOnAsyncCaught = getBoolean("core.thread-safe.async-catcher.throw-on-caught", true);
} }
public static boolean allowSpawnerModify; public static boolean allowSpawnerModify;
private static void allowSpawnerModify() { private static void allowSpawnerModify() {
allowSpawnerModify = getBoolean("alternative.allow-spawner-modify", true); allowSpawnerModify = getBoolean("alternative.allow-spawner-modify", true);
} }
public static boolean noResponseDoGC;
private static void noResponseDoGC() {
noResponseDoGC = getBoolean("alternative.gc-before-stuck-restart", true);
}
public static String messageKick;
public static String messageBan;
public static String messageBanReason;
public static String messageBanExpires;
public static String messageBanIp;
public static String messageDupLogin;
public static String messageJoin;
public static String messageJoinRenamed;
public static String messageKickKeepAlive;
public static String messagePlayerQuit;
private static void messagekickKeepAlive() {
messageKick = getString("messages.disconnect.kick-player", "Kicked by an operator.");
messageBan = getString("messages.disconnect.ban-player-name", "You are banned from this server! %s %s");
messageBanReason = getString("messages.disconnect.ban-reason", "\nReason: ");
messageBanExpires = getString("messages.disconnect.ban-expires", "\nYour ban will be removed on ");
messageBanIp = getString("messages.disconnect.ban-player-ip", "Your IP address is banned from this server! %s %s");
messageDupLogin = getString("messages.disconnect.kick-player-duplicate-login", "You logged in from another location");
messageJoin = getString("messages.connect.player-join-server", "§e%s joined the game");
messageJoinRenamed = getString("messages.connect.renamed-player-join-server", "§e%s (formerly known as %s) joined the game");
messageKickKeepAlive = getString("messages.disconnect.kick-player-timeout-keep-alive", "Timed out");
messagePlayerQuit = getString("messages.disconnect.player-quit-server", "§e%s left the game");
}
public static String serverBrandName;
private static void serverBrandName() {
serverBrandName = getString("alternative.modified-server-brand-name", "");
}
public static boolean disableEndPortalCreate;
private static void disableEndPortalCreate() {
disableEndPortalCreate = getBoolean("alternative.disable-end-portal-create", false);
}
public static int primaryThreadPriority;
private static void primaryThreadPriority() {
primaryThreadPriority = getInt("core.primary-thread-priority", 7);
}
public static long playersInfoUpdateInterval;
private static void playersInfoUpdateInterval() {
playersInfoUpdateInterval = getSeconds(getString("core.tick-rate.players-info-update-interval", "30s")) * 10;
}
public static long versionUpdateInterval;
private static void versionUpdateInterval() {
versionUpdateInterval = getSeconds(getString("alternative.version-update-interval", "3600s")) * 1000; // 1 hour
}
public static boolean sendLightOnlyChunkSection;
private static void sendLightOnlyChunkSection() {
sendLightOnlyChunkSection = getBoolean("core.send-light-only-chunk-sections", true);
}
public static boolean forceHardcoreDifficulty;
private static void forceHardcoreDifficulty() {
forceHardcoreDifficulty = getBoolean("alternative.force-difficulty-on-hardcore", true);
}
public static int fileIOThreads;
private static void fileIOThreads() {
fileIOThreads = getInt("core.chunk-save-threads", 2);
}
public static int parallelMode;
private static void parallelMode() {
parallelMode = getInt("core.parallel-mode", 1);
}
} }

View File

@@ -1,22 +1,27 @@
package io.akarin.server.core; package io.akarin.server.core;
import io.akarin.api.Akari; import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import io.akarin.api.internal.Akari;
import net.minecraft.server.EntityPlayer; import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EnumDifficulty;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PacketPlayOutKeepAlive; import net.minecraft.server.PacketPlayOutKeepAlive;
import net.minecraft.server.PacketPlayOutPlayerInfo;
import net.minecraft.server.PacketPlayOutUpdateTime; import net.minecraft.server.PacketPlayOutUpdateTime;
import net.minecraft.server.PlayerConnection; import net.minecraft.server.PlayerConnection;
import net.minecraft.server.WorldServer;
public class AkarinSlackScheduler extends Thread { public class AkarinSlackScheduler extends Thread {
public static AkarinSlackScheduler get() { public static AkarinSlackScheduler get() {
return Singleton.instance; return Singleton.instance;
} }
public static void boot() { public void boot() {
Singleton.instance.setName("Akarin Slack Scheduler Thread"); setName("Akarin Slack Scheduler Thread");
Singleton.instance.setPriority(MIN_PRIORITY); setDaemon(true);
Singleton.instance.setDaemon(true); start();
Singleton.instance.start();
Akari.logger.info("Slack scheduler service started"); Akari.logger.info("Slack scheduler service started");
} }
@@ -24,49 +29,80 @@ public class AkarinSlackScheduler extends Thread {
private static final AkarinSlackScheduler instance = new AkarinSlackScheduler(); private static final AkarinSlackScheduler instance = new AkarinSlackScheduler();
} }
/*
* Timers
*/
private long updateTime; private long updateTime;
private long resendPlayersInfo;
@Override @Override
public void run() { public void run() {
MinecraftServer server = MinecraftServer.getServer(); MinecraftServer server = MinecraftServer.getServer();
while (server.isRunning()) {
long startProcessTiming = System.currentTimeMillis();
// Send time updates to everyone, it will get the right time from the world the player is in. // Send time updates to everyone, it will get the right time from the world the player is in.
if (++updateTime == AkarinGlobalConfig.timeUpdateInterval * 10) { // Time update, from MinecraftServer#D
if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) {
for (EntityPlayer player : server.getPlayerList().players) { for (EntityPlayer player : server.getPlayerList().players) {
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time // Add support for per player time
player.playerConnection.sendPacket(new PacketPlayOutUpdateTime(player.world.getTime(), player.getPlayerTime(), player.world.getGameRules().getBoolean("doDaylightCycle")));
} }
updateTime = 0; updateTime = 0;
} }
// Keep alive, from PlayerConnection#e
for (EntityPlayer player : server.getPlayerList().players) { for (EntityPlayer player : server.getPlayerList().players) {
PlayerConnection conn = player.playerConnection; PlayerConnection conn = player.playerConnection;
// Paper - give clients a longer time to respond to pings as per pre 1.12.2 timings // Paper - give clients a longer time to respond to pings as per pre 1.12.2 timings
// This should effectively place the keepalive handling back to "as it was" before 1.12.2 // This should effectively place the keepalive handling back to "as it was" before 1.12.2
long currentTime = System.currentTimeMillis(); long currentTime = System.nanoTime() / 1000000L;
long elapsedTime = currentTime - conn.getLastPing(); long elapsedTime = currentTime - conn.getLastPing();
if (conn.isPendingPing()) { if (conn.isPendingPing()) {
// We're pending a ping from the client // We're pending a ping from the client
if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout * 1000L) { // check keepalive limit, don't fire if already disconnected if (!conn.processedDisconnect && elapsedTime >= AkarinGlobalConfig.keepAliveTimeout) { // check keepalive limit, don't fire if already disconnected
Akari.callbackQueue.add(() -> { Akari.callbackQueue.add(() -> {
Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info Akari.logger.warn("{} was kicked due to keepalive timeout!", conn.player.getName()); // more info
conn.disconnect("disconnect.timeout"); conn.disconnect("disconnect.timeout");
}); });
} }
} else { } else {
if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval * 1000L) { // 15 seconds default if (elapsedTime >= AkarinGlobalConfig.keepAliveSendInterval) { // 15 seconds default
conn.setPendingPing(true); conn.setPendingPing(true);
conn.setLastPing(currentTime); conn.setLastPing(currentTime);
conn.setKeepAliveID(currentTime); conn.setKeepAliveID(currentTime);
conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); conn.sendPacket(new PacketPlayOutKeepAlive(conn.getKeepAliveID())); // 15s lagg you should stop your server
} }
} }
} }
// Force hardcore difficulty, from WorldServer#doTick
if (AkarinGlobalConfig.forceHardcoreDifficulty)
for (WorldServer world : server.worlds) {
if (world.getWorldData().isHardcore() && world.getDifficulty() != EnumDifficulty.HARD) {
world.getWorldData().setDifficulty(EnumDifficulty.HARD);
}
}
// Update player info, from PlayerList#tick
if (++resendPlayersInfo > AkarinGlobalConfig.playersInfoUpdateInterval) {
for (EntityPlayer player : server.getPlayerList().players) {
player.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_LATENCY, Iterables.filter(server.getPlayerList().players, new Predicate<EntityPlayer>() {
@Override
public boolean apply(EntityPlayer each) {
return player.getBukkitEntity().canSee(each.getBukkitEntity());
}
})));
}
resendPlayersInfo = 0;
}
try { try {
Thread.sleep(100); long sleepFixed = 100 - (System.currentTimeMillis() - startProcessTiming);
} catch (InterruptedException ex) { if (sleepFixed > 0) Thread.sleep(sleepFixed);
Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!"); } catch (InterruptedException interrupted) {
ex.printStackTrace(); continue;
}
} }
} }

View File

@@ -1,49 +0,0 @@
package io.akarin.server.core;
import java.util.List;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import net.minecraft.server.EnumProtocolDirection;
import net.minecraft.server.HandshakeListener;
import net.minecraft.server.LegacyPingHandler;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.NetworkManager;
import net.minecraft.server.PacketDecoder;
import net.minecraft.server.PacketEncoder;
import net.minecraft.server.PacketPrepender;
import net.minecraft.server.PacketSplitter;
public class ChannelAdapter extends ChannelInitializer<Channel> {
private final List<NetworkManager> managers;
public ChannelAdapter(List<NetworkManager> list) {
managers = list;
}
public static ChannelAdapter create(List<NetworkManager> managers) {
return new ChannelAdapter(managers);
}
@Override
protected void initChannel(Channel channel) {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException ex) {
;
}
channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30))
.addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection()))
.addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
.addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
managers.add(manager);
channel.pipeline().addLast("packet_handler", manager);
manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager));
}
}

View File

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

View File

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

View File

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

View File

@@ -17,7 +17,7 @@ import com.destroystokyo.paper.Metrics;
import com.destroystokyo.paper.Metrics.CustomChart; import com.destroystokyo.paper.Metrics.CustomChart;
@Mixin(value = Metrics.class, remap = false) @Mixin(value = Metrics.class, remap = false)
public class MixinMetrics { public abstract class MixinMetrics {
// The url to which the data is sent - bukkit/Torch (keep our old name) // The url to which the data is sent - bukkit/Torch (keep our old name)
private final static String URL = "https://bStats.org/submitData/bukkit"; private final static String URL = "https://bStats.org/submitData/bukkit";

View File

@@ -0,0 +1,22 @@
package io.akarin.server.mixin.bootstrap;
import org.spigotmc.RestartCommand;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
@Mixin(value = RestartCommand.class, remap = false)
public abstract class MixinRestartCommand {
@Inject(method = "restart()V", at = @At("HEAD"))
private static void beforeRestart(CallbackInfo ci) {
if (AkarinGlobalConfig.noResponseDoGC) {
Akari.logger.warn("Attempting to garbage collect, may takes a few seconds");
System.runFinalization();
System.gc();
}
}
}

View File

@@ -10,8 +10,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.Akari; import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.BiomeBase; import net.minecraft.server.BiomeBase;
import net.minecraft.server.Block; import net.minecraft.server.Block;
import net.minecraft.server.BlockFire; import net.minecraft.server.BlockFire;
@@ -25,7 +24,7 @@ import net.minecraft.server.PotionRegistry;
import net.minecraft.server.SoundEffect; import net.minecraft.server.SoundEffect;
@Mixin(value = DispenserRegistry.class, remap = false) @Mixin(value = DispenserRegistry.class, remap = false)
public class ParallelRegistry { public abstract class ParallelRegistry {
/** /**
* Registry order: SoundEffect -> Block * Registry order: SoundEffect -> Block
*/ */
@@ -40,8 +39,6 @@ public class ParallelRegistry {
*/ */
private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY); private static final ExecutorService STAGE_STANDALONE = Executors.newFixedThreadPool(3, Akari.STAGE_FACTORY);
private static final int TERMINATION_IN_SEC = AkarinGlobalConfig.registryTerminationSeconds;
// We should keep the original order in codes thought orderless in runtime // We should keep the original order in codes thought orderless in runtime
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
@@ -131,11 +128,12 @@ public class ParallelRegistry {
// Shutdown BLOCK and STANDALONE stage // Shutdown BLOCK and STANDALONE stage
STAGE_STANDALONE.shutdown(); STAGE_STANDALONE.shutdown();
STAGE_BLOCK.shutdown(); STAGE_BLOCK.shutdown();
STAGE_BLOCK.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); STAGE_BLOCK.awaitTermination(10, TimeUnit.MINUTES);
STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated STAGE_BLOCK_BASE.shutdown(); // This must after STAGE_BLOCK terminated
STAGE_BLOCK_BASE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); STAGE_BLOCK_BASE.awaitTermination(20, TimeUnit.MINUTES);
STAGE_STANDALONE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); // Behind the shutdown of BLOCK_BASE should faster STAGE_STANDALONE.awaitTermination(30, TimeUnit.MINUTES); // Behind the shutdown of BLOCK_BASE should faster
} }
} }

View File

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

View File

@@ -1,16 +0,0 @@
package io.akarin.server.mixin.core;
import org.spigotmc.AsyncCatcher;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(value = AsyncCatcher.class, remap = false)
public class DesyncCatcher {
@Shadow public static boolean enabled;
@Overwrite
public static void catchOp(String reason) {
;
}
}

View File

@@ -0,0 +1,28 @@
package io.akarin.server.mixin.core;
import org.spigotmc.AsyncCatcher;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
@Mixin(value = AsyncCatcher.class, remap = false)
public abstract class MixinAsyncCatcher {
@Shadow public static boolean enabled;
@Overwrite
public static void catchOp(String reason) {
if (enabled) {
if (Akari.isPrimaryThread()) return;
if (AkarinGlobalConfig.throwOnAsyncCaught) {
throw new IllegalStateException("Asynchronous " + reason + "!");
} else {
Akari.logger.warn("Asynchronous " + reason + "!");
Thread.dumpStack();
}
}
}
}

View File

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

View File

@@ -0,0 +1,18 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.ChunkSection;
@Mixin(value = ChunkSection.class, remap = false)
public abstract class MixinChunkSection {
@Shadow private int nonEmptyBlockCount;
@Overwrite // OBFHELPER: isEmpty
public boolean a() {
return AkarinGlobalConfig.sendLightOnlyChunkSection ? false : nonEmptyBlockCount == 0;
}
}

View File

@@ -0,0 +1,61 @@
package io.akarin.server.mixin.core;
import java.util.Date;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import com.mojang.authlib.GameProfile;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandBan;
import net.minecraft.server.CommandException;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ExceptionUsage;
import net.minecraft.server.GameProfileBanEntry;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandBan.class, remap = false)
public abstract class MixinCommandBan {
@Overwrite
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
if (args.length >= 1 && args[0].length() > 1) {
GameProfile profile = server.getUserCache().getProfile(args[0]);
if (profile == null) {
throw new CommandException("commands.ban.failed", new Object[] {args[0]});
} else {
// Akarin start - use string
boolean hasReason = true; // Akarin
String message = null;
if (args.length >= 2) {
message = "";
for (int i = 2; i < args.length; i++) {
message = message + args[i];
}
} else {
hasReason = false; // Akarin
message = Akari.EMPTY_STRING; // Akarin - modify message
}
// Akarin end
GameProfileBanEntry entry = new GameProfileBanEntry(profile, (Date) null, sender.getName(), (Date) null, message);
server.getPlayerList().getProfileBans().add(entry);
EntityPlayer entityplayer = server.getPlayerList().getPlayer(args[0]);
if (entityplayer != null) {
entityplayer.playerConnection.disconnect(hasReason ? message : AkarinGlobalConfig.messageBan);
}
CommandAbstract.a(sender, (ICommand) this, "commands.ban.success", args[0]); // OBFHELPER: notifyCommandListener
}
} else {
throw new ExceptionUsage("commands.ban.usage");
}
}
}

View File

@@ -0,0 +1,49 @@
package io.akarin.server.mixin.core;
import java.util.Date;
import java.util.List;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandBanIp;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.IpBanEntry;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandBanIp.class, remap = false)
public abstract class MixinCommandBanIp {
@Overwrite // OBFHELPER: banIp
protected void a(MinecraftServer server, ICommandListener sender, String args, @Nullable String banReason) {
// Akarin start - modify message
boolean hasReason = true;
if (banReason == null) {
banReason = Akari.EMPTY_STRING;
hasReason = false;
}
// Akarin end
IpBanEntry ipbanentry = new IpBanEntry(args, (Date) null, sender.getName(), (Date) null, banReason);
server.getPlayerList().getIPBans().add(ipbanentry);
List<EntityPlayer> withIpPlayers = server.getPlayerList().b(args); // OBFHELPER: getPlayersMatchingAddress
String[] banPlayerNames = new String[withIpPlayers.size()];
for (int i = 0; i < banPlayerNames.length; i++) {
EntityPlayer each = withIpPlayers.get(i);
banPlayerNames[i] = each.getName();
each.playerConnection.disconnect(hasReason ? banReason : AkarinGlobalConfig.messageBanIp); // Akarin
}
if (withIpPlayers.isEmpty()) {
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success", args); // OBFHELPER: notifyCommandListener
} else {
CommandAbstract.a(sender, (ICommand) this, "commands.banip.success.players", args, CommandAbstract.a(banPlayerNames)); // OBFHELPER: notifyCommandListener - joinNiceString
}
}
}

View File

@@ -0,0 +1,44 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.CommandAbstract;
import net.minecraft.server.CommandException;
import net.minecraft.server.CommandKick;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.ExceptionPlayerNotFound;
import net.minecraft.server.ExceptionUsage;
import net.minecraft.server.ICommand;
import net.minecraft.server.ICommandListener;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CommandKick.class, remap = false)
public abstract class MixinCommandKick {
@Overwrite
public void execute(MinecraftServer server, ICommandListener sender, String[] args) throws CommandException {
if (args.length > 0 && args[0].length() > 1) {
EntityPlayer target = server.getPlayerList().getPlayer(args[0]);
if (target == null) {
throw new ExceptionPlayerNotFound("commands.generic.player.notFound", args[0]);
} else {
if (args.length >= 2) {
// Akarin start - use string
String message = "";
for (int i = 2; i < args.length; i++) {
message = message + args[i];
}
target.playerConnection.disconnect(message);
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success.reason", target.getName(), message); // OBFHELPER: notifyCommandListener
// Akarin end
} else {
target.playerConnection.disconnect(AkarinGlobalConfig.messageKick); // Akarin
CommandAbstract.a(sender, (ICommand) this, "commands.kick.success", target.getName()); // OBFHELPER: notifyCommandListener
}
}
} else {
throw new ExceptionUsage("commands.kick.usage");
}
}
}

View File

@@ -7,19 +7,40 @@ import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MinecraftServer;
@Mixin(value = CraftServer.class, remap = false) @Mixin(value = CraftServer.class, remap = false)
public class MixinCraftServer { public abstract class MixinCraftServer {
@Shadow @Final @Mutable private String serverName; @Shadow @Final @Mutable private String serverName;
@Shadow @Final @Mutable private String serverVersion;
@Shadow @Final protected MinecraftServer console;
private boolean needApplyServerName = true; private boolean needApplyServerName = true;
private boolean needApplyServerVersion = true;
@Overwrite @Overwrite
public String getName() { public String getName() {
// We cannot apply the name modification in <init> method, // We cannot apply the name modification in <init> method,
// cause the initializer will be added to the tail // cause the initializer will be added to the tail
if (needApplyServerName) { if (needApplyServerName) {
serverName = "Akarin"; serverName = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? "Akarin" : AkarinGlobalConfig.serverBrandName;
needApplyServerName = false; needApplyServerName = false;
} }
return serverName; return serverName;
} }
@Overwrite
public String getVersion() {
if (needApplyServerVersion) {
serverVersion = AkarinGlobalConfig.serverBrandName.equals(Akari.EMPTY_STRING) ? serverVersion : serverVersion.replace("Akarin", AkarinGlobalConfig.serverBrandName);
needApplyServerVersion = false;
}
return serverVersion + " (MC: " + console.getVersion() + ")";
}
@Overwrite
public boolean isPrimaryThread() {
return Akari.isPrimaryThread();
}
} }

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,16 +1,38 @@
package io.akarin.server.mixin.core; package io.akarin.server.mixin.core;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.bukkit.craftbukkit.util.Waitable;
import org.spigotmc.AsyncCatcher;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import io.akarin.api.internal.Akari;
import net.minecraft.server.MCUtil; import net.minecraft.server.MCUtil;
import net.minecraft.server.MinecraftServer;
@Mixin(value = MCUtil.class, remap = false) @Mixin(value = MCUtil.class, remap = false)
public class MixinMCUtil { public abstract class MixinMCUtil {
@Overwrite @Overwrite
public static <T> T ensureMain(String reason, Supplier<T> run) { public static <T> T ensureMain(String reason, Supplier<T> run) {
if (AsyncCatcher.enabled && !Akari.isPrimaryThread()) {
new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
Waitable<T> wait = new Waitable<T>() {
@Override
protected T evaluate() {
return run.get();
}
};
MinecraftServer.getServer().processQueue.add(wait);
try {
return wait.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
return null;
}
return run.get(); return run.get();
} }
} }

View File

@@ -2,10 +2,18 @@ package io.akarin.server.mixin.core;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import org.apache.commons.lang.WordUtils;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer; import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor;
import org.bukkit.event.inventory.InventoryMoveItemEvent; import org.bukkit.event.inventory.InventoryMoveItemEvent;
import org.bukkit.event.world.WorldLoadEvent;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Mutable;
@@ -16,9 +24,12 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import co.aikar.timings.MinecraftTimings; import co.aikar.timings.MinecraftTimings;
import io.akarin.api.Akari; import io.akarin.api.internal.Akari;
import io.akarin.api.internal.Akari.AssignableFactory;
import io.akarin.api.internal.mixin.IMixinWorldServer;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.AkarinSlackScheduler; import io.akarin.server.core.AkarinSlackScheduler;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.CrashReport; import net.minecraft.server.CrashReport;
import net.minecraft.server.CustomFunctionData; import net.minecraft.server.CustomFunctionData;
import net.minecraft.server.ITickable; import net.minecraft.server.ITickable;
@@ -32,12 +43,38 @@ import net.minecraft.server.TileEntityHopper;
import net.minecraft.server.WorldServer; import net.minecraft.server.WorldServer;
@Mixin(value = MinecraftServer.class, remap = false) @Mixin(value = MinecraftServer.class, remap = false)
public class MixinMinecraftServer { public abstract class MixinMinecraftServer {
@Shadow @Final public Thread primaryThread;
private boolean tickedPrimaryEntities;
private int cachedWorldSize;
@Overwrite @Overwrite
public String getServerModName() { public String getServerModName() {
return "Akarin"; return "Akarin";
} }
@Inject(method = "run()V", at = @At(
value = "INVOKE",
target = "net/minecraft/server/MinecraftServer.aw()J",
shift = At.Shift.BEFORE
))
private void prerun(CallbackInfo info) {
primaryThread.setPriority(AkarinGlobalConfig.primaryThreadPriority < Thread.NORM_PRIORITY ? Thread.NORM_PRIORITY :
(AkarinGlobalConfig.primaryThreadPriority > Thread.MAX_PRIORITY ? 10 : AkarinGlobalConfig.primaryThreadPriority));
Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
//TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0;
}
AkarinSlackScheduler.get().boot();
}
@Overwrite
public boolean isMainThread() {
return Akari.isPrimaryThread();
}
/* /*
* Forcely disable snooper * Forcely disable snooper
*/ */
@@ -47,35 +84,81 @@ public class MixinMinecraftServer {
@Overwrite @Overwrite
public void b(MojangStatisticsGenerator generator) {} public void b(MojangStatisticsGenerator generator) {}
@Inject(method = "run()V", at = @At( /*
value = "INVOKE", * Parallel spawn chunks generation
target = "net/minecraft/server/MinecraftServer.aw()J", */
shift = At.Shift.BEFORE @Shadow public abstract boolean isRunning();
)) @Shadow(aliases = "a_") protected abstract void output(String s, int i);
private void prerun(CallbackInfo info) { @Shadow(aliases = "t") protected abstract void enablePluginsPostWorld();
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i); private void prepareChunks(WorldServer world, int index) {
TileEntityHopper.skipHopperEvents = world.paperConfig.disableHopperMoveEvents || InventoryMoveItemEvent.getHandlerList().getRegisteredListeners().length == 0; MinecraftServer.LOGGER.info("Preparing start region for level " + index + " (Seed: " + world.getSeed() + ")");
BlockPosition spawnPos = world.getSpawn();
long lastRecord = System.currentTimeMillis();
int preparedChunks = 0;
short radius = world.paperConfig.keepLoadedRange;
for (int skipX = -radius; skipX <= radius && isRunning(); skipX += 16) {
for (int skipZ = -radius; skipZ <= radius && isRunning(); skipZ += 16) {
long now = System.currentTimeMillis();
if (now - lastRecord > 1000L) {
output("Preparing spawn area (level " + index + ") ", preparedChunks * 100 / 625);
lastRecord = now;
} }
AkarinSlackScheduler.boot(); preparedChunks++;
world.getChunkProviderServer().getChunkAt(spawnPos.getX() + skipX >> 4, spawnPos.getZ() + skipZ >> 4);
}
}
} }
@Overwrite
protected void l() throws InterruptedException {
ExecutorCompletionService<?> executor = new ExecutorCompletionService<>(Executors.newFixedThreadPool(worlds.size(), new AssignableFactory("Akarin Parallel Terrain Generation Thread - $")));
for (int index = 0; index < worlds.size(); index++) {
WorldServer world = this.worlds.get(index);
if (!world.getWorld().getKeepSpawnInMemory()) continue;
int fIndex = index;
executor.submit(() -> prepareChunks(world, fIndex), null);
}
for (WorldServer world : this.worlds) {
if (world.getWorld().getKeepSpawnInMemory()) executor.take();
}
if (WorldLoadEvent.getHandlerList().getRegisteredListeners().length != 0) {
for (WorldServer world : this.worlds) {
this.server.getPluginManager().callEvent(new WorldLoadEvent(world.getWorld()));
}
}
enablePluginsPostWorld();
}
/*
* Parallel world ticking
*/
@Shadow public CraftServer server; @Shadow public CraftServer server;
@Shadow @Mutable protected Queue<FutureTask<?>> j; @Shadow @Mutable protected Queue<FutureTask<?>> j;
@Shadow public Queue<Runnable> processQueue; @Shadow public Queue<Runnable> processQueue;
@Shadow private int ticks; @Shadow private int ticks;
@Shadow public List<WorldServer> worlds; @Shadow public List<WorldServer> worlds;
@Shadow private PlayerList v; @Shadow(aliases = "v") private PlayerList playerList;
@Shadow @Final private List<ITickable> o; @Shadow(aliases = "o") @Final private List<ITickable> tickables;
@Shadow public PlayerList getPlayerList() { return null; } @Shadow public abstract PlayerList getPlayerList();
@Shadow public ServerConnection an() { return null; } @Shadow(aliases = "an") public abstract ServerConnection serverConnection();
@Shadow public CustomFunctionData aL() { return null; } @Shadow(aliases = "aL") public abstract CustomFunctionData functionManager();
private void tickEntities(WorldServer world) { private boolean tickEntities(WorldServer world) {
try { try {
world.timings.tickEntities.startTiming();
world.tickEntities(); world.tickEntities();
world.timings.tickEntities.stopTiming();
world.getTracker().updatePlayers();
world.explosionDensityCache.clear(); // Paper - Optimize explosions
} catch (Throwable throwable) { } catch (Throwable throwable) {
CrashReport crashreport; CrashReport crashreport;
try { try {
@@ -86,11 +169,14 @@ public class MixinMinecraftServer {
world.a(crashreport); world.a(crashreport);
throw new ReportedException(crashreport); throw new ReportedException(crashreport);
} }
return true;
} }
private void tickWorld(WorldServer world) { private void tickWorld(WorldServer world) {
try { try {
world.timings.doTick.startTiming();
world.doTick(); world.doTick();
world.timings.doTick.stopTiming();
} catch (Throwable throwable) { } catch (Throwable throwable) {
CrashReport crashreport; CrashReport crashreport;
try { try {
@@ -104,7 +190,7 @@ public class MixinMinecraftServer {
} }
@Overwrite @Overwrite
public void D() throws InterruptedException { public void D() throws InterruptedException, ExecutionException, CancellationException {
Runnable runnable; Runnable runnable;
Akari.callbackTiming.startTiming(); Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run(); while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
@@ -130,75 +216,108 @@ public class MixinMinecraftServer {
ChunkIOExecutor.tick(); ChunkIOExecutor.tick();
MinecraftTimings.chunkIOTickTimer.stopTiming(); MinecraftTimings.chunkIOTickTimer.stopTiming();
Akari.worldTiming.startTiming(); if (cachedWorldSize != worlds.size()) Akari.resizeTickExecutors((cachedWorldSize = worlds.size()));
if (AkarinGlobalConfig.legacyWorldTimings) { switch (AkarinGlobalConfig.parallelMode) {
for (int i = 0; i < worlds.size(); ++i) { case 1:
worlds.get(i).timings.tickEntities.startTiming(); case 2:
worlds.get(i).timings.doTick.startTiming(); default:
}
}
Akari.silentTiming = true; // Disable timings
Akari.STAGE_TICK.submit(() -> {
// Never tick one world concurrently! // Never tick one world concurrently!
// TODO better treat world index for (int i = 0; i < cachedWorldSize; i++) {
for (int i = 1; i <= worlds.size(); ++i) { // Impl Note:
WorldServer world = worlds.get(i < worlds.size() ? i : 0); // Entities ticking: index 1 -> ... -> 0 (parallel)
synchronized (world.tickLock) { // 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); tickEntities(world);
} }
} }
}, null); }, null);
for (int i = 0; i < worlds.size(); ++i) { Akari.STAGE_TICK.submit(() -> {
for (int i = 0; i < cachedWorldSize; ++i) {
WorldServer world = worlds.get(i); WorldServer world = worlds.get(i);
synchronized (world.tickLock) { synchronized (((IMixinWorldServer) world).lock()) {
tickWorld(world); tickWorld(world);
} }
} }
}, null);
Akari.STAGE_TICK.take(); Akari.STAGE_TICK.take();
Akari.silentTiming = false; // Enable timings Akari.STAGE_TICK.take();
Akari.worldTiming.stopTiming(); break;
if (AkarinGlobalConfig.legacyWorldTimings) { case -1:
for (int i = 0; i < worlds.size(); ++i) { for (int i = 0; i < cachedWorldSize; ++i) {
worlds.get(i).timings.tickEntities.stopTiming(); WorldServer world = worlds.get(i);
worlds.get(i).timings.doTick.startTiming(); tickWorld(world);
tickEntities(world);
} }
break;
} }
Akari.callbackTiming.startTiming(); Akari.callbackTiming.startTiming();
while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run(); while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
Akari.callbackTiming.stopTiming(); Akari.callbackTiming.stopTiming();
for (int i = 0; i < worlds.size(); ++i) {
WorldServer world = worlds.get(i);
tickConflictSync(world);
world.getTracker().updatePlayers();
world.explosionDensityCache.clear(); // Paper - Optimize explosions
}
MinecraftTimings.connectionTimer.startTiming(); MinecraftTimings.connectionTimer.startTiming();
this.an().c(); serverConnection().c();
MinecraftTimings.connectionTimer.stopTiming(); MinecraftTimings.connectionTimer.stopTiming();
MinecraftTimings.playerListTimer.startTiming(); Akari.callbackTiming.startTiming();
this.v.tick(); while ((runnable = Akari.callbackQueue.poll()) != null) runnable.run();
MinecraftTimings.playerListTimer.stopTiming(); Akari.callbackTiming.stopTiming();
MinecraftTimings.commandFunctionsTimer.startTiming(); MinecraftTimings.commandFunctionsTimer.startTiming();
this.aL().e(); functionManager().e();
MinecraftTimings.commandFunctionsTimer.stopTiming(); MinecraftTimings.commandFunctionsTimer.stopTiming();
MinecraftTimings.tickablesTimer.startTiming(); MinecraftTimings.tickablesTimer.startTiming();
for (int i = 0; i < this.o.size(); ++i) { for (int i = 0; i < this.tickables.size(); ++i) {
this.o.get(i).e(); tickables.get(i).e();
} }
MinecraftTimings.tickablesTimer.stopTiming(); MinecraftTimings.tickablesTimer.stopTiming();
} }
public void tickConflictSync(WorldServer world) {
;
}
} }

View File

@@ -0,0 +1,29 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import co.aikar.timings.MinecraftTimings;
import co.aikar.timings.Timing;
import io.akarin.api.internal.Akari;
import net.minecraft.server.CancelledPacketHandleException;
import net.minecraft.server.IAsyncTaskHandler;
import net.minecraft.server.Packet;
import net.minecraft.server.PacketListener;
import net.minecraft.server.PlayerConnectionUtils;
@Mixin(value = PlayerConnectionUtils.class, remap = false)
public abstract class MixinPlayerConnectionUtils {
@Overwrite
public static <T extends PacketListener> void ensureMainThread(final Packet<T> packet, final T listener, IAsyncTaskHandler iasynctaskhandler) throws CancelledPacketHandleException {
if (!iasynctaskhandler.isMainThread()) {
Timing timing = MinecraftTimings.getPacketTiming(packet);
// MinecraftServer#postToMainThread inlined thread check, no twice
Akari.callbackQueue.add(() -> {
try (Timing ignored = timing.startTiming()) {
packet.a(listener);
}
});
throw CancelledPacketHandleException.INSTANCE;
}
}
}

View File

@@ -1,55 +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 io.akarin.api.Akari;
import io.akarin.server.core.AkarinGlobalConfig;
@Mixin(targets = "co.aikar.timings.TimingHandler", remap = false)
public class MixinTimingHandler {
@Shadow @Final String name;
@Shadow private boolean enabled;
@Shadow private volatile long start;
@Shadow private volatile int timingDepth;
@Overwrite
public void stopTimingIfSync() {
if (Bukkit.isPrimaryThread()) {
stopTiming(true); // Avoid twice thread check
}
}
@Overwrite
public void stopTiming() {
// Akarin start - avoid twice thread check
stopTiming(false);
}
@Shadow void addDiff(long diff) {}
public void stopTiming(boolean sync) {
if (enabled && --timingDepth == 0 && start != 0) {
if (Akari.silentTiming) { // It must be off-main thread now
start = 0;
return;
} else {
if (!sync && !Bukkit.isPrimaryThread()) {
if (AkarinGlobalConfig.silentAsyncTimings) {
Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name);
new Throwable().printStackTrace();
}
start = 0;
return;
}
}
addDiff(System.nanoTime() - start);
start = 0;
}
}
}

View File

@@ -1,21 +1,58 @@
package io.akarin.server.mixin.core; package io.akarin.server.mixin.core;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Set; import java.util.Set;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.command.defaults.VersionCommand; import org.bukkit.command.defaults.VersionCommand;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.Akari; import com.google.common.base.Charsets;
import io.akarin.api.internal.Akari;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import net.minecraft.server.MCUtil; import net.minecraft.server.MCUtil;
@Mixin(value = VersionCommand.class, remap = false) @Mixin(value = VersionCommand.class, remap = false)
public class MixinVersionCommand { public abstract class MixinVersionCommand {
@Shadow private static int getFromRepo(String repo, String hash) { return 0; } @Overwrite
private static int getFromRepo(String repo, String hash) {
try {
HttpURLConnection connection = (HttpURLConnection) new URL("https://api.github.com/repos/" + repo + "/compare/ver/1.12.2..." + hash).openConnection();
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) return -2; // Unknown commit
try (
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), Charsets.UTF_8))
) {
JSONObject obj = (JSONObject) new JSONParser().parse(reader);
String status = (String) obj.get("status");
switch (status) {
case "identical":
return 0;
case "behind":
return ((Number) obj.get("behind_by")).intValue();
default:
return -1;
}
} catch (ParseException | NumberFormatException e) {
e.printStackTrace();
return -1;
}
} catch (IOException e) {
e.printStackTrace();
return -1;
}
}
/** /**
* Match current version with repository and calculate the distance * Match current version with repository and calculate the distance
@@ -59,6 +96,9 @@ public class MixinVersionCommand {
// This should be lying in 'obtainVersion' method, but bump for faster returning // This should be lying in 'obtainVersion' method, but bump for faster returning
if (customVersion) return; if (customVersion) return;
synchronized (versionWaiters) {
versionWaiters.add(sender);
}
if (versionObtaining) return; if (versionObtaining) return;
// The volatile guarantees the safety between different threads. // The volatile guarantees the safety between different threads.
// Remembers that we are still on main thread now, // Remembers that we are still on main thread now,
@@ -70,7 +110,7 @@ public class MixinVersionCommand {
if (hasVersion) { if (hasVersion) {
long current = System.currentTimeMillis(); long current = System.currentTimeMillis();
if (current - lastCheckMillis > 7200000 /* 2 hours */) { if (current - lastCheckMillis > AkarinGlobalConfig.versionUpdateInterval) {
lastCheckMillis = current; lastCheckMillis = current;
hasVersion = false; hasVersion = false;
} else { } else {
@@ -100,11 +140,9 @@ public class MixinVersionCommand {
// We post all things because a custom version is rare (expiring is not rare), // We post all things because a custom version is rare (expiring is not rare),
// and we'd better post this task as early as we can, since it's a will (horrible destiny). // and we'd better post this task as early as we can, since it's a will (horrible destiny).
MCUtil.scheduleAsyncTask(() -> { MCUtil.scheduleAsyncTask(() -> {
// This should be lying in 'sendVersion' method, but comes here for relax main thread
versionWaiters.add(sender);
sender.sendMessage("Checking version, please wait..."); sender.sendMessage("Checking version, please wait...");
String version = Bukkit.getVersion(); String version = Akari.getServerVersion();
if (version == null) { if (version == null) {
version = "Unique"; // Custom - > Unique version = "Unique"; // Custom - > Unique
customVersion = true; customVersion = true;
@@ -140,9 +178,11 @@ public class MixinVersionCommand {
versionMessage = message; versionMessage = message;
hasVersion = true; hasVersion = true;
synchronized (versionWaiters) {
for (CommandSender sender : versionWaiters) { for (CommandSender sender : versionWaiters) {
sender.sendMessage(versionMessage); sender.sendMessage(versionMessage);
} }
versionWaiters.clear(); versionWaiters.clear();
} }
} }
}

View File

@@ -0,0 +1,36 @@
package io.akarin.server.mixin.core;
import java.util.List;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.AxisAlignedBB;
import net.minecraft.server.Entity;
import net.minecraft.server.World;
/**
* Fixes MC-103516(https://bugs.mojang.com/browse/MC-103516)
*/
@Mixin(value = World.class, remap = false)
public abstract class MixinWorld {
@Shadow public abstract List<Entity> getEntities(@Nullable Entity entity, AxisAlignedBB box);
/**
* Returns true if there are no solid, live entities in the specified AxisAlignedBB, excluding the given entity
*/
@Overwrite
public boolean a(AxisAlignedBB box, @Nullable Entity target) { // OBFHELPER: checkNoEntityCollision
List<Entity> list = this.getEntities(null, box);
for (Entity each : list) {
if (!each.dead && each.i && each != target && (target == null || !each.x(target))) { // OBFHELPER: preventEntitySpawning - isRidingSameEntity
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,27 @@
package io.akarin.server.mixin.core;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.WorldManager;
import net.minecraft.server.WorldServer;
@Mixin(value = WorldManager.class, remap = false)
public abstract class MixinWorldManager {
@Shadow @Final private WorldServer world;
@Overwrite
public void a(Entity entity) {
this.world.getTracker().entriesLock.writeLock().lock(); // Akarin
this.world.getTracker().track(entity);
this.world.getTracker().entriesLock.writeLock().unlock(); // Akarin
if (entity instanceof EntityPlayer) {
this.world.worldProvider.a((EntityPlayer) entity);
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,84 @@
package io.akarin.server.mixin.cps;
import org.spigotmc.SlackActivityAccountant;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import net.minecraft.server.Chunk;
import net.minecraft.server.ChunkProviderServer;
import net.minecraft.server.IChunkLoader;
import net.minecraft.server.WorldServer;
@Mixin(value = ChunkProviderServer.class, remap = false)
public abstract class MixinChunkProviderServer {
@Shadow @Final public WorldServer world;
@Shadow public Long2ObjectOpenHashMap<Chunk> chunks;
public void unload(Chunk chunk) {
if (this.world.worldProvider.c(chunk.locX, chunk.locZ)) {
// Akarin - avoid using the queue and simply check the unloaded flag during unloads
// this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(chunk.locX, chunk.locZ)));
chunk.setShouldUnload(true);
}
}
@Shadow public abstract boolean unloadChunk(Chunk chunk, boolean save);
@Shadow @Final private IChunkLoader chunkLoader;
@Shadow @Final private static double UNLOAD_QUEUE_RESIZE_FACTOR;
@Overwrite
public boolean unloadChunks() {
if (!this.world.savingDisabled) {
long now = System.currentTimeMillis();
long unloadAfter = world.paperConfig.delayChunkUnloadsBy;
SlackActivityAccountant activityAccountant = world.getMinecraftServer().slackActivityAccountant;
activityAccountant.startActivity(0.5);
ObjectIterator<Entry<Chunk>> it = chunks.long2ObjectEntrySet().fastIterator();
int remainingChunks = chunks.size();
int targetSize = Math.min(remainingChunks - 100, (int) (remainingChunks * UNLOAD_QUEUE_RESIZE_FACTOR)); // Paper - Make more aggressive
while (it.hasNext()) {
Entry<Chunk> entry = it.next();
Chunk chunk = entry.getValue();
if (chunk != null && chunk.isUnloading()) {
if (chunk.scheduledForUnload != null) {
if (now - chunk.scheduledForUnload <= unloadAfter) continue;
}
if (unloadChunk(chunk, true)) {
it.remove();
}
chunk.setShouldUnload(false);
chunk.scheduledForUnload = null;
if (--remainingChunks <= targetSize && activityAccountant.activityTimeIsExhausted()) break;
}
}
activityAccountant.endActivity();
this.chunkLoader.b(); // OBFHELPER: chunkTick
}
return false;
}
@Redirect(method = "unloadChunk", at = @At(
value = "INVOKE",
target = "it/unimi/dsi/fastutil/longs/Long2ObjectOpenHashMap.remove(J)Ljava/lang/Object;"
))
private Object remove(Long2ObjectOpenHashMap<Chunk> chunks, long chunkHash) {
return null;
}
@Overwrite
public String getName() {
return "ServerChunkCache: " + chunks.size();
}
}

View File

@@ -0,0 +1,39 @@
package io.akarin.server.mixin.cps;
import java.util.Set;
import org.bukkit.craftbukkit.CraftWorld;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import net.minecraft.server.Chunk;
import net.minecraft.server.WorldServer;
@Mixin(value = CraftWorld.class, remap = false)
public abstract class MixinCraftWorld {
@Shadow @Final private WorldServer world;
@Redirect(method = "processChunkGC()V", at = @At(
value = "INVOKE",
target = "java/util/Set.contains(Ljava/lang/Object;)Z",
opcode = Opcodes.INVOKEINTERFACE
))
public boolean checkUnloading(Set<Long> set, Object chunkHash) {
return false;
}
@Redirect(method = "regenerateChunk", at = @At(
value = "INVOKE",
target = "java/util/Set.remove(Ljava/lang/Object;)Z",
opcode = Opcodes.INVOKEINTERFACE
))
public boolean regenChunk(Set<Long> set, Object chunkHash) {
Chunk chunk = world.getChunkProviderServer().chunks.get(chunkHash);
if (chunk != null) chunk.setShouldUnload(false);
return true;
}
}

View File

@@ -1,14 +0,0 @@
package io.akarin.server.mixin.nsc;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import net.minecraft.server.PlayerConnection;
@Mixin(value = PlayerConnection.class, remap = false)
public class MixinPlayerConnection {
@Overwrite
private long d() {
return System.currentTimeMillis(); // nanoTime() / 1000000L
}
}

View File

@@ -7,6 +7,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.spigotmc.SpigotConfig; import org.spigotmc.SpigotConfig;
@@ -17,41 +18,52 @@ import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import io.akarin.api.LocalAddress; import io.akarin.api.internal.LocalAddress;
import io.akarin.server.core.AkarinGlobalConfig; import io.akarin.server.core.AkarinGlobalConfig;
import io.akarin.server.core.ChannelAdapter;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel; import io.netty.channel.ServerChannel;
import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.server.ChatComponentText; import net.minecraft.server.ChatComponentText;
import net.minecraft.server.EnumProtocolDirection;
import net.minecraft.server.HandshakeListener;
import net.minecraft.server.LegacyPingHandler;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.NetworkManager; import net.minecraft.server.NetworkManager;
import net.minecraft.server.PacketDecoder;
import net.minecraft.server.PacketEncoder;
import net.minecraft.server.PacketPlayOutKickDisconnect; import net.minecraft.server.PacketPlayOutKickDisconnect;
import net.minecraft.server.PacketPrepender;
import net.minecraft.server.PacketSplitter;
import net.minecraft.server.ServerConnection; import net.minecraft.server.ServerConnection;
@Mixin(value = ServerConnection.class, remap = false) @Mixin(value = ServerConnection.class, remap = false)
public class NonblockingServerConnection { public abstract class NonblockingServerConnection {
private final static Logger logger = LogManager.getLogger("NSC"); private final static Logger logger = LogManager.getLogger("NSC");
/** /**
* Contains all endpoints added to this NetworkSystem * Contains all endpoints added to this NetworkSystem
*/ */
@Shadow @Mutable @Final private List<ChannelFuture> g; @Shadow(aliases = "g") @Mutable @Final private List<ChannelFuture> endPoints;
/** /**
* A list containing all NetworkManager instances of all endpoints * A list containing all NetworkManager instances of all endpoints
*/ */
@Shadow @Mutable @Final private List<NetworkManager> h; @Shadow(aliases = "h") @Mutable @Final private List<NetworkManager> networkManagers;
@Overwrite @Overwrite
private void addPending() {} // just keep compatibility private void addPending() {} // just keep compatibility
@Shadow @Final private MinecraftServer f; @Shadow(aliases = "f") @Final private MinecraftServer server;
/** /**
* Adds channels (endpoint) that listens on publicly accessible network ports * Adds channels (endpoint) that listens on publicly accessible network ports
@@ -65,7 +77,7 @@ public class NonblockingServerConnection {
Class<? extends ServerChannel> channelClass; Class<? extends ServerChannel> channelClass;
EventLoopGroup loopGroup; EventLoopGroup loopGroup;
if (Epoll.isAvailable() && this.f.af()) { // PAIL: MinecraftServer::useNativeTransport if (Epoll.isAvailable() && this.server.af()) { // OBFHELPER: MinecraftServer::useNativeTransport
channelClass = EpollServerSocketChannel.class; channelClass = EpollServerSocketChannel.class;
loopGroup = ServerConnection.b.c(); loopGroup = ServerConnection.b.c();
logger.info("Using epoll channel type"); logger.info("Using epoll channel type");
@@ -75,8 +87,27 @@ public class NonblockingServerConnection {
logger.info("Using nio channel type"); logger.info("Using nio channel type");
} }
ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(ChannelAdapter.create(h)).group(loopGroup); ServerBootstrap bootstrap = new ServerBootstrap().channel(channelClass).childHandler(new ChannelInitializer<Channel>() {
synchronized (g) { @Override
protected void initChannel(Channel channel) throws Exception {
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException ex) {
;
}
channel.pipeline().addLast("timeout", new ReadTimeoutHandler(30))
.addLast("legacy_query", new LegacyPingHandler(MinecraftServer.getServer().getServerConnection()))
.addLast("splitter", new PacketSplitter()).addLast("decoder", new PacketDecoder(EnumProtocolDirection.SERVERBOUND))
.addLast("prepender", new PacketPrepender()).addLast("encoder", new PacketEncoder(EnumProtocolDirection.CLIENTBOUND));
NetworkManager manager = new NetworkManager(EnumProtocolDirection.SERVERBOUND);
networkManagers.add(manager);
channel.pipeline().addLast("packet_handler", manager);
manager.setPacketListener(new HandshakeListener(MinecraftServer.getServer(), manager));
}
}).group(loopGroup);
synchronized (endPoints) {
data.addAll(Lists.transform(AkarinGlobalConfig.extraAddress, s -> { data.addAll(Lists.transform(AkarinGlobalConfig.extraAddress, s -> {
String[] info = s.split(":"); String[] info = s.split(":");
try { try {
@@ -87,19 +118,19 @@ public class NonblockingServerConnection {
return null; return null;
} }
})); }));
data.forEach(address -> g.add(bootstrap.localAddress(address.host(), address.port()).bind().syncUninterruptibly())); // supports multi-port bind data.forEach(address -> endPoints.add(bootstrap.localAddress(address.host(), address.port()).bind().syncUninterruptibly())); // supports multi-port bind
} }
} }
@Shadow public volatile boolean d; // PAIL: neverTerminate @Shadow public volatile boolean d; // OBFHELPER: neverTerminate
/** /**
* Shuts down all open endpoints * Shuts down all open endpoints
*/ */
public void b() { public void b() {
this.d = false; this.d = false;
try { try {
synchronized (g) { // safe fixes synchronized (endPoints) { // safe fixes
for (ChannelFuture channel : g) channel.channel().close().sync(); for (ChannelFuture channel : endPoints) channel.channel().close().sync();
} }
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
logger.error("Interrupted whilst closing channel"); logger.error("Interrupted whilst closing channel");
@@ -108,15 +139,15 @@ public class NonblockingServerConnection {
public void processPackets(NetworkManager manager) { public void processPackets(NetworkManager manager) {
try { try {
manager.a(); // PAIL: NetworkManager::processReceivedPackets manager.a(); // OBFHELPER: NetworkManager::processReceivedPackets
} catch (Exception ex) { } catch (Exception ex) {
logger.warn("Failed to handle packet for {}", new Object[] { manager.getSocketAddress(), ex }); logger.warn("Failed to handle packet for {}", manager.getSocketAddress(), ex);
final ChatComponentText kick = new ChatComponentText("Internal server error"); final ChatComponentText message = new ChatComponentText("Internal server error");
manager.sendPacket(new PacketPlayOutKickDisconnect(kick), new GenericFutureListener<Future<? super Void>>() { manager.sendPacket(new PacketPlayOutKickDisconnect(message), new GenericFutureListener<Future<? super Void>>() {
@Override @Override
public void operationComplete(Future<? super Void> future) throws Exception { public void operationComplete(Future<? super Void> future) throws Exception {
manager.close(kick); manager.close(message);
} }
}, new GenericFutureListener[0]); }, new GenericFutureListener[0]);
manager.stopReading(); manager.stopReading();
@@ -128,16 +159,16 @@ public class NonblockingServerConnection {
*/ */
@Overwrite @Overwrite
public void c() throws InterruptedException { public void c() throws InterruptedException {
synchronized (h) { synchronized (networkManagers) {
// Spigot - This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order // Spigot - This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
if (SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % SpigotConfig.playerShuffle == 0) { if (SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % SpigotConfig.playerShuffle == 0) {
Collections.shuffle(h); Collections.shuffle(networkManagers);
} }
Iterator<NetworkManager> it = h.iterator(); Iterator<NetworkManager> it = networkManagers.iterator();
while (it.hasNext()) { while (it.hasNext()) {
NetworkManager manager = it.next(); NetworkManager manager = it.next();
if (manager.h()) continue; // PAIL: NetworkManager::hasNoChannel if (manager.h()) continue; // OBFHELPER: NetworkManager::hasNoChannel
if (manager.isConnected()) { if (manager.isConnected()) {
processPackets(manager); processPackets(manager);

View File

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

View File

@@ -0,0 +1,34 @@
package io.akarin.server.mixin.optimization;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import net.minecraft.server.AxisAlignedBB;
import net.minecraft.server.Entity;
import net.minecraft.server.Material;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
@Mixin(value = Entity.class, remap = false)
public abstract class MixinEntity {
@Shadow public World world;
@Shadow public abstract AxisAlignedBB getBoundingBox();
private boolean isInLava;
private int lastLavaCheck = Integer.MIN_VALUE;
@Overwrite // OBFHELPER: isInLava
public boolean au() {
/*
* This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285)
* Thanks @Poweruser
*/
int currentTick = MinecraftServer.currentTick;
if (this.lastLavaCheck != currentTick) {
this.lastLavaCheck = currentTick;
this.isInLava = this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
}
return this.isInLava;
}
}

View File

@@ -42,20 +42,21 @@ import net.minecraft.server.World;
@Mixin(value = EntityHorseAbstract.class, remap = false) @Mixin(value = EntityHorseAbstract.class, remap = false)
public abstract class MixinEntityHorseAbstract extends Entity { public abstract class MixinEntityHorseAbstract extends Entity {
@Shadow @Final private static DataWatcherObject<Optional<UUID>> bJ; @Shadow(aliases = "bJ") @Final private static DataWatcherObject<Optional<UUID>> OWNER_UNIQUE_ID;
@Nullable private Optional<UUID> cachedOwnerId; @Nullable private Optional<UUID> cachedOwnerId;
@Nullable
@Overwrite @Overwrite
@Nullable public UUID getOwnerUUID() { public UUID getOwnerUUID() {
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(bJ); if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(OWNER_UNIQUE_ID);
return cachedOwnerId.orNull(); return cachedOwnerId.orNull();
} }
@Overwrite @Overwrite
public void setOwnerUUID(@Nullable UUID uuid) { public void setOwnerUUID(@Nullable UUID uuid) {
cachedOwnerId = Optional.fromNullable(uuid); cachedOwnerId = Optional.fromNullable(uuid);
datawatcher.set(bJ, cachedOwnerId); datawatcher.set(OWNER_UNIQUE_ID, cachedOwnerId);
} }
/** /**

View File

@@ -46,8 +46,9 @@ public abstract class MixinEntityTameableAnimal extends Entity {
@Nullable private Optional<UUID> cachedOwnerId; @Nullable private Optional<UUID> cachedOwnerId;
@Nullable
@Overwrite @Overwrite
@Nullable public UUID getOwnerUUID() { public UUID getOwnerUUID() {
if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by); if (cachedOwnerId == null) cachedOwnerId = datawatcher.get(by);
return cachedOwnerId.orNull(); return cachedOwnerId.orNull();
} }

View File

@@ -0,0 +1,47 @@
package io.akarin.server.mixin.optimization;
import java.io.File;
import java.io.FileOutputStream;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import com.destroystokyo.paper.exception.ServerInternalException;
import net.minecraft.server.IDataManager;
import net.minecraft.server.MCUtil;
import net.minecraft.server.NBTCompressedStreamTools;
import net.minecraft.server.NBTTagCompound;
import net.minecraft.server.PersistentBase;
import net.minecraft.server.PersistentCollection;
@Mixin(value = PersistentCollection.class, remap = false)
public abstract class MixinPersistentCollection {
@Shadow(aliases = "b") @Final private IDataManager dataManager;
@Overwrite
private void a(PersistentBase persistentbase) {
if (this.dataManager == null) return;
File file = this.dataManager.getDataFile(persistentbase.id);
if (file == null) return;
NBTTagCompound nbttagcompound = new NBTTagCompound();
nbttagcompound.set("data", persistentbase.b(new NBTTagCompound()));
// Akarin start
MCUtil.scheduleAsyncTask(() -> {
try {
FileOutputStream fileoutputstream = new FileOutputStream(file);
NBTCompressedStreamTools.a(nbttagcompound, fileoutputstream);
fileoutputstream.close();
} catch (Exception exception) {
exception.printStackTrace();
ServerInternalException.reportInternalException(exception); // Paper
}
});
// Akarin end
}
}

View File

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

View File

@@ -0,0 +1,510 @@
package io.akarin.server.mixin.optimization;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.ArrayUtils;
import org.bukkit.event.block.BlockRedstoneEvent;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Overwrite;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import net.minecraft.server.BaseBlockPosition;
import net.minecraft.server.Block;
import net.minecraft.server.BlockDiodeAbstract;
import net.minecraft.server.BlockObserver;
import net.minecraft.server.BlockPiston;
import net.minecraft.server.BlockPosition;
import net.minecraft.server.BlockRedstoneComparator;
import net.minecraft.server.BlockRedstoneTorch;
import net.minecraft.server.BlockRedstoneWire;
import net.minecraft.server.BlockRepeater;
import net.minecraft.server.Blocks;
import net.minecraft.server.EnumDirection;
import net.minecraft.server.IBlockAccess;
import net.minecraft.server.IBlockData;
import net.minecraft.server.Material;
import net.minecraft.server.World;
@Mixin(value = BlockRedstoneWire.class, remap = false)
public abstract class PandaRedstoneWire extends Block {
protected PandaRedstoneWire(Material material) {
super(material);
}
/** Positions that need to be turned off **/
private List<BlockPosition> turnOff = Lists.newArrayList();
/** Positions that need to be checked to be turned on **/
private List<BlockPosition> turnOn = Lists.newArrayList();
/** Positions of wire that was updated already (Ordering determines update order and is therefore required!) **/
private final Set<BlockPosition> updatedRedstoneWire = Sets.newLinkedHashSet();
/** Ordered arrays of the facings; Needed for the update order.
* I went with a vertical-first order here, but vertical last would work to.
* However it should be avoided to update the vertical axis between the horizontal ones as this would cause unneeded directional behavior. **/
private static final EnumDirection[] facingsHorizontal = {EnumDirection.WEST, EnumDirection.EAST, EnumDirection.NORTH, EnumDirection.SOUTH};
private static final EnumDirection[] facingsVertical = {EnumDirection.DOWN, EnumDirection.UP};
private static final EnumDirection[] facings = ArrayUtils.addAll(facingsVertical, facingsHorizontal);
/** Offsets for all surrounding blocks that need to receive updates **/
private static final BaseBlockPosition[] surroundingBlocksOffset;
static {
Set<BaseBlockPosition> set = Sets.newLinkedHashSet();
for (EnumDirection facing : facings) {
set.add(facing.getDirectionPosition());
}
for (EnumDirection facing1 : facings) {
BaseBlockPosition v1 = facing1.getDirectionPosition();
for (EnumDirection facing2 : facings) {
BaseBlockPosition v2 = facing2.getDirectionPosition();
set.add(new BaseBlockPosition(v1.getX() + v2.getX(), v1.getY() + v2.getY(), v1.getZ() + v2.getZ()));
}
}
set.remove(BaseBlockPosition.ZERO);
surroundingBlocksOffset = set.toArray(new BaseBlockPosition[set.size()]);
}
@Shadow(aliases = "g") private boolean canProvidePower;
@Shadow public abstract int getPower(World world, BlockPosition pos, int strength);
@Shadow(aliases = "b") public abstract boolean isPowerSourceAt(IBlockAccess worldIn, BlockPosition pos, EnumDirection side);
@Inject(method = "e", at = @At("HEAD"), cancellable = true)
private void onUpdateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
this.updateSurroundingRedstone(worldIn, pos);
cir.setReturnValue(state);
}
@Inject(method = "a*", at = @At("HEAD"), cancellable = true)
private void onCalculateCurrentChanges(World worldIn, BlockPosition pos1, BlockPosition pos2, IBlockData state, CallbackInfoReturnable<IBlockData> cir) {
this.calculateCurrentChanges(worldIn, pos1);
cir.setReturnValue(state);
}
/**
* Recalculates all surrounding wires and causes all needed updates
*
* @author panda
*
* @param world World
* @param pos Position that needs updating
*/
private void updateSurroundingRedstone(World world, BlockPosition pos) {
// Recalculate the connected wires
this.calculateCurrentChanges(world, pos);
// Set to collect all the updates, to only execute them once. Ordering required.
Set<BlockPosition> blocksNeedingUpdate = Sets.newLinkedHashSet();
// Add the needed updates
for (BlockPosition posi : this.updatedRedstoneWire) {
this.addBlocksNeedingUpdate(world, posi, blocksNeedingUpdate);
}
// Add all other updates to keep known behaviors
// They are added in a backwards order because it preserves a commonly used behavior with the update order
Iterator<BlockPosition> it = Lists.newLinkedList(this.updatedRedstoneWire).descendingIterator();
while (it.hasNext()) {
this.addAllSurroundingBlocks(it.next(), blocksNeedingUpdate);
}
// Remove updates on the wires as they just were updated
blocksNeedingUpdate.removeAll(this.updatedRedstoneWire);
/*
* Avoid unnecessary updates on the just updated wires A huge scale test
* showed about 40% more ticks per second It's probably less in normal
* usage but likely still worth it
*/
this.updatedRedstoneWire.clear();
// Execute updates
for (BlockPosition posi : blocksNeedingUpdate) {
world.applyPhysics(posi, (BlockRedstoneWire) (Object) this, false);
}
}
/**
* Turns on or off all connected wires
*
* @param worldIn World
* @param position Position of the wire that received the update
*/
private void calculateCurrentChanges(World worldIn, BlockPosition position) {
// Turn off all connected wires first if needed
if (worldIn.getType(position).getBlock() == (BlockRedstoneWire) (Object) this) {
turnOff.add(position);
} else {
// In case this wire was removed, check the surrounding wires
this.checkSurroundingWires(worldIn, position);
}
while (!turnOff.isEmpty()) {
BlockPosition pos = turnOff.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
this.canProvidePower = true;
int wirePower = this.getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
wirePower--;
int newPower = Math.max(blockPower, wirePower);
// Akarin start - BlockRedstoneEvent
if (oldPower != newPower) {
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
worldIn.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
}
// Akarin end
// Power lowered?
if (newPower < oldPower) {
// If it's still powered by a direct source (but weaker) mark for turn on
if (blockPower > 0 && !this.turnOn.contains(pos)) {
this.turnOn.add(pos);
}
// Set all the way to off for now, because wires that were powered by this need to update first
setWireState(worldIn, pos, state, 0);
// Power rose?
} else if (newPower > oldPower) {
// Set new Power
this.setWireState(worldIn, pos, state, newPower);
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(worldIn, pos);
}
// Now all needed wires are turned off. Time to turn them on again if there is a power source.
while (!this.turnOn.isEmpty()) {
BlockPosition pos = this.turnOn.remove(0);
if (pos == null) continue; // Akarin
IBlockData state = worldIn.getType(pos);
int oldPower = state.get(BlockRedstoneWire.POWER).intValue();
this.canProvidePower = false;
int blockPower = worldIn.z(pos); // OBFHELPER: isBlockIndirectlyGettingPowered
this.canProvidePower = true;
int wirePower = this.getSurroundingWirePower(worldIn, pos);
// Lower the strength as it moved a block
wirePower--;
int newPower = Math.max(blockPower, wirePower);
// Akarin start - BlockRedstoneEvent
BlockRedstoneEvent event = new BlockRedstoneEvent(worldIn.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), oldPower, newPower);
worldIn.getServer().getPluginManager().callEvent(event);
newPower = event.getNewCurrent();
// Akarin end
if (newPower > oldPower) {
setWireState(worldIn, pos, state, newPower);
} else if (newPower < oldPower) {
// Add warning
}
// Check if surrounding wires need to change based on the current/new state and add them to the lists
this.checkSurroundingWires(worldIn, pos);
}
this.turnOff.clear();
this.turnOn.clear();
}
/**
* Checks if an wire needs to be marked for update depending on the power next to it
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire that might need to change
* @param otherPower Power of the wire next to it
*/
private void addWireToList(World worldIn, BlockPosition pos, int otherPower) {
IBlockData state = worldIn.getType(pos);
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
int power = state.get(BlockRedstoneWire.POWER).intValue();
// Could get powered stronger by the neighbor?
if (power < (otherPower - 1) && !this.turnOn.contains(pos)) {
// Mark for turn on check.
this.turnOn.add(pos);
}
// Should have powered the neighbor? Probably was powered by it and is in turn off phase.
if (power > otherPower && !this.turnOff.contains(pos)) {
// Mark for turn off check.
this.turnOff.add(pos);
}
}
}
/**
* Checks if the wires around need to get updated depending on this wires state.
* Checks all wires below before the same layer before on top to keep
* some more rotational symmetry around the y-axis.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
*/
private void checkSurroundingWires(World worldIn, BlockPosition pos) {
IBlockData state = worldIn.getType(pos);
int ownPower = 0;
if (state.getBlock() == (BlockRedstoneWire) (Object) this) {
ownPower = state.get(BlockRedstoneWire.POWER).intValue();
}
// Check wires on the same layer first as they appear closer to the wire
for (EnumDirection facing : facingsHorizontal) {
BlockPosition offsetPos = pos.shift(facing);
if (facing.getAxis().isHorizontal()) {
this.addWireToList(worldIn, offsetPos, ownPower);
}
}
for (EnumDirection facingVertical : facingsVertical) {
BlockPosition offsetPos = pos.shift(facingVertical);
boolean solidBlock = worldIn.getType(offsetPos).k(); // OBFHELPER: isBlockNormalCube
for (EnumDirection facingHorizontal : facingsHorizontal) {
// wire can travel upwards if the block on top doesn't cut the wire (is non-solid)
// it can travel down if the block below is solid and the block "diagonal" doesn't cut off the wire (is non-solid)
if ((facingVertical == EnumDirection.UP && !solidBlock) || (facingVertical == EnumDirection.DOWN && solidBlock && !worldIn.getType(offsetPos.shift(facingHorizontal)).k())) { // OBFHELPER: isBlockNormalCube
this.addWireToList(worldIn, offsetPos.shift(facingHorizontal), ownPower);
}
}
}
}
/**
* Gets the maximum power of the surrounding wires
*
* @author panda
*
* @param worldIn World
* @param pos Position of the asking wire
* @return The maximum power of the wires that could power the wire at pos
*/
private int getSurroundingWirePower(World worldIn, BlockPosition pos) {
int wirePower = 0;
for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition offsetPos = pos.shift(enumfacing);
// Wires on the same layer
wirePower = this.getPower(worldIn, offsetPos, wirePower);
// Block below the wire need to be solid (Upwards diode of slabs/stairs/glowstone) and no block should cut the wire
if(worldIn.getType(offsetPos).l() && !worldIn.getType(pos.up()).l()) { // OBFHELPER: isNormalCube
wirePower = this.getPower(worldIn, offsetPos.up(), wirePower);
// Only get from power below if no block is cutting the wire
} else if (!worldIn.getType(offsetPos).l()) { // OBFHELPER: isNormalCube
wirePower = this.getPower(worldIn, offsetPos.down(), wirePower);
}
}
return wirePower;
}
/**
* Adds all blocks that need to receive an update from a redstone change in this position.
* This means only blocks that actually could change.
*
* @author panda
*
* @param worldIn World
* @param pos Position of the wire
* @param set Set to add the update positions too
*/
private void addBlocksNeedingUpdate(World worldIn, BlockPosition pos, Set<BlockPosition> set) {
List<EnumDirection> connectedSides = this.getSidesToPower(worldIn, pos);
// Add the blocks next to the wire first (closest first order)
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
// canConnectTo() is not the nicest solution here as it returns true for e.g. the front of a repeater
// canBlockBePowereFromSide catches these cases
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN
&& (!facing.getAxis().isHorizontal() || canConnectToBlock(worldIn.getType(offsetPos), facing))) continue;
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos), facing, true))
set.add(offsetPos);
}
// Later add blocks around the surrounding blocks that get powered
for (EnumDirection facing : facings) {
BlockPosition offsetPos = pos.shift(facing);
if (!connectedSides.contains(facing.opposite()) && facing != EnumDirection.DOWN || !worldIn.getType(offsetPos).l()) continue; // OBFHELPER: isNormalCube
for (EnumDirection facing1 : facings) {
if (this.canBlockBePoweredFromSide(worldIn.getType(offsetPos.shift(facing1)), facing1, false))
set.add(offsetPos.shift(facing1));
}
}
}
/**
* Checks if a block can get powered from a side.
* This behavior would better be implemented per block type as follows:
* - return false as default. (blocks that are not affected by redstone don't need to be updated, it doesn't really hurt if they are either)
* - return true for all blocks that can get powered from all side and change based on it (doors, fence gates, trap doors, note blocks, lamps, dropper, hopper, TNT, rails, possibly more)
* - implement own logic for pistons, repeaters, comparators and redstone torches
* The current implementation was chosen to keep everything in one class.
*
* Why is this extra check needed?
* 1. It makes sure that many old behaviors still work (QC + Pistons).
* 2. It prevents updates from "jumping".
* Or rather it prevents this wire to update a block that would get powered by the next one of the same line.
* This is to prefer as it makes understanding the update order of the wire really easy. The signal "travels" from the power source.
*
* @author panda
*
* @param state State of the block
* @param side Side from which it gets powered
* @param isWire True if it's powered by a wire directly, False if through a block
* @return True if the block can change based on the power level it gets on the given side, false otherwise
*/
private boolean canBlockBePoweredFromSide(IBlockData state, EnumDirection side, boolean isWire) {
if (state.getBlock() instanceof BlockPiston && state.get(BlockPiston.FACING) == side.opposite()) {
return false;
}
if (state.getBlock() instanceof BlockDiodeAbstract && state.get(BlockDiodeAbstract.FACING) != side.opposite()) {
if (isWire && state.getBlock() instanceof BlockRedstoneComparator
&& state.get(BlockRedstoneComparator.FACING).k() != side.getAxis() && side.getAxis().isHorizontal()) {
return true;
}
return false;
}
if (state.getBlock() instanceof BlockRedstoneTorch) {
if (isWire || state.get(BlockRedstoneTorch.FACING) != side) {
return false;
}
}
return true;
}
/**
* Creates a list of all horizontal sides that can get powered by a wire.
* The list is ordered the same as the facingsHorizontal.
*
* @param worldIn World
* @param pos Position of the wire
* @return List of all facings that can get powered by this wire
*/
private List<EnumDirection> getSidesToPower(World worldIn, BlockPosition pos) {
List<EnumDirection> retval = Lists.newArrayList();
for (EnumDirection facing : facingsHorizontal) {
if (isPowerSourceAt(worldIn, pos, facing))
retval.add(facing);
}
if (retval.isEmpty()) return Lists.newArrayList(facingsHorizontal);
boolean northsouth = retval.contains(EnumDirection.NORTH) || retval.contains(EnumDirection.SOUTH);
boolean eastwest = retval.contains(EnumDirection.EAST) || retval.contains(EnumDirection.WEST);
if (northsouth) {
retval.remove(EnumDirection.EAST);
retval.remove(EnumDirection.WEST);
}
if (eastwest) {
retval.remove(EnumDirection.NORTH);
retval.remove(EnumDirection.SOUTH);
}
return retval;
}
/**
* Adds all surrounding positions to a set.
* This is the neighbor blocks, as well as their neighbors
*
* @param pos
* @param set
*/
private void addAllSurroundingBlocks(BlockPosition pos, Set<BlockPosition> set) {
for (BaseBlockPosition vect : surroundingBlocksOffset) {
set.add(pos.a(vect)); // OBFHELPER: add
}
}
/**
* Sets the block state of a wire with a new power level and marks for updates
*
* @author panda
*
* @param worldIn World
* @param pos Position at which the state needs to be set
* @param state Old state
* @param power Power it should get set to
*/
private void setWireState(World worldIn, BlockPosition pos, IBlockData state, int power) {
state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(power));
worldIn.setTypeAndData(pos, state, 2);
updatedRedstoneWire.add(pos);
}
/**
* @author panda
* @reason Uses local surrounding block offset list for notifications.
*
* @param world The world
* @param pos The position
* @param state The block state
*/
@Override
@Overwrite
public void onPlace(World world, BlockPosition pos, IBlockData state) {
this.updateSurroundingRedstone(world, pos);
for (BaseBlockPosition vec : surroundingBlocksOffset) {
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
}
}
/**
* @author panda
* @reason Uses local surrounding block offset list for notifications.
*
* @param world The world
* @param pos The position
*/
@Override
@Overwrite
public void remove(World world, BlockPosition pos, IBlockData state) {
super.remove(world, pos, state);
this.updateSurroundingRedstone(world, pos);
for (BaseBlockPosition vec : surroundingBlocksOffset) {
world.applyPhysics(pos.a(vec), this, false); // OBFHELPER: add
}
}
/**
* @author panda
* @reason Changed to use getSidesToPower() to avoid duplicate implementation.
*
* @param blockState The block state
* @param blockAccess The block access
* @param pos The position
* @param side The side
*/
@Override
@Overwrite
public int b(IBlockData blockState, IBlockAccess blockAccess, BlockPosition pos, EnumDirection side) { // OBFHELPER: getWeakPower
if (!this.canProvidePower) {
return 0;
} else {
if (side == EnumDirection.UP || this.getSidesToPower((World) blockAccess, pos).contains(side)) {
return blockState.get(BlockRedstoneWire.POWER).intValue();
} else {
return 0;
}
}
}
private static boolean canConnectToBlock(IBlockData blockState, @Nullable EnumDirection side) {
Block block = blockState.getBlock();
if (block == Blocks.REDSTONE_WIRE) {
return true;
} else if (Blocks.UNPOWERED_REPEATER.D(blockState)) { // OBFHELPER: isSameDiode
EnumDirection enumdirection1 = blockState.get(BlockRepeater.FACING);
return enumdirection1 == side || enumdirection1.opposite() == side;
} else if (Blocks.dk == blockState.getBlock()) {
return side == blockState.get(BlockObserver.FACING); // OBFHELPER: OBSERVER
} else {
return blockState.m() && side != null; // OBFHELPER: canProvidePower
}
}
}

View File

@@ -16,11 +16,11 @@ import net.minecraft.server.WorldGenBigTree;
* Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547) * Fixes MC-128547(https://bugs.mojang.com/browse/MC-128547)
*/ */
@Mixin(value = WorldGenBigTree.class, remap = false) @Mixin(value = WorldGenBigTree.class, remap = false)
public class WeakBigTree { public abstract class WeakBigTree {
@Shadow private World l; @Shadow(aliases = "l") private World worldReference;
@Inject(method = "generate", at = @At("RETURN")) @Inject(method = "generate", at = @At("RETURN"))
private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable<?> info) { private void clearWorldRef(World world, Random random, BlockPosition pos, CallbackInfoReturnable<?> info) {
l = null; // Akarin - remove references to world objects to avoid memory leaks world = null; // Akarin - remove references to world objects to avoid memory leaks
} }
} }

View File

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

View File

@@ -0,0 +1,56 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.Entity;
import net.minecraft.server.World;
@Mixin(value = Entity.class, remap = false, priority = 1001)
public abstract class MixinEntity {
private static final String ENTITY_RIDABLE_COOLDOWN_FIELD = "Lnet/minecraft/entity/Entity;j:I"; // PUTFIELD: rideCooldown
private static final String ENTITY_PORTAL_COUNTER_FIELD = "Lnet/minecraft/entity/Entity;al:I"; // PUTFIELD: portalCounter
@Shadow protected int j;
@Shadow protected int al;
@Shadow public World world;
// OBFHELPER: onEntityUpdate
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_RIDABLE_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupEntityCooldown(Entity self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
this.j = Math.max(0, this.j - ticks); // OBFHELPER: rideCooldown
}
@Redirect(method = "Y()V", at = @At(value = "FIELD", target = ENTITY_PORTAL_COUNTER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPortalCounter(Entity self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world).getRealTimeTicks();
this.al += ticks; // OBFHELPER: portalCounter
}
}

View File

@@ -0,0 +1,51 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityAgeable;
@Mixin(value = EntityAgeable.class, remap = false)
public abstract class MixinEntityAgeable {
private static final String ENTITY_AGEABLE_SET_GROWING_AGE_METHOD = "Lnet/minecraft/entity/EntityAgeable;setAgeRaw(I)V";
// OBFHELPER: onLivingUpdate
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 0))
public void fixupGrowingUp(EntityAgeable self, int age) {
// Subtract the one the original update method added
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
self.setAgeRaw(Math.min(0, age + diff));
}
@Redirect(method = "n()V", at = @At(value = "INVOKE", target = ENTITY_AGEABLE_SET_GROWING_AGE_METHOD, ordinal = 1))
public void fixupBreedingCooldown(EntityAgeable self, int age) {
// Subtract the one the original update method added
int diff = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks() - 1;
self.setAgeRaw(Math.max(0, age - diff));
}
}

View File

@@ -0,0 +1,55 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityExperienceOrb;
@Mixin(value = EntityExperienceOrb.class, remap = false)
public abstract class MixinEntityExperienceOrb {
private static final String ENTITY_XP_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;c:I"; // PUTFIELD: delayBeforeCanPickup
private static final String ENTITY_XP_AGE_FIELD = "Lnet/minecraft/entity/item/EntityExperienceOrb;b:I"; // PUTFIELD: xpOrbAge
@Shadow public int c; // OBFHELPER: delayBeforeCanPickup
@Shadow public int b; // OBFHELPER: xpOrbAge
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPickupDelay(EntityExperienceOrb self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.c = Math.max(0, this.c - ticks); // OBFHELPER: delayBeforeCanPickup
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_XP_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupAge(EntityExperienceOrb self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.b += ticks; // OBFHELPER: xpOrbAge
}
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityHuman;
@Mixin(value = EntityHuman.class, remap = false)
public abstract class MixinEntityHuman {
private static final String ENTITY_PLAYER_XP_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityHuman;bD:I"; // PUTFIELD: xpCooldown
private static final String ENTITY_PLAYER_SLEEP_TIMER_FIELD = "Lnet/minecraft/entity/player/EntityHuman;sleepTicks:I";
@Shadow public int bD;
@Shadow private int sleepTicks;
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_XP_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupXpCooldown(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.bD = Math.max(0, this.bD - ticks); // OBFHELPER: xpCooldown
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupSleepTimer(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.sleepTicks += ticks;
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_SLEEP_TIMER_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 2))
public void fixupWakeTimer(EntityHuman self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.sleepTicks += ticks;
}
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityInsentient;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.World;
@Mixin(value = EntityInsentient.class, remap = false)
public abstract class MixinEntityInsentient extends EntityLiving {
private static final String ENTITY_LIVING_AGE_FIELD = "Lnet/minecraft/entity/EntityInsentient;ticksFarFromPlayer:I";
public MixinEntityInsentient(World world) {
super(world);
}
@Redirect(method = "doTick()V", at = @At(value = "FIELD", target = ENTITY_LIVING_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupEntityDespawnAge(EntityInsentient self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.ticksFarFromPlayer += ticks;
}
}

View File

@@ -0,0 +1,55 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityItem;
@Mixin(value = EntityItem.class, remap = false)
public abstract class MixinEntityItem {
private static final String ENTITY_ITEM_DELAY_PICKUP_FIELD = "Lnet/minecraft/entity/item/EntityItem;pickupDelay:I";
private static final String ENTITY_ITEM_AGE_FIELD = "Lnet/minecraft/entity/item/EntityItem;age:I";
@Shadow public int age;
@Shadow private int pickupDelay;
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_DELAY_PICKUP_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPickupDelay(EntityItem self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.pickupDelay = Math.max(0, this.pickupDelay - ticks);
}
@Redirect(method = "B_()V", at = @At(value = "FIELD", target = ENTITY_ITEM_AGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupAge(EntityItem self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.age += ticks;
}
}

View File

@@ -0,0 +1,51 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.World;
@Mixin(value = EntityPlayer.class, remap = false)
public abstract class MixinEntityPlayer extends Entity {
private static final String ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD = "Lnet/minecraft/entity/player/EntityPlayer;portalCooldown:I";
public MixinEntityPlayer(World worldIn) {
super(worldIn);
}
// OBFHELPER: decrementTimeUntilPortal
@Redirect(method = "I()V", at = @At(value = "FIELD", target = ENTITY_PLAYER_MP_PORTAL_COOLDOWN_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupPortalCooldown(EntityPlayer self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
this.portalCooldown = Math.max(0, this.portalCooldown - ticks);
}
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.EntityZombieVillager;
@Mixin(value = EntityZombieVillager.class, remap = false)
public abstract class MixinEntityZombieVillager {
private static final String ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD = "Lnet/minecraft/entity/monster/EntityZombieVillager;du()I"; // INVOKE: getConversionProgress
@Shadow(aliases = "du") protected abstract int getConversionProgress();
// OBFHELPER: onUpdate
@Redirect(method = "B_()V", at = @At(value = "INVOKE", target = ENTITY_ZOMBIE_GET_CONVERSION_BOOST_METHOD, ordinal = 0))
public int fixupConversionTimeBoost(EntityZombieVillager self) {
int ticks = (int) ((IMixinRealTimeTicking) self.getWorld()).getRealTimeTicks();
return this.getConversionProgress() * ticks;
}
}

View File

@@ -0,0 +1,30 @@
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
@Mixin(value = MinecraftServer.class, remap = false, priority = 1001)
public abstract class MixinMinecraftServer implements IMixinRealTimeTicking {
private static long lastTickNanos = System.nanoTime();
private static long realTimeTicks = 1;
@Inject(method = "C()V", at = @At("HEAD")) // OBFHELPER: fullTick
public void onTickUpdateRealTimeTicks(CallbackInfo ci) {
long currentNanos = System.nanoTime();
realTimeTicks = (currentNanos - lastTickNanos) / 50000000;
if (realTimeTicks < 1) {
realTimeTicks = 1;
}
lastTickNanos = currentNanos;
}
@Override
public long getRealTimeTicks() {
return realTimeTicks;
}
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerConnection;
@Mixin(value = PlayerConnection.class, remap = false)
public abstract class MixinPlayerConnection {
private static final String NET_HANDLER_PLAY_CHAT_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;chatThrottle:I";
private static final String NET_HANDLER_PLAY_DROP_SPAM_FIELD = "Lnet/minecraft/network/PlayerConnection;itemDropThreshold:I";
@Shadow private volatile int chatThrottle;
@Shadow(aliases = "j") private int itemDropThreshold;
@Shadow @Final private MinecraftServer minecraftServer;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_CHAT_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupChatSpamCheck(PlayerConnection self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
this.chatThrottle = Math.max(0, this.chatThrottle - ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = NET_HANDLER_PLAY_DROP_SPAM_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupDropSpamCheck(PlayerConnection self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.minecraftServer).getRealTimeTicks();
this.itemDropThreshold = Math.max(0, this.itemDropThreshold - ticks);
}
}

View File

@@ -0,0 +1,49 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.PlayerInteractManager;
import net.minecraft.server.World;
@Mixin(value = PlayerInteractManager.class, remap = false)
public abstract class MixinPlayerInteractManager {
private static final String PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD = "Lnet/minecraft/server/management/PlayerInteractManager;currentTick:I";
@Shadow public World world;
@Shadow private int currentTick;
// OBFHELPER: updateBlockRemoving
@Redirect(method = "a()V", at = @At(value = "FIELD", target = PLAYER_INTERACTION_BLOCK_DAMAGE_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupDiggingTime(PlayerInteractManager self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.world.getMinecraftServer()).getRealTimeTicks();
this.currentTick += ticks;
}
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.TileEntity;
import net.minecraft.server.TileEntityBrewingStand;
@Mixin(value = TileEntityBrewingStand.class, remap = false)
public abstract class MixinTileEntityBrewingStand extends TileEntity {
private static final String BREWING_STAND_BREW_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityBrewingStand;brewTime:I";
@Shadow private int brewTime;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = BREWING_STAND_BREW_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupBrewTime(TileEntityBrewingStand self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.brewTime = Math.max(0, this.brewTime - ticks);
}
}

View File

@@ -0,0 +1,64 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MathHelper;
import net.minecraft.server.TileEntity;
import net.minecraft.server.TileEntityFurnace;
@Mixin(value = TileEntityFurnace.class, remap = false)
public abstract class MixinTileEntityFurnace extends TileEntity {
private static final String FURNACE_BURN_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;burnTime:I";
private static final String FURNACE_COOK_TIME_FIELD = "Lnet/minecraft/tileentity/TileEntityFurnace;cookTime:I";
@Shadow private int burnTime;
@Shadow private int cookTime;
@Shadow private int cookTimeTotal;
// OBFHELPER: update
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_BURN_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupBurnTime(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.burnTime = Math.max(0, this.burnTime - ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 0))
public void fixupCookTime(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.cookTime = Math.min(this.cookTimeTotal, this.cookTime + ticks);
}
@Redirect(method = "e()V", at = @At(value = "FIELD", target = FURNACE_COOK_TIME_FIELD, opcode = Opcodes.PUTFIELD, ordinal = 3))
public void fixupCookTimeCooldown(TileEntityFurnace self, int modifier) {
int ticks = (int) ((IMixinRealTimeTicking) this.getWorld()).getRealTimeTicks();
this.cookTime = MathHelper.clamp(this.cookTime - (2 * ticks), 0, this.cookTimeTotal);
}
}

View File

@@ -0,0 +1,46 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
@Mixin(value = World.class, remap = false, priority = 1001)
public abstract class MixinWorld implements IMixinRealTimeTicking {
@Shadow @Nullable public abstract MinecraftServer getMinecraftServer();
@Override
public long getRealTimeTicks() {
if (this.getMinecraftServer() != null) {
return ((IMixinRealTimeTicking) this.getMinecraftServer()).getRealTimeTicks();
}
return 1;
}
}

View File

@@ -0,0 +1,61 @@
/*
* This file is part of Sponge, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.akarin.server.mixin.realtime;
import org.bukkit.World.Environment;
import org.bukkit.generator.ChunkGenerator;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import io.akarin.api.internal.mixin.IMixinRealTimeTicking;
import net.minecraft.server.IDataManager;
import net.minecraft.server.MethodProfiler;
import net.minecraft.server.World;
import net.minecraft.server.WorldData;
import net.minecraft.server.WorldProvider;
import net.minecraft.server.WorldServer;
@Mixin(value = WorldServer.class, remap = false, priority = 1001)
public abstract class MixinWorldServer extends World implements IMixinRealTimeTicking {
protected MixinWorldServer(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, Environment env) {
super(idatamanager, worlddata, worldprovider, methodprofiler, flag, gen, env);
}
@Inject(method = "doTick()V", at = @At("HEAD"))
public void fixTimeOfDay(CallbackInfo ci) {
if (this.getGameRules().getBoolean("doDaylightCycle")) {
// Subtract the one the original tick method is going to add
long diff = this.getRealTimeTicks() - 1;
// Don't set if we're not changing it as other mods might be listening for changes
if (diff > 0) {
this.worldData.setDayTime(this.worldData.getDayTime() + diff);
}
}
}
}

View File

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

File diff suppressed because it is too large Load Diff

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

@@ -11,17 +11,15 @@ import java.util.Random;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
* <b>Akarin Changes Note</b><br> * Akarin Changes Note
* <br> * 1) Expose private members (cause mixin errors)
* 1) Expose private members<br>
* @author cakoyo
*/ */
public class EnchantmentManager { public class EnchantmentManager {
public static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null); // Akarin - private -> public private static final EnchantmentManager.EnchantmentModifierProtection a = new EnchantmentManager.EnchantmentModifierProtection(null);
private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null); private static final EnchantmentManager.EnchantmentModifierDamage b = new EnchantmentManager.EnchantmentModifierDamage(null);
public static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null); // Akarin - private -> public private static final EnchantmentManager.EnchantmentModifierThorns c = new EnchantmentManager.EnchantmentModifierThorns(null);
public static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null); // Akarin - private -> public private static final EnchantmentManager.EnchantmentModifierArthropods d = new EnchantmentManager.EnchantmentModifierArthropods(null);
public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) { public static int getEnchantmentLevel(Enchantment enchantment, ItemStack itemstack) {
if (itemstack.isEmpty()) { if (itemstack.isEmpty()) {
@@ -120,6 +118,7 @@ public class EnchantmentManager {
EnchantmentManager.a.a = 0; EnchantmentManager.a.a = 0;
EnchantmentManager.a.b = damagesource; EnchantmentManager.a.b = damagesource;
a(EnchantmentManager.a, iterable); a(EnchantmentManager.a, iterable);
EnchantmentManager.a.b = null; // Reaper - Fix MC-128547
return EnchantmentManager.a.a; return EnchantmentManager.a.a;
} }
@@ -147,6 +146,11 @@ public class EnchantmentManager {
a(EnchantmentManager.c, entityliving.getItemInMainHand()); a(EnchantmentManager.c, entityliving.getItemInMainHand());
} }
// Reaper start - Fix MC-128547
EnchantmentManager.c.b = null;
EnchantmentManager.c.a = null;
// Reaper end
} }
public static void b(EntityLiving entityliving, Entity entity) { public static void b(EntityLiving entityliving, Entity entity) {
@@ -159,7 +163,10 @@ public class EnchantmentManager {
if (entityliving instanceof EntityHuman) { if (entityliving instanceof EntityHuman) {
a(EnchantmentManager.d, entityliving.getItemInMainHand()); 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) { public static int a(Enchantment enchantment, EntityLiving entityliving) {

File diff suppressed because it is too large Load Diff

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

@@ -2,6 +2,7 @@ package net.minecraft.server;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import java.util.ArrayDeque; // Paper import java.util.ArrayDeque; // Paper
import java.util.ArrayList; import java.util.ArrayList;
@@ -31,10 +32,8 @@ import org.bukkit.inventory.MainHand;
// CraftBukkit end // CraftBukkit end
/** /**
* <b>Akarin Changes Note</b><br> * Akarin Changes Note
* <br> * 2) Add lock to player track (safety issue)
* 1) Add volatile to fields<br>
* @author cakoyo
*/ */
public class EntityPlayer extends EntityHuman implements ICrafting { public class EntityPlayer extends EntityHuman implements ICrafting {
@@ -85,6 +84,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// Paper end // Paper end
private int containerUpdateDelay; // Paper private int containerUpdateDelay; // Paper
// Paper start - cancellable death event
public boolean queueHealthUpdatePacket = false;
public net.minecraft.server.PacketPlayOutUpdateHealth queuedHealthUpdatePacket;
// Paper end
// CraftBukkit start // CraftBukkit start
public String displayName; public String displayName;
@@ -129,7 +132,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (worldserver.worldProvider.m() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) { if (worldserver.worldProvider.m() && worldserver.getWorldData().getGameType() != EnumGamemode.ADVENTURE) {
int i = Math.max(0, minecraftserver.a(worldserver)); int i = Math.max(0, minecraftserver.a(worldserver));
int j = MathHelper.floor(worldserver.getWorldBorder().b(blockposition.getX(), blockposition.getZ())); int j = MathHelper.floor(worldserver.getWorldBorder().b((double) blockposition.getX(), (double) blockposition.getZ()));
if (j < i) { if (j < i) {
i = j; i = j;
@@ -146,7 +149,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit end // CraftBukkit end
@Override
public void a(NBTTagCompound nbttagcompound) { public void a(NBTTagCompound nbttagcompound) {
super.a(nbttagcompound); super.a(nbttagcompound);
if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world if (this.locY > 300) this.locY = 257; // Paper - bring down to a saner Y level if out of world
@@ -174,7 +176,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
public static void a(DataConverterManager dataconvertermanager) { public static void a(DataConverterManager dataconvertermanager) {
dataconvertermanager.a(DataConverterTypes.PLAYER, new DataInspector() { dataconvertermanager.a(DataConverterTypes.PLAYER, new DataInspector() {
@Override
public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) { public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) {
if (nbttagcompound.hasKeyOfType("RootVehicle", 10)) { if (nbttagcompound.hasKeyOfType("RootVehicle", 10)) {
NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle"); NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("RootVehicle");
@@ -189,7 +190,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
}); });
} }
@Override
public void b(NBTTagCompound nbttagcompound) { public void b(NBTTagCompound nbttagcompound) {
super.b(nbttagcompound); super.b(nbttagcompound);
nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId()); nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId());
@@ -221,7 +221,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit start - World fallback code, either respawn location or global spawn // CraftBukkit start - World fallback code, either respawn location or global spawn
@Override
public void spawnIn(World world) { public void spawnIn(World world) {
super.spawnIn(world); super.spawnIn(world);
if (world == null) { if (world == null) {
@@ -246,13 +245,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit end // CraftBukkit end
@Override
public void levelDown(int i) { public void levelDown(int i) {
super.levelDown(i); super.levelDown(i);
this.lastSentExp = -1; this.lastSentExp = -1;
} }
@Override
public void enchantDone(ItemStack itemstack, int i) { public void enchantDone(ItemStack itemstack, int i) {
super.enchantDone(itemstack, i); super.enchantDone(itemstack, i);
this.lastSentExp = -1; this.lastSentExp = -1;
@@ -262,29 +259,24 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.activeContainer.addSlotListener(this); this.activeContainer.addSlotListener(this);
} }
@Override
public void enterCombat() { public void enterCombat() {
super.enterCombat(); super.enterCombat();
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT)); this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTER_COMBAT));
} }
@Override
public void exitCombat() { public void exitCombat() {
super.exitCombat(); super.exitCombat();
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT)); this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.END_COMBAT));
} }
@Override
protected void a(IBlockData iblockdata) { protected void a(IBlockData iblockdata) {
CriterionTriggers.d.a(this, iblockdata); CriterionTriggers.d.a(this, iblockdata);
} }
@Override
protected ItemCooldown l() { protected ItemCooldown l() {
return new ItemCooldownPlayer(this); return new ItemCooldownPlayer(this);
} }
@Override
public void B_() { public void B_() {
// CraftBukkit start // CraftBukkit start
if (this.joining) { if (this.joining) {
@@ -304,7 +296,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// Paper end // Paper end
if (!this.world.isClientSide && !this.activeContainer.canUse(this)) { if (!this.world.isClientSide && !this.activeContainer.canUse(this)) {
this.closeInventory(); this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); // Paper
this.activeContainer = this.defaultContainer; this.activeContainer = this.defaultContainer;
} }
@@ -359,7 +351,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
ItemStack itemstack = this.inventory.getItem(i); ItemStack itemstack = this.inventory.getItem(i);
if (!itemstack.isEmpty() && itemstack.getItem().f()) { if (!itemstack.isEmpty() && itemstack.getItem().f()) {
Packet packet = ((ItemWorldMapBase) itemstack.getItem()).a(itemstack, this.world, this); Packet packet = ((ItemWorldMapBase) itemstack.getItem()).a(itemstack, this.world, (EntityHuman) this);
if (packet != null) { if (packet != null) {
this.playerConnection.sendPacket(packet); this.playerConnection.sendPacket(packet);
@@ -425,7 +417,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
if (this.oldLevel != this.expLevel) { if (this.oldLevel != this.expLevel) {
CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer(this), this.oldLevel, this.expLevel); CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer((EntityPlayer) this), this.oldLevel, this.expLevel);
this.oldLevel = this.expLevel; this.oldLevel = this.expLevel;
} }
// CraftBukkit end // CraftBukkit end
@@ -450,13 +442,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void die(DamageSource damagesource) { public void die(DamageSource damagesource) {
boolean flag = this.world.getGameRules().getBoolean("showDeathMessages"); boolean flag = this.world.getGameRules().getBoolean("showDeathMessages");
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); //this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
// CraftBukkit start - fire PlayerDeathEvent // CraftBukkit start - fire PlayerDeathEvent
if (this.dead) { if (this.dead) {
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag)); // Paper - moved down for cancellable death event
return; return;
} }
java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.inventory.getSize()); java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(this.inventory.getSize());
@@ -474,6 +466,16 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
String deathmessage = chatmessage.toPlainText(); String deathmessage = chatmessage.toPlainText();
org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory); org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory);
// Paper start - cancellable death event
if (event.isCancelled()) {
// make compatible with plugins that might have already set the health in an event listener
if (this.getHealth() <= 0) {
this.setHealth((float) event.getReviveHealth());
}
return;
}
this.playerConnection.sendPacket(new PacketPlayOutCombatEvent(this.getCombatTracker(), PacketPlayOutCombatEvent.EnumCombatEventType.ENTITY_DIED, flag));
// Paper end
String deathMessage = event.getDeathMessage(); String deathMessage = event.getDeathMessage();
@@ -483,9 +485,9 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) { if (scoreboardteambase != null && scoreboardteambase.getDeathMessageVisibility() != ScoreboardTeamBase.EnumNameTagVisibility.ALWAYS) {
if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) { if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) {
this.server.getPlayerList().a(this, chatmessage); this.server.getPlayerList().a((EntityHuman) this, chatmessage);
} else if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) { } else if (scoreboardteambase.getDeathMessageVisibility() == ScoreboardTeamBase.EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) {
this.server.getPlayerList().b(this, chatmessage); this.server.getPlayerList().b((EntityHuman) this, chatmessage);
} }
} else { } else {
this.server.getPlayerList().sendMessage(chatmessage); this.server.getPlayerList().sendMessage(chatmessage);
@@ -501,7 +503,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.inventory.clear(); this.inventory.clear();
} }
this.closeInventory(); this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DEATH); // Paper
this.setSpectatorTarget(this); // Remove spectated target this.setSpectatorTarget(this); // Remove spectated target
// CraftBukkit end // CraftBukkit end
@@ -518,7 +520,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
EntityLiving entityliving = this.ci(); EntityLiving entityliving = this.ci();
if (entityliving != null) { if (entityliving != null) {
EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = EntityTypes.eggInfo.get(EntityTypes.a(entityliving)); EntityTypes.MonsterEggInfo entitytypes_monsteregginfo = (EntityTypes.MonsterEggInfo) EntityTypes.eggInfo.get(EntityTypes.a((Entity) entityliving));
if (entitytypes_monsteregginfo != null) { if (entitytypes_monsteregginfo != null) {
this.b(entitytypes_monsteregginfo.killedByEntityStatistic); this.b(entitytypes_monsteregginfo.killedByEntityStatistic);
@@ -534,7 +536,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.getCombatTracker().g(); this.getCombatTracker().g();
} }
@Override
public void a(Entity entity, int i, DamageSource damagesource) { public void a(Entity entity, int i, DamageSource damagesource) {
if (entity != this) { if (entity != this) {
super.a(entity, i, damagesource); super.a(entity, i, damagesource);
@@ -603,7 +604,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return Lists.newArrayList(); return Lists.newArrayList();
} }
@Override
public boolean damageEntity(DamageSource damagesource, float f) { public boolean damageEntity(DamageSource damagesource, float f) {
if (this.isInvulnerable(damagesource)) { if (this.isInvulnerable(damagesource)) {
return false; return false;
@@ -629,12 +629,21 @@ 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
} }
} }
} }
@Override
public boolean a(EntityHuman entityhuman) { public boolean a(EntityHuman entityhuman) {
return !this.canPvP() ? false : super.a(entityhuman); return !this.canPvP() ? false : super.a(entityhuman);
} }
@@ -644,7 +653,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return this.world.pvpMode; return this.world.pvpMode;
} }
@Override
@Nullable @Nullable
public Entity b(int i) { public Entity b(int i) {
if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154 if (this.isSleeping()) return this; // CraftBukkit - SPIGOT-3154
@@ -683,7 +691,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public boolean a(EntityPlayer entityplayer) { public boolean a(EntityPlayer entityplayer) {
return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer)); return entityplayer.isSpectator() ? this.getSpecatorTarget() == this : (this.isSpectator() ? false : super.a(entityplayer));
} }
@@ -699,13 +706,11 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void receive(Entity entity, int i) { public void receive(Entity entity, int i) {
super.receive(entity, i); super.receive(entity, i);
this.activeContainer.b(); this.activeContainer.b();
} }
@Override
public EntityHuman.EnumBedResult a(BlockPosition blockposition) { public EntityHuman.EnumBedResult a(BlockPosition blockposition) {
EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition); EntityHuman.EnumBedResult entityhuman_enumbedresult = super.a(blockposition);
@@ -713,7 +718,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.b(StatisticList.ab); this.b(StatisticList.ab);
PacketPlayOutBed packetplayoutbed = new PacketPlayOutBed(this, blockposition); PacketPlayOutBed packetplayoutbed = new PacketPlayOutBed(this, blockposition);
this.x().getTracker().a(this, packetplayoutbed); this.x().getTracker().a((Entity) this, (Packet) packetplayoutbed);
this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch);
this.playerConnection.sendPacket(packetplayoutbed); this.playerConnection.sendPacket(packetplayoutbed);
CriterionTriggers.p.a(this); CriterionTriggers.p.a(this);
@@ -722,7 +727,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return entityhuman_enumbedresult; return entityhuman_enumbedresult;
} }
@Override
public void a(boolean flag, boolean flag1, boolean flag2) { public void a(boolean flag, boolean flag1, boolean flag2) {
if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one! if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one!
if (this.isSleeping()) { if (this.isSleeping()) {
@@ -736,7 +740,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public boolean a(Entity entity, boolean flag) { public boolean a(Entity entity, boolean flag) {
Entity entity1 = this.bJ(); Entity entity1 = this.bJ();
@@ -753,7 +756,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public void stopRiding() { public void stopRiding() {
Entity entity = this.bJ(); Entity entity = this.bJ();
@@ -767,23 +769,22 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
if (entity instanceof EntityPlayer) { if (entity instanceof EntityPlayer) {
WorldServer worldServer = (WorldServer) entity.getWorld(); WorldServer worldServer = (WorldServer) entity.getWorld();
worldServer.tracker.untrackEntity(this); worldServer.tracker.untrackEntity(this);
worldServer.tracker.entriesLock.writeLock().lock(); // Akarin - ProtocolSupport will overwrite track method
worldServer.tracker.track(this); worldServer.tracker.track(this);
worldServer.tracker.entriesLock.writeLock().unlock(); // Akarin - ProtocolSupport will overwrite track method
} }
// Paper end // Paper end
} }
@Override
public boolean isInvulnerable(DamageSource damagesource) { public boolean isInvulnerable(DamageSource damagesource) {
return super.isInvulnerable(damagesource) || this.L(); return super.isInvulnerable(damagesource) || this.L();
} }
@Override
protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {} protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {}
@Override
protected void b(BlockPosition blockposition) { protected void b(BlockPosition blockposition) {
if (!this.isSpectator()) { if (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); super.b(blockposition);
} }
@@ -810,9 +811,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
super.a(d0, flag, iblockdata, blockposition); super.a(d0, flag, iblockdata, blockposition);
} }
@Override
public void openSign(TileEntitySign tileentitysign) { public void openSign(TileEntitySign tileentitysign) {
tileentitysign.a(this); tileentitysign.a((EntityHuman) this);
this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition())); this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition()));
} }
@@ -821,7 +821,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return containerCounter; // CraftBukkit return containerCounter; // CraftBukkit
} }
@Override
public void openTileEntity(ITileEntityContainer itileentitycontainer) { public void openTileEntity(ITileEntityContainer itileentitycontainer) {
// CraftBukkit start - Inventory open hook // CraftBukkit start - Inventory open hook
if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).b() != null && this.isSpectator()) { if (false && itileentitycontainer instanceof ILootable && ((ILootable) itileentitycontainer).b() != null && this.isSpectator()) {
@@ -841,7 +840,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public void openContainer(IInventory iinventory) { public void openContainer(IInventory iinventory) {
// CraftBukkit start - Inventory open hook // CraftBukkit start - Inventory open hook
// Copied from below // Copied from below
@@ -871,7 +869,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).setChatModifier((new ChatModifier()).setColor(EnumChatFormat.RED)), true); this.a((new ChatMessage("container.spectatorCantOpen", new Object[0])).setChatModifier((new ChatModifier()).setColor(EnumChatFormat.RED)), true);
} else { } else {
if (this.activeContainer != this.defaultContainer) { if (this.activeContainer != this.defaultContainer) {
this.closeInventory(); this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
} }
if (iinventory instanceof ITileInventory) { if (iinventory instanceof ITileInventory) {
@@ -901,7 +899,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public void openTrade(IMerchant imerchant) { public void openTrade(IMerchant imerchant) {
// CraftBukkit start - Inventory open hook // CraftBukkit start - Inventory open hook
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world)); Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world));
@@ -929,7 +926,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) { public void openHorseInventory(EntityHorseAbstract entityhorseabstract, IInventory iinventory) {
// CraftBukkit start - Inventory open hook // CraftBukkit start - Inventory open hook
Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this)); Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorseabstract, this));
@@ -939,7 +935,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit end // CraftBukkit end
if (this.activeContainer != this.defaultContainer) { if (this.activeContainer != this.defaultContainer) {
this.closeInventory(); this.closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.OPEN_NEW); // Paper
} }
this.nextContainerCounter(); this.nextContainerCounter();
@@ -949,26 +945,23 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.activeContainer.addSlotListener(this); this.activeContainer.addSlotListener(this);
} }
@Override
public void a(ItemStack itemstack, EnumHand enumhand) { public void a(ItemStack itemstack, EnumHand enumhand) {
Item item = itemstack.getItem(); Item item = itemstack.getItem();
if (item == Items.WRITTEN_BOOK) { if (item == Items.WRITTEN_BOOK) {
PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer()); PacketDataSerializer packetdataserializer = new PacketDataSerializer(Unpooled.buffer());
packetdataserializer.a(enumhand); packetdataserializer.a((Enum) enumhand);
this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", packetdataserializer)); this.playerConnection.sendPacket(new PacketPlayOutCustomPayload("MC|BOpen", packetdataserializer));
} }
} }
@Override
public void a(TileEntityCommand tileentitycommand) { public void a(TileEntityCommand tileentitycommand) {
tileentitycommand.c(true); tileentitycommand.c(true);
this.a((TileEntity) tileentitycommand); this.a((TileEntity) tileentitycommand);
} }
@Override
public void a(Container container, int i, ItemStack itemstack) { public void a(Container container, int i, ItemStack itemstack) {
if (!(container.getSlot(i) instanceof SlotResult)) { if (!(container.getSlot(i) instanceof SlotResult)) {
if (container == this.defaultContainer) { if (container == this.defaultContainer) {
@@ -985,7 +978,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a(container, container.a()); this.a(container, container.a());
} }
@Override
public void a(Container container, NonNullList<ItemStack> nonnulllist) { public void a(Container container, NonNullList<ItemStack> nonnulllist) {
this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist)); this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, nonnulllist));
this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried()));
@@ -996,12 +988,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// CraftBukkit end // CraftBukkit end
} }
@Override
public void setContainerData(Container container, int i, int j) { public void setContainerData(Container container, int i, int j) {
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j)); this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, j));
} }
@Override
public void setContainerData(Container container, IInventory iinventory) { public void setContainerData(Container container, IInventory iinventory) {
for (int i = 0; i < iinventory.h(); ++i) { for (int i = 0; i < iinventory.h(); ++i) {
this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i))); this.playerConnection.sendPacket(new PacketPlayOutWindowData(container.windowId, i, iinventory.getProperty(i)));
@@ -1009,9 +999,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void closeInventory() { public void closeInventory() {
CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit // Paper start
closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNKNOWN);
}
public void closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
CraftEventFactory.handleInventoryCloseEvent(this, reason); // CraftBukkit
// Paper end
this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId)); this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId));
this.r(); this.r();
} }
@@ -1023,7 +1017,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
public void r() { public void r() {
this.activeContainer.b(this); this.activeContainer.b((EntityHuman) this);
this.activeContainer = this.defaultContainer; this.activeContainer = this.defaultContainer;
} }
@@ -1043,7 +1037,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void a(Statistic statistic, int i) { public void a(Statistic statistic, int i) {
if (statistic != null) { if (statistic != null) {
this.bZ.b(this, statistic, i); this.bZ.b(this, statistic, i);
@@ -1058,7 +1051,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public void a(Statistic statistic) { public void a(Statistic statistic) {
if (statistic != null) { if (statistic != null) {
this.bZ.setStatistic(this, statistic, 0); this.bZ.setStatistic(this, statistic, 0);
@@ -1073,12 +1065,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
} }
@Override
public void a(List<IRecipe> list) { public void a(List<IRecipe> list) {
this.cr.a(list, this); this.cr.a(list, this);
} }
@Override
public void a(MinecraftKey[] aminecraftkey) { public void a(MinecraftKey[] aminecraftkey) {
ArrayList arraylist = Lists.newArrayList(); ArrayList arraylist = Lists.newArrayList();
MinecraftKey[] aminecraftkey1 = aminecraftkey; MinecraftKey[] aminecraftkey1 = aminecraftkey;
@@ -1099,7 +1089,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.a((List<IRecipe>) arraylist); // CraftBukkit - decompile error this.a((List<IRecipe>) arraylist); // CraftBukkit - decompile error
} }
@Override
public void b(List<IRecipe> list) { public void b(List<IRecipe> list) {
this.cr.b(list, this); this.cr.b(list, this);
} }
@@ -1130,12 +1119,10 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit end // CraftBukkit end
@Override
public void a(IChatBaseComponent ichatbasecomponent, boolean flag) { public void a(IChatBaseComponent ichatbasecomponent, boolean flag) {
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT)); this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent, flag ? ChatMessageType.GAME_INFO : ChatMessageType.CHAT));
} }
@Override
protected void v() { protected void v() {
if (!this.activeItem.isEmpty() && this.isHandRaised()) { if (!this.activeItem.isEmpty() && this.isHandRaised()) {
this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9)); this.playerConnection.sendPacket(new PacketPlayOutEntityStatus(this, (byte) 9));
@@ -1169,6 +1156,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.getDataWatcher().set(EntityPlayer.br, entityplayer.getDataWatcher().get(EntityPlayer.br)); this.getDataWatcher().set(EntityPlayer.br, entityplayer.getDataWatcher().get(EntityPlayer.br));
this.lastSentExp = -1; this.lastSentExp = -1;
this.lastHealthSent = -1.0F; this.lastHealthSent = -1.0F;
setSneaking(false); // NeonPaper - fix MC-10657
this.ch = -1; this.ch = -1;
// this.cr.a((RecipeBook) entityplayer.cr); // CraftBukkit // this.cr.a((RecipeBook) entityplayer.cr); // CraftBukkit
// Paper start - Optimize remove queue // Paper start - Optimize remove queue
@@ -1182,7 +1170,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.setShoulderEntityRight(entityplayer.getShoulderEntityRight()); this.setShoulderEntityRight(entityplayer.getShoulderEntityRight());
} }
@Override
protected void a(MobEffect mobeffect) { protected void a(MobEffect mobeffect) {
super.a(mobeffect); super.a(mobeffect);
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
@@ -1194,14 +1181,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
CriterionTriggers.z.a(this); CriterionTriggers.z.a(this);
} }
@Override
protected void a(MobEffect mobeffect, boolean flag) { protected void a(MobEffect mobeffect, boolean flag) {
super.a(mobeffect, flag); super.a(mobeffect, flag);
this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect)); this.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.getId(), mobeffect));
CriterionTriggers.z.a(this); CriterionTriggers.z.a(this);
} }
@Override
protected void b(MobEffect mobeffect) { protected void b(MobEffect mobeffect) {
super.b(mobeffect); super.b(mobeffect);
this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect())); this.playerConnection.sendPacket(new PacketPlayOutRemoveEntityEffect(this.getId(), mobeffect.getMobEffect()));
@@ -1212,22 +1197,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
CriterionTriggers.z.a(this); CriterionTriggers.z.a(this);
} }
@Override
public void enderTeleportTo(double d0, double d1, double d2) { public void enderTeleportTo(double d0, double d1, double d2) {
this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch); this.playerConnection.a(d0, d1, d2, this.yaw, this.pitch);
} }
@Override
public void a(Entity entity) { public void a(Entity entity) {
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4)); this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 4));
} }
@Override
public void b(Entity entity) { public void b(Entity entity) {
this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5)); this.x().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(entity, 5));
} }
@Override
public void updateAbilities() { public void updateAbilities() {
if (this.playerConnection != null) { if (this.playerConnection != null) {
this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities)); this.playerConnection.sendPacket(new PacketPlayOutAbilities(this.abilities));
@@ -1239,7 +1220,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return (WorldServer) this.world; return (WorldServer) this.world;
} }
@Override
public void a(EnumGamemode enumgamemode) { public void a(EnumGamemode enumgamemode) {
// CraftBukkit start // CraftBukkit start
if (enumgamemode == this.playerInteractManager.getGameMode()) { if (enumgamemode == this.playerInteractManager.getGameMode()) {
@@ -1254,7 +1234,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
// CraftBukkit end // CraftBukkit end
this.playerInteractManager.setGameMode(enumgamemode); this.playerInteractManager.setGameMode(enumgamemode);
this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, enumgamemode.getId())); this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(3, (float) enumgamemode.getId()));
if (enumgamemode == EnumGamemode.SPECTATOR) { if (enumgamemode == EnumGamemode.SPECTATOR) {
this.releaseShoulderEntities(); this.releaseShoulderEntities();
this.stopRiding(); this.stopRiding();
@@ -1266,22 +1246,18 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.cE(); this.cE();
} }
@Override
public boolean isSpectator() { public boolean isSpectator() {
return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR; return this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR;
} }
@Override
public boolean z() { public boolean z() {
return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE; return this.playerInteractManager.getGameMode() == EnumGamemode.CREATIVE;
} }
@Override
public void sendMessage(IChatBaseComponent ichatbasecomponent) { public void sendMessage(IChatBaseComponent ichatbasecomponent) {
this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent)); this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent));
} }
@Override
public boolean a(int i, String s) { public boolean a(int i, String s) {
/* CraftBukkit start /* CraftBukkit start
if ("seed".equals(s) && !this.server.aa()) { if ("seed".equals(s) && !this.server.aa()) {
@@ -1354,7 +1330,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1)); this.playerConnection.sendPacket(new PacketPlayOutResourcePackSend(s, s1));
} }
@Override
public BlockPosition getChunkCoordinates() { public BlockPosition getChunkCoordinates() {
return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ); return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ);
} }
@@ -1384,7 +1359,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
this.removeQueue.remove(Integer.valueOf(entity.getId())); this.removeQueue.remove(Integer.valueOf(entity.getId()));
} }
@Override
protected void G() { protected void G() {
if (this.isSpectator()) { if (this.isSpectator()) {
this.bY(); this.bY();
@@ -1397,13 +1371,13 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
public Entity getSpecatorTarget() { public Entity getSpecatorTarget() {
return this.co == null ? this : this.co; return (Entity) (this.co == null ? this : this.co);
} }
public void setSpectatorTarget(Entity entity) { public void setSpectatorTarget(Entity entity) {
Entity entity1 = this.getSpecatorTarget(); Entity entity1 = this.getSpecatorTarget();
this.co = entity == null ? this : entity; this.co = (Entity) (entity == null ? this : entity);
if (entity1 != this.co) { if (entity1 != this.co) {
this.playerConnection.sendPacket(new PacketPlayOutCamera(this.co)); this.playerConnection.sendPacket(new PacketPlayOutCamera(this.co));
this.playerConnection.a(this.co.locX, this.co.locY, this.co.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit this.playerConnection.a(this.co.locX, this.co.locY, this.co.locZ, this.yaw, this.pitch, TeleportCause.SPECTATE); // CraftBukkit
@@ -1411,7 +1385,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
protected void I() { protected void I() {
if (this.portalCooldown > 0 && !this.worldChangeInvuln) { if (this.portalCooldown > 0 && !this.worldChangeInvuln) {
--this.portalCooldown; --this.portalCooldown;
@@ -1419,7 +1392,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
@Override
public void attack(Entity entity) { public void attack(Entity entity) {
if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) { if (this.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
this.setSpectatorTarget(entity); this.setSpectatorTarget(entity);
@@ -1438,7 +1410,6 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
return listName; // CraftBukkit return listName; // CraftBukkit
} }
@Override
public void a(EnumHand enumhand) { public void a(EnumHand enumhand) {
super.a(enumhand); super.a(enumhand);
this.ds(); this.ds();
@@ -1476,8 +1447,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
} }
// CraftBukkit start - Add per-player time and weather. // CraftBukkit start - Add per-player time and weather.
public volatile long timeOffset = 0; // Akarin - volatile public long timeOffset = 0;
public volatile boolean relativeTime = true; // Akarin - volatile public boolean relativeTime = true;
public long getPlayerTime() { public long getPlayerTime() {
if (this.relativeTime) { if (this.relativeTime) {

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