Compare commits

..

237 Commits
2.2.5 ... 4.1.2

Author SHA1 Message Date
Jikoo
f0a66570d2 Bump version to 4.1.2 for release 2020-06-25 19:39:38 -04:00
Jikoo
201a3578bf Add 1.16 support, drop 1.14 (#141) 2020-06-25 19:38:38 -04:00
Nathaniel Freeman
1228fc2de2 es_ES translation 2020-06-24 07:12:20 -04:00
NotMyFault
2561e75ae5 Fix typo 2020-06-02 19:01:33 -04:00
N0tMyFaultOG
80661b9465 Add german translation 2020-06-02 17:59:31 -04:00
Jikoo
53701da34f Bump version to 4.1.2-SNAPSHOT for development 2020-05-29 10:32:32 -04:00
Jikoo
9cf4712813 Bump version to 4.1.1 for release 2020-05-29 10:32:03 -04:00
Jikoo
abe8fccdb5 Document new nodes 2020-05-29 10:27:50 -04:00
Jikoo
4499bb5453 Don't break behavior on existing setups
Permission to use /openinv and /openender will grant online/offline access so that existing permissions configurations do not have to be redone. Users looking to implement the new permissions will need to deny them.
2020-05-29 10:22:40 -04:00
Phoenix616
5405b7d7b9 Add permission for online/offline opening (Resolves #129) 2020-05-29 10:08:26 -04:00
129fc5e32c631b5007cb2cd8d45d416002b42bab
34199d12a4 Add localization for Brazilian Portuguese 2020-04-30 17:42:10 -04:00
Jikoo
67131ab9f9 Save after adding new keys 2020-04-24 10:47:51 -04:00
Jikoo
723381cb2b Fix IndexOutOfBounds on no args
Closes #128
2020-04-22 12:07:19 -04:00
Jikoo
34e7252d77 Correct lang + replacement mistake with inventory names 2020-04-19 18:04:10 -04:00
Jikoo
a1b2df74cc Save existing vehicles if we had to load player (#22) 2020-04-19 17:54:45 -04:00
Jikoo
7210f9153e Bump version to 4.1.1-SNAPSHOT for development 2020-04-18 08:05:09 -04:00
Jikoo
dcdfac0d3b Bump version to 4.1.0 for release 2020-04-18 08:04:38 -04:00
Jikoo
c7b4554a6c It's been over 3 years :)
The common module was designed to prevent the internal modules depending on the core plugin. With the introduction of localization, this overcomplication became ever more exacerbated.
Probably will play around a bit more to remove freshly introduced static abuse before release.
Closes #61
2020-03-24 21:01:59 -04:00
Jikoo
c51acb4e72 Add basic /searchcontainer
May be subject to change as I (very slowly) work towards console support for commands.
Closes #113
2020-03-15 10:43:08 -04:00
Jikoo
23d41cd6c8 Simplify and expand tab completion 2020-03-15 09:50:51 -04:00
Jikoo
8bc389496b Update cache for Java 8 2020-03-15 09:49:11 -04:00
Jikoo
1cc36d08bd Modify players before other plugins access them
Closes #123
2020-03-11 09:17:17 -04:00
Jikoo
c8938f451d Update copyright 2020-02-01 08:03:15 -05:00
Jikoo
4f2665fedc Improve any/silent container commands
* Merged duplicate code
* Added on/off parameters - closes #120
* Added tab completion for parameters
2020-02-01 08:00:03 -05:00
unixminecraft
7d93bb06ad Updated /sc check command to correctly display if SilentChest is active or not. 2020-01-14 22:40:01 -05:00
Jikoo
19c491411f Bump version to 4.0.9-SNAPSHOT for development 2019-12-12 14:37:03 -05:00
Jikoo
65c6eb2693 Bump version to 4.0.8 for release 2019-12-12 14:36:38 -05:00
Jikoo
ab1f4ace8a Fix AnyContainer breaking modified ender chest sizes 2019-12-12 14:33:42 -05:00
Jikoo
e7dc52bee4 Drop support for 1.13, latest 2 versions only. 2019-12-12 14:21:33 -05:00
Jikoo
a9c5799677 Merge branch 'master' of https://github.com/lishid/OpenInv 2019-12-12 13:59:38 -05:00
Andre LeBlanc
6d6257720a add support for 1.15 2019-12-12 13:59:12 -05:00
Andre LeBlanc
3e235fa688 add support for 1.15 2019-12-12 12:49:21 -05:00
Jikoo
dd02355ac2 Properly remove SilentContainer viewers
Closes #115
2019-12-03 13:50:27 -05:00
Jikoo
4d3f5d67a8 Update copyright
Practically should make it 2020 at this rate, whoops

(cherry picked from commit ff76f2b95a)
2019-12-03 13:32:41 -05:00
Jikoo
7ad5faa2ce Clean up maven instructions
(cherry picked from commit b92543e078)
2019-12-03 13:32:37 -05:00
Jikoo
c28383ef36 Work around 1.14 tab completion issues 2019-10-01 08:30:28 -04:00
Jikoo
5fbab1ab8f Bump version to 4.0.8-SNAPSHOT for development 2019-09-29 19:43:14 -04:00
Jikoo
d97b092978 Bump version to 4.0.7 for release 2019-09-29 19:42:59 -04:00
Jikoo
154bf56c2a Include api-version in plugin.yml
Closes #111, #112
2019-09-29 19:40:06 -04:00
William Blake Galbreath
d65a9caeb2 Add support for varying sizes of inventories 2019-08-01 21:04:23 -04:00
Jikoo
b3c4253606 Bump version to 4.0.7-SNAPSHOT for development 2019-07-21 13:30:02 -04:00
Jikoo
b64000e89b Bump version to 4.0.6 for release 2019-07-21 13:29:45 -04:00
Jikoo
3faf41ef18 More clear error when loot is not generated 2019-07-19 18:16:36 -04:00
Jikoo
db27d25a93 More Bukkit API, less breaking with new versions 2019-07-19 18:14:14 -04:00
Jikoo
0f02e6fe66 Right, 1.13 is a thing 2019-07-19 18:12:06 -04:00
Jikoo
ec07637e9f Bump version to 4.0.6-SNAPSHOT for development 2019-07-19 16:26:24 -04:00
Jikoo
b269f3509c Bump version to 4.0.5 for release 2019-07-19 16:26:03 -04:00
Jikoo
f2e4c8a3ad Fix barrel triggering AnyContainer (#108) 2019-07-19 16:24:58 -04:00
Jikoo
35839f1548 Bump version to 4.0.5-SNAPSHOT for development 2019-07-19 07:21:37 -04:00
Jikoo
a4a462a1fa Bump version to 4.0.4 for release 2019-07-19 07:21:16 -04:00
Jikoo
b9f901af66 Add silent barrels - fixes #106 2019-07-19 07:18:23 -04:00
Jikoo
1333056613 Correct item order - fixes #107 2019-07-19 07:13:11 -04:00
Jikoo
fb23df313a Bump version to 4.0.4-SNAPSHOT for development 2019-05-27 20:53:44 -04:00
Jikoo
96dc400c83 Bump version to 4.0.3 for release 2019-05-27 20:53:02 -04:00
Jikoo
b801d36f12 Turns out api-version below 1.13 is unsupported.
Compiling for 1.8, no api-version.
2019-05-27 20:52:49 -04:00
Jikoo
4c27599dd9 Bump version to 4.0.3-SNAPSHOT for development 2019-05-27 20:23:16 -04:00
Jikoo
64ad0a5267 Bump version to 4.0.2 for release 2019-05-27 20:22:50 -04:00
Jikoo
cba7f44192 Include api-version in plugin.yml (Closes #102) 2019-05-27 20:22:02 -04:00
Jikoo
e8ace6bd06 Update to 1.14.2 2019-05-27 20:12:39 -04:00
Jikoo
fd70f260ba Fix mistake in string comparison utility 2019-05-27 20:05:58 -04:00
Jikoo
be9874ba1d Fix pom changes causing Spigot API to be shaded into final jar 2019-05-27 20:05:06 -04:00
Jikoo
096faef161 Bump version to 4.0.2-SNAPSHOT for development 2019-05-15 19:20:50 -04:00
Jikoo
df96d5106a Bump version to 4.0.1 for release 2019-05-15 19:20:25 -04:00
Jikoo
fe77a1d2e7 Drop support for 1.14, add support for 1.14.1 2019-05-15 19:19:40 -04:00
Jikoo
3096e43540 Update to Java 8 and Minecraft 1.8.8 2019-05-05 22:20:43 -04:00
Jikoo
2939551d65 Delete modules for versions no longer being supported 2019-05-05 18:03:22 -04:00
Jikoo
fb74fd3ced Bump version to 4.0.1-SNAPSHOT for development 2019-05-05 16:55:36 -04:00
Jikoo
393fe1d837 Bump version to 4.0.0 for release 2019-05-05 16:55:12 -04:00
Jikoo
f80df9d83b Copy pasted javadocs are super great 2019-05-05 16:49:41 -04:00
Jikoo
5c006f8ebd QOL: Warn users when plugin is running on an unsupported version 2019-05-05 15:43:41 -04:00
Jikoo
57097e626e Reduce duplicate code in core plugin 2019-05-05 15:39:54 -04:00
Jikoo
0f266c8a64 Custom inventory titles for 1.14 2019-05-05 15:15:17 -04:00
Jikoo
185f037254 Revert API changes, fix SpecialPlayerInventory implementation 2019-05-05 12:40:47 -04:00
Jikoo
e4d0020296 Update to 1.14 (#101) 2019-04-28 20:37:45 -04:00
Jikoo
9e37cbbca8 Prepare for 1.14
To continue providing renamed ender chests/player inventories we can no longer just provide an Inventory, a full InventoryView is required. To avoid confusion the old method has been removed entirely, leading to a major API revision bump.
2019-04-28 20:37:05 -04:00
Jikoo
adc35e9ad5 Correct players not being properly released 2019-04-28 12:11:59 -04:00
Jikoo
a469c732cc Store UUID instead of name for faster re-open lookups 2019-04-28 11:03:00 -04:00
Jikoo
c5c1f47167 Hacky workaround for Java 9 reflection changes (#93) 2019-04-12 20:56:05 -04:00
Jikoo
dc59356f76 Bump version to 3.3.8-SNAPSHOT for development 2019-01-18 18:36:28 -05:00
Jikoo
d497772ad7 Bump version to 3.3.7 for release 2019-01-18 18:35:50 -05:00
Jikoo
4f4d46974a Support 1.13.2 (#94) 2019-01-15 20:01:41 -05:00
Jikoo
96c38895e2 Bump version to 3.3.7-SNAPSHOT for development 2018-08-26 14:47:13 -04:00
Jikoo
5753648d77 Bump version to 3.3.6 for release 2018-08-26 14:46:33 -04:00
Jikoo
3725711b94 Support Spigot 1.13.1 2018-08-26 14:43:21 -04:00
Jikoo
c22e18c096 Correct unnecessary AnyContainer usage 2018-08-26 14:37:29 -04:00
Jikoo
6bff759d72 Bump version to 3.3.6-SNAPSHOT for development 2018-08-13 21:31:46 -04:00
Jikoo
582c7201dd Bump version to 3.3.5 for release 2018-08-13 21:31:17 -04:00
Jikoo
59a8ed70bb Correct double chest logic for Silent/AnyContainer in 1.13 2018-08-09 18:50:24 -04:00
Jikoo
27812c133d Bump version to 3.3.5-SNAPSHOT for development 2018-07-22 09:45:01 -04:00
Jikoo
165f3fb00e Bump version to 3.3.4 for release 2018-07-22 09:44:27 -04:00
Jikoo
60dcb64558 Bump version for alpha release 2018-07-15 12:05:52 -04:00
Jikoo
077b34d304 Support 1.13-pre7 2018-07-15 12:01:45 -04:00
Jikoo
8ce6e2f8d3 Reverse order of wildcard permission check, respect explicit values (#85) 2018-05-26 06:58:56 -04:00
Jikoo
f5dce23f18 Bump version to 3.3.4-SNAPSHOT for development 2018-02-07 18:36:15 -05:00
Jikoo
669cfd72aa Bump version to 3.3.3 for release 2018-02-07 18:35:39 -05:00
Jikoo
9e66885690 First load should not "update" nonexistent config, just use a default 2018-02-07 18:17:33 -05:00
Jikoo
a802769265 Sneaky tabs, trying to slip in there. 2018-02-04 10:35:35 -05:00
Jikoo
50c45b6616 Cleaned up code, updated copyright at long last. 2018-02-04 10:29:09 -05:00
Jikoo
eaf01fa32c Don't cache simultaneously added players forever 2017-11-19 14:46:52 -05:00
Jikoo
4abaa6ea51 Backport spectate abuse to 1.8-1.10 to support PaperSpigot
This may break functionality on C(K)auldron and other server implementations with different remapping names.
2017-11-08 19:07:23 -05:00
Jikoo
7c621bf899 Bump version to 3.3.3-SNAPSHOT for development 2017-11-08 17:53:16 -05:00
Jikoo
f5c682ff2b Bump version to 3.3.2 for release 2017-11-08 17:52:41 -05:00
Jikoo
e652b43670 Classic mistake, changed my mind and only changed part of the code.
Also the actual mistake. Never forget. (#81)
2017-11-08 17:40:01 -05:00
Jikoo
f07cef5621 Bump version to 3.3.2-SNAPSHOT for development 2017-11-07 18:18:33 -05:00
Jikoo
ded6421e33 Bump version to 3.3.1 for release 2017-11-07 18:17:12 -05:00
Jikoo
cbb2b4d232 Lowercase permissions can wait for a major version bump
I don't want to double our permissions checks for backwards compatibility.
2017-11-07 18:16:38 -05:00
Jikoo
48c61eeb82 Support lowercase permissions for consistency. 2017-11-06 19:23:26 -05:00
Jikoo
e3d1af010b Add permissions to alter the default state of Any/SilentChest (#76) 2017-11-05 20:00:22 -05:00
Jikoo
2195677651 Actually fix CME (#71) 2017-08-06 16:35:18 -04:00
Jikoo
c72af5dbac Bump version to 3.3.1-SNAPSHOT for development 2017-07-05 05:53:38 -04:00
Jikoo
931cdb6fb1 Bump version to 3.3.0 for release 2017-07-05 05:52:33 -04:00
Jikoo
9db18613a1 Abuse spectator mode for SilentContainer (#64, #68)
Potentially causes issues with Cauldron and similar mods that use different mapping names.
2017-07-04 13:22:03 -04:00
Jikoo
784935a975 Synchronize cache (for real), updated dependencies
Closes #69
2017-07-03 18:06:18 -04:00
Jikoo
d9abe76531 Bump version to 3.2.2-SNAPSHOT for development 2017-06-09 20:21:30 -04:00
Jikoo
ca85a392aa Correct field name for list of inventory contents (#66) 2017-06-09 20:15:51 -04:00
Jikoo
307f47e4d3 Bump version to 3.2.1-SNAPSHOT for development 2017-06-08 18:44:10 -04:00
Jikoo
96c59f163d Slightly improve API, bump version to 3.2.0 for release
Methods are now properly annotated @Nullable when they may return null. More descriptive exceptions are thrown when issues occur instead of just returning null.
2017-06-08 18:36:01 -04:00
Jikoo
01f147b13c Fix incorrect method call for checking if a block is occluding
Switched to using the API for statistic manipulation.
2017-06-08 18:26:26 -04:00
Jikoo
1c104b775f For improved compatibility, our own Levenshtein implementation 2017-05-28 09:53:59 -04:00
Jikoo
9a53e46dd3 Replaced modern profile with recent
Going forward, the primary file uploaded to Bukkit will probably be the smaller build made with the `recent` profile, supporting only the last 2 versions. The uberjar created by the `all` will be provided as an additional download. OpenInv v3.1.3-SNAPSHOT `all` uberjar currently is 276KB. The `recent` jar is only 81KB. It's getting silly, and will only continue to get worse.
2017-05-26 05:52:35 -04:00
Jikoo
6744e71127 Support 1.12-pre2 (#65) 2017-05-14 11:47:15 -04:00
Jikoo
12c3ebb798 Bump version to 3.1.3-SNAPSHOT for development 2017-02-13 20:02:12 -05:00
Jikoo
32ec8d8147 Bump version to 3.1.2 for release 2017-02-13 20:01:18 -05:00
Jikoo
443e0c489e Synchronize cache (#63) 2017-02-13 19:56:32 -05:00
Jikoo
7ab0003b62 Bump version to 3.1.2-SNAPSHOT for development 2017-01-30 13:38:36 -05:00
Jikoo
12c0cac311 Bump version to 3.1.1 for release 2017-01-30 13:38:10 -05:00
Jikoo
f54481e872 Fix (NoSuchMethod)Exception being caught instead of NoSuchMethodError
Closes #62
2017-01-30 13:35:58 -05:00
Jikoo
f27dd8a233 Bump version to 3.1.1-SNAPSHOT for development 2017-01-20 13:02:47 -05:00
Jikoo
d6df1a0871 Bump version to 3.1.0 for release 2017-01-20 12:59:03 -05:00
Jikoo
e34e51d127 Better support other Player implementations
Closes #60
Removed all volatile comments - they require me to occasionally manually maintain them and NMS/OBC imports are already kept separate
2017-01-20 12:56:31 -05:00
Jikoo
5ca2ae461a Properly use Maven versioning for better support of dependent plugins
Closes #59
Bumped version to 3.1.0-SNAPSHOT for development, really should have bumped the minor version a while back.
2017-01-17 18:06:35 -05:00
Jikoo
4095e8039d RIPo.bukkit.org
repo.bukkit.org is dead, Spigot doesn't host builds that old. Didn't realize that I'd installed it locally when compiling CB 1.4.5 to use with OpenInv. Whoops.

Corrected dependency order - no idea why this compiled at all before. My bad.
2017-01-15 08:08:51 -05:00
Jikoo
73f390ab20 Jitpack's Maven version is pretty outdated. Hopefully fixes build.
The `all` profile is no longer active by default, just in case.
2017-01-15 07:33:53 -05:00
Jikoo
2f7d3bc235 Call InventoryOpenEvent when using SilentChest
Backport fix for incorrect parameter as well, just in case.
Fixed 1.7 and lower forcing all chests' default names to be "Large chest" instead of the translatable string.
2017-01-10 14:12:58 -05:00
Jikoo
ef486032f1 Fix incorrect parameter for SilentChest PacketPlayOutOpenWindow
Probably fixes issues with ProtocolSupport, and if it doesn't there's nothing else I can do as we'll be as close to identical to NMS as possible.
2017-01-10 12:50:24 -05:00
Jikoo
72ef873772 Fixes for shulker boxes and AnyChest in 1.11
Fixes IBlockData used being the default for a shulker box, closes #55
Corrected using wrong method to check if blocked in 1.11.2. Something something NMS version bumps.
2016-12-31 07:05:52 -05:00
Jikoo
9b6e32a775 Fix statistic changes breaking silent/anycontainer in 1.11.1+
C'mon Spigot team, this is why the revision bumps existed in the first place, to allow plugins that deal with NMS to easily and gracefully fail on unsupported versions. 1.11.1 should be R2, 1.11.2 should be R3. I get that NMS isn't officially supported, but honestly, either do away with the versioned packages entirely or keep up with them properly. It's a single string change, it's not like it's prohibitively difficult.
2016-12-29 09:05:01 -05:00
Jikoo
f05110c9b8 Fix getOnlinePlayers call to support all versions
Moved IPlayerDataManager from api module to common module - it is not part of the API as there is no supported way to obtain an instance of it.
2016-12-22 09:55:51 -05:00
Jikoo
a41f89b011 Release any players held by a disabling plugin 2016-12-16 15:13:04 -05:00
Jikoo
d24827ffcb Allow plugins to indicate to OpenInv that a Player is in use
This allows API users to prevent issues caused by multiple different copies of the Player being loaded, such as #49. Multiple instances of the same player could be obtained by calling IOpenInv#loadPlayer, waiting for OpenInv to remove it from the cache, then calling the method again.
2016-12-14 19:49:18 -05:00
Jikoo
802ce28103 Fix issue caused by module cleanup while backporting awarding achievement 2016-12-05 19:02:43 -05:00
Jikoo
f31356b227 Fix permissions mistakes in readme 2016-12-05 19:01:21 -05:00
Jikoo
7942466863 Maven cleanliness and API progress
The project was very messy and due to older Bukkit packaging conventions, 1_4_5 and 1_4_6 were sorted away from the rest of the versioned code. All of the versioned internals are now submodules of the internal module.
Rather than use the hackish existing method of abusing the shade plugin to combine "dependencies" for a dummy assembly project, we're actually using the assembly plugin.
Profiles are still split up between the parent pom and the internal module pom, but they're much more clean.

The API is now its own module and can be compiled and released as a separate file for developers. Soon, Bukkit ticket 20, you'll be closed.
2016-11-30 21:26:56 -05:00
Jikoo
db2cade4e2 Don't convert names in config to UUIDs on the main thread
This is pretty messy, but I can't think of a better way to avoid saving about 5 times in a row. Then again, I did just wake up, so my brain may not be on point yet.
2016-11-27 17:30:39 -05:00
Jikoo
718b4bb5dd Added /searchenchant, fixed a couple little mistakes
Mistakes being breaking UUID-based lookups and /anychest's toggle.
MY BAD, SORRY.
2016-11-27 06:38:24 -05:00
Jikoo
6aa25dd2dc Don't use reflection to increment container counter
Fixes #53 (pretty please with a cherry on top)
2016-11-27 06:13:17 -05:00
Jikoo
0bbcf6cdb2 Strip removed config options, add DisableSaving and bump config version 2016-11-26 17:05:06 -05:00
ShadowRanger
f11d60f78c Reimplement ShadowRanger's configuration updater and path changes
Bumped version for release
2016-11-26 16:57:03 -05:00
Jikoo
8a6b98614f Added support for UUID-based player lookups in 1.7.5+
You could argue that ShadowRanger's conversion of everything to UUID is better, but that would result in us having to contact Mojang's servers simply to fetch a player by UUID for versions < 1.7.5. It seems excessive (not to mention that uncached contact can result in rate limiting) when the server itself will not remember who they are across name changes. If they can re-obtain everything in their inventory, they can re-run /ac.
2016-11-26 15:31:53 -05:00
Jikoo
d7eec528e4 Fix OpenInv/OpenEnder permissions logic a little bit
/openender should respect exempt and crossworld permissions
/openinv should allow opening of own inventory if exempt without override
2016-11-25 22:26:25 -05:00
Jikoo
a10c61168a Add modules for 1.4.5 through 1.10, flesh out readme 2016-11-25 22:23:18 -05:00
Jikoo
5c4886c66b Reimplement /searchender 2016-11-25 16:59:41 -05:00
Jikoo
4335b8dc2c Update plugin to jikoo/master - numerous fixes and changes
* Added permissions to commands in plugin.yml
* Removed item wand functionality - see 3549431fbc for reasoning
* Changed a lot of player loading logic
* Added config option DisableSaving - see Jikoo#6
* Fixed closing SilentChest not dropping item on cursor
* Added SilentChest support for shulker boxes
2016-11-25 16:54:53 -05:00
Jikoo
3bf7225712 Convert to a multi-module Maven setup
This is much more user-friendly - users can either compile a specific module or create a profile to compile for the specific Minecraft versions they're looking to support. It means that it's much easier for people to continue assisting with the development of OpenInv in the future without access to every version of CB since 1.4.5.

This commit restores and updates most of the old system.
2016-11-25 16:10:40 -05:00
Jacob Martin
b76440ab9a Update to 1.11 2016-11-20 19:25:02 -05:00
ShadowRanger
3152605bca Merge pull request #47 from ShadowRanger/master
Update to 1.10
2016-06-09 22:49:02 +10:00
ShadowRanger
eaabd30dc2 Update to 1.10 2016-06-09 22:46:36 +10:00
ShadowRanger
de0d28b12d Update to 1.10 2016-06-09 22:43:20 +10:00
ShadowRanger
c3a8bc9486 Update to 1.10 2016-06-09 22:41:21 +10:00
ShadowRanger
5b77404597 Merge pull request #45 from ShadowRanger/master
Update to 1.9.4
2016-05-13 00:23:03 +10:00
ShadowRanger
d9c1b00b50 Update to 1.9.4 2016-05-13 00:21:37 +10:00
ShadowRanger
2fc439b306 Update to 1.9.4 2016-05-13 00:20:24 +10:00
ShadowRanger
2cb1b01fbf Merge pull request #44 from Tim-Schneider/master
Update to 1.9.4
2016-05-12 13:49:37 +10:00
Tim-Schneider
d16cca9847 fix for minecraft 1.9.4 2016-05-10 21:50:34 +02:00
ShadowRanger
e39f009661 Merge pull request #43 from ShadowRanger/master
Implement Jikoo's data duplication glitch fix
2016-05-05 11:32:40 +10:00
ShadowRanger
1d622b653d Implement Jikoo's data duplication glitch fix 2016-05-05 11:32:01 +10:00
ShadowRanger
582a958195 Merge pull request #42 from ShadowRanger/master
Implement Jikoo's data duplication glitch fix
2016-05-05 11:25:28 +10:00
ShadowRanger
9245b4cc27 Implement Jikoo's data duplication glitch fix 2016-05-04 23:18:20 +10:00
ShadowRanger
ba9396ad5c Implement Jikoo's data duplication glitch fix 2016-05-04 14:38:10 +10:00
ShadowRanger
27de16cba9 Merge pull request #39 from ShadowRanger/master
General refactoring
2016-04-12 14:38:37 +10:00
ShadowRanger
90ea073089 General refactoring 2016-04-12 14:34:46 +10:00
ShadowRanger
4811e53d12 General refactoring 2016-04-12 14:33:44 +10:00
ShadowRanger
c7f38adb3f Implement cbarber's player data glitch and online/offline event changes 2016-04-12 14:25:09 +10:00
ShadowRanger
99a7359be3 General refactoring 2016-03-10 14:04:20 +11:00
ShadowRanger
d42cc3e275 General refactoring 2016-03-10 14:01:56 +11:00
ShadowRanger
b8f4589b87 General refactoring 2016-03-10 13:47:17 +11:00
ShadowRanger
f9ac6804b2 General refactoring 2016-03-10 13:37:28 +11:00
ShadowRanger
62f5d54676 Merge pull request #38 from ShadowRanger/master
Added 1.9 shield slot support
2016-03-09 14:15:40 +11:00
ShadowRanger
8feca3fb21 Added 1.9 shield slot support 2016-03-09 14:14:27 +11:00
ShadowRanger
7b16140ee1 Merge pull request #37 from ShadowRanger/master
Update to CraftBukkit 1.9
2016-03-04 13:48:51 +11:00
ShadowRanger
14ec280d26 Updated to CraftBukkit 1.9 2016-03-04 13:46:29 +11:00
ShadowRanger
71ac05ae85 Updated to CraftBukkit 1.9 2016-03-04 13:45:54 +11:00
ShadowRanger
c4a9cb0a4a Updated to CraftBukkit 1.9 2016-03-02 20:11:45 +11:00
ShadowRanger
7ae87d1dbe Updated to CraftBukkit 1.9 2016-03-02 13:46:42 +11:00
ShadowRanger
346036b7e7 Updated to CraftBukkit 1.9 2016-03-02 13:44:16 +11:00
ShadowRanger
69c355c8cd Merge pull request #33 from ShadowRanger/master
Fixed an error & updated Bukkit
2015-08-27 19:55:33 +10:00
ShadowRanger
8dbbe80dbf Fixed an error & updated Bukkit 2015-08-27 19:52:56 +10:00
ShadowRanger
77e6ce8c7c Merge pull request #32 from ShadowRanger/master
Minor change + version bump
2015-07-21 09:45:36 +10:00
ShadowRanger
cd405a9b9e Minor fix/change 2015-07-21 09:43:06 +10:00
ShadowRanger
b5d6cf047c Bump version for release 2015-07-21 09:29:34 +10:00
Lishid
aaa9c4e190 Merge pull request #31 from LulzFTW/master
Fix openinv item status always being false
2015-07-20 12:24:21 -07:00
LulzFTW
6ea09dc964 Fix openinv item status always being false 2015-07-20 15:16:31 -04:00
ShadowRanger
eace5ca8fb Merge pull request #30 from ShadowRanger/master
Bump version for release
2015-07-16 19:15:51 +10:00
ShadowRanger
03308be7f5 Bump version for release 2015-07-16 19:14:57 +10:00
Lishid
4f40459b96 Merge pull request #29 from ShadowRanger/master
Fix & improve UUID retrieval
2015-07-10 15:55:17 -07:00
ShadowRanger
791368b100 Fix & improve UUID retrieval 2015-07-10 19:44:45 +10:00
ShadowRanger
5dd3f5bbd7 Merge pull request #27 from ShadowRanger/master
Bug fixes, changes & improvements
2015-06-25 10:17:14 +10:00
ShadowRanger
d1d60e55d5 More fixes and improvements - continued 2015-06-24 19:37:09 +10:00
ShadowRanger
471b631838 More fixes and improvements - continued 2015-06-24 19:19:45 +10:00
ShadowRanger
2f1fd87435 More fixes and improvements 2015-06-23 19:07:19 +10:00
ShadowRanger
a03c73f8d6 More fixes and improvements 2015-06-23 19:05:26 +10:00
ShadowRanger
f7029e5ee2 Another small change 2015-06-23 16:46:14 +10:00
ShadowRanger
802e405c4e Small change to update not required message 2015-06-23 16:28:58 +10:00
ShadowRanger
55deabe56b Small fixes 2015-06-23 16:17:51 +10:00
ShadowRanger
f8e4e69e53 Bug fix and changes 2015-06-23 15:11:35 +10:00
ShadowRanger
3c7d1696ff Bug fix and changes 2015-06-23 13:35:25 +10:00
ShadowRanger
902def1dd7 Bug fix and changes 2015-06-23 13:31:26 +10:00
ShadowRanger
f260ed1175 Merge pull request #26 from ShadowRanger/master
General refactoring + minor changes
2015-06-22 20:30:14 +10:00
ShadowRanger
bc40e91215 Minor no permissions message change 2015-06-22 20:26:36 +10:00
ShadowRanger
120b35d394 General refactoring - revert mistake 2015-06-22 20:17:30 +10:00
ShadowRanger
ded568ec3f General refactoring 2015-06-22 20:15:28 +10:00
Lishid
e958059572 Merge pull request #25 from ShadowRanger/master
Maven, UUIDs, dropped support for old CB, and a bunch of good stuff.
2015-06-22 01:56:48 -07:00
ShadowRanger
d988becf1f Migrate to UUID usage 2015-06-22 18:30:27 +10:00
ShadowRanger
e8476d5b05 Migrate to UUID usage 2015-06-22 12:03:04 +10:00
Lishid
1ea0307156 Merge pull request #21 from Phoenix616/master
Asynchronous offline player lookup
2015-05-26 22:09:41 -07:00
Max Lee
4ae47b09dc Update to v1_8_R3 NMS (1.8.4 to 1.8.6) 2015-05-27 02:13:20 +02:00
Max Lee
3e9bb63894 We are working with tasks here. 2015-04-03 01:39:08 +02:00
Max Lee
dce86d9c69 Made enderchests equally protected as inventories 2015-04-03 01:34:32 +02:00
Max Lee
4013098201 Add crossworld check to enderchests 2015-04-03 01:32:38 +02:00
Max Lee
56f633d58b world not work 2015-04-03 01:32:10 +02:00
Max Lee
6c1fb14cda Make inventory lookup of offline players asynchron 2015-04-03 01:30:42 +02:00
Max Lee
ad0e55cf4c Fix armor 2015-04-03 00:44:09 +02:00
Max Lee
c4fb5489fa Try to get the offline player directly 2015-04-03 00:29:51 +02:00
Lishid
3d3006c681 Merge pull request #19 from Hummer12007/master
Added CraftBukkit v1_8_R2 support
2015-03-14 18:09:48 -04:00
Hummer12007
a9d983ddc6 Added CraftBukkit v1_8_R2 support 2015-03-14 23:12:48 +02:00
Lishid
f6554a2a51 Merge pull request #17 from Hummer12007/master
Fix trapped chests linking with ordinary chests, instead of trapped ones
2015-01-13 21:46:56 -05:00
Hummer12007
ec3426b827 Use the same method for getting BlockChest, when checking whether AnyChest is needed due to the side block as in d0494aa2 2015-01-13 21:55:16 +02:00
Hummer12007
d0494aa2e7 Fix trapped chests linking with ordinary chests, instead of trapped ones. 2015-01-13 21:48:27 +02:00
lishid
6adcd491db Added targets path to gitignore 2015-01-05 21:21:44 -05:00
lishid
bc709336e1 Fixed silent chest issues 2015-01-05 21:15:35 -05:00
lishid
7d56a04283 Derp... 2014-12-31 16:26:54 -05:00
lishid
7851404d69 Fixed constructor not found issue. 2014-12-31 10:50:19 -05:00
lishid
30b5acc3b4 Removed duplicate file 2014-12-30 22:31:54 -05:00
lishid
29b0bc980f minor refactor + update internal chest logic 2014-12-30 22:28:52 -05:00
lishid
b05279ed4b Moved to proper directories 2014-12-30 22:15:43 -05:00
Lishid
88f3cb0a3f Merge pull request #16 from Fysac/master
Add support for CB 1.8 R1
2014-12-01 11:46:07 -08:00
Fysac
f48fdac085 Add support for CB 1.8 R1 2014-12-01 12:18:43 -05:00
82 changed files with 8226 additions and 2726 deletions

11
.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
**/.settings
**/.gitignore
**/.project
**/.classpath
**/.idea
**.iml
**/target
**/bin
**/out
**dependency-reduced-pom.xml
**pom.xml.versionsBackup

View File

@@ -1,3 +0,0 @@
#Wed Jun 01 17:38:54 EDT 2011
eclipse.preferences.version=1
org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false

13
README
View File

@@ -1,13 +0,0 @@
Copyright (C) 2011-2014 lishid. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

168
README.MD Normal file
View File

@@ -0,0 +1,168 @@
## About
OpenInv is a [Bukkit plugin](https://dev.bukkit.org/bukkit-plugins/openinv/) which allows users to open and edit anyone's inventory or ender chest - online or not!
## Features
- **OpenInv**: Open anyone's inventory, even if they're offline.
- Read-only mode! No edits allowed! Don't grant the permission `OpenInv.editinv`
- Cross-world support! Don't grant `OpenInv.crossworld`
- No self-opening! Don't grant `OpenInv.openself`
- Drop items as the player! Place items in the unused slots to the right of the armor to drop them
- **OpenEnder**: Open anyone's ender chest, even if they're offline.
- Read-only mode! No edits allowed! Don't grant `OpenInv.editender`
- Cross-world support! Don't grant `OpenInv.crossworld`
- No opening others! Don't grant `OpenInv.openenderall`
- **SilentContainer**: Open containers without displaying an animation or making sound.
- **AnyContainer**: Open containers, even if blocked by ocelots or blocks.
## Commands
<table width=100%>
<tr>
<th width=175px>Command</th>
<th>Aliases</th>
<th>Description</th>
</tr>
<tr>
<td>/openinv [player]</td>
<td>oi, inv, open</td>
<td>Open a player's inventory. If unspecified, will select last player opened or own if none opened previously.</td>
</tr>
<tr>
<td>/openender [player]</td>
<td>oe</td>
<td>Open a player's ender chest. If unspecified, will select last player opened or own if none opened previously.</td>
</tr>
<tr>
<td>/searchinv &ltitem&gt [minAmount]</td>
<td>si</td>
<td>Lists all online players that have a certain item in their inventory.</td>
</tr>
<tr>
<td>/searchender &ltitem&gt [minAmount]</td>
<td>se</td>
<td>Lists all online players that have a certain item in their ender chest.</td>
</tr>
<tr>
<td>/searchenchant &lt[enchantment] [MinLevel]&gt</td>
<td>searchenchants</td>
<td>Lists all online players with a specific enchantment.</td>
</tr>
<tr>
<td>/anycontainer [check]</td>
<td>ac, anychest</td>
<td>Check or toggle the AnyContainer function, allowing opening blocked containers.</td>
</tr>
<tr>
<td>/silentcontainer [check]</td>
<td>sc, silentchest</td>
<td>Check or toggle the SilentContainer function, allowing opening containers silently.</td>
</tr>
</table>
## Permissions
<table>
<tr>
<th>Node</th>
<th>Description</th>
</tr>
<tr>
<td>OpenInv.*</td>
<td>Gives permission to use all of OpenInv.</td>
</tr>
<tr>
<td>OpenInv.openinv</td>
<td>Required to use /openinv.</td>
</tr>
<tr>
<td>OpenInv.openself</td>
<td>Required to open own inventory.</td>
</tr>
<tr>
<td>OpenInv.editinv</td>
<td>Required to make changes to open inventories.</td>
</tr>
<tr>
<td>OpenInv.openonline</td>
<td>Allows users to open online players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
</tr>
<tr>
<td>OpenInv.openoffline</td>
<td>Allows users to open offline players' inventories. For compatibility reasons this is granted by the nodes OpenInv.openinv and OpenInv.openender.</td>
</tr>
<tr>
<td>OpenInv.openender</td>
<td>Required to use /openender.</td>
</tr>
<tr>
<td>OpenInv.editender</td>
<td>Required to make changes to open ender chests.</td>
</tr>
<tr>
<td>OpenInv.openenderall</td>
<td>Allows users to open others' ender chests. Without it, users can only open their own.</td>
</tr>
<tr>
<td>OpenInv.exempt</td>
<td>Prevents the player's inventory being opened by others.</td>
</tr>
<tr>
<td>OpenInv.override</td>
<td>Allows bypassing of the exempt permission.</td>
</tr>
<tr>
<td>OpenInv.crossworld</td>
<td>Allows cross-world usage of /openinv and /openender.</td>
</tr>
<tr>
<td>OpenInv.search</td>
<td>Required to use /searchinv and /searchender.</td>
</tr>
<tr>
<td>OpenInv.searchenchant</td>
<td>Required to use /searchenchant.</td>
</tr>
<tr>
<td>OpenInv.anychest</td>
<td>Required to use /anychest.</td>
</tr>
<tr>
<td>OpenInv.any.default</td>
<td>Cause AnyContainer to be enabled by default.</td>
</tr>
<tr>
<td>OpenInv.silent</td>
<td>Required to use /silentcontainer.</td>
</tr>
<tr>
<td>OpenInv.silent.default</td>
<td>Cause SilentContainer to be enabled by default.</td>
</tr>
</table>
## For Developers
To compile, the relevant Craftbukkit/Spigot jars must be installed in your local repository using the install plugin.
Ex: `mvn install:install-file -Dpackaging=jar -Dfile=spigot-1.8-R0.1-SNAPSHOT.jar -DgroupId=org.spigotmc -DartifactId=spigot -Dversion=1.8-R0.1-SNAPSHOT`
To compile for a single version, specify the NMS revision you are targeting: `mvn -pl <NMS module> -am clean install`
To compile for a set of versions, you'll need to use a profile. The only provided profile is `all`. Select a profile using the `-P` argument: `mvn clean package -am -P all`
For more information, check out the [official Maven guide](http://maven.apache.org/guides/introduction/introduction-to-profiles.html).
The final file is `target/OpenInv.jar`
## License
```
Copyright (C) 2011-2020 lishid. All rights reserved.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
```

57
api/pom.xml Normal file
View File

@@ -0,0 +1,57 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvapi</artifactId>
<name>OpenInvAPI</name>
<dependencies>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>17.0.0</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,309 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.IInventoryAccess;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.StringMetric;
import java.util.UUID;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Interface defining behavior for the OpenInv plugin.
*
* @author Jikoo
*/
public interface IOpenInv {
/**
* Check the configuration value for whether or not OpenInv saves player data when unloading
* players. This is exclusively for users who do not allow editing of inventories, only viewing,
* and wish to prevent any possibility of bugs such as lishid#40. If true, OpenInv will not ever
* save any edits made to players.
*
* @return false unless configured otherwise
*/
boolean disableSaving();
/**
* Gets the active ISilentContainer implementation.
*
* @return the ISilentContainer
* @throws IllegalStateException if the server version is unsupported
*/
@NotNull IAnySilentContainer getAnySilentContainer();
/**
* Gets the active IInventoryAccess implementation.
*
* @return the IInventoryAccess
* @throws IllegalStateException if the server version is unsupported
*/
@Deprecated
default @NotNull IInventoryAccess getInventoryAccess() {
return new InventoryAccess();
}
/**
* Gets the provided player's AnyChest setting.
*
* @param player the OfflinePlayer
* @return true if AnyChest is enabled
* @throws IllegalStateException if the server version is unsupported
*/
boolean getPlayerAnyChestStatus(@NotNull OfflinePlayer player);
/**
* Gets a unique identifier by which the OfflinePlayer can be referenced. Using the value
* returned to look up a Player will generally be much faster for later implementations.
*
* @param offline the OfflinePlayer
* @return the identifier
* @throws IllegalStateException if the server version is unsupported
*/
default @NotNull String getPlayerID(@NotNull OfflinePlayer offline) {
return offline.getUniqueId().toString();
}
/**
* Gets a player's SilentChest setting.
*
* @param offline the OfflinePlayer
* @return true if SilentChest is enabled
* @throws IllegalStateException if the server version is unsupported
*/
boolean getPlayerSilentChestStatus(@NotNull OfflinePlayer offline);
/**
* Gets an ISpecialEnderChest for the given Player.
*
* @param player the Player
* @param online true if the Player is currently online
* @return the ISpecialEnderChest
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
*/
@NotNull ISpecialEnderChest getSpecialEnderChest(@NotNull Player player, boolean online) throws InstantiationException;
/**
* Gets an ISpecialPlayerInventory for the given Player.
*
* @param player the Player
* @param online true if the Player is currently online
* @return the ISpecialPlayerInventory
* @throws IllegalStateException if the server version is unsupported
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
*/
@NotNull ISpecialPlayerInventory getSpecialInventory(@NotNull Player player, boolean online) throws InstantiationException;
/**
* Checks if the server version is supported by OpenInv.
*
* @return true if the server version is supported
*/
boolean isSupportedVersion();
/**
* Load a Player from an OfflinePlayer. May return null under some circumstances.
*
* @param offline the OfflinePlayer to load a Player for
* @return the Player, or null
* @throws IllegalStateException if the server version is unsupported
*/
@Nullable Player loadPlayer(@NotNull final OfflinePlayer offline);
/**
* Get an OfflinePlayer by name.
* <p>
* Note: This method is potentially very heavily blocking. It should not ever be called on the
* main thread, and if it is, a stack trace will be displayed alerting server owners to the
* call.
*
* @param name the name of the Player
* @return the OfflinePlayer with the closest matching name or null if no players have ever logged in
*/
default @Nullable OfflinePlayer matchPlayer(@NotNull String name) {
// Warn if called on the main thread - if we resort to searching offline players, this may take several seconds.
if (Bukkit.getServer().isPrimaryThread()) {
this.getLogger().warning("Call to OpenInv#matchPlayer made on the main thread!");
this.getLogger().warning("This can cause the server to hang, potentially severely.");
this.getLogger().warning("Trace:");
for (StackTraceElement element : new Throwable().fillInStackTrace().getStackTrace()) {
this.getLogger().warning(element.toString());
}
}
OfflinePlayer player;
try {
UUID uuid = UUID.fromString(name);
player = Bukkit.getOfflinePlayer(uuid);
// Ensure player is a real player, otherwise return null
if (player.hasPlayedBefore() || player.isOnline()) {
return player;
}
} catch (IllegalArgumentException ignored) {
// Not a UUID
}
// Ensure name is valid if server is in online mode to avoid unnecessary searching
if (Bukkit.getServer().getOnlineMode() && !name.matches("[a-zA-Z0-9_]{3,16}")) {
return null;
}
player = Bukkit.getServer().getPlayerExact(name);
if (player != null) {
return player;
}
player = Bukkit.getServer().getOfflinePlayer(name);
if (player.hasPlayedBefore()) {
return player;
}
player = Bukkit.getServer().getPlayer(name);
if (player != null) {
return player;
}
float bestMatch = 0;
for (OfflinePlayer offline : Bukkit.getServer().getOfflinePlayers()) {
if (offline.getName() == null) {
// Loaded by UUID only, name has never been looked up.
continue;
}
float currentMatch = StringMetric.compareJaroWinkler(name, offline.getName());
if (currentMatch == 1.0F) {
return offline;
}
if (currentMatch > bestMatch) {
bestMatch = currentMatch;
player = offline;
}
}
// Only null if no players have played ever, otherwise even the worst match will do.
return player;
}
/**
* Open an ISpecialInventory for a Player.
*
* @param player the Player
* @param inventory the ISpecialInventory
* @return the InventoryView for the opened ISpecialInventory
*/
@Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
/**
* Check the configuration value for whether or not OpenInv displays a notification to the user
* when a container is activated with AnyChest.
*
* @return true unless configured otherwise
*/
boolean notifyAnyChest();
/**
* Check the configuration value for whether or not OpenInv displays a notification to the user
* when a container is activated with SilentChest.
*
* @return true unless configured otherwise
*/
boolean notifySilentChest();
/**
* Mark a Player as no longer in use by a Plugin to allow OpenInv to remove it from the cache
* when eligible.
*
* @param player the Player
* @param plugin the Plugin no longer holding a reference to the Player
* @throws IllegalStateException if the server version is unsupported
*/
void releasePlayer(@NotNull Player player, @NotNull Plugin plugin);
/**
* Mark a Player as in use by a Plugin to prevent it from being removed from the cache. Used to
* prevent issues with multiple copies of the same Player being loaded such as lishid#49.
* Changes made to loaded copies overwrite changes to the others when saved, leading to
* duplication bugs and more.
* <p>
* When finished with the Player object, be sure to call {@link #releasePlayer(Player, Plugin)}
* to prevent the cache from keeping it stored until the plugin is disabled.
* <p>
* When using a Player object from OpenInv, you must handle the Player coming online, replacing
* your Player reference with the Player from the PlayerJoinEvent. In addition, you must change
* any values in the Player to reflect any unsaved alterations to the existing Player which do
* not affect the inventory or ender chest contents.
* <p>
* OpenInv only saves player data when unloading a Player from the cache, and then only if
* {@link #disableSaving()} returns false. If you are making changes that OpenInv does not cause
* to persist when a Player logs in as noted above, it is suggested that you manually call
* {@link Player#saveData()} when releasing your reference to ensure your changes persist.
*
* @param player the Player
* @param plugin the Plugin holding the reference to the Player
* @throws IllegalStateException if the server version is unsupported
*/
void retainPlayer(@NotNull Player player, @NotNull Plugin plugin);
/**
* Sets a player's AnyChest setting.
*
* @param offline the OfflinePlayer
* @param status the status
* @throws IllegalStateException if the server version is unsupported
*/
void setPlayerAnyChestStatus(@NotNull OfflinePlayer offline, boolean status);
/**
* Sets a player's SilentChest setting.
*
* @param offline the OfflinePlayer
* @param status the status
* @throws IllegalStateException if the server version is unsupported
*/
void setPlayerSilentChestStatus(@NotNull OfflinePlayer offline, boolean status);
/**
* Forcibly unload a cached Player's data.
*
* @param offline the OfflinePlayer to unload
* @throws IllegalStateException if the server version is unsupported
*/
void unload(@NotNull OfflinePlayer offline);
Logger getLogger();
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public interface IAnySilentContainer {
/**
* Opens the container at the given coordinates for the Player. If you do not want blocked
* containers to open, be sure to check {@link #isAnyContainerNeeded(Player, Block)}
* first.
*
* @param player the Player opening the container
* @param silent whether the container's noise is to be silenced
* @param block the Block
* @return true if the container can be opened
*/
boolean activateContainer(@NotNull Player player, boolean silent, @NotNull Block block);
/**
* Closes the Player's currently open container silently, if necessary.
*
* @param player the Player closing a container
*/
void deactivateContainer(@NotNull Player player);
/**
* Checks if the container at the given coordinates is blocked.
*
* @param player the Player opening the container
* @param block the Block
* @return true if the container is blocked
*/
boolean isAnyContainerNeeded(@NotNull Player player, @NotNull Block block);
/**
* Checks if the given block is a container which can be unblocked or silenced.
*
* @param block the BlockState
* @return true if the Block is a supported container
*/
boolean isAnySilentContainer(@NotNull Block block);
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Deprecated
public interface IInventoryAccess {
/**
* Gets an ISpecialEnderChest from an Inventory or null if the Inventory is not backed by an
* ISpecialEnderChest.
*
* @param inventory the Inventory
* @return the ISpecialEnderChest or null
*/
@Deprecated
@Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory);
/**
* Gets an ISpecialPlayerInventory from an Inventory or null if the Inventory is not backed by
* an ISpecialPlayerInventory.
*
* @param inventory the Inventory
* @return the ISpecialPlayerInventory or null
*/
@Deprecated
@Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory);
/**
* Check if an Inventory is an ISpecialEnderChest implementation.
*
* @param inventory the Inventory
* @return true if the Inventory is backed by an ISpecialEnderChest
*/
@Deprecated
boolean isSpecialEnderChest(@NotNull Inventory inventory);
/**
* Check if an Inventory is an ISpecialPlayerInventory implementation.
*
* @param inventory the Inventory
* @return true if the Inventory is backed by an ISpecialPlayerInventory
*/
@Deprecated
boolean isSpecialPlayerInventory(@NotNull Inventory inventory);
}

View File

@@ -1,23 +1,21 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
public interface IPlayerDataManager {
public Player loadPlayer(String name);
}
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
public interface ISpecialEnderChest extends ISpecialInventory {
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public interface ISpecialInventory {
/**
* Gets the Inventory associated with this ISpecialInventory.
*
* @return the Inventory
*/
@NotNull Inventory getBukkitInventory();
/**
* Sets the Player associated with this ISpecialInventory online.
*
* @param player the Player coming online
*/
void setPlayerOnline(@NotNull Player player);
/**
* Sets the Player associated with this ISpecialInventory offline.
*/
void setPlayerOffline();
/**
* Gets whether or not this ISpecialInventory is in use.
*
* @return true if the ISpecialInventory is in use
*/
boolean isInUse();
}

View File

@@ -1,24 +1,21 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
public interface IInventoryAccess {
public boolean check(Inventory inventory, HumanEntity player);
}
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
public interface ISpecialPlayerInventory extends ISpecialInventory {
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.lishid.openinv.internal.IInventoryAccess;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import org.bukkit.Bukkit;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class InventoryAccess implements IInventoryAccess {
private static Class<?> craftInventory = null;
private static Method getInventory = null;
static {
String packageName = Bukkit.getServer().getClass().getPackage().getName();
String version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
craftInventory = Class.forName("org.bukkit.craftbukkit." + version + ".inventory.CraftInventory");
} catch (ClassNotFoundException ignored) {}
try {
getInventory = craftInventory.getDeclaredMethod("getInventory");
} catch (NoSuchMethodException ignored) {}
}
public static boolean isUseable() {
return craftInventory != null && getInventory != null;
}
public static boolean isPlayerInventory(@NotNull Inventory inventory) {
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
return getInventory.invoke(inventory) instanceof ISpecialPlayerInventory;
} catch (ReflectiveOperationException ignored) {}
}
return grabFieldOfTypeFromObject(ISpecialPlayerInventory.class, inventory) != null;
}
public static ISpecialPlayerInventory getPlayerInventory(@NotNull Inventory inventory) {
Object inv = null;
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
inv = getInventory.invoke(inventory);
} catch (ReflectiveOperationException ignored) {}
}
if (inv == null) {
inv = grabFieldOfTypeFromObject(ISpecialPlayerInventory.class, inventory);
}
if (inv instanceof ISpecialPlayerInventory) {
return (ISpecialPlayerInventory) inv;
}
return null;
}
public static boolean isEnderChest(@NotNull Inventory inventory) {
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
return getInventory.invoke(inventory) instanceof ISpecialEnderChest;
} catch (ReflectiveOperationException ignored) {}
}
return grabFieldOfTypeFromObject(ISpecialEnderChest.class, inventory) != null;
}
public static ISpecialEnderChest getEnderChest(@NotNull Inventory inventory) {
Object inv = null;
if (craftInventory.isAssignableFrom(inventory.getClass())) {
try {
inv = getInventory.invoke(inventory);
} catch (ReflectiveOperationException ignored) {}
}
if (inv == null) {
inv = grabFieldOfTypeFromObject(ISpecialEnderChest.class, inventory);
}
if (inv instanceof ISpecialEnderChest) {
return (ISpecialEnderChest) inv;
}
return null;
}
private static <T> T grabFieldOfTypeFromObject(final Class<T> type, final Object object) {
// Use reflection to find the IInventory
Class<?> clazz = object.getClass();
T result = null;
for (Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
if (type.isAssignableFrom(f.getDeclaringClass())) {
try {
result = type.cast(f.get(object));
} catch (Exception e) {
e.printStackTrace();
}
}
}
return result;
}
@Deprecated
@Override
public @Nullable ISpecialEnderChest getSpecialEnderChest(@NotNull Inventory inventory) {
return getEnderChest(inventory);
}
@Deprecated
@Override
public @Nullable ISpecialPlayerInventory getSpecialPlayerInventory(@NotNull Inventory inventory) {
return getPlayerInventory(inventory);
}
@Deprecated
@Override
public boolean isSpecialEnderChest(@NotNull Inventory inventory) {
return isEnderChest(inventory);
}
@Deprecated
@Override
public boolean isSpecialPlayerInventory(@NotNull Inventory inventory) {
return isPlayerInventory(inventory);
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
public class StringMetric {
public static float compareJaroWinkler(String a, String b) {
final float jaroScore = compareJaro(a, b);
if (jaroScore < (float) 0.7) {
return jaroScore;
}
String prefix = commonPrefix(a, b);
int prefixLength = Math.min(prefix.codePointCount(0, prefix.length()), 4);
return jaroScore + (prefixLength * (float) 0.1 * (1.0f - jaroScore));
}
private static float compareJaro(String a, String b) {
if (a.isEmpty() && b.isEmpty()) {
return 1.0f;
}
if (a.isEmpty() || b.isEmpty()) {
return 0.0f;
}
final int[] charsA = a.codePoints().toArray();
final int[] charsB = b.codePoints().toArray();
// Intentional integer division to round down.
final int halfLength = Math.max(0, Math.max(charsA.length, charsB.length) / 2 - 1);
final int[] commonA = getCommonCodePoints(charsA, charsB, halfLength);
final int[] commonB = getCommonCodePoints(charsB, charsA, halfLength);
// commonA and commonB will always contain the same multi-set of
// characters. Because getCommonCharacters has been optimized, commonA
// and commonB are -1-padded. So in this loop we count transposition
// and use commonCharacters to determine the length of the multi-set.
float transpositions = 0;
int commonCharacters = 0;
for (int length = commonA.length; commonCharacters < length
&& commonA[commonCharacters] > -1; commonCharacters++) {
if (commonA[commonCharacters] != commonB[commonCharacters]) {
transpositions++;
}
}
if (commonCharacters == 0) {
return 0.0f;
}
float aCommonRatio = commonCharacters / (float) charsA.length;
float bCommonRatio = commonCharacters / (float) charsB.length;
float transpositionRatio = (commonCharacters - transpositions / 2.0f) / commonCharacters;
return (aCommonRatio + bCommonRatio + transpositionRatio) / 3.0f;
}
/*
* Returns an array of code points from a within b. A character in b is
* counted as common when it is within separation distance from the position
* in a.
*/
private static int[] getCommonCodePoints(final int[] charsA, final int[] charsB, final int separation) {
final int[] common = new int[Math.min(charsA.length, charsB.length)];
final boolean[] matched = new boolean[charsB.length];
// Iterate of string a and find all characters that occur in b within
// the separation distance. Mark any matches found to avoid
// duplicate matchings.
int commonIndex = 0;
for (int i = 0, length = charsA.length; i < length; i++) {
final int character = charsA[i];
final int index = indexOf(character, charsB, i - separation, i
+ separation + 1, matched);
if (index > -1) {
common[commonIndex++] = character;
matched[index] = true;
}
}
if (commonIndex < common.length) {
common[commonIndex] = -1;
}
// Both invocations will yield the same multi-set terminated by -1, so
// they can be compared for transposition without making a copy.
return common;
}
/*
* Search for code point in buffer starting at fromIndex to toIndex - 1.
*
* Returns -1 when not found.
*/
private static int indexOf(int character, int[] buffer, int fromIndex, int toIndex, boolean[] matched) {
// compare char with range of characters to either side
for (int j = Math.max(0, fromIndex), length = Math.min(toIndex, buffer.length); j < length; j++) {
// check if found
if (buffer[j] == character && !matched[j]) {
return j;
}
}
return -1;
}
private static String commonPrefix(CharSequence a, CharSequence b) {
int maxPrefixLength = Math.min(a.length(), b.length());
int p;
p = 0;
while (p < maxPrefixLength && a.charAt(p) == b.charAt(p)) {
++p;
}
if (validSurrogatePairAt(a, p - 1) || validSurrogatePairAt(b, p - 1)) {
--p;
}
return a.subSequence(0, p).toString();
}
private static boolean validSurrogatePairAt(CharSequence string, int index) {
return index >= 0 && index <= string.length() - 2 && Character.isHighSurrogate(string.charAt(index)) && Character.isLowSurrogate(string.charAt(index + 1));
}
private StringMetric(){}
}

58
assembly/pom.xml Normal file
View File

@@ -0,0 +1,58 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvassembly</artifactId>
<name>OpenInvAssembly</name>
<build>
<directory>../target</directory>
<finalName>OpenInv</finalName>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>reactor-uberjar</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<descriptors>
<descriptor>src/assembly/reactor-uberjar.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,44 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd">
<id>reactor-uberjar</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<moduleSets>
<moduleSet>
<useAllReactorProjects>true</useAllReactorProjects>
<binaries>
<outputDirectory>/</outputDirectory>
<unpack>true</unpack>
<!-- unpackOptions must be present or build breaks. -->
<unpackOptions/>
</binaries>
</moduleSet>
</moduleSets>
</assembly>

44
internal/pom.xml Normal file
View File

@@ -0,0 +1,44 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvinternal</artifactId>
<name>OpenInvInternal</name>
<packaging>pom</packaging>
<profiles>
<profile>
<id>all</id>
<modules>
<module>v1_8_R3</module>
<module>v1_15_R1</module>
<module>v1_16_R1</module>
</modules>
</profile>
</profiles>
</project>

75
internal/v1_15_R1/pom.xml Normal file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvadapter1_15_R1</artifactId>
<name>OpenInvAdapter1_15_R1</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.15.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,360 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_15_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_15_R1.Block;
import net.minecraft.server.v1_15_R1.BlockBarrel;
import net.minecraft.server.v1_15_R1.BlockChest;
import net.minecraft.server.v1_15_R1.BlockChestTrapped;
import net.minecraft.server.v1_15_R1.BlockEnderChest;
import net.minecraft.server.v1_15_R1.BlockPosition;
import net.minecraft.server.v1_15_R1.BlockPropertyChestType;
import net.minecraft.server.v1_15_R1.BlockShulkerBox;
import net.minecraft.server.v1_15_R1.ChatMessage;
import net.minecraft.server.v1_15_R1.Container;
import net.minecraft.server.v1_15_R1.ContainerChest;
import net.minecraft.server.v1_15_R1.Containers;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.EnumChatFormat;
import net.minecraft.server.v1_15_R1.EnumGamemode;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.IChatBaseComponent;
import net.minecraft.server.v1_15_R1.ITileInventory;
import net.minecraft.server.v1_15_R1.InventoryEnderChest;
import net.minecraft.server.v1_15_R1.InventoryLargeChest;
import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.TileEntity;
import net.minecraft.server.v1_15_R1.TileEntityChest;
import net.minecraft.server.v1_15_R1.TileEntityEnderChest;
import net.minecraft.server.v1_15_R1.TileEntityLootable;
import net.minecraft.server.v1_15_R1.TileInventory;
import net.minecraft.server.v1_15_R1.World;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Barrel;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
public class AnySilentContainer implements IAnySilentContainer {
private Field playerInteractManagerGamemode;
public AnySilentContainer() {
try {
this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("gamemode");
this.playerInteractManagerGamemode.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail.");
e.printStackTrace();
}
}
@Override
public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) {
if (bukkitBlock.getType() == Material.ENDER_CHEST) {
return true;
}
BlockState state = bukkitBlock.getState();
return state instanceof org.bukkit.block.Chest
|| state instanceof org.bukkit.block.ShulkerBox
|| state instanceof org.bukkit.block.Barrel;
}
@Override
public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) {
BlockState blockState = block.getState();
// Barrels do not require AnyContainer.
if (blockState instanceof Barrel) {
return false;
}
// Enderchests require a non-occluding block on top to open.
if (blockState instanceof EnderChest) {
return block.getRelative(0, 1, 0).getType().isOccluding();
}
// Shulker boxes require 1/2 a block clear in the direction they open.
if (blockState instanceof ShulkerBox) {
BoundingBox boundingBox = block.getBoundingBox();
if (boundingBox.getVolume() > 1) {
// Shulker box is already open.
return false;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Directional)) {
// Shouldn't be possible. Just in case, demand AnyChest.
return true;
}
Directional directional = (Directional) blockData;
BlockFace face = directional.getFacing();
boundingBox.shift(face.getDirection());
// Return whether or not bounding boxes overlap.
return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox);
}
if (!(blockState instanceof org.bukkit.block.Chest)) {
return false;
}
if (isBlockedChest(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
return false;
}
Chest chest = (Chest) blockData;
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
BlockFace relativeFace = BlockFace.values()[ordinal];
org.bukkit.block.Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest)) {
return false;
}
Chest relativeChest = (Chest) relativeData;
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return isBlockedChest(relative);
}
private boolean isBlockedChest(org.bukkit.block.Block block) {
org.bukkit.block.Block relative = block.getRelative(0, 1, 0);
return relative.getType().isOccluding()
|| block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
}
@Override
public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final World world = player.world;
final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final TileEntity tile = world.getTileEntity(blockPosition);
if (tile == null) {
return false;
}
if (tile instanceof TileEntityEnderChest) {
// Anychest ender chest. See net.minecraft.server.BlockEnderChest
InventoryEnderChest enderChest = player.getEnderChest();
enderChest.a((TileEntityEnderChest) tile);
player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> {
Containers containers;
int rows = enderChest.getSize() / 9;
switch (rows) {
case 1:
containers = Containers.GENERIC_9X1;
break;
case 2:
containers = Containers.GENERIC_9X2;
break;
case 3:
default:
containers = Containers.GENERIC_9X3;
break;
case 4:
containers = Containers.GENERIC_9X4;
break;
case 5:
containers = Containers.GENERIC_9X5;
break;
case 6:
containers = Containers.GENERIC_9X6;
break;
}
return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows);
}, BlockEnderChest.e));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(tile instanceof ITileInventory)) {
return false;
}
ITileInventory tileInventory = (ITileInventory) tile;
IBlockData blockData = world.getType(blockPosition);
Block block = blockData.getBlock();
if (block instanceof BlockChest) {
BlockPropertyChestType chestType = blockData.get(BlockChest.c);
if (chestType != BlockPropertyChestType.SINGLE) {
BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.i(blockData));
IBlockData adjacentBlockData = world.getType(adjacentBlockPosition);
if (adjacentBlockData.getBlock() == block) {
BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c);
if (adjacentChestType != BlockPropertyChestType.SINGLE && chestType != adjacentChestType
&& adjacentBlockData.get(BlockChest.FACING) == blockData.get(BlockChest.FACING)) {
TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition);
if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) {
TileEntityChest rightChest = chestType == BlockPropertyChestType.RIGHT ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile;
TileEntityChest leftChest = chestType == BlockPropertyChestType.RIGHT ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory);
if (rightChest.lootTable != null || leftChest.lootTable != null) {
player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true);
return false;
}
tileInventory = new ITileInventory() {
public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) {
leftChest.d(playerInventory.player);
rightChest.d(playerInventory.player);
return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest));
}
public IChatBaseComponent getScoreboardDisplayName() {
return new ChatMessage("container.chestDouble");
}
};
}
}
}
}
if (block instanceof BlockChestTrapped) {
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
} else {
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
}
}
if (block instanceof BlockShulkerBox) {
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
}
if (block instanceof BlockBarrel) {
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silentchest || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
player.openContainer(tileInventory);
return true;
}
// SilentChest requires access to setting players' gamemode directly.
if (this.playerInteractManagerGamemode == null) {
return false;
}
if (tile instanceof TileEntityLootable) {
TileEntityLootable lootable = (TileEntityLootable) tile;
if (lootable.lootTable != null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
}
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.openContainer(tileInventory);
this.forceGameMode(player, gamemode);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.playerInteractManagerGamemode == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.activeContainer.b(player);
player.activeContainer.a(player, false);
player.activeContainer.transferTo(player.defaultContainer, player.getBukkitEntity());
player.activeContainer = player.defaultContainer;
this.forceGameMode(player, gamemode);
}
private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) {
if (this.playerInteractManagerGamemode == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
if (!this.playerInteractManagerGamemode.isAccessible()) {
// Just in case, ensure accessible.
this.playerInteractManagerGamemode.setAccessible(true);
}
this.playerInteractManagerGamemode.set(player.playerInteractManager, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,289 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_15_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_15_R1.ChatComponentText;
import net.minecraft.server.v1_15_R1.ChatMessageType;
import net.minecraft.server.v1_15_R1.Container;
import net.minecraft.server.v1_15_R1.Containers;
import net.minecraft.server.v1_15_R1.DimensionManager;
import net.minecraft.server.v1_15_R1.Entity;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.MinecraftServer;
import net.minecraft.server.v1_15_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.PacketPlayOutChat;
import net.minecraft.server.v1_15_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_15_R1.PlayerInteractManager;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_15_R1.CraftServer;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftContainer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
}
return nmsPlayer;
}
@Nullable
@Override
public Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
// See net.minecraft.server.PlayerList#attemptLogin
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(DimensionManager.OVERWORLD), profile,
new PlayerInteractManager(server.getWorldServer(DimensionManager.OVERWORLD)));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity
Player target = entity.getBukkitEntity();
if (target != null) {
// Load data
target.loadData();
}
// Return the entity
return target;
}
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
EntityPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
return null;
}
String title;
if (inventory instanceof SpecialEnderChest) {
HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner();
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Ender Chest";
}
} else if (inventory instanceof SpecialPlayerInventory) {
EntityHuman owner = ((PlayerInventory) inventory).player;
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Inventory";
}
} else {
return player.openInventory(inventory.getBukkitInventory());
}
String finalTitle = title;
Container container = new CraftContainer(new InventoryView() {
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return finalTitle;
}
}, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
switch (inventory.getBukkitInventory().getSize()) {
case 9:
return Containers.GENERIC_9X1;
case 18:
return Containers.GENERIC_9X2;
case 27:
default:
return Containers.GENERIC_9X3;
case 36:
return Containers.GENERIC_9X4;
case 41: // PLAYER
case 45:
return Containers.GENERIC_9X5;
case 54:
return Containers.GENERIC_9X6;
}
}
};
container.setTitle(new ChatComponentText(title));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, container.getType(),
new ChatComponentText(container.getBukkitView().getTitle())));
nmsPlayer.activeContainer = container;
container.addSlotListener(nmsPlayer);
return container.getBukkitView();
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO));
}
}
}

View File

@@ -0,0 +1,255 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_15_R1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import net.minecraft.server.v1_15_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_15_R1.ContainerUtil;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.IInventoryListener;
import net.minecraft.server.v1_15_R1.InventoryEnderChest;
import net.minecraft.server.v1_15_R1.ItemStack;
import net.minecraft.server.v1_15_R1.NonNullList;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest {
private final CraftInventory inventory;
private EntityPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChest().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
try {
this.owner = PlayerDataManager.getHandle(player);
InventoryEnderChest enderChest = owner.getEnderChest();
for (int i = 0; i < enderChest.getSize(); ++i) {
enderChest.setItem(i, this.items.get(i));
}
this.items = enderChest.items;
} catch (Exception ignored) {}
this.playerOnline = true;
}
}
@Override
public void update() {
this.owner.getEnderChest().update();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChest().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChest().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChest().getViewers();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChest().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChest().getOwner();
}
@Override
public Location getLocation() {
return null;
}
@Override
public void a(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().a(iinventorylistener);
}
@Override
public void b(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().b(iinventorylistener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.a;
}
@Override
public ItemStack splitStack(int i, int j) {
ItemStack itemstack = ContainerUtil.a(this.items, i, j);
if (!itemstack.isEmpty()) {
this.update();
}
return itemstack;
}
@Override
public ItemStack a(ItemStack itemstack) {
ItemStack itemstack1 = itemstack.cloneItemStack();
for (int i = 0; i < this.getSize(); ++i) {
ItemStack itemstack2 = this.getItem(i);
if (itemstack2.isEmpty()) {
this.setItem(i, itemstack1);
this.update();
return ItemStack.a;
}
if (ItemStack.c(itemstack2, itemstack1)) {
int j = Math.min(this.getMaxStackSize(), itemstack2.getMaxStackSize());
int k = Math.min(itemstack1.getCount(), j - itemstack2.getCount());
if (k > 0) {
itemstack2.add(k);
itemstack1.subtract(k);
if (itemstack1.isEmpty()) {
this.update();
return ItemStack.a;
}
}
}
}
if (itemstack1.getCount() != itemstack.getCount()) {
this.update();
}
return itemstack1;
}
@Override
public ItemStack splitWithoutUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.a;
} else {
this.items.set(i, ItemStack.a);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.update();
}
@Override
public int getSize() {
return this.owner.getEnderChest().getSize();
}
@Override
public boolean isNotEmpty() {
for (ItemStack itemstack : this.items) {
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Override
public int getMaxStackSize() {
return 64;
}
@Override
public boolean a(EntityHuman entityhuman) {
return true;
}
@Override
public void startOpen(EntityHuman entityhuman) {
}
@Override
public void closeContainer(EntityHuman entityhuman) {
}
@Override
public boolean b(int i, ItemStack itemstack) {
return true;
}
@Override
public void clear() {
this.items.clear();
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.b(itemstack);
}
}
}

View File

@@ -0,0 +1,759 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_15_R1;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.server.v1_15_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_15_R1.ChatMessage;
import net.minecraft.server.v1_15_R1.ContainerUtil;
import net.minecraft.server.v1_15_R1.CrashReport;
import net.minecraft.server.v1_15_R1.CrashReportSystemDetails;
import net.minecraft.server.v1_15_R1.EntityHuman;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.EnumItemSlot;
import net.minecraft.server.v1_15_R1.IBlockData;
import net.minecraft.server.v1_15_R1.IChatBaseComponent;
import net.minecraft.server.v1_15_R1.Item;
import net.minecraft.server.v1_15_R1.ItemArmor;
import net.minecraft.server.v1_15_R1.ItemStack;
import net.minecraft.server.v1_15_R1.NBTTagCompound;
import net.minecraft.server.v1_15_R1.NBTTagList;
import net.minecraft.server.v1_15_R1.NonNullList;
import net.minecraft.server.v1_15_R1.PacketPlayOutSetSlot;
import net.minecraft.server.v1_15_R1.PlayerInventory;
import net.minecraft.server.v1_15_R1.ReportedException;
import net.minecraft.server.v1_15_R1.World;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private EntityHuman player;
private NonNullList<ItemStack> items, armor, extraSlots;
private List<NonNullList<ItemStack>> f;
public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
EntityPlayer entityPlayer = PlayerDataManager.getHandle(player);
entityPlayer.inventory.transaction.addAll(this.transaction);
this.player = entityPlayer;
for (int i = 0; i < getSize(); ++i) {
this.player.inventory.setItem(i, getRawItem(i));
}
this.player.inventory.itemInHandIndex = this.itemInHandIndex;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
this.playerOnline = true;
}
}
@Override
public boolean a(final EntityHuman entityhuman) {
return true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public ItemStack getItem(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.a;
}
return list.get(i);
}
private ItemStack getRawItem(int i) {
NonNullList<ItemStack> list = null;
for (NonNullList<ItemStack> next : this.f) {
if (i < next.size()) {
list = next;
break;
}
i -= next.size();
}
return list == null ? ItemStack.a : list.get(i);
}
@Override
public IChatBaseComponent getDisplayName() {
return new ChatMessage(this.player.getName());
}
@Override
public boolean hasCustomName() {
return false;
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
@Override
public int getSize() {
return 45;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setItem(int i, final ItemStack itemstack) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
this.player.drop(itemstack, true);
return;
}
list.set(i, itemstack);
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public ItemStack splitStack(int i, final int j) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.a;
}
return list.get(i).isEmpty() ? ItemStack.a : ContainerUtil.a(list, i, j);
}
@Override
public ItemStack splitWithoutUpdate(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.a;
}
if (!list.get(i).isEmpty()) {
ItemStack itemstack = list.get(i);
list.set(i, ItemStack.a);
return itemstack;
}
return ItemStack.a;
}
@Override
public List<ItemStack> getContents() {
return this.f.stream().flatMap(List::stream).collect(Collectors.toList());
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.transaction.add(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.transaction.remove(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.transaction;
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public ItemStack getItemInHand() {
return d(this.itemInHandIndex) ? this.items.get(this.itemInHandIndex) : ItemStack.a;
}
private boolean a(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && this.b(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
private boolean b(ItemStack itemstack, ItemStack itemstack1) {
return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1);
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (!this.a(itemstack, itemstack1)) {
remains -= (itemstack1.getMaxStackSize() < this.getMaxStackSize() ? itemstack1.getMaxStackSize() : this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
return itemstack.getCount() - remains;
}
@Override
public int getFirstEmptySlotIndex() {
for (int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void c(int i) {
this.itemInHandIndex = this.i();
ItemStack itemstack = this.items.get(this.itemInHandIndex);
this.items.set(this.itemInHandIndex, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int c(ItemStack itemstack) {
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.items.get(i);
if (!this.items.get(i).isEmpty() && this.b(itemstack, this.items.get(i)) && !this.items.get(i).f() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) {
return i;
}
}
return -1;
}
@Override
public int i() {
int i;
int j;
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (!this.items.get(i).hasEnchantments()) {
return i;
}
}
return this.itemInHandIndex;
}
@Override
public int a(Predicate<ItemStack> predicate, int i) {
int j = 0;
int k;
for (k = 0; k < this.getSize(); ++k) {
ItemStack itemstack = this.getItem(k);
if (!itemstack.isEmpty() && predicate.test(itemstack)) {
int l = i <= 0 ? itemstack.getCount() : Math.min(i - j, itemstack.getCount());
j += l;
if (i != 0) {
itemstack.subtract(l);
if (itemstack.isEmpty()) {
this.setItem(k, ItemStack.a);
}
if (i > 0 && j >= i) {
return j;
}
}
}
}
if (!this.getCarried().isEmpty() && predicate.test(this.getCarried())) {
k = i <= 0 ? this.getCarried().getCount() : Math.min(i - j, this.getCarried().getCount());
j += k;
if (i != 0) {
this.getCarried().subtract(k);
if (this.getCarried().isEmpty()) {
this.setCarried(ItemStack.a);
}
if (i > 0 && j >= i) {
return j;
}
}
}
return j;
}
private int i(ItemStack itemstack) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
return i == -1 ? itemstack.getCount() : this.d(i, itemstack);
}
private int d(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
itemstack1 = new ItemStack(item, 0);
if (itemstack.hasTag()) {
itemstack1.setTag(itemstack.getTag().clone());
}
this.setItem(i, itemstack1);
}
int k = j;
if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) {
k = itemstack1.getMaxStackSize() - itemstack1.getCount();
}
if (k > this.getMaxStackSize() - itemstack1.getCount()) {
k = this.getMaxStackSize() - itemstack1.getCount();
}
if (k == 0) {
return j;
} else {
j -= k;
itemstack1.add(k);
itemstack1.d(5);
return j;
}
}
@Override
public int firstPartial(ItemStack itemstack) {
if (this.a(this.getItem(this.itemInHandIndex), itemstack)) {
return this.itemInHandIndex;
} else if (this.a(this.getItem(40), itemstack)) {
return 40;
} else {
for (int i = 0; i < this.items.size(); ++i) {
if (this.a(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void j() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
if (!itemStacks.get(i).isEmpty()) {
itemStacks.get(i).a(this.player.world, this.player, i, this.itemInHandIndex == i);
}
}
}
}
@Override
public boolean pickup(ItemStack itemstack) {
return this.c(-1, itemstack);
}
@Override
public boolean c(int i, ItemStack itemstack) {
if (itemstack.isEmpty()) {
return false;
} else {
try {
if (itemstack.f()) {
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i >= 0) {
this.items.set(i, itemstack.cloneItemStack());
this.items.get(i).d(5);
itemstack.setCount(0);
return true;
} else if (this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemstack.getCount();
if (i == -1) {
itemstack.setCount(this.i(itemstack));
} else {
itemstack.setCount(this.d(i, itemstack));
}
} while(!itemstack.isEmpty() && itemstack.getCount() < j);
if (itemstack.getCount() == j && this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return itemstack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added");
crashreportsystemdetails.a("Item ID", Item.getId(itemstack.getItem()));
crashreportsystemdetails.a("Item data", itemstack.getDamage());
crashreportsystemdetails.a("Item name", () -> itemstack.getName().getString());
throw new ReportedException(crashreport);
}
}
}
@Override
public void a(World world, ItemStack itemstack) {
if (!world.isClientSide) {
while(!itemstack.isEmpty()) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i == -1) {
this.player.drop(itemstack, false);
break;
}
int j = itemstack.getMaxStackSize() - this.getItem(i).getCount();
if (this.c(i, itemstack.cloneAndSubtract(j))) {
((EntityPlayer)this.player).playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i)));
}
}
}
}
@Override
public void f(ItemStack itemstack) {
for (List<ItemStack> list : this.f) {
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) == itemstack) {
list.set(i, ItemStack.a);
break;
}
}
}
}
@Override
public float a(IBlockData iblockdata) {
return this.items.get(this.itemInHandIndex).a(iblockdata);
}
@Override
public NBTTagList a(NBTTagList nbttaglist) {
NBTTagCompound nbttagcompound;
int i;
for (i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) i);
this.items.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 100));
this.armor.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.extraSlots.size(); ++i) {
if (!this.extraSlots.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 150));
this.extraSlots.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
return nbttaglist;
}
@Override
public void b(NBTTagList nbttaglist) {
this.items.clear();
this.armor.clear();
this.extraSlots.clear();
for(int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound = nbttaglist.getCompound(i);
int j = nbttagcompound.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.a(nbttagcompound);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.extraSlots.size() + 150) {
this.extraSlots.set(j - 150, itemstack);
}
}
}
}
@Override
public boolean isNotEmpty() {
Iterator iterator = this.items.iterator();
ItemStack itemstack;
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.armor.iterator();
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.extraSlots.iterator();
while (iterator.hasNext()) {
itemstack = (ItemStack)iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Nullable
@Override
public IChatBaseComponent getCustomName() {
return null;
}
@Override
public boolean b(IBlockData iblockdata) {
return this.getItem(this.itemInHandIndex).canDestroySpecialBlock(iblockdata);
}
@Override
public void a(float f) {
if (f > 0.0F) {
f /= 4.0F;
if (f < 1.0F) {
f = 1.0F;
}
for (int i = 0; i < this.armor.size(); ++i) {
ItemStack itemstack = this.armor.get(0);
int index = i;
if (itemstack.getItem() instanceof ItemArmor) {
itemstack.damage((int) f, this.player, (entityhuman) -> entityhuman.c(EnumItemSlot.a(EnumItemSlot.Function.ARMOR, index)));
}
}
}
}
@Override
public void dropContents() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
ItemStack itemstack = itemStacks.get(i);
if (!itemstack.isEmpty()) {
itemStacks.set(i, ItemStack.a);
this.player.a(itemstack, true, false);
}
}
}
}
@Override
public boolean h(ItemStack itemstack) {
return this.f.stream().flatMap(List::stream).anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemstack));
}
@Override
public void a(PlayerInventory playerinventory) {
for (int i = 0; i < playerinventory.getSize(); ++i) {
this.setItem(i, playerinventory.getItem(i));
}
this.itemInHandIndex = playerinventory.itemInHandIndex;
}
@Override
public void clear() {
this.f.forEach(List::clear);
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.a(itemstack);
}
}
}

75
internal/v1_16_R1/pom.xml Normal file
View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvadapter1_16_R1</artifactId>
<name>OpenInvAdapter1_16_R1</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.16.1-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,360 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_16_R1.Block;
import net.minecraft.server.v1_16_R1.BlockBarrel;
import net.minecraft.server.v1_16_R1.BlockChest;
import net.minecraft.server.v1_16_R1.BlockChestTrapped;
import net.minecraft.server.v1_16_R1.BlockEnderChest;
import net.minecraft.server.v1_16_R1.BlockPosition;
import net.minecraft.server.v1_16_R1.BlockPropertyChestType;
import net.minecraft.server.v1_16_R1.BlockShulkerBox;
import net.minecraft.server.v1_16_R1.ChatMessage;
import net.minecraft.server.v1_16_R1.Container;
import net.minecraft.server.v1_16_R1.ContainerChest;
import net.minecraft.server.v1_16_R1.Containers;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.EnumChatFormat;
import net.minecraft.server.v1_16_R1.EnumGamemode;
import net.minecraft.server.v1_16_R1.IBlockData;
import net.minecraft.server.v1_16_R1.IChatBaseComponent;
import net.minecraft.server.v1_16_R1.ITileInventory;
import net.minecraft.server.v1_16_R1.InventoryEnderChest;
import net.minecraft.server.v1_16_R1.InventoryLargeChest;
import net.minecraft.server.v1_16_R1.PlayerInteractManager;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.TileEntity;
import net.minecraft.server.v1_16_R1.TileEntityChest;
import net.minecraft.server.v1_16_R1.TileEntityEnderChest;
import net.minecraft.server.v1_16_R1.TileEntityLootable;
import net.minecraft.server.v1_16_R1.TileInventory;
import net.minecraft.server.v1_16_R1.World;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.block.Barrel;
import org.bukkit.block.BlockFace;
import org.bukkit.block.BlockState;
import org.bukkit.block.EnderChest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Chest;
import org.bukkit.entity.Cat;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.bukkit.util.BoundingBox;
import org.jetbrains.annotations.NotNull;
public class AnySilentContainer implements IAnySilentContainer {
private Field playerInteractManagerGamemode;
public AnySilentContainer() {
try {
this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("gamemode");
this.playerInteractManagerGamemode.setAccessible(true);
} catch (NoSuchFieldException | SecurityException e) {
System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail.");
e.printStackTrace();
}
}
@Override
public boolean isAnySilentContainer(@NotNull final org.bukkit.block.Block bukkitBlock) {
if (bukkitBlock.getType() == Material.ENDER_CHEST) {
return true;
}
BlockState state = bukkitBlock.getState();
return state instanceof org.bukkit.block.Chest
|| state instanceof org.bukkit.block.ShulkerBox
|| state instanceof org.bukkit.block.Barrel;
}
@Override
public boolean isAnyContainerNeeded(@NotNull final Player p, @NotNull final org.bukkit.block.Block block) {
BlockState blockState = block.getState();
// Barrels do not require AnyContainer.
if (blockState instanceof Barrel) {
return false;
}
// Enderchests require a non-occluding block on top to open.
if (blockState instanceof EnderChest) {
return block.getRelative(0, 1, 0).getType().isOccluding();
}
// Shulker boxes require 1/2 a block clear in the direction they open.
if (blockState instanceof ShulkerBox) {
BoundingBox boundingBox = block.getBoundingBox();
if (boundingBox.getVolume() > 1) {
// Shulker box is already open.
return false;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Directional)) {
// Shouldn't be possible. Just in case, demand AnyChest.
return true;
}
Directional directional = (Directional) blockData;
BlockFace face = directional.getFacing();
boundingBox.shift(face.getDirection());
// Return whether or not bounding boxes overlap.
return block.getRelative(face, 1).getBoundingBox().overlaps(boundingBox);
}
if (!(blockState instanceof org.bukkit.block.Chest)) {
return false;
}
if (isBlockedChest(block)) {
return true;
}
BlockData blockData = block.getBlockData();
if (!(blockData instanceof Chest) || ((Chest) blockData).getType() == Chest.Type.SINGLE) {
return false;
}
Chest chest = (Chest) blockData;
int ordinal = (chest.getFacing().ordinal() + 4 + (chest.getType() == Chest.Type.RIGHT ? -1 : 1)) % 4;
BlockFace relativeFace = BlockFace.values()[ordinal];
org.bukkit.block.Block relative = block.getRelative(relativeFace);
if (relative.getType() != block.getType()) {
return false;
}
BlockData relativeData = relative.getBlockData();
if (!(relativeData instanceof Chest)) {
return false;
}
Chest relativeChest = (Chest) relativeData;
if (relativeChest.getFacing() != chest.getFacing()
|| relativeChest.getType() != (chest.getType() == Chest.Type.RIGHT ? Chest.Type.LEFT : Chest.Type.RIGHT)) {
return false;
}
return isBlockedChest(relative);
}
private boolean isBlockedChest(org.bukkit.block.Block block) {
org.bukkit.block.Block relative = block.getRelative(0, 1, 0);
return relative.getType().isOccluding()
|| block.getWorld().getNearbyEntities(BoundingBox.of(relative), entity -> entity instanceof Cat).size() > 0;
}
@Override
public boolean activateContainer(@NotNull final Player bukkitPlayer, final boolean silentchest,
@NotNull final org.bukkit.block.Block bukkitBlock) {
// Silent ender chest is API-only
if (silentchest && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
final World world = player.world;
final BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
final TileEntity tile = world.getTileEntity(blockPosition);
if (tile == null) {
return false;
}
if (tile instanceof TileEntityEnderChest) {
// Anychest ender chest. See net.minecraft.server.BlockEnderChest
InventoryEnderChest enderChest = player.getEnderChest();
enderChest.a((TileEntityEnderChest) tile);
player.openContainer(new TileInventory((containerCounter, playerInventory, ignored) -> {
Containers<?> containers;
int rows = enderChest.getSize() / 9;
switch (rows) {
case 1:
containers = Containers.GENERIC_9X1;
break;
case 2:
containers = Containers.GENERIC_9X2;
break;
case 3:
default:
containers = Containers.GENERIC_9X3;
break;
case 4:
containers = Containers.GENERIC_9X4;
break;
case 5:
containers = Containers.GENERIC_9X5;
break;
case 6:
containers = Containers.GENERIC_9X6;
break;
}
return new ContainerChest(containers, containerCounter, playerInventory, enderChest, rows);
}, BlockEnderChest.e));
bukkitPlayer.incrementStatistic(Statistic.ENDERCHEST_OPENED);
return true;
}
if (!(tile instanceof ITileInventory)) {
return false;
}
ITileInventory tileInventory = (ITileInventory) tile;
IBlockData blockData = world.getType(blockPosition);
Block block = blockData.getBlock();
if (block instanceof BlockChest) {
BlockPropertyChestType chestType = blockData.get(BlockChest.c);
if (chestType != BlockPropertyChestType.SINGLE) {
BlockPosition adjacentBlockPosition = blockPosition.shift(BlockChest.h(blockData));
IBlockData adjacentBlockData = world.getType(adjacentBlockPosition);
if (adjacentBlockData.getBlock() == block) {
BlockPropertyChestType adjacentChestType = adjacentBlockData.get(BlockChest.c);
if (adjacentChestType != BlockPropertyChestType.SINGLE && chestType != adjacentChestType
&& adjacentBlockData.get(BlockChest.FACING) == blockData.get(BlockChest.FACING)) {
TileEntity adjacentTile = world.getTileEntity(adjacentBlockPosition);
if (adjacentTile instanceof TileEntityChest && tileInventory instanceof TileEntityChest) {
TileEntityChest rightChest = chestType == BlockPropertyChestType.RIGHT ? ((TileEntityChest) tileInventory) : (TileEntityChest) adjacentTile;
TileEntityChest leftChest = chestType == BlockPropertyChestType.RIGHT ? (TileEntityChest) adjacentTile : ((TileEntityChest) tileInventory);
if (rightChest.lootTable != null || leftChest.lootTable != null) {
player.a(new ChatMessage("Loot not generated! Please disable /silentcontainer.").a(EnumChatFormat.RED), true);
return false;
}
tileInventory = new ITileInventory() {
public Container createMenu(int containerCounter, PlayerInventory playerInventory, EntityHuman entityHuman) {
leftChest.d(playerInventory.player);
rightChest.d(playerInventory.player);
return ContainerChest.b(containerCounter, playerInventory, new InventoryLargeChest(rightChest, leftChest));
}
public IChatBaseComponent getScoreboardDisplayName() {
return new ChatMessage("container.chestDouble");
}
};
}
}
}
}
if (block instanceof BlockChestTrapped) {
bukkitPlayer.incrementStatistic(Statistic.TRAPPED_CHEST_TRIGGERED);
} else {
bukkitPlayer.incrementStatistic(Statistic.CHEST_OPENED);
}
}
if (block instanceof BlockShulkerBox) {
bukkitPlayer.incrementStatistic(Statistic.SHULKER_BOX_OPENED);
}
if (block instanceof BlockBarrel) {
bukkitPlayer.incrementStatistic(Statistic.OPEN_BARREL);
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silentchest || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
player.openContainer(tileInventory);
return true;
}
// SilentChest requires access to setting players' gamemode directly.
if (this.playerInteractManagerGamemode == null) {
return false;
}
if (tile instanceof TileEntityLootable) {
TileEntityLootable lootable = (TileEntityLootable) tile;
if (lootable.lootTable != null) {
OpenInv.getPlugin(OpenInv.class).sendSystemMessage(bukkitPlayer, "messages.error.lootNotGenerated");
return false;
}
}
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.openContainer(tileInventory);
this.forceGameMode(player, gamemode);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.playerInteractManagerGamemode == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
case SHULKER_BOX:
case BARREL:
break;
default:
return;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.activeContainer.b(player);
player.activeContainer.a(player, false);
player.activeContainer.transferTo(player.defaultContainer, player.getBukkitEntity());
player.activeContainer = player.defaultContainer;
this.forceGameMode(player, gamemode);
}
private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) {
if (this.playerInteractManagerGamemode == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
if (!this.playerInteractManagerGamemode.isAccessible()) {
// Just in case, ensure accessible.
this.playerInteractManagerGamemode.setAccessible(true);
}
this.playerInteractManagerGamemode.set(player.playerInteractManager, gameMode);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,296 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_16_R1.ChatComponentText;
import net.minecraft.server.v1_16_R1.ChatMessageType;
import net.minecraft.server.v1_16_R1.Container;
import net.minecraft.server.v1_16_R1.Containers;
import net.minecraft.server.v1_16_R1.Entity;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.MinecraftServer;
import net.minecraft.server.v1_16_R1.NBTCompressedStreamTools;
import net.minecraft.server.v1_16_R1.NBTTagCompound;
import net.minecraft.server.v1_16_R1.PacketPlayOutChat;
import net.minecraft.server.v1_16_R1.PacketPlayOutOpenWindow;
import net.minecraft.server.v1_16_R1.PlayerInteractManager;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.SystemUtils;
import net.minecraft.server.v1_16_R1.World;
import net.minecraft.server.v1_16_R1.WorldNBTStorage;
import net.minecraft.server.v1_16_R1.WorldServer;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_16_R1.CraftServer;
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_16_R1.event.CraftEventFactory;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftContainer;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private @Nullable Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(final Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
}
return nmsPlayer;
}
@Nullable
@Override
public Player loadPlayer(@NotNull final OfflinePlayer offline) {
// Ensure player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
// See net.minecraft.server.PlayerList#attemptLogin
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
WorldServer worldServer = server.getWorldServer(World.OVERWORLD);
if (worldServer == null) {
return null;
}
EntityPlayer entity = new EntityPlayer(server, worldServer, profile, new PlayerInteractManager(worldServer));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity
Player target = entity.getBukkitEntity();
if (target != null) {
// Load data
target.loadData();
}
// Return the entity
return target;
}
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityPlayer)
try {
WorldNBTStorage worldNBTStorage = player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = player.save(new NBTTagCompound());
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("RootVehicle", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("RootVehicle", oldData.getCompound("RootVehicle"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueIDString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getDisplayName().getString());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
EntityPlayer nmsPlayer = getHandle(player);
if (nmsPlayer.playerConnection == null) {
return null;
}
String title;
if (inventory instanceof SpecialEnderChest) {
HumanEntity owner = (HumanEntity) ((SpecialEnderChest) inventory).getBukkitOwner();
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.enderchest", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Ender Chest";
}
} else if (inventory instanceof SpecialPlayerInventory) {
EntityHuman owner = ((PlayerInventory) inventory).player;
title = OpenInv.getPlugin(OpenInv.class).getLocalizedMessage(player, "container.player", "%player%", owner.getName());
if (title == null) {
title = owner.getName() + "'s Inventory";
}
} else {
return player.openInventory(inventory.getBukkitInventory());
}
String finalTitle = title;
Container container = new CraftContainer(new InventoryView() {
@Override
public @NotNull Inventory getTopInventory() {
return inventory.getBukkitInventory();
}
@Override
public @NotNull Inventory getBottomInventory() {
return player.getInventory();
}
@Override
public @NotNull HumanEntity getPlayer() {
return player;
}
@Override
public @NotNull InventoryType getType() {
return inventory.getBukkitInventory().getType();
}
@Override
public @NotNull String getTitle() {
return finalTitle;
}
}, nmsPlayer, nmsPlayer.nextContainerCounter()) {
@Override
public Containers<?> getType() {
switch (inventory.getBukkitInventory().getSize()) {
case 9:
return Containers.GENERIC_9X1;
case 18:
return Containers.GENERIC_9X2;
case 27:
default:
return Containers.GENERIC_9X3;
case 36:
return Containers.GENERIC_9X4;
case 41: // PLAYER
case 45:
return Containers.GENERIC_9X5;
case 54:
return Containers.GENERIC_9X6;
}
}
};
container.setTitle(new ChatComponentText(title));
container = CraftEventFactory.callInventoryOpenEvent(nmsPlayer, container);
if (container == null) {
return null;
}
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutOpenWindow(container.windowId, container.getType(),
new ChatComponentText(container.getBukkitView().getTitle())));
nmsPlayer.activeContainer = container;
container.addSlotListener(nmsPlayer);
return container.getBukkitView();
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), ChatMessageType.GAME_INFO, SystemUtils.b));
}
}
}

View File

@@ -0,0 +1,256 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.util.List;
import net.minecraft.server.v1_16_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R1.ContainerUtil;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.IInventoryListener;
import net.minecraft.server.v1_16_R1.InventoryEnderChest;
import net.minecraft.server.v1_16_R1.ItemStack;
import net.minecraft.server.v1_16_R1.NonNullList;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialEnderChest extends InventoryEnderChest implements ISpecialEnderChest {
private final CraftInventory inventory;
private EntityPlayer owner;
private NonNullList<ItemStack> items;
private boolean playerOnline;
public SpecialEnderChest(final Player player, final Boolean online) {
super(PlayerDataManager.getHandle(player));
this.inventory = new CraftInventory(this);
this.owner = PlayerDataManager.getHandle(player);
this.playerOnline = online;
this.items = this.owner.getEnderChest().items;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return inventory;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
try {
this.owner = PlayerDataManager.getHandle(player);
InventoryEnderChest enderChest = owner.getEnderChest();
for (int i = 0; i < enderChest.getSize(); ++i) {
enderChest.setItem(i, this.items.get(i));
}
this.items = enderChest.items;
} catch (Exception ignored) {}
this.playerOnline = true;
}
}
@Override
public void update() {
this.owner.getEnderChest().update();
}
@Override
public List<ItemStack> getContents() {
return this.items;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.owner.getEnderChest().onOpen(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.owner.getEnderChest().onClose(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.owner.getEnderChest().getViewers();
}
@Override
public void setMaxStackSize(int i) {
this.owner.getEnderChest().setMaxStackSize(i);
}
@Override
public InventoryHolder getOwner() {
return this.owner.getEnderChest().getOwner();
}
@Override
public @Nullable Location getLocation() {
return null;
}
@Override
public void a(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().a(iinventorylistener);
}
@Override
public void b(IInventoryListener iinventorylistener) {
this.owner.getEnderChest().b(iinventorylistener);
}
@Override
public ItemStack getItem(int i) {
return i >= 0 && i < this.items.size() ? this.items.get(i) : ItemStack.b;
}
@Override
public ItemStack splitStack(int i, int j) {
ItemStack itemstack = ContainerUtil.a(this.items, i, j);
if (!itemstack.isEmpty()) {
this.update();
}
return itemstack;
}
@Override
public ItemStack a(ItemStack itemstack) {
ItemStack itemstack1 = itemstack.cloneItemStack();
for (int i = 0; i < this.getSize(); ++i) {
ItemStack itemstack2 = this.getItem(i);
if (itemstack2.isEmpty()) {
this.setItem(i, itemstack1);
this.update();
return ItemStack.b;
}
if (ItemStack.c(itemstack2, itemstack1)) {
int j = Math.min(this.getMaxStackSize(), itemstack2.getMaxStackSize());
int k = Math.min(itemstack1.getCount(), j - itemstack2.getCount());
if (k > 0) {
itemstack2.add(k);
itemstack1.subtract(k);
if (itemstack1.isEmpty()) {
this.update();
return ItemStack.b;
}
}
}
}
if (itemstack1.getCount() != itemstack.getCount()) {
this.update();
}
return itemstack1;
}
@Override
public ItemStack splitWithoutUpdate(int i) {
ItemStack itemstack = this.items.get(i);
if (itemstack.isEmpty()) {
return ItemStack.b;
} else {
this.items.set(i, ItemStack.b);
return itemstack;
}
}
@Override
public void setItem(int i, ItemStack itemstack) {
this.items.set(i, itemstack);
if (!itemstack.isEmpty() && itemstack.getCount() > this.getMaxStackSize()) {
itemstack.setCount(this.getMaxStackSize());
}
this.update();
}
@Override
public int getSize() {
return this.owner.getEnderChest().getSize();
}
@Override
public boolean isEmpty() {
for (ItemStack itemstack : this.items) {
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Override
public int getMaxStackSize() {
return 64;
}
@Override
public boolean a(EntityHuman entityhuman) {
return true;
}
@Override
public void startOpen(EntityHuman entityhuman) {
}
@Override
public void closeContainer(EntityHuman entityhuman) {
}
@Override
public boolean b(int i, ItemStack itemstack) {
return true;
}
@Override
public void clear() {
this.items.clear();
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.b(itemstack);
}
}
}

View File

@@ -0,0 +1,733 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_16_R1;
import com.google.common.collect.ImmutableList;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.server.v1_16_R1.AutoRecipeStackManager;
import net.minecraft.server.v1_16_R1.ChatMessage;
import net.minecraft.server.v1_16_R1.ContainerUtil;
import net.minecraft.server.v1_16_R1.CrashReport;
import net.minecraft.server.v1_16_R1.CrashReportSystemDetails;
import net.minecraft.server.v1_16_R1.DamageSource;
import net.minecraft.server.v1_16_R1.EntityHuman;
import net.minecraft.server.v1_16_R1.EntityPlayer;
import net.minecraft.server.v1_16_R1.EnumItemSlot;
import net.minecraft.server.v1_16_R1.IBlockData;
import net.minecraft.server.v1_16_R1.IChatBaseComponent;
import net.minecraft.server.v1_16_R1.IInventory;
import net.minecraft.server.v1_16_R1.Item;
import net.minecraft.server.v1_16_R1.ItemArmor;
import net.minecraft.server.v1_16_R1.ItemStack;
import net.minecraft.server.v1_16_R1.NBTTagCompound;
import net.minecraft.server.v1_16_R1.NBTTagList;
import net.minecraft.server.v1_16_R1.NonNullList;
import net.minecraft.server.v1_16_R1.PacketPlayOutSetSlot;
import net.minecraft.server.v1_16_R1.PlayerInventory;
import net.minecraft.server.v1_16_R1.ReportedException;
import net.minecraft.server.v1_16_R1.World;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R1.entity.CraftHumanEntity;
import org.bukkit.craftbukkit.v1_16_R1.inventory.CraftInventory;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
private final CraftInventory inventory;
private boolean playerOnline;
private EntityHuman player;
private NonNullList<ItemStack> items, armor, extraSlots;
private List<NonNullList<ItemStack>> f;
public SpecialPlayerInventory(final Player bukkitPlayer, final Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.inventory = new CraftInventory(this);
this.playerOnline = online;
this.player = super.player;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
}
@Override
public void setPlayerOnline(@NotNull final Player player) {
if (!this.playerOnline) {
EntityPlayer entityPlayer = PlayerDataManager.getHandle(player);
entityPlayer.inventory.transaction.addAll(this.transaction);
this.player = entityPlayer;
for (int i = 0; i < getSize(); ++i) {
this.player.inventory.setItem(i, getRawItem(i));
}
this.player.inventory.itemInHandIndex = this.itemInHandIndex;
this.items = this.player.inventory.items;
this.armor = this.player.inventory.armor;
this.extraSlots = this.player.inventory.extraSlots;
this.f = ImmutableList.of(this.items, this.armor, this.extraSlots);
this.playerOnline = true;
}
}
@Override
public boolean a(final EntityHuman entityhuman) {
return true;
}
@Override
public @NotNull CraftInventory getBukkitInventory() {
return this.inventory;
}
@Override
public ItemStack getItem(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
return list.get(i);
}
private ItemStack getRawItem(int i) {
NonNullList<ItemStack> list = null;
for (NonNullList<ItemStack> next : this.f) {
if (i < next.size()) {
list = next;
break;
}
i -= next.size();
}
return list == null ? ItemStack.b : list.get(i);
}
@Override
public IChatBaseComponent getDisplayName() {
return new ChatMessage(this.player.getName());
}
@Override
public boolean hasCustomName() {
return false;
}
private int getReversedArmorSlotNum(final int i) {
if (i == 0) {
return 3;
}
if (i == 1) {
return 2;
}
if (i == 2) {
return 1;
}
if (i == 3) {
return 0;
}
return i;
}
private int getReversedItemSlotNum(final int i) {
if (i >= 27) {
return i - 27;
}
return i + 9;
}
@Override
public int getSize() {
return 45;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void setItem(int i, final ItemStack itemstack) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
this.player.drop(itemstack, true);
return;
}
list.set(i, itemstack);
}
@Override
public void setPlayerOffline() {
this.playerOnline = false;
}
@Override
public ItemStack splitStack(int i, final int j) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
return list.get(i).isEmpty() ? ItemStack.b : ContainerUtil.a(list, i, j);
}
@Override
public ItemStack splitWithoutUpdate(int i) {
List<ItemStack> list = this.items;
if (i >= list.size()) {
i -= list.size();
list = this.armor;
} else {
i = this.getReversedItemSlotNum(i);
}
if (i >= list.size()) {
i -= list.size();
list = this.extraSlots;
} else if (list == this.armor) {
i = this.getReversedArmorSlotNum(i);
}
if (i >= list.size()) {
return ItemStack.b;
}
if (!list.get(i).isEmpty()) {
ItemStack itemstack = list.get(i);
list.set(i, ItemStack.b);
return itemstack;
}
return ItemStack.b;
}
@Override
public List<ItemStack> getContents() {
return this.f.stream().flatMap(List::stream).collect(Collectors.toList());
}
@Override
public List<ItemStack> getArmorContents() {
return this.armor;
}
@Override
public void onOpen(CraftHumanEntity who) {
this.transaction.add(who);
}
@Override
public void onClose(CraftHumanEntity who) {
this.transaction.remove(who);
}
@Override
public List<HumanEntity> getViewers() {
return this.transaction;
}
@Override
public InventoryHolder getOwner() {
return this.player.getBukkitEntity();
}
@Override
public Location getLocation() {
return this.player.getBukkitEntity().getLocation();
}
@Override
public ItemStack getItemInHand() {
return d(this.itemInHandIndex) ? this.items.get(this.itemInHandIndex) : ItemStack.b;
}
private boolean isSimilarAndNotFull(ItemStack itemstack, ItemStack itemstack1) {
return !itemstack.isEmpty() && this.b(itemstack, itemstack1) && itemstack.isStackable() && itemstack.getCount() < itemstack.getMaxStackSize() && itemstack.getCount() < this.getMaxStackSize();
}
private boolean b(ItemStack itemstack, ItemStack itemstack1) {
return itemstack.getItem() == itemstack1.getItem() && ItemStack.equals(itemstack, itemstack1);
}
@Override
public int canHold(ItemStack itemstack) {
int remains = itemstack.getCount();
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
return itemstack.getCount();
}
if (!this.isSimilarAndNotFull(itemstack, itemstack1)) {
remains -= Math.min(itemstack1.getMaxStackSize(), this.getMaxStackSize()) - itemstack1.getCount();
}
if (remains <= 0) {
return itemstack.getCount();
}
}
ItemStack offhandItemStack = this.getItem(this.items.size() + this.armor.size());
if (this.isSimilarAndNotFull(offhandItemStack, itemstack)) {
remains -= Math.min(offhandItemStack.getMaxStackSize(), this.getMaxStackSize()) - offhandItemStack.getCount();
}
return itemstack.getCount() - remains;
}
@Override
public int getFirstEmptySlotIndex() {
for (int i = 0; i < this.items.size(); ++i) {
if (this.items.get(i).isEmpty()) {
return i;
}
}
return -1;
}
@Override
public void c(int i) {
this.itemInHandIndex = this.i();
ItemStack itemstack = this.items.get(this.itemInHandIndex);
this.items.set(this.itemInHandIndex, this.items.get(i));
this.items.set(i, itemstack);
}
@Override
public int c(ItemStack itemstack) {
for (int i = 0; i < this.items.size(); ++i) {
ItemStack itemstack1 = this.items.get(i);
if (!this.items.get(i).isEmpty() && this.b(itemstack, this.items.get(i)) && !this.items.get(i).f() && !itemstack1.hasEnchantments() && !itemstack1.hasName()) {
return i;
}
}
return -1;
}
@Override
public int i() {
int i;
int j;
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (this.items.get(i).isEmpty()) {
return i;
}
}
for (j = 0; j < 9; ++j) {
i = (this.itemInHandIndex + j) % 9;
if (!this.items.get(i).hasEnchantments()) {
return i;
}
}
return this.itemInHandIndex;
}
@Override
public int a(Predicate<ItemStack> predicate, int i, IInventory iinventory) {
byte b0 = 0;
boolean flag = i == 0;
int j = b0 + ContainerUtil.a(this, predicate, i - b0, flag);
j += ContainerUtil.a(iinventory, predicate, i - j, flag);
j += ContainerUtil.a(this.getCarried(), predicate, i - j, flag);
if (this.getCarried().isEmpty()) {
this.setCarried(ItemStack.b);
}
return j;
}
private int i(ItemStack itemstack) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
return i == -1 ? itemstack.getCount() : this.d(i, itemstack);
}
private int d(int i, ItemStack itemstack) {
Item item = itemstack.getItem();
int j = itemstack.getCount();
ItemStack itemstack1 = this.getItem(i);
if (itemstack1.isEmpty()) {
itemstack1 = new ItemStack(item, 0);
NBTTagCompound tag = itemstack.getTag();
if (tag != null) {
itemstack1.setTag(tag.clone());
}
this.setItem(i, itemstack1);
}
int k = j;
if (j > itemstack1.getMaxStackSize() - itemstack1.getCount()) {
k = itemstack1.getMaxStackSize() - itemstack1.getCount();
}
if (k > this.getMaxStackSize() - itemstack1.getCount()) {
k = this.getMaxStackSize() - itemstack1.getCount();
}
if (k != 0) {
j -= k;
itemstack1.add(k);
itemstack1.d(5);
}
return j;
}
@Override
public int firstPartial(ItemStack itemstack) {
if (this.isSimilarAndNotFull(this.getItem(this.itemInHandIndex), itemstack)) {
return this.itemInHandIndex;
} else if (this.isSimilarAndNotFull(this.getItem(40), itemstack)) {
return 40;
} else {
for (int i = 0; i < this.items.size(); ++i) {
if (this.isSimilarAndNotFull(this.items.get(i), itemstack)) {
return i;
}
}
return -1;
}
}
@Override
public void j() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
if (!itemStacks.get(i).isEmpty()) {
itemStacks.get(i).a(this.player.world, this.player, i, this.itemInHandIndex == i);
}
}
}
}
@Override
public boolean pickup(ItemStack itemstack) {
return this.c(-1, itemstack);
}
@Override
public boolean c(int i, ItemStack itemstack) {
if (itemstack.isEmpty()) {
return false;
} else {
try {
if (itemstack.f()) {
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i >= 0) {
this.items.set(i, itemstack.cloneItemStack());
this.items.get(i).d(5);
itemstack.setCount(0);
return true;
} else if (this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return false;
}
} else {
int j;
do {
j = itemstack.getCount();
if (i == -1) {
itemstack.setCount(this.i(itemstack));
} else {
itemstack.setCount(this.d(i, itemstack));
}
} while(!itemstack.isEmpty() && itemstack.getCount() < j);
if (itemstack.getCount() == j && this.player.abilities.canInstantlyBuild) {
itemstack.setCount(0);
return true;
} else {
return itemstack.getCount() < j;
}
}
} catch (Throwable var6) {
CrashReport crashreport = CrashReport.a(var6, "Adding item to inventory");
CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Item being added");
crashreportsystemdetails.a("Item ID", Item.getId(itemstack.getItem()));
crashreportsystemdetails.a("Item data", itemstack.getDamage());
crashreportsystemdetails.a("Item name", () -> itemstack.getName().getString());
throw new ReportedException(crashreport);
}
}
}
@Override
public void a(World world, ItemStack itemstack) {
if (!world.isClientSide) {
while(!itemstack.isEmpty()) {
int i = this.firstPartial(itemstack);
if (i == -1) {
i = this.getFirstEmptySlotIndex();
}
if (i == -1) {
this.player.drop(itemstack, false);
break;
}
int j = itemstack.getMaxStackSize() - this.getItem(i).getCount();
if (this.c(i, itemstack.cloneAndSubtract(j))) {
((EntityPlayer)this.player).playerConnection.sendPacket(new PacketPlayOutSetSlot(-2, i, this.getItem(i)));
}
}
}
}
@Override
public void f(ItemStack itemstack) {
for (List<ItemStack> list : this.f) {
for (int i = 0; i < list.size(); ++i) {
if (list.get(i) == itemstack) {
list.set(i, ItemStack.b);
break;
}
}
}
}
@Override
public float a(IBlockData iblockdata) {
return this.items.get(this.itemInHandIndex).a(iblockdata);
}
@Override
public NBTTagList a(NBTTagList nbttaglist) {
NBTTagCompound nbttagcompound;
int i;
for (i = 0; i < this.items.size(); ++i) {
if (!this.items.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) i);
this.items.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.armor.size(); ++i) {
if (!this.armor.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 100));
this.armor.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
for (i = 0; i < this.extraSlots.size(); ++i) {
if (!this.extraSlots.get(i).isEmpty()) {
nbttagcompound = new NBTTagCompound();
nbttagcompound.setByte("Slot", (byte) (i + 150));
this.extraSlots.get(i).save(nbttagcompound);
nbttaglist.add(nbttagcompound);
}
}
return nbttaglist;
}
@Override
public void b(NBTTagList nbttaglist) {
this.items.clear();
this.armor.clear();
this.extraSlots.clear();
for(int i = 0; i < nbttaglist.size(); ++i) {
NBTTagCompound nbttagcompound = nbttaglist.getCompound(i);
int j = nbttagcompound.getByte("Slot") & 255;
ItemStack itemstack = ItemStack.a(nbttagcompound);
if (!itemstack.isEmpty()) {
if (j < this.items.size()) {
this.items.set(j, itemstack);
} else if (j >= 100 && j < this.armor.size() + 100) {
this.armor.set(j - 100, itemstack);
} else if (j >= 150 && j < this.extraSlots.size() + 150) {
this.extraSlots.set(j - 150, itemstack);
}
}
}
}
@Override
public boolean isEmpty() {
Iterator<ItemStack> iterator = this.items.iterator();
ItemStack itemstack;
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.armor.iterator();
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
iterator = this.extraSlots.iterator();
while (iterator.hasNext()) {
itemstack = iterator.next();
if (!itemstack.isEmpty()) {
return false;
}
}
return true;
}
@Nullable
@Override
public IChatBaseComponent getCustomName() {
return null;
}
@Override
public void a(DamageSource damagesource, float f) {
if (f > 0.0F) {
f /= 4.0F;
if (f < 1.0F) {
f = 1.0F;
}
for (int i = 0; i < this.armor.size(); ++i) {
ItemStack itemstack = this.armor.get(0);
int index = i;
if ((!damagesource.isFire() || !itemstack.getItem().u()) && itemstack.getItem() instanceof ItemArmor) {
itemstack.damage((int) f, this.player, (entityHuman) -> entityHuman.broadcastItemBreak(EnumItemSlot.a(EnumItemSlot.Function.ARMOR, index)));
}
}
}
}
@Override
public void dropContents() {
for (List<ItemStack> itemStacks : this.f) {
for (int i = 0; i < itemStacks.size(); ++i) {
ItemStack itemstack = itemStacks.get(i);
if (!itemstack.isEmpty()) {
itemStacks.set(i, ItemStack.b);
this.player.a(itemstack, true, false);
}
}
}
}
@Override
public boolean h(ItemStack itemstack) {
return this.f.stream().flatMap(List::stream).anyMatch(itemStack1 -> !itemStack1.isEmpty() && itemStack1.doMaterialsMatch(itemstack));
}
@Override
public void a(PlayerInventory playerinventory) {
for (int i = 0; i < playerinventory.getSize(); ++i) {
this.setItem(i, playerinventory.getItem(i));
}
this.itemInHandIndex = playerinventory.itemInHandIndex;
}
@Override
public void clear() {
this.f.forEach(List::clear);
}
@Override
public void a(AutoRecipeStackManager autorecipestackmanager) {
for (ItemStack itemstack : this.items) {
autorecipestackmanager.a(itemstack);
}
}
}

74
internal/v1_8_R3/pom.xml Normal file
View File

@@ -0,0 +1,74 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvinternal</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvadapter1_8_R3</artifactId>
<name>OpenInvAdapter1_8_R3</name>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot</artifactId>
<version>1.8.8-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvplugincore</artifactId>
<version>4.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,250 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R3;
import com.lishid.openinv.internal.IAnySilentContainer;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.AxisAlignedBB;
import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockChest;
import net.minecraft.server.v1_8_R3.BlockEnderChest;
import net.minecraft.server.v1_8_R3.BlockPosition;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityOcelot;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.EnumDirection;
import net.minecraft.server.v1_8_R3.ITileInventory;
import net.minecraft.server.v1_8_R3.InventoryEnderChest;
import net.minecraft.server.v1_8_R3.InventoryLargeChest;
import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.StatisticList;
import net.minecraft.server.v1_8_R3.TileEntity;
import net.minecraft.server.v1_8_R3.TileEntityChest;
import net.minecraft.server.v1_8_R3.TileEntityEnderChest;
import net.minecraft.server.v1_8_R3.World;
import net.minecraft.server.v1_8_R3.WorldSettings.EnumGamemode;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
public class AnySilentContainer implements IAnySilentContainer {
private Field playerInteractManagerGamemode;
public AnySilentContainer() {
try {
this.playerInteractManagerGamemode = PlayerInteractManager.class.getDeclaredField("gamemode");
this.playerInteractManagerGamemode.setAccessible(true);
} catch (Exception e) {
System.err.println("[OpenInv] Unable to directly write player gamemode! SilentChest will fail.");
e.printStackTrace();
}
}
@Override
public boolean isAnySilentContainer(@NotNull org.bukkit.block.Block bukkitBlock) {
return bukkitBlock.getType() == Material.ENDER_CHEST || bukkitBlock.getState() instanceof org.bukkit.block.Chest;
}
@Override
public boolean isAnyContainerNeeded(@NotNull Player bukkitPlayer, @NotNull org.bukkit.block.Block bukkitBlock) {
World world = PlayerDataManager.getHandle(bukkitPlayer).world;
BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
Block block = world.getType(blockPosition).getBlock();
if (block instanceof BlockEnderChest) {
// Ender chests are not blocked by ocelots.
return world.getType(blockPosition.up()).getBlock().c();
}
// Check if chest is blocked or has an ocelot on top
if (isBlockedChest(world, blockPosition)) {
return true;
}
// Check for matching adjacent chests that are blocked or have an ocelot on top
for (EnumDirection localEnumDirection : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition localBlockPosition = blockPosition.shift(localEnumDirection);
Block localBlock = world.getType(localBlockPosition).getBlock();
if (localBlock != block) {
continue;
}
TileEntity localTileEntity = world.getTileEntity(localBlockPosition);
if (!(localTileEntity instanceof TileEntityChest)) {
continue;
}
if (isBlockedChest(world, localBlockPosition)) {
return true;
}
}
return false;
}
private boolean isBlockedChest(World world, BlockPosition blockPosition) {
// For reference, loot at net.minecraft.server.BlockChest
return world.getType(blockPosition.up()).getBlock().c() || hasOcelotOnTop(world, blockPosition);
}
private boolean hasOcelotOnTop(World world, BlockPosition blockPosition) {
for (Entity localEntity : world.a(EntityOcelot.class,
new AxisAlignedBB(blockPosition.getX(), blockPosition.getY() + 1,
blockPosition.getZ(), blockPosition.getX() + 1, blockPosition.getY() + 2,
blockPosition.getZ() + 1))) {
EntityOcelot localEntityOcelot = (EntityOcelot) localEntity;
if (localEntityOcelot.isSitting()) {
return true;
}
}
return false;
}
@Override
public boolean activateContainer(@NotNull Player bukkitPlayer, boolean silent, @NotNull org.bukkit.block.Block bukkitBlock) {
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
// Silent ender chest is pretty much API-only
if (silent && bukkitBlock.getType() == Material.ENDER_CHEST) {
bukkitPlayer.openInventory(bukkitPlayer.getEnderChest());
player.b(StatisticList.V);
return true;
}
World world = player.world;
BlockPosition blockPosition = new BlockPosition(bukkitBlock.getX(), bukkitBlock.getY(), bukkitBlock.getZ());
Object tile = world.getTileEntity(blockPosition);
if (tile == null) {
return false;
}
if (tile instanceof TileEntityEnderChest) {
// Anychest ender chest. See net.minecraft.server.BlockEnderChest
InventoryEnderChest enderChest = player.getEnderChest();
enderChest.a((TileEntityEnderChest) tile);
player.openContainer(enderChest);
player.b(StatisticList.V);
return true;
}
if (!(tile instanceof ITileInventory)) {
return false;
}
ITileInventory tileInventory = (ITileInventory) tile;
Block block = world.getType(blockPosition).getBlock();
if (block instanceof BlockChest) {
for (EnumDirection localEnumDirection : EnumDirection.EnumDirectionLimit.HORIZONTAL) {
BlockPosition localBlockPosition = blockPosition.shift(localEnumDirection);
Block localBlock = world.getType(localBlockPosition).getBlock();
if (localBlock != block) {
continue;
}
TileEntity localTileEntity = world.getTileEntity(localBlockPosition);
if (!(localTileEntity instanceof TileEntityChest)) {
continue;
}
if ((localEnumDirection == EnumDirection.WEST) || (localEnumDirection == EnumDirection.NORTH)) {
tileInventory = new InventoryLargeChest("container.chestDouble",
(TileEntityChest) localTileEntity, tileInventory);
} else {
tileInventory = new InventoryLargeChest("container.chestDouble",
tileInventory, (TileEntityChest) localTileEntity);
}
break;
}
BlockChest blockChest = (BlockChest) block;
if (blockChest.b == 0) {
player.b(StatisticList.aa);
} else if (blockChest.b == 1) {
player.b(StatisticList.U);
}
}
// AnyChest only - SilentChest not active, container unsupported, or unnecessary.
if (!silent || player.playerInteractManager.getGameMode() == EnumGamemode.SPECTATOR) {
player.openContainer(tileInventory);
return true;
}
// SilentChest requires access to setting players' gamemode directly.
if (this.playerInteractManagerGamemode == null) {
return false;
}
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.openContainer(tileInventory);
this.forceGameMode(player, gamemode);
return true;
}
@Override
public void deactivateContainer(@NotNull final Player bukkitPlayer) {
if (this.playerInteractManagerGamemode == null) {
return;
}
InventoryView view = bukkitPlayer.getOpenInventory();
switch (view.getType()) {
case CHEST:
case ENDER_CHEST:
break;
default:
return;
}
EntityPlayer player = PlayerDataManager.getHandle(bukkitPlayer);
EnumGamemode gamemode = player.playerInteractManager.getGameMode();
this.forceGameMode(player, EnumGamemode.SPECTATOR);
player.activeContainer.b(player);
player.activeContainer.a(player, false);
player.activeContainer.transferTo(player.defaultContainer, player.getBukkitEntity());
player.activeContainer = player.defaultContainer;
this.forceGameMode(player, gamemode);
}
private void forceGameMode(final EntityPlayer player, final EnumGamemode gameMode) {
if (this.playerInteractManagerGamemode == null) {
// No need to warn repeatedly, error on startup and lack of function should be enough.
return;
}
try {
if (!this.playerInteractManagerGamemode.isAccessible()) {
// Just in case, ensure accessible.
this.playerInteractManagerGamemode.setAccessible(true);
}
this.playerInteractManagerGamemode.set(player.playerInteractManager, gameMode);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,201 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R3;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialInventory;
import com.mojang.authlib.GameProfile;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.ChatComponentText;
import net.minecraft.server.v1_8_R3.Entity;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.MinecraftServer;
import net.minecraft.server.v1_8_R3.NBTCompressedStreamTools;
import net.minecraft.server.v1_8_R3.NBTTagCompound;
import net.minecraft.server.v1_8_R3.PacketPlayOutChat;
import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.WorldNBTStorage;
import org.apache.logging.log4j.LogManager;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.Server;
import org.bukkit.craftbukkit.v1_8_R3.CraftServer;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PlayerDataManager implements IPlayerDataManager {
private Field bukkitEntity;
public PlayerDataManager() {
try {
bukkitEntity = Entity.class.getDeclaredField("bukkitEntity");
} catch (NoSuchFieldException e) {
System.out.println("Unable to obtain field to inject custom save process - players' mounts may be deleted when loaded.");
e.printStackTrace();
bukkitEntity = null;
}
}
@NotNull
public static EntityPlayer getHandle(Player player) {
if (player instanceof CraftPlayer) {
return ((CraftPlayer) player).getHandle();
}
Server server = player.getServer();
EntityPlayer nmsPlayer = null;
if (server instanceof CraftServer) {
nmsPlayer = ((CraftServer) server).getHandle().getPlayer(player.getName());
}
if (nmsPlayer == null) {
// Could use reflection to examine fields, but it's honestly not worth the bother.
throw new RuntimeException("Unable to fetch EntityPlayer from provided Player implementation");
}
return nmsPlayer;
}
@Nullable
@Override
public Player loadPlayer(@NotNull OfflinePlayer offline) {
// Ensure the player has data
if (!offline.hasPlayedBefore()) {
return null;
}
// Create a profile and entity to load the player data
GameProfile profile = new GameProfile(offline.getUniqueId(),
offline.getName() != null ? offline.getName() : offline.getUniqueId().toString());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile,
new PlayerInteractManager(server.getWorldServer(0)));
try {
injectPlayer(entity);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// Get the bukkit entity
Player target = entity.getBukkitEntity();
if (target != null) {
// Load data
target.loadData();
}
// Return the entity
return target;
}
void injectPlayer(EntityPlayer player) throws IllegalAccessException {
if (bukkitEntity == null) {
return;
}
bukkitEntity.setAccessible(true);
bukkitEntity.set(player, new CraftPlayer(player.server.server, player) {
@Override
public void saveData() {
super.saveData();
// See net.minecraft.server.WorldNBTStorage#save(EntityHuman)
try {
WorldNBTStorage worldNBTStorage = (WorldNBTStorage) player.server.getPlayerList().playerFileData;
NBTTagCompound playerData = new NBTTagCompound();
player.e(playerData);
if (!isOnline()) {
// Special case: save old vehicle data
NBTTagCompound oldData = worldNBTStorage.load(player);
if (oldData != null && oldData.hasKeyOfType("Riding", 10)) {
// See net.minecraft.server.PlayerList#a(NetworkManager, EntityPlayer) and net.minecraft.server.EntityPlayer#b(NBTTagCompound)
playerData.set("Riding", oldData.getCompound("Riding"));
}
}
File file = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat.tmp");
File file1 = new File(worldNBTStorage.getPlayerDir(), player.getUniqueID().toString() + ".dat");
NBTCompressedStreamTools.a(playerData, new FileOutputStream(file));
if (file1.exists()) {
file1.delete();
}
file.renameTo(file1);
} catch (Exception e) {
LogManager.getLogger().warn("Failed to save player data for {}", player.getName());
}
}
});
}
@NotNull
@Override
public Player inject(@NotNull Player player) {
try {
EntityPlayer nmsPlayer = getHandle(player);
injectPlayer(nmsPlayer);
return nmsPlayer.getBukkitEntity();
} catch (IllegalAccessException e) {
e.printStackTrace();
return player;
}
}
@Nullable
@Override
public InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return player.openInventory(inventory.getBukkitInventory());
}
@Override
public void sendSystemMessage(@NotNull Player player, @NotNull String message) {
int newline = message.indexOf('\n');
if (newline != -1) {
// No newlines in action bar chat.
message = message.substring(0, newline);
}
if (message.isEmpty()) {
return;
}
EntityPlayer nmsPlayer = getHandle(player);
// For action bar chat, color codes are still supported but JSON text color is not allowed. Do not convert text.
if (nmsPlayer.playerConnection != null) {
nmsPlayer.playerConnection.sendPacket(new PacketPlayOutChat(new ChatComponentText(message), (byte) 2));
}
}
@NotNull
@Override
public String getLocale(Player player) {
return getHandle(player).locale;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R3;
import com.lishid.openinv.internal.ISpecialEnderChest;
import java.lang.reflect.Field;
import net.minecraft.server.v1_8_R3.EntityPlayer;
import net.minecraft.server.v1_8_R3.IInventory;
import net.minecraft.server.v1_8_R3.InventoryEnderChest;
import net.minecraft.server.v1_8_R3.InventorySubcontainer;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
private final InventoryEnderChest enderChest;
private final CraftInventory inventory = new CraftInventory(this);
private boolean playerOnline;
public SpecialEnderChest(Player player, Boolean online) {
super(PlayerDataManager.getHandle(player).getEnderChest().getName(),
PlayerDataManager.getHandle(player).getEnderChest().hasCustomName(),
PlayerDataManager.getHandle(player).getEnderChest().getSize());
this.playerOnline = online;
EntityPlayer nmsPlayer = PlayerDataManager.getHandle(player);
this.enderChest = nmsPlayer.getEnderChest();
this.bukkitOwner = nmsPlayer.getBukkitEntity();
this.items = enderChest.getContents();
}
@Override
public @NotNull Inventory getBukkitInventory() {
return inventory;
}
@Override
public void setPlayerOnline(@NotNull Player player) {
if (!playerOnline) {
try {
EntityPlayer nmsPlayer = PlayerDataManager.getHandle(player);
this.bukkitOwner = nmsPlayer.getBukkitEntity();
InventoryEnderChest playerEnderChest = nmsPlayer.getEnderChest();
Field field = playerEnderChest.getClass().getField("items");
field.setAccessible(true);
field.set(playerEnderChest, this.items);
} catch (Exception ignored) {}
playerOnline = true;
}
}
@Override
public void setPlayerOffline() {
playerOnline = false;
}
@Override
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public void update() {
super.update();
enderChest.update();
}
}

View File

@@ -1,89 +1,73 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
package com.lishid.openinv.internal.v1_8_R3;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import net.minecraft.server.v1_8_R3.ItemStack;
import net.minecraft.server.v1_8_R3.PlayerInventory;
import org.bukkit.craftbukkit.v1_8_R3.inventory.CraftInventory;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
import org.jetbrains.annotations.NotNull;
public class SpecialPlayerInventory extends PlayerInventory implements ISpecialPlayerInventory {
CraftPlayer owner;
public boolean playerOnline = false;
private ItemStack[] extra = new ItemStack[5];
private CraftInventory inventory = new CraftInventory(this);
public SpecialPlayerInventory(Player p, Boolean online) {
super(((CraftPlayer) p).getHandle());
this.owner = ((CraftPlayer) p);
private final ItemStack[] extra = new ItemStack[5];
private final CraftInventory inventory = new CraftInventory(this);
private boolean playerOnline;
public SpecialPlayerInventory(Player bukkitPlayer, Boolean online) {
super(PlayerDataManager.getHandle(bukkitPlayer));
this.playerOnline = online;
this.items = player.inventory.items;
this.armor = player.inventory.armor;
OpenInv.inventories.put(owner.getName().toLowerCase(), this);
}
@Override
public Inventory getBukkitInventory() {
public @NotNull Inventory getBukkitInventory() {
return inventory;
}
@Override
public void InventoryRemovalCheck() {
owner.saveData();
if (transaction.isEmpty() && !playerOnline) {
OpenInv.inventories.remove(owner.getName().toLowerCase());
}
}
@Override
public void PlayerGoOnline(Player player) {
public void setPlayerOnline(@NotNull Player player) {
if (!playerOnline) {
CraftPlayer p = (CraftPlayer) player;
p.getHandle().inventory.items = this.items;
p.getHandle().inventory.armor = this.armor;
p.saveData();
this.player = PlayerDataManager.getHandle(player);
this.player.inventory.items = this.items;
this.player.inventory.armor = this.armor;
playerOnline = true;
}
}
@Override
public void PlayerGoOffline() {
public void setPlayerOffline() {
playerOnline = false;
this.InventoryRemovalCheck();
}
@Override
public void onClose(CraftHumanEntity who) {
super.onClose(who);
this.InventoryRemovalCheck();
public boolean isInUse() {
return !this.getViewers().isEmpty();
}
@Override
public ItemStack[] getContents() {
ItemStack[] C = new ItemStack[getSize()];
System.arraycopy(items, 0, C, 0, items.length);
System.arraycopy(items, 0, C, items.length, armor.length);
return C;
ItemStack[] contents = new ItemStack[getSize()];
System.arraycopy(items, 0, contents, 0, items.length);
System.arraycopy(armor, 0, contents, items.length, armor.length);
return contents;
}
@Override
@@ -98,16 +82,14 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
if (i >= is.length) {
i -= is.length;
is = this.armor;
}
else {
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
}
else if (is == this.armor) {
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
@@ -121,16 +103,14 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
if (i >= is.length) {
i -= is.length;
is = this.armor;
}
else {
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
}
else if (is == this.armor) {
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
@@ -141,9 +121,8 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
itemstack = is[i];
is[i] = null;
return itemstack;
}
else {
itemstack = is[i].a(j);
} else {
itemstack = is[i].cloneAndSubtract(j);
if (is[i].count == 0) {
is[i] = null;
}
@@ -151,9 +130,8 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
return itemstack;
}
}
else {
return null;
}
return null;
}
@Override
@@ -163,16 +141,14 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
if (i >= is.length) {
i -= is.length;
is = this.armor;
}
else {
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
}
else if (is == this.armor) {
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
@@ -182,9 +158,8 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
is[i] = null;
return itemstack;
}
else {
return null;
}
return null;
}
@Override
@@ -194,48 +169,49 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
if (i >= is.length) {
i -= is.length;
is = this.armor;
}
else {
} else {
i = getReversedItemSlotNum(i);
}
if (i >= is.length) {
i -= is.length;
is = this.extra;
}
else if (is == this.armor) {
} else if (is == this.armor) {
i = getReversedArmorSlotNum(i);
}
// Effects
if (is == this.extra) {
owner.getHandle().drop(itemstack, true);
player.drop(itemstack, true);
itemstack = null;
}
is[i] = itemstack;
owner.getHandle().defaultContainer.b();
player.defaultContainer.b();
}
private int getReversedItemSlotNum(int i) {
if (i >= 27)
if (i >= 27) {
return i - 27;
else
return i + 9;
}
return i + 9;
}
private int getReversedArmorSlotNum(int i) {
if (i == 0)
if (i == 0) {
return 3;
if (i == 1)
}
if (i == 1) {
return 2;
if (i == 2)
}
if (i == 2) {
return 1;
if (i == 3)
}
if (i == 3) {
return 0;
else
return i;
}
return i;
}
@Override
@@ -247,7 +223,8 @@ public class SpecialPlayerInventory extends PlayerInventory implements ISpecialP
}
@Override
public boolean a(EntityHuman entityhuman) {
public boolean hasCustomName() {
return true;
}
}
}

80
plugin/pom.xml Normal file
View File

@@ -0,0 +1,80 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<version>4.1.2</version>
</parent>
<artifactId>openinvplugincore</artifactId>
<name>OpenInvPlugin</name>
<dependencies>
<dependency>
<groupId>com.lishid</groupId>
<artifactId>openinvapi</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.15.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,564 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.lishid.openinv.commands.ContainerSettingCommand;
import com.lishid.openinv.commands.OpenInvCommand;
import com.lishid.openinv.commands.SearchContainerCommand;
import com.lishid.openinv.commands.SearchEnchantCommand;
import com.lishid.openinv.commands.SearchInvCommand;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.listeners.InventoryClickListener;
import com.lishid.openinv.listeners.InventoryCloseListener;
import com.lishid.openinv.listeners.InventoryDragListener;
import com.lishid.openinv.listeners.PlayerListener;
import com.lishid.openinv.listeners.PluginListener;
import com.lishid.openinv.util.Cache;
import com.lishid.openinv.util.ConfigUpdater;
import com.lishid.openinv.util.InternalAccessor;
import com.lishid.openinv.util.LanguageManager;
import com.lishid.openinv.util.Permissions;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Open other player's inventory
*
* @author lishid
*/
public class OpenInv extends JavaPlugin implements IOpenInv {
private final Map<String, ISpecialPlayerInventory> inventories = new HashMap<>();
private final Map<String, ISpecialEnderChest> enderChests = new HashMap<>();
private final Multimap<String, Class<? extends Plugin>> pluginUsage = HashMultimap.create();
private final Cache<String, Player> playerCache = new Cache<>(300000L,
value -> {
String key = OpenInv.this.getPlayerID(value);
return OpenInv.this.inventories.containsKey(key)
&& OpenInv.this.inventories.get(key).isInUse()
|| OpenInv.this.enderChests.containsKey(key)
&& OpenInv.this.enderChests.get(key).isInUse()
|| OpenInv.this.pluginUsage.containsKey(key);
},
value -> {
String key = OpenInv.this.getPlayerID(value);
// Check if inventory is stored, and if it is, remove it and eject all viewers
if (OpenInv.this.inventories.containsKey(key)) {
Inventory inv = OpenInv.this.inventories.remove(key).getBukkitInventory();
List<HumanEntity> viewers = inv.getViewers();
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
entity.closeInventory();
}
}
// Check if ender chest is stored, and if it is, remove it and eject all viewers
if (OpenInv.this.enderChests.containsKey(key)) {
Inventory inv = OpenInv.this.enderChests.remove(key).getBukkitInventory();
List<HumanEntity> viewers = inv.getViewers();
for (HumanEntity entity : viewers.toArray(new HumanEntity[0])) {
entity.closeInventory();
}
}
if (!OpenInv.this.disableSaving() && !value.isOnline()) {
value.saveData();
}
return true;
});
private InternalAccessor accessor;
private LanguageManager languageManager;
/**
* Evicts all viewers lacking cross-world permissions from a Player's inventory.
*
* @param player the Player
*/
public void changeWorld(final Player player) {
String key = this.getPlayerID(player);
// Check if the player is cached. If not, neither of their inventories is open.
if (!this.playerCache.containsKey(key)) {
return;
}
if (this.inventories.containsKey(key)) {
Iterator<HumanEntity> iterator = this.inventories.get(key).getBukkitInventory().getViewers().iterator();
while (iterator.hasNext()) {
HumanEntity human = iterator.next();
// If player has permission or is in the same world, allow continued access
// Just in case, also allow null worlds.
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) {
continue;
}
human.closeInventory();
}
}
if (this.enderChests.containsKey(key)) {
Iterator<HumanEntity> iterator = this.enderChests.get(key).getBukkitInventory().getViewers().iterator();
while (iterator.hasNext()) {
HumanEntity human = iterator.next();
if (Permissions.CROSSWORLD.hasPermission(human) || human.getWorld().equals(player.getWorld())) {
continue;
}
human.closeInventory();
}
}
}
@Override
public boolean disableSaving() {
return this.getConfig().getBoolean("settings.disable-saving", false);
}
@NotNull
@Override
public IAnySilentContainer getAnySilentContainer() {
return this.accessor.getAnySilentContainer();
}
@Override
public boolean getPlayerAnyChestStatus(@NotNull final OfflinePlayer player) {
boolean defaultState = false;
if (player.isOnline()) {
Player onlinePlayer = player.getPlayer();
if (onlinePlayer != null) {
defaultState = Permissions.ANY_DEFAULT.hasPermission(onlinePlayer);
}
}
return this.getConfig().getBoolean("toggles.any-chest." + this.getPlayerID(player), defaultState);
}
@Override
public boolean getPlayerSilentChestStatus(@NotNull final OfflinePlayer offline) {
boolean defaultState = false;
if (offline.isOnline()) {
Player onlinePlayer = offline.getPlayer();
if (onlinePlayer != null) {
defaultState = Permissions.SILENT_DEFAULT.hasPermission(onlinePlayer);
}
}
return this.getConfig().getBoolean("toggles.silent-chest." + this.getPlayerID(offline), defaultState);
}
@NotNull
@Override
public ISpecialEnderChest getSpecialEnderChest(@NotNull final Player player, final boolean online)
throws InstantiationException {
String id = this.getPlayerID(player);
if (this.enderChests.containsKey(id)) {
return this.enderChests.get(id);
}
ISpecialEnderChest inv = this.accessor.newSpecialEnderChest(player, online);
this.enderChests.put(id, inv);
this.playerCache.put(id, player);
return inv;
}
@NotNull
@Override
public ISpecialPlayerInventory getSpecialInventory(@NotNull final Player player, final boolean online)
throws InstantiationException {
String id = this.getPlayerID(player);
if (this.inventories.containsKey(id)) {
return this.inventories.get(id);
}
ISpecialPlayerInventory inv = this.accessor.newSpecialPlayerInventory(player, online);
this.inventories.put(id, inv);
this.playerCache.put(id, player);
return inv;
}
@Override
public boolean isSupportedVersion() {
return this.accessor != null && this.accessor.isSupported();
}
@Nullable
@Override
public Player loadPlayer(@NotNull final OfflinePlayer offline) {
String key = this.getPlayerID(offline);
if (this.playerCache.containsKey(key)) {
return this.playerCache.get(key);
}
// TODO: wrap Player to ensure all methods can safely be called offline
Player loaded;
if (offline.isOnline()) {
loaded = offline.getPlayer();
this.playerCache.put(key, loaded);
return loaded;
}
if (!this.isSupportedVersion()) {
return null;
}
if (Bukkit.isPrimaryThread()) {
return this.accessor.getPlayerDataManager().loadPlayer(offline);
}
Future<Player> future = Bukkit.getScheduler().callSyncMethod(this,
() -> OpenInv.this.accessor.getPlayerDataManager().loadPlayer(offline));
int ticks = 0;
while (!future.isDone() && !future.isCancelled() && ticks < 10) {
++ticks;
try {
Thread.sleep(50L);
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
if (!future.isDone() || future.isCancelled()) {
return null;
}
try {
loaded = future.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return null;
}
if (loaded != null) {
this.playerCache.put(key, loaded);
}
return loaded;
}
@Override
public @Nullable InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory) {
return this.accessor.getPlayerDataManager().openInventory(player, inventory);
}
public void sendMessage(@NotNull CommandSender sender, @NotNull String key) {
String message = this.languageManager.getValue(key, getLocale(sender));
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
String message = this.languageManager.getValue(key, getLocale(sender), replacements);
if (message != null && !message.isEmpty()) {
sender.sendMessage(message);
}
}
public void sendSystemMessage(@NotNull Player player, @NotNull String key) {
String message = this.languageManager.getValue(key, getLocale(player));
if (message != null) {
this.accessor.getPlayerDataManager().sendSystemMessage(player, message);
}
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key) {
return this.languageManager.getValue(key, getLocale(sender));
}
public @Nullable String getLocalizedMessage(@NotNull CommandSender sender, @NotNull String key, String... replacements) {
return this.languageManager.getValue(key, getLocale(sender), replacements);
}
@Nullable
private String getLocale(@NotNull CommandSender sender) {
if (sender instanceof Player) {
return this.accessor.getPlayerDataManager().getLocale((Player) sender);
} else {
return this.getConfig().getString("settings.locale", "en_us");
}
}
@Override
public boolean notifyAnyChest() {
return this.getConfig().getBoolean("notify.any-chest", true);
}
@Override
public boolean notifySilentChest() {
return this.getConfig().getBoolean("notify.silent-chest", true);
}
@Override
public void onDisable() {
if (this.disableSaving()) {
return;
}
if (this.isSupportedVersion()) {
this.playerCache.invalidateAll();
}
}
@Override
public void onEnable() {
// Save default configuration if not present.
this.saveDefaultConfig();
// Get plugin manager
PluginManager pm = this.getServer().getPluginManager();
this.accessor = new InternalAccessor(this);
this.languageManager = new LanguageManager(this, "en_us");
// Version check
if (this.accessor.isSupported()) {
// Update existing configuration. May require internal access.
new ConfigUpdater(this).checkForUpdates();
// Register listeners
pm.registerEvents(new PlayerListener(this), this);
pm.registerEvents(new PluginListener(this), this);
pm.registerEvents(new InventoryClickListener(), this);
pm.registerEvents(new InventoryCloseListener(this), this);
// Bukkit will handle missing events for us, attempt to register InventoryDragEvent without a version check
pm.registerEvents(new InventoryDragListener(), this);
// Register commands to their executors
OpenInvCommand openInv = new OpenInvCommand(this);
this.setCommandExecutor("openinv", openInv);
this.setCommandExecutor("openender", openInv);
this.setCommandExecutor("searchcontainer", new SearchContainerCommand(this));
SearchInvCommand searchInv = new SearchInvCommand(this);
this.setCommandExecutor("searchinv", searchInv);
this.setCommandExecutor("searchender", searchInv);
this.setCommandExecutor("searchenchant", new SearchEnchantCommand(this));
ContainerSettingCommand settingCommand = new ContainerSettingCommand(this);
this.setCommandExecutor("silentcontainer", settingCommand);
this.setCommandExecutor("anycontainer", settingCommand);
} else {
this.getLogger().info("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported.");
this.getLogger().info("If this version is a recent release, check for an update.");
this.getLogger().info("If this is an older version, ensure that you've downloaded the legacy support version.");
}
}
private void setCommandExecutor(String commandName, CommandExecutor executor) {
PluginCommand command = this.getCommand(commandName);
if (command != null) {
command.setExecutor(executor);
}
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!this.accessor.isSupported()) {
sender.sendMessage("Your version of CraftBukkit (" + this.accessor.getVersion() + ") is not supported.");
sender.sendMessage("If this version is a recent release, check for an update.");
return true;
}
return false;
}
public void releaseAllPlayers(final Plugin plugin) {
Iterator<Map.Entry<String, Class<? extends Plugin>>> iterator = this.pluginUsage.entries().iterator();
if (!iterator.hasNext()) {
return;
}
for (Map.Entry<String, Class<? extends Plugin>> entry = iterator.next(); iterator.hasNext(); entry = iterator.next()) {
if (entry.getValue().equals(plugin.getClass())) {
iterator.remove();
}
}
}
@Override
public void releasePlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
String key = this.getPlayerID(player);
if (!this.pluginUsage.containsEntry(key, plugin.getClass())) {
return;
}
this.pluginUsage.remove(key, plugin.getClass());
}
@Override
public void retainPlayer(@NotNull final Player player, @NotNull final Plugin plugin) {
String key = this.getPlayerID(player);
if (this.pluginUsage.containsEntry(key, plugin.getClass())) {
return;
}
this.pluginUsage.put(key, plugin.getClass());
}
@Override
public void setPlayerAnyChestStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.any-chest." + this.getPlayerID(offline), status);
this.saveConfig();
}
/**
* Method for handling a Player going offline.
*
* @param player the Player
* @throws IllegalStateException if the server version is unsupported
*/
public void setPlayerOffline(final Player player) {
String key = this.getPlayerID(player);
// Check if the player is cached. If not, neither of their inventories is open.
if (!this.playerCache.containsKey(key)) {
return;
}
// Replace stored player with our own version
this.playerCache.put(key, this.accessor.getPlayerDataManager().inject(player));
if (this.inventories.containsKey(key)) {
this.inventories.get(key).setPlayerOffline();
}
if (this.enderChests.containsKey(key)) {
this.enderChests.get(key).setPlayerOffline();
}
}
/**
* Method for handling a Player coming online.
*
* @param player the Player
* @throws IllegalStateException if the server version is unsupported
*/
public void setPlayerOnline(final Player player) {
String key = this.getPlayerID(player);
// Check if the player is cached. If not, neither of their inventories is open.
if (!this.playerCache.containsKey(key)) {
return;
}
this.playerCache.put(key, player);
if (this.inventories.containsKey(key)) {
this.inventories.get(key).setPlayerOnline(player);
new BukkitRunnable() {
@Override
public void run() {
if (player.isOnline()) {
player.updateInventory();
}
}
}.runTask(this);
}
if (this.enderChests.containsKey(key)) {
this.enderChests.get(key).setPlayerOnline(player);
}
}
@Override
public void setPlayerSilentChestStatus(@NotNull final OfflinePlayer offline, final boolean status) {
this.getConfig().set("toggles.silent-chest." + this.getPlayerID(offline), status);
this.saveConfig();
}
/**
* Displays all applicable help for OpenInv commands.
*
* @param player the Player to help
*/
public void showHelp(final Player player) {
// Get registered commands
for (String commandName : this.getDescription().getCommands().keySet()) {
PluginCommand command = this.getCommand(commandName);
// Ensure command is successfully registered and player can use it
if (command == null || !command.testPermissionSilent(player)) {
continue;
}
// Send usage
player.sendMessage(command.getUsage().replace("<command>", commandName));
List<String> aliases = command.getAliases();
if (aliases.isEmpty()) {
continue;
}
// Assemble alias list
StringBuilder aliasBuilder = new StringBuilder(" (aliases: ");
for (String alias : aliases) {
aliasBuilder.append(alias).append(", ");
}
aliasBuilder.delete(aliasBuilder.length() - 2, aliasBuilder.length()).append(')');
// Send all aliases
player.sendMessage(aliasBuilder.toString());
}
}
@Override
public void unload(@NotNull final OfflinePlayer offline) {
this.playerCache.invalidate(this.getPlayerID(offline));
}
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public class ContainerSettingCommand implements TabExecutor {
private final OpenInv plugin;
public ContainerSettingCommand(final OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
Player player = (Player) sender;
boolean any = command.getName().startsWith("any");
Function<Player, Boolean> getSetting = any ? plugin::getPlayerAnyChestStatus : plugin::getPlayerSilentChestStatus;
BiConsumer<OfflinePlayer, Boolean> setSetting = any ? plugin::setPlayerAnyChestStatus : plugin::setPlayerSilentChestStatus;
if (args.length > 0) {
args[0] = args[0].toLowerCase();
if (args[0].equals("on")) {
setSetting.accept(player, true);
} else if (args[0].equals("off")) {
setSetting.accept(player, false);
} else if (!args[0].equals("check")) {
// Invalid argument, show usage.
return false;
}
} else {
setSetting.accept(player, !getSetting.apply(player));
}
String onOff = plugin.getLocalizedMessage(player, getSetting.apply(player) ? "messages.info.on" : "messages.info.off");
if (onOff == null) {
onOff = String.valueOf(getSetting.apply(player));
}
plugin.sendMessage(sender, "messages.info.settingState","%setting%", any ? "AnyContainer" : "SilentContainer", "%state%", onOff);
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!command.testPermissionSilent(sender) || args.length != 1) {
return Collections.emptyList();
}
return TabCompleter.completeString(args[0], new String[] {"check", "on", "off"});
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.ISpecialInventory;
import com.lishid.openinv.util.Permissions;
import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
public class OpenInvCommand implements TabExecutor {
private final OpenInv plugin;
private final HashMap<Player, String> openInvHistory = new HashMap<>();
private final HashMap<Player, String> openEnderHistory = new HashMap<>();
public OpenInvCommand(final OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull final CommandSender sender, @NotNull final Command command, @NotNull final String label, @NotNull final String[] args) {
if (!(sender instanceof Player)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
if (args.length > 0 && (args[0].equalsIgnoreCase("help") || args[0].equals("?"))) {
this.plugin.showHelp((Player) sender);
return true;
}
final Player player = (Player) sender;
final boolean openinv = command.getName().equals("openinv");
// History management
String history = (openinv ? this.openInvHistory : this.openEnderHistory).get(player);
if (history == null || history.isEmpty()) {
history = player.getName();
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, history);
}
final String name;
// Read from history if target is not named
if (args.length < 1) {
name = history;
} else {
name = args[0];
}
new BukkitRunnable() {
@Override
public void run() {
final OfflinePlayer offlinePlayer = OpenInvCommand.this.plugin.matchPlayer(name);
if (offlinePlayer == null || !offlinePlayer.hasPlayedBefore() && !offlinePlayer.isOnline()) {
plugin.sendMessage(player, "messages.error.invalidPlayer");
return;
}
new BukkitRunnable() {
@Override
public void run() {
if (!player.isOnline()) {
return;
}
OpenInvCommand.this.openInventory(player, offlinePlayer, openinv);
}
}.runTask(OpenInvCommand.this.plugin);
}
}.runTaskAsynchronously(this.plugin);
return true;
}
private void openInventory(final Player player, final OfflinePlayer target, boolean openinv) {
Player onlineTarget;
boolean online = target.isOnline();
if (!online) {
if (Permissions.OPENOFFLINE.hasPermission(player)) {
// Try loading the player's data
onlineTarget = this.plugin.loadPlayer(target);
} else {
plugin.sendMessage(player, "messages.error.permissionPlayerOffline");
return;
}
} else {
if (Permissions.OPENONLINE.hasPermission(player)) {
onlineTarget = target.getPlayer();
} else {
plugin.sendMessage(player, "messages.error.permissionPlayerOnline");
return;
}
}
if (onlineTarget == null) {
plugin.sendMessage(player, "messages.error.invalidPlayer");
return;
}
// Permissions checks
if (onlineTarget.equals(player)) {
// Inventory: Additional permission required to open own inventory
if (openinv && !Permissions.OPENSELF.hasPermission(player)) {
plugin.sendMessage(player, "messages.error.permissionOpenSelf");
return;
}
} else {
// Enderchest: Additional permission required to open others' ender chests
if (!openinv && !Permissions.ENDERCHEST_ALL.hasPermission(player)) {
plugin.sendMessage(player, "messages.error.permissionEnderAll");
return;
}
// Protected check
if (!Permissions.OVERRIDE.hasPermission(player)
&& Permissions.EXEMPT.hasPermission(onlineTarget)) {
plugin.sendMessage(player, "messages.error.permissionExempt",
"%target%", onlineTarget.getDisplayName());
return;
}
// Crossworld check
if (!Permissions.CROSSWORLD.hasPermission(player)
&& !onlineTarget.getWorld().equals(player.getWorld())) {
plugin.sendMessage(player, "messages.error.permissionCrossWorld",
"%target%", onlineTarget.getDisplayName());
return;
}
}
// Record the target
(openinv ? this.openInvHistory : this.openEnderHistory).put(player, this.plugin.getPlayerID(target));
// Create the inventory
final ISpecialInventory inv;
try {
inv = openinv ? this.plugin.getSpecialInventory(onlineTarget, online) : this.plugin.getSpecialEnderChest(onlineTarget, online);
} catch (Exception e) {
plugin.sendMessage(player, "messages.error.commandException");
e.printStackTrace();
return;
}
// Open the inventory
plugin.openInventory(player, inv);
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!command.testPermissionSilent(sender) || args.length != 1) {
return Collections.emptyList();
}
return TabCompleter.completeOnlinePlayer(sender, args[0]);
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.List;
import org.bukkit.Chunk;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryHolder;
import org.jetbrains.annotations.NotNull;
/**
* Command for searching containers in a radius of chunks.
*/
public class SearchContainerCommand implements TabExecutor {
private final OpenInv plugin;
public SearchContainerCommand(OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!(sender instanceof Player)) {
plugin.sendMessage(sender, "messages.error.consoleUnsupported");
return true;
}
if (args.length < 1) {
// Must supply material
return false;
}
Material material = Material.getMaterial(args[0].toUpperCase());
if (material == null) {
plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args[0]);
return false;
}
int radius = 5;
if (args.length > 1) {
try {
radius = Integer.parseInt(args[1]);
} catch (NumberFormatException e) {
// Invalid radius supplied
return false;
}
}
Player senderPlayer = (Player) sender;
World world = senderPlayer.getWorld();
Chunk centerChunk = senderPlayer.getLocation().getChunk();
StringBuilder locations = new StringBuilder();
for (int dX = -radius; dX <= radius; ++dX) {
for (int dZ = -radius; dZ <= radius; ++dZ) {
if (!world.loadChunk(centerChunk.getX() + dX, centerChunk.getZ() + dZ, false)) {
continue;
}
Chunk chunk = world.getChunkAt(centerChunk.getX() + dX, centerChunk.getZ() + dZ);
for (BlockState tileEntity : chunk.getTileEntities()) {
if (!(tileEntity instanceof InventoryHolder)) {
continue;
}
InventoryHolder holder = (InventoryHolder) tileEntity;
if (!holder.getInventory().contains(material)) {
continue;
}
locations.append(holder.getInventory().getType().name().toLowerCase()).append(" (")
.append(tileEntity.getX()).append(',').append(tileEntity.getY()).append(',')
.append(tileEntity.getZ()).append("), ");
}
}
}
// Matches found, delete trailing comma and space
if (locations.length() > 0) {
locations.delete(locations.length() - 2, locations.length());
} else {
plugin.sendMessage(sender, "messages.info.container.noMatches",
"%target%", material.name());
return true;
}
plugin.sendMessage(sender, "messages.info.container.matches",
"%target%", material.name(), "%detail%", locations.toString());
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) {
return Collections.emptyList();
}
String argument = args[args.length - 1];
if (args.length == 1) {
return TabCompleter.completeEnum(argument, Material.class);
} else {
return TabCompleter.completeInteger(argument);
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
/**
* Command adding the ability to search online players' inventories for enchantments of a specific
* type at or above the level specified.
*
* @author Jikoo
*/
public class SearchEnchantCommand implements TabExecutor {
private final OpenInv plugin;
public SearchEnchantCommand(OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length == 0) {
return false;
}
Enchantment enchant = null;
int level = 0;
for (String argument : args) {
try {
level = Integer.parseInt(argument);
continue;
} catch (NumberFormatException ignored) {}
argument = argument.toLowerCase();
int colon = argument.indexOf(':');
NamespacedKey key;
try {
if (colon > -1 && colon < argument.length() - 1) {
key = new NamespacedKey(argument.substring(0, colon), argument.substring(colon + 1));
} else {
key = NamespacedKey.minecraft(argument);
}
} catch (IllegalArgumentException ignored) {
continue;
}
Enchantment localEnchant = Enchantment.getByKey(key);
if (localEnchant != null) {
enchant = localEnchant;
}
}
// Arguments not set correctly
if (level == 0 && enchant == null) {
return false;
}
StringBuilder players = new StringBuilder();
for (Player player : plugin.getServer().getOnlinePlayers()) {
boolean flagInventory = containsEnchantment(player.getInventory(), enchant, level);
boolean flagEnder = containsEnchantment(player.getEnderChest(), enchant, level);
// No matches, continue
if (!flagInventory && !flagEnder) {
continue;
}
// Matches, append details
players.append(player.getName()).append(" (");
if (flagInventory) {
players.append("inv");
}
if (flagEnder) {
if (flagInventory) {
players.append(',');
}
players.append("ender");
}
players.append("), ");
}
if (players.length() > 0) {
// Matches found, delete trailing comma and space
players.delete(players.length() - 2, players.length());
} else {
plugin.sendMessage(sender, "messages.info.player.noMatches",
"%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level);
return true;
}
plugin.sendMessage(sender, "messages.info.player.matches",
"%target%", (enchant != null ? enchant.getKey().toString() : "") + " >= " + level,
"%detail%", players.toString());
return true;
}
private boolean containsEnchantment(Inventory inventory, Enchantment enchant, int minLevel) {
for (ItemStack item : inventory.getContents()) {
if (item == null || item.getType() == Material.AIR) {
continue;
}
if (enchant != null) {
if (item.containsEnchantment(enchant) && item.getEnchantmentLevel(enchant) >= minLevel) {
return true;
}
} else {
if (!item.hasItemMeta()) {
continue;
}
ItemMeta meta = item.getItemMeta();
if (meta == null || !meta.hasEnchants()) {
continue;
}
for (int enchLevel : meta.getEnchants().values()) {
if (enchLevel >= minLevel) {
return true;
}
}
}
}
return false;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (!command.testPermissionSilent(sender) || args.length < 1 || args.length > 2) {
return Collections.emptyList();
}
if (args.length == 1) {
return TabCompleter.completeObject(args[0], enchantment -> enchantment.getKey().toString(), Enchantment.values());
} else {
return TabCompleter.completeInteger(args[1]);
}
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.TabCompleter;
import java.util.Collections;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.jetbrains.annotations.NotNull;
public class SearchInvCommand implements TabExecutor {
private final OpenInv plugin;
public SearchInvCommand(OpenInv plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
Material material = null;
if (args.length >= 1) {
material = Material.getMaterial(args[0].toUpperCase());
}
if (material == null) {
plugin.sendMessage(sender, "messages.error.invalidMaterial", "%target%", args.length > 0 ? args[0] : "null");
return false;
}
int count = 1;
if (args.length >= 2) {
try {
count = Integer.parseInt(args[1]);
} catch (NumberFormatException ex) {
plugin.sendMessage(sender, "messages.error.invalidNumber", "%target%", args[1]);
return false;
}
}
StringBuilder players = new StringBuilder();
boolean searchInv = command.getName().equals("searchinv");
for (Player player : plugin.getServer().getOnlinePlayers()) {
Inventory inventory = searchInv ? player.getInventory() : player.getEnderChest();
if (inventory.contains(material, count)) {
players.append(player.getName()).append(", ");
}
}
// Matches found, delete trailing comma and space
if (players.length() > 0) {
players.delete(players.length() - 2, players.length());
} else {
plugin.sendMessage(sender, "messages.info.player.noMatches",
"%target%", material.name());
return true;
}
plugin.sendMessage(sender, "messages.info.player.matches",
"%target%", material.name(), "%detail%", players.toString());
return true;
}
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
if (args.length < 1 || args.length > 2 || !command.testPermissionSilent(sender)) {
return Collections.emptyList();
}
String argument = args[args.length - 1];
if (args.length == 1) {
return TabCompleter.completeEnum(argument, Material.class);
} else {
return TabCompleter.completeInteger(argument);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.InventoryView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface IPlayerDataManager {
/**
* Loads a Player for an OfflinePlayer.
* </p>
* This method is potentially blocking, and should not be called on the main thread.
*
* @param offline the OfflinePlayer
* @return the Player loaded
*/
@Nullable
Player loadPlayer(@NotNull OfflinePlayer offline);
/**
* Creates a new Player from an existing one that will function slightly better offline.
*
* @return the Player
*/
@NotNull
Player inject(@NotNull Player player);
/**
* Opens an ISpecialInventory for a Player.
*
* @param player the Player opening the ISpecialInventory
* @param inventory the Inventory
*`
* @return the InventoryView opened
*/
@Nullable
InventoryView openInventory(@NotNull Player player, @NotNull ISpecialInventory inventory);
void sendSystemMessage(@NotNull Player player, @NotNull String message);
@NotNull
default String getLocale(Player player) {
return player.getLocale();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.listeners;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
public class InventoryClickListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onInventoryClick(InventoryClickEvent event) {
HumanEntity entity = event.getWhoClicked();
Inventory inventory = event.getInventory();
if (InventoryAccess.isPlayerInventory(inventory) && !Permissions.EDITINV.hasPermission(entity)
|| InventoryAccess.isEnderChest(inventory) && !Permissions.EDITENDER.hasPermission(entity)) {
event.setCancelled(true);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.listeners;
import com.lishid.openinv.IOpenInv;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
public class InventoryCloseListener implements Listener {
private final IOpenInv plugin;
public InventoryCloseListener(final IOpenInv plugin) {
this.plugin = plugin;
}
@EventHandler
public void onInventoryClose(final InventoryCloseEvent event) {
if (!(event.getPlayer() instanceof Player)) {
return;
}
Player player = (Player) event.getPlayer();
if (this.plugin.getPlayerSilentChestStatus(player)) {
this.plugin.getAnySilentContainer().deactivateContainer(player);
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.listeners;
import com.lishid.openinv.util.InventoryAccess;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.HumanEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
/**
* Listener for InventoryDragEvents to prevent unpermitted modification of special inventories.
*
* @author Jikoo
*/
public class InventoryDragListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onInventoryDrag(InventoryDragEvent event) {
HumanEntity entity = event.getWhoClicked();
Inventory inventory = event.getInventory();
if (InventoryAccess.isPlayerInventory(inventory) && !Permissions.EDITINV.hasPermission(entity)
|| InventoryAccess.isEnderChest(inventory) && !Permissions.EDITENDER.hasPermission(entity)) {
event.setCancelled(true);
}
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.listeners;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.util.Permissions;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
public class PlayerListener implements Listener {
private final OpenInv plugin;
public PlayerListener(OpenInv plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(final PlayerJoinEvent event) {
plugin.setPlayerOnline(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
plugin.setPlayerOffline(event.getPlayer());
}
@EventHandler
public void onWorldChange(PlayerChangedWorldEvent event) {
plugin.changeWorld(event.getPlayer());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getPlayer().isSneaking()
|| event.useInteractedBlock() == Result.DENY || event.getClickedBlock() == null
|| !plugin.getAnySilentContainer().isAnySilentContainer(event.getClickedBlock())) {
return;
}
Player player = event.getPlayer();
boolean any = Permissions.ANYCHEST.hasPermission(player) && plugin.getPlayerAnyChestStatus(player);
boolean needsAny = plugin.getAnySilentContainer().isAnyContainerNeeded(player, event.getClickedBlock());
if (!any && needsAny) {
return;
}
boolean silent = Permissions.SILENT.hasPermission(player) && plugin.getPlayerSilentChestStatus(player);
// If anycontainer or silentcontainer is active
if (any || silent) {
if (plugin.getAnySilentContainer().activateContainer(player, silent, event.getClickedBlock())) {
if (silent && plugin.notifySilentChest() && needsAny && plugin.notifyAnyChest()) {
plugin.sendSystemMessage(player, "messages.info.containerBlockedSilent");
} else if (needsAny && plugin.notifyAnyChest()) {
plugin.sendSystemMessage(player, "messages.info.containerBlocked");
} else if (silent && plugin.notifySilentChest()) {
plugin.sendSystemMessage(player, "messages.info.containerSilent");
}
}
event.setCancelled(true);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.listeners;
import com.lishid.openinv.OpenInv;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginDisableEvent;
/**
* Listener for plugin-related events.
*
* @author Jikoo
*/
public class PluginListener implements Listener {
private final OpenInv plugin;
public PluginListener(OpenInv plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPluginDisable(PluginDisableEvent event) {
plugin.releaseAllPlayers(event.getPlugin());
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.google.common.collect.Multimap;
import com.google.common.collect.TreeMultimap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
/**
* A minimal thread-safe time-based cache implementation backed by a HashMap and TreeMultimap.
*
* @author Jikoo
*/
public class Cache<K, V> {
private final Map<K, V> internal;
private final Multimap<Long, K> expiry;
private final long retention;
private final Function<V, Boolean> inUseCheck, postRemoval;
/**
* Constructs a Cache with the specified retention duration, in use function, and post-removal function.
*
* @param retention duration after which keys are automatically invalidated if not in use
* @param inUseCheck Function used to check if a key is considered in use
* @param postRemoval Function used to perform any operations required when a key is invalidated
*/
public Cache(final long retention, final Function<V, Boolean> inUseCheck, final Function<V, Boolean> postRemoval) {
this.internal = new HashMap<>();
this.expiry = TreeMultimap.create(Long::compareTo, (k1, k2) -> Objects.equals(k1, k2) ? 0 : 1);
this.retention = retention;
this.inUseCheck = inUseCheck;
this.postRemoval = postRemoval;
}
/**
* Set a key and value pair. Keys are unique. Using an existing key will cause the old value to
* be overwritten and the expiration timer to be reset.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
*/
public void put(final K key, final V value) {
// Invalidate key - runs lazy check and ensures value won't be cleaned up early
this.invalidate(key);
synchronized (this.internal) {
this.internal.put(key, value);
this.expiry.put(System.currentTimeMillis() + this.retention, key);
}
}
/**
* Returns the value to which the specified key is mapped, or null if no value is mapped for the key.
*
* @param key the key whose associated value is to be returned
* @return the value to which the specified key is mapped, or null if no value is mapped for the key
*/
public V get(final K key) {
// Run lazy check to clean cache
this.lazyCheck();
synchronized (this.internal) {
return this.internal.get(key);
}
}
/**
* Returns true if the specified key is mapped to a value.
*
* @param key key to check if a mapping exists for
* @return true if a mapping exists for the specified key
*/
public boolean containsKey(final K key) {
// Run lazy check to clean cache
this.lazyCheck();
synchronized (this.internal) {
return this.internal.containsKey(key);
}
}
/**
* Forcibly invalidates a key, even if it is considered to be in use.
*
* @param key key to invalidate
*/
public void invalidate(final K key) {
// Run lazy check to clean cache
this.lazyCheck();
synchronized (this.internal) {
if (!this.internal.containsKey(key)) {
// Value either not present or cleaned by lazy check. Either way, we're good
return;
}
// Remove stored object
this.internal.remove(key);
// Remove expiration entry - prevents more work later, plus prevents issues with values invalidating early
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator.hasNext();) {
if (key.equals(iterator.next().getValue())) {
iterator.remove();
break;
}
}
}
}
/**
* Forcibly invalidates all keys, even if they are considered to be in use.
*/
public void invalidateAll() {
synchronized (this.internal) {
for (V value : this.internal.values()) {
this.postRemoval.apply(value);
}
this.expiry.clear();
this.internal.clear();
}
}
/**
* Invalidate all expired keys that are not considered in use. If a key is expired but is
* considered in use by the provided Function, its expiration time is reset.
*/
private void lazyCheck() {
long now = System.currentTimeMillis();
synchronized (this.internal) {
List<K> inUse = new ArrayList<>();
for (Iterator<Map.Entry<Long, K>> iterator = this.expiry.entries().iterator(); iterator
.hasNext();) {
Map.Entry<Long, K> entry = iterator.next();
if (entry.getKey() > now) {
break;
}
iterator.remove();
if (this.inUseCheck.apply(this.internal.get(entry.getValue()))) {
inUse.add(entry.getValue());
continue;
}
V value = this.internal.remove(entry.getValue());
if (value == null) {
continue;
}
this.postRemoval.apply(value);
}
long nextExpiry = now + this.retention;
for (K value : inUse) {
this.expiry.put(nextExpiry, value);
}
}
}
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.lishid.openinv.OpenInv;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.bukkit.OfflinePlayer;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.scheduler.BukkitRunnable;
public class ConfigUpdater {
private final OpenInv plugin;
public ConfigUpdater(OpenInv plugin) {
this.plugin = plugin;
}
public void checkForUpdates() {
final int version = plugin.getConfig().getInt("config-version", 1);
if (version >= plugin.getConfig().getDefaults().getInt("config-version")) {
return;
}
plugin.getLogger().info("Configuration update found! Performing update...");
// Backup the old config file
try {
plugin.getConfig().save(new File(plugin.getDataFolder(), "config_old.yml"));
plugin.getLogger().info("Backed up config.yml to config_old.yml before updating.");
} catch (IOException e) {
plugin.getLogger().warning("Could not back up config.yml before updating!");
}
new BukkitRunnable() {
@Override
public void run() {
if (version < 2) {
updateConfig1To2();
}
if (version < 3) {
updateConfig2To3();
}
if (version < 4) {
updateConfig3To4();
}
new BukkitRunnable() {
@Override
public void run() {
plugin.saveConfig();
plugin.getLogger().info("Configuration update complete!");
}
}.runTaskLater(plugin, 1L); // Run on 1 tick delay; on older versions Bukkit's scheduler is not guaranteed FIFO
}
}.runTaskAsynchronously(plugin);
}
private void updateConfig3To4() {
new BukkitRunnable() {
@Override
public void run() {
plugin.getConfig().set("notify", null);
plugin.getConfig().set("settings.locale", "en_US");
plugin.getConfig().set("config-version", 4);
}
}.runTask(plugin);
}
private void updateConfig2To3() {
new BukkitRunnable() {
@Override
public void run() {
plugin.getConfig().set("config-version", 3);
plugin.getConfig().set("items.open-inv", null);
plugin.getConfig().set("ItemOpenInv", null);
plugin.getConfig().set("toggles.items.open-inv", null);
plugin.getConfig().set("settings.disable-saving",
plugin.getConfig().getBoolean("DisableSaving", false));
plugin.getConfig().set("DisableSaving", null);
}
}.runTask(plugin);
}
private void updateConfig1To2() {
new BukkitRunnable() {
@Override
public void run() {
// Get the old config settings
boolean notifySilentChest = plugin.getConfig().getBoolean("NotifySilentChest", true);
boolean notifyAnyChest = plugin.getConfig().getBoolean("NotifyAnyChest", true);
plugin.getConfig().set("ItemOpenInvItemID", null);
plugin.getConfig().set("NotifySilentChest", null);
plugin.getConfig().set("NotifyAnyChest", null);
plugin.getConfig().set("config-version", 2);
plugin.getConfig().set("notify.any-chest", notifyAnyChest);
plugin.getConfig().set("notify.silent-chest", notifySilentChest);
}
}.runTask(plugin);
updateToggles("AnyChest", "toggles.any-chest");
updateToggles("SilentChest", "toggles.silent-chest");
}
private void updateToggles(final String sectionName, final String newSectionName) {
// Ensure section exists
if (!plugin.getConfig().isConfigurationSection(sectionName)) {
return;
}
ConfigurationSection section = plugin.getConfig().getConfigurationSection(sectionName);
Set<String> keys = section.getKeys(false);
// Ensure section has content
if (keys == null || keys.isEmpty()) {
return;
}
final Map<String, Boolean> toggles = new HashMap<>();
for (String playerName : keys) {
OfflinePlayer player = plugin.matchPlayer(playerName);
if (player != null) {
toggles.put(plugin.getPlayerID(player), section.getBoolean(playerName + ".toggle", false));
}
}
new BukkitRunnable() {
@Override
public void run() {
// Wipe old ConfigurationSection
plugin.getConfig().set(sectionName, null);
// Prepare new ConfigurationSection
ConfigurationSection newSection;
if (plugin.getConfig().isConfigurationSection(newSectionName)) {
newSection = plugin.getConfig().getConfigurationSection(newSectionName);
} else {
newSection = plugin.getConfig().createSection(newSectionName);
}
// Set new values
for (Map.Entry<String, Boolean> entry : toggles.entrySet()) {
newSection.set(entry.getKey(), entry.getValue());
}
}
}.runTask(plugin);
}
}

View File

@@ -0,0 +1,180 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.lishid.openinv.internal.IAnySilentContainer;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
public class InternalAccessor {
private final Plugin plugin;
private final String version;
private boolean supported = false;
private IPlayerDataManager playerDataManager;
private IAnySilentContainer anySilentContainer;
public InternalAccessor(final Plugin plugin) {
this.plugin = plugin;
String packageName = plugin.getServer().getClass().getPackage().getName();
this.version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
// TODO: implement support for CraftMagicNumbers#getMappingsVersion
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialPlayerInventory");
Class.forName("com.lishid.openinv.internal." + this.version + ".SpecialEnderChest");
this.playerDataManager = this.createObject(IPlayerDataManager.class, "PlayerDataManager");
this.anySilentContainer = this.createObject(IAnySilentContainer.class, "AnySilentContainer");
this.supported = InventoryAccess.isUseable();
} catch (Exception ignored) {}
}
private <T> T createObject(final Class<? extends T> assignableClass, final String className,
final Object... params) throws ClassCastException, ClassNotFoundException,
InstantiationException, IllegalAccessException, IllegalArgumentException,
InvocationTargetException, NoSuchMethodException, SecurityException {
// Fetch internal class if it exists.
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + this.version + "." + className);
if (!assignableClass.isAssignableFrom(internalClass)) {
String message = String.format("Found class %s but cannot cast to %s!", internalClass.getName(), assignableClass.getName());
this.plugin.getLogger().warning(message);
throw new IllegalStateException(message);
}
// Quick return: no parameters, no need to fiddle about finding the correct constructor.
if (params.length == 0) {
return assignableClass.cast(internalClass.getConstructor().newInstance());
}
// Search constructors for one matching the given parameters
nextConstructor: for (Constructor<?> constructor : internalClass.getConstructors()) {
Class<?>[] requiredClasses = constructor.getParameterTypes();
if (requiredClasses.length != params.length) {
continue;
}
for (int i = 0; i < params.length; ++i) {
if (!requiredClasses[i].isAssignableFrom(params[i].getClass())) {
continue nextConstructor;
}
}
return assignableClass.cast(constructor.newInstance(params));
}
StringBuilder builder = new StringBuilder("Found class ").append(internalClass.getName())
.append(" but cannot find any matching constructors for [");
for (Object object : params) {
builder.append(object.getClass().getName()).append(", ");
}
builder.delete(builder.length() - 2, builder.length());
String message = builder.append(']').toString();
this.plugin.getLogger().warning(message);
throw new IllegalArgumentException(message);
}
/**
* Creates an instance of the IAnySilentContainer implementation for the current server version.
*
* @return the IAnySilentContainer
* @throws IllegalStateException if server version is unsupported
*/
public IAnySilentContainer getAnySilentContainer() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
return this.anySilentContainer;
}
/**
* Creates an instance of the IPlayerDataManager implementation for the current server version.
*
* @return the IPlayerDataManager
* @throws IllegalStateException if server version is unsupported
*/
public IPlayerDataManager getPlayerDataManager() {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
return this.playerDataManager;
}
/**
* Gets the server implementation version. If not initialized, returns the string "null"
* instead.
*
* @return the version, or "null"
*/
public String getVersion() {
return this.version != null ? this.version : "null";
}
/**
* Checks if the server implementation is supported.
*
* @return true if initialized for a supported server version
*/
public boolean isSupported() {
return this.supported;
}
/**
* Creates an instance of the ISpecialEnderChest implementation for the given Player, or
* null if the current version is unsupported.
*
* @param player the Player
* @param online true if the Player is online
* @return the ISpecialEnderChest created
* @throws InstantiationException if the ISpecialEnderChest could not be instantiated
*/
public ISpecialEnderChest newSpecialEnderChest(final Player player, final boolean online) throws InstantiationException {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
try {
return this.createObject(ISpecialEnderChest.class, "SpecialEnderChest", player, online);
} catch (Exception e) {
throw new InstantiationException(String.format("Unable to create a new ISpecialEnderChest: %s", e.getMessage()));
}
}
/**
* Creates an instance of the ISpecialPlayerInventory implementation for the given Player..
*
* @param player the Player
* @param online true if the Player is online
* @return the ISpecialPlayerInventory created
* @throws InstantiationException if the ISpecialPlayerInventory could not be instantiated
*/
public ISpecialPlayerInventory newSpecialPlayerInventory(final Player player, final boolean online) throws InstantiationException {
if (!this.supported) {
throw new IllegalStateException(String.format("Unsupported server version %s!", this.version));
}
try {
return this.createObject(ISpecialPlayerInventory.class, "SpecialPlayerInventory", player, online);
} catch (Exception e) {
throw new InstantiationException(String.format("Unable to create a new ISpecialPlayerInventory: %s", e.getMessage()));
}
}
}

View File

@@ -0,0 +1,170 @@
/*
* Copyright (C) 2011-2020 Jikoo. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import com.lishid.openinv.OpenInv;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.YamlConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* A simple language manager supporting both custom and bundled languages.
*
* @author Jikoo
*/
public class LanguageManager {
private final OpenInv plugin;
private final String defaultLocale;
private Map<String, YamlConfiguration> locales;
public LanguageManager(@NotNull OpenInv plugin, @NotNull String defaultLocale) {
this.plugin = plugin;
this.defaultLocale = defaultLocale;
this.locales = new HashMap<>();
getOrLoadLocale(defaultLocale);
}
private YamlConfiguration getOrLoadLocale(@NotNull String locale) {
YamlConfiguration loaded = locales.get(locale);
if (loaded != null) {
return loaded;
}
InputStream resourceStream = plugin.getResource(locale + ".yml");
YamlConfiguration localeConfigDefaults;
if (resourceStream == null) {
localeConfigDefaults = new YamlConfiguration();
} else {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceStream))) {
localeConfigDefaults = YamlConfiguration.loadConfiguration(reader);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to load resource " + locale + ".yml", e);
localeConfigDefaults = new YamlConfiguration();
}
}
File file = new File(plugin.getDataFolder(), locale + ".yml");
YamlConfiguration localeConfig;
if (!file.exists()) {
localeConfig = localeConfigDefaults;
try {
localeConfigDefaults.save(file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
}
} else {
localeConfig = YamlConfiguration.loadConfiguration(file);
// Add new language keys
List<String> newKeys = new ArrayList<>();
for (String key : localeConfigDefaults.getKeys(true)) {
if (localeConfigDefaults.isConfigurationSection(key)) {
continue;
}
if (localeConfig.isSet(key)) {
continue;
}
localeConfig.set(key, localeConfigDefaults.get(key));
newKeys.add(key);
}
if (!newKeys.isEmpty()) {
plugin.getLogger().info("[LanguageManager] Added new language keys: " + String.join(", ", newKeys));
try {
localeConfig.save(file);
} catch (IOException e) {
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Unable to save resource " + locale + ".yml", e);
}
}
}
if (!locale.equals(defaultLocale)) {
localeConfigDefaults = locales.get(defaultLocale);
// Check for missing keys
List<String> newKeys = new ArrayList<>();
for (String key : localeConfigDefaults.getKeys(true)) {
if (localeConfigDefaults.isConfigurationSection(key)) {
continue;
}
if (localeConfig.isSet(key)) {
continue;
}
newKeys.add(key);
}
if (!newKeys.isEmpty()) {
plugin.getLogger().info("[LanguageManager] Missing translations from " + locale + ".yml: " + String.join(", ", newKeys));
}
// Fall through to default locale
localeConfig.setDefaults(localeConfigDefaults);
}
locales.put(locale, localeConfig);
return localeConfig;
}
@Nullable
public String getValue(@NotNull String key, @Nullable String locale) {
String value = getOrLoadLocale(locale == null ? defaultLocale : locale.toLowerCase()).getString(key);
if (value == null || value.isEmpty()) {
return null;
}
value = ChatColor.translateAlternateColorCodes('&', value);
return value;
}
@Nullable
public String getValue(@NotNull String key, @Nullable String locale, @NotNull String... replacements) {
if (replacements.length % 2 != 0) {
plugin.getLogger().log(Level.WARNING, "[LanguageManager] Replacement data is uneven", new Exception());
}
String value = getValue(key, locale);
if (value == null) {
return null;
}
for (int i = 0; i < replacements.length; i += 2) {
value = value.replace(replacements[i], replacements[i + 1]);
}
return value;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import org.bukkit.permissions.Permissible;
public enum Permissions {
OPENINV("openinv"),
OVERRIDE("override"),
EXEMPT("exempt"),
CROSSWORLD("crossworld"),
SILENT("silent"),
SILENT_DEFAULT("silent.default"),
ANYCHEST("anychest"),
ANY_DEFAULT("any.default"),
ENDERCHEST("openender"),
ENDERCHEST_ALL("openenderall"),
SEARCH("search"),
EDITINV("editinv"),
EDITENDER("editender"),
OPENSELF("openself"),
OPENONLINE("openonline"),
OPENOFFLINE("openoffline");
private final String permission;
Permissions(String permission) {
this.permission = "OpenInv." + permission;
}
public boolean hasPermission(Permissible permissible) {
boolean hasPermission = permissible.hasPermission(permission);
if (hasPermission || permissible.isPermissionSet(permission)) {
return hasPermission;
}
StringBuilder permissionDestroyer = new StringBuilder(permission);
for (int lastPeriod = permissionDestroyer.lastIndexOf("."); lastPeriod > 0;
lastPeriod = permissionDestroyer.lastIndexOf(".")) {
permissionDestroyer.delete(lastPeriod + 1, permissionDestroyer.length()).append('*');
hasPermission = permissible.hasPermission(permissionDestroyer.toString());
if (hasPermission || permissible.isPermissionSet(permissionDestroyer.toString())) {
return hasPermission;
}
permissionDestroyer.delete(lastPeriod, permissionDestroyer.length());
}
return permissible.hasPermission("*");
}
}

View File

@@ -0,0 +1,147 @@
/*
* Copyright (C) 2011-2020 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.Function;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.util.StringUtil;
/**
* Utility class for common tab completions.
*/
public class TabCompleter {
/**
* Offer tab completions for whole numbers.
*
* @param argument the argument to complete
* @return integer options
*/
public static List<String> completeInteger(String argument) {
// Ensure existing argument is actually a number
if (!argument.isEmpty()) {
try {
Integer.parseInt(argument);
} catch (NumberFormatException e) {
return Collections.emptyList();
}
}
List<String> completions = new ArrayList<>(10);
for (int i = 0; i < 10; ++i) {
completions.add(argument + i);
}
return completions;
}
/**
* Offer tab completions for a given Enum.
*
* @param argument the argument to complete
* @param enumClazz the Enum to complete for
* @return the matching Enum values
*/
public static List<String> completeEnum(String argument, Class<? extends Enum<?>> enumClazz) {
argument = argument.toLowerCase(Locale.ENGLISH);
List<String> completions = new ArrayList<>();
for (Enum<?> enumConstant : enumClazz.getEnumConstants()) {
String name = enumConstant.name().toLowerCase();
if (name.startsWith(argument)) {
completions.add(name);
}
}
return completions;
}
/**
* Offer tab completions for a given array of Strings.
*
* @param argument the argument to complete
* @param options the Strings which may be completed
* @return the matching Strings
*/
public static List<String> completeString(String argument, String[] options) {
argument = argument.toLowerCase(Locale.ENGLISH);
List<String> completions = new ArrayList<>();
for (String option : options) {
if (option.startsWith(argument)) {
completions.add(option);
}
}
return completions;
}
/**
* Offer tab completions for visible online Players' names.
*
* @param sender the command's sender
* @param argument the argument to complete
* @return the matching Players' names
*/
public static List<String> completeOnlinePlayer(CommandSender sender, String argument) {
List<String> completions = new ArrayList<>();
Player senderPlayer = sender instanceof Player ? (Player) sender : null;
for (Player player : Bukkit.getOnlinePlayers()) {
if (senderPlayer != null && !senderPlayer.canSee(player)) {
continue;
}
if (StringUtil.startsWithIgnoreCase(player.getName(), argument)) {
completions.add(player.getName());
}
}
return completions;
}
/**
* Offer tab completions for a given array of Objects.
*
* @param argument the argument to complete
* @param converter the Function for converting the Object into a comparable String
* @param options the Objects which may be completed
* @return the matching Strings
*/
public static <T> List<String> completeObject(String argument, Function<T, String> converter, T[] options) {
argument = argument.toLowerCase(Locale.ENGLISH);
List<String> completions = new ArrayList<>();
for (T option : options) {
String optionString = converter.apply(option).toLowerCase();
if (optionString.startsWith(argument)) {
completions.add(optionString);
}
}
return completions;
}
private TabCompleter() {}
}

View File

@@ -0,0 +1,4 @@
config-version: 4
settings:
disable-saving: false
locale: 'en_us'

View File

@@ -0,0 +1,30 @@
messages:
error:
consoleUnsupported: 'Du kannst diesen Befehl nicht von der Konsole ausführen.'
lootNotGenerated: '&cBeute noch nicht erstellt! Bitte schalte &b/silentcontainer&c aus.'
invalidMaterial: '&cUngültiges Material: "%target%"'
invalidNumber: '&cUngültige Nummer: "%target%"'
invalidPlayer: '&cSpieler nicht gefunden.'
permissionOpenSelf: '&cDu darfst dein eigenes Inventar nicht öffnen.'
permissionEnderAll: '&cDu darfst die Endertruhen von anderen Spielern nicht öffnen.'
permissionExempt: '&c%target%''s Inventar ist geschützt.'
permissionCrossWorld: '&c%target% ist nicht in der selben Welt wie du.'
permissionPlayerOnline: '&cDu darfst keine Inventare von Spielern öffnen, die online sind.'
permissionPlayerOffline: '&cDu darfst keine Inventare von Spielern öffnen, die offline sind.'
commandException: '&cAn error occurred. Please check console for details.'
info:
containerBlocked: 'Du öffnest einen gesperrten Container.'
containerBlockedSilent: 'Du öffnest einen gesperrten Container lautlos.'
containerSilent: 'Du öffnest einen Container lautlos.'
settingState: '%setting%: %state%'
player:
noMatches: 'Keine Spieler mit %target% gefunden.'
matches: 'Spieler hält %target%: %detail%'
container:
noMatches: 'Keine Container mit %target% gefunden.'
matches: 'Container hat %target%: %detail%'
on: 'an'
off: 'aus'
container:
player: '%player%''s Inventar'
enderchest: '%player%''s Endertruhe'

View File

@@ -0,0 +1,30 @@
messages:
error:
consoleUnsupported: 'You cannot use this command from console.'
lootNotGenerated: '&cLoot not generated! Please disable &b/silentcontainer&c.'
invalidMaterial: '&cInvalid material: "%target%"'
invalidNumber: '&cInvalid number: "%target%"'
invalidPlayer: '&cPlayer not found!'
permissionOpenSelf: '&cYou''re not allowed to open your own inventory.'
permissionEnderAll: '&cYou''re not allowed to access other players'' ender chests.'
permissionExempt: '&c%target%''s inventory is protected.'
permissionCrossWorld: '&c%target% is not in your world.'
permissionPlayerOnline: '&cYou''re not allowed to open the inventory of online players.'
permissionPlayerOffline: '&cYou''re not allowed to open the inventory of offline players.'
commandException: '&cAn error occurred. Please check console for details.'
info:
containerBlocked: 'You are opening a blocked container.'
containerBlockedSilent: 'You are opening a blocked container silently.'
containerSilent: 'You are opening a container silently.'
settingState: '%setting%: %state%'
player:
noMatches: 'No players found with %target%.'
matches: 'Players holding %target%: %detail%'
container:
noMatches: 'No containers found with %target%.'
matches: 'Containers holding %target%: %detail%'
on: 'on'
off: 'off'
container:
player: '%player%''s Inventory'
enderchest: '%player%''s Ender Chest'

View File

@@ -0,0 +1,31 @@
messages:
error:
consoleUnsupported: 'No puedes usar este comando desde la consola.'
lootNotGenerated: '&c¡Botín no generado! Desactiva &b/silentcontainer&c.'
invalidMaterial: '&cMaterial no válido: "%target%"'
invalidNumber: '&cNúmero no válido: "%target%"'
invalidPlayer: '&cJugador no encontrado'
permissionOpenSelf: '&cNo tienes permisos para abrir tu inventario de esa forma.'
permissionEnderAll: '&cNo tienes permiso para abrir cofres de ender de otros jugadores.'
permissionExempt: '&c El inventario de %target% está protegido.'
permissionCrossWorld: '&c%target% no está en tu dimensión.'
permissionPlayerOnline: '&cNo estás autorizado para abrir inventarios de jugadores conectados.'
permissionPlayerOffline: '&cNo estás autorizado para abrir inventarios de jugadores desconectados.'
commandException: '&cSe ha producido un error, lee la consola para mas información.'
info:
containerBlocked: 'Estás abriendo un contenedor protegido.'
containerBlockedSilent: 'Estás abriendo sigilosamente un contenedor protegido.'
containerSilent: 'Estás abriendo un contenedor sigilosamente.'
settingState: '%setting%: %state%'
player:
noMatches: 'No se encontraron jugadores con %target%.'
matches: 'Jugadores con %target%: %detail%'
container:
noMatches: 'No se encontraron contenedores con %target%.'
matches: 'Contenedores con %target%: %detail%'
on: 'activado'
off: 'desactivado'
container:
player: 'Inventario de %player%'
enderchest: 'Cofre de Ender de %player%'

View File

@@ -0,0 +1,88 @@
name: OpenInv
main: com.lishid.openinv.OpenInv
version: ${project.version}
author: lishid
authors: [Jikoo, ShadowRanger]
description: >
This plugin allows you to open a player's inventory as a chest and interact with it in real time.
api-version: "1.14"
permissions:
OpenInv.any.default:
description: Permission for AnyContainer to default on prior to toggling.
default: false
OpenInv.silent.default:
description: Permission for SilentContainer to default on prior to toggling.
default: false
OpenInv.*:
description: Permission for all OpenInv features.
default: op
children:
OpenInv.openinv: true
OpenInv.openender: true
OpenInv.search: true
OpenInv.silent: true
OpenInv.anychest: true
OpenInv.searchenchant: true
OpenInv.searchcontainer: true
OpenInv.openonline: true
OpenInv.openoffline: true
OpenInv.openinv:
default: op
children:
OpenInv.openonline: true
OpenInv.openoffline: true
OpenInv.openender:
default: op
children:
OpenInv.openonline: true
OpenInv.openoffline: true
commands:
openinv:
aliases: [oi, inv, open]
description: Open a player's inventory
permission: OpenInv.openinv
usage: |-
/<command> [Player] - Open a player's inventory
openender:
aliases: [oe]
description: Opens the enderchest of a player
permission: OpenInv.openender
usage: |-
/<command> [Player] - Open a player's enderchest
searchinv:
aliases: [si]
description: Search and list players having a specific item
permission: OpenInv.search
usage: |-
/<command> <Material> [MinAmount] - MinAmount is optional, the minimum amount required
searchender:
aliases: [se]
permission: OpenInv.search
description: Searches and lists players having a specific item in their ender chest
usage: |-
/<command> <Material> [MinAmount] - MinAmount is optional, the minimum amount required
silentcontainer:
aliases: [sc, silent, silentchest]
description: SilentContainer stops sounds and animations when using containers.
permission: OpenInv.silent
usage: |-
/<command> [check|on|off] - Check, toggle, or set SilentContainer
anycontainer:
aliases: [ac, anychest]
description: AnyContainer allows using blocked containers.
permission: OpenInv.anychest
usage: |-
/<command> [check|on|off] - Check, toggle, or set AnyContainer
searchenchant:
aliases: [searchenchants]
description: Search and list players with a specific enchantment.
permission: OpenInv.searchenchant
usage: |-
/<command> <[Enchantment] [MinLevel]> - Enchantment is the enchantment type, MinLevel is the minimum level. One is optional
searchcontainer:
aliases: [searchchest]
description: Search and list containers with a specific material.
permission: OpenInv.searchcontainer
usage: /<command> <Material> [ChunkRadius] - ChunkRadius is optional, the length that will be searched for matching items. Default 5

View File

@@ -0,0 +1,30 @@
messages:
error:
consoleUnsupported: 'Voce nao consegue usar esse comando pelo console.'
lootNotGenerated: '&cPilhagem nao gerada! Por favor desabilite &b/silentcontainer&c.'
invalidMaterial: '&cMaterial invalido: "%target%"'
invalidNumber: '&cNumero invalido: "%target%"'
invalidPlayer: '&cJogador nao encontrado!'
permissionOpenSelf: '&cVoce nao pode abrir seu proprio inventario.'
permissionEnderAll: '&cVoce nao tem permissao para abrir baus de ender de outros jogadores.'
permissionExempt: '&cO inventario de %target% e protegido.'
permissionCrossWorld: '&c%target% nao esta no seu mundo.'
permissionPlayerOnline: '&cVoce nao tem permissao para abrir o inventario de jogadores online.'
permissionPlayerOffline: '&cVoce nao tem permissao para abrir o inventario de jogadores offline.'
commandException: '&cUm erro ocorreu. Por favor cheque o console para detalhes.'
info:
containerBlocked: 'Voce esta abrindo um recipiente bloqueado.'
containerBlockedSilent: 'Voce esta abrindo um recipiente bloqueado silenciosamente.'
containerSilent: 'Voce esta abrindo um recipiente silenciosamente.'
settingState: '%setting%: %state%'
player:
noMatches: 'Nenhum jogador encontrado com %target%.'
matches: 'Jogadores segurando %target%: %detail%'
container:
noMatches: 'Nenhum recipiente encontrado com %target%.'
matches: 'Recipientes contendo %target%: %detail%'
on: 'ligado'
off: 'desligado'
container:
player: 'Inventario de %player%'
enderchest: 'Bau de Ender de %player%'

124
pom.xml Normal file
View File

@@ -0,0 +1,124 @@
<!--
~ Copyright (C) 2011-2020 lishid. All rights reserved.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, version 3.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lishid</groupId>
<artifactId>openinvparent</artifactId>
<name>OpenInvParent</name>
<url>http://dev.bukkit.org/bukkit-plugins/openinv/</url>
<version>4.1.2</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>api</module>
<module>plugin</module>
<module>internal</module>
<module>assembly</module>
</modules>
<profiles>
<!--
~ N.B.: All version-specific code is handled in submodules of the internal module.
~
~ Internal submodules built by each profile are handled in internal/pom.xml
-->
<profile>
<id>all</id>
<activation>
<property>
<name>all</name>
<value>true</value>
</property>
</activation>
</profile>
<profile>
<id>latest</id>
<activation>
<property>
<name>latest</name>
<value>true</value>
</property>
</activation>
</profile>
<profile>
<id>recent</id>
<activation>
<property>
<name>recent</name>
<value>true</value>
</property>
</activation>
</profile>
</profiles>
<repositories>
<repository>
<id>spigot-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public/</url>
</repository>
</repositories>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<!-- Keep the file clean, don't include every single pom from all modules -->
<excludes>
<exclude>META-INF/maven/**</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -1,206 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import java.util.HashMap;
import java.util.logging.Logger;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.permissions.Permissible;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import com.lishid.openinv.commands.*;
import com.lishid.openinv.internal.IAnySilentChest;
import com.lishid.openinv.internal.IInventoryAccess;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalAccessor;
import com.lishid.openinv.utils.UpdateManager;
/**
* Open other player's inventory
*
* @author lishid
*/
public class OpenInv extends JavaPlugin {
public static final Logger logger = Logger.getLogger("Minecraft.OpenInv");
public static HashMap<String, ISpecialPlayerInventory> inventories = new HashMap<String, ISpecialPlayerInventory>();
public static HashMap<String, ISpecialEnderChest> enderChests = new HashMap<String, ISpecialEnderChest>();
private UpdateManager updater = new UpdateManager();
public static OpenInv mainPlugin;
public static IPlayerDataManager playerLoader;
public static IInventoryAccess inventoryAccess;
public static IAnySilentChest anySilentChest;
public void onEnable() {
// Get plugin manager
PluginManager pm = getServer().getPluginManager();
// Version check
boolean success = InternalAccessor.Initialize(this.getServer());
if (!success) {
OpenInv.log("Your version of CraftBukkit is not supported.");
OpenInv.log("Please look for an updated version of OpenInv.");
pm.disablePlugin(this);
return;
}
playerLoader = InternalAccessor.Instance.newPlayerDataManager();
inventoryAccess = InternalAccessor.Instance.newInventoryAccess();
anySilentChest = InternalAccessor.Instance.newAnySilentChest();
mainPlugin = this;
FileConfiguration config = getConfig();
config.set("CheckForUpdates", config.getBoolean("CheckForUpdates", true));
config.set("NotifySilentChest", config.getBoolean("NotifySilentChest", true));
config.set("NotifyAnyChest", config.getBoolean("NotifyAnyChest", true));
config.set("ItemOpenInvItemID", config.getInt("ItemOpenInvItemID", 280));
config.addDefault("ItemOpenInvItemID", 280);
config.addDefault("CheckForUpdates", true);
config.addDefault("NotifySilentChest", true);
config.addDefault("NotifyAnyChest", true);
config.options().copyDefaults(true);
saveConfig();
pm.registerEvents(new OpenInvPlayerListener(), this);
pm.registerEvents(new OpenInvEntityListener(), this);
pm.registerEvents(new OpenInvInventoryListener(), this);
getCommand("openinv").setExecutor(new OpenInvPluginCommand(this));
getCommand("searchinv").setExecutor(new SearchInvPluginCommand());
getCommand("toggleopeninv").setExecutor(new ToggleOpenInvPluginCommand());
getCommand("silentchest").setExecutor(new SilentChestPluginCommand(this));
getCommand("anychest").setExecutor(new AnyChestPluginCommand(this));
getCommand("openender").setExecutor(new OpenEnderPluginCommand(this));
updater.Initialize(this, getFile());
}
public static boolean NotifySilentChest() {
return mainPlugin.getConfig().getBoolean("NotifySilentChest", true);
}
public static boolean NotifyAnyChest() {
return mainPlugin.getConfig().getBoolean("NotifyAnyChest", true);
}
public static boolean GetCheckForUpdates() {
return mainPlugin.getConfig().getBoolean("CheckForUpdates", true);
}
public static boolean GetPlayerItemOpenInvStatus(String name) {
return mainPlugin.getConfig().getBoolean("ItemOpenInv." + name.toLowerCase() + ".toggle", false);
}
public static void SetPlayerItemOpenInvStatus(String name, boolean status) {
mainPlugin.getConfig().set("ItemOpenInv." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static boolean GetPlayerSilentChestStatus(String name) {
return mainPlugin.getConfig().getBoolean("SilentChest." + name.toLowerCase() + ".toggle", false);
}
public static void SetPlayerSilentChestStatus(String name, boolean status) {
mainPlugin.getConfig().set("SilentChest." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static boolean GetPlayerAnyChestStatus(String name) {
return mainPlugin.getConfig().getBoolean("AnyChest." + name.toLowerCase() + ".toggle", true);
}
public static void SetPlayerAnyChestStatus(String name, boolean status) {
mainPlugin.getConfig().set("AnyChest." + name.toLowerCase() + ".toggle", status);
mainPlugin.saveConfig();
}
public static int GetItemOpenInvItem() {
if (mainPlugin.getConfig().get("ItemOpenInvItemID") == null) {
SaveToConfig("ItemOpenInvItemID", 280);
}
return mainPlugin.getConfig().getInt("ItemOpenInvItemID", 280);
}
public static Object GetFromConfig(String data, Object defaultValue) {
Object val = mainPlugin.getConfig().get(data);
if (val == null) {
mainPlugin.getConfig().set(data, defaultValue);
return defaultValue;
}
else {
return val;
}
}
public static void SaveToConfig(String data, Object value) {
mainPlugin.getConfig().set(data, value);
mainPlugin.saveConfig();
}
/**
* Log an information
*/
public static void log(String text) {
logger.info("[OpenInv] " + text);
}
/**
* Log an error
*/
public static void log(Throwable e) {
logger.severe("[OpenInv] " + e.toString());
e.printStackTrace();
}
public static void ShowHelp(Player player) {
player.sendMessage(ChatColor.GREEN + "/openinv <Player> - Open a player's inventory");
player.sendMessage(ChatColor.GREEN + " (aliases: oi, inv, open)");
player.sendMessage(ChatColor.GREEN + "/openender <Player> - Open a player's enderchest");
player.sendMessage(ChatColor.GREEN + " (aliases: oe, enderchest)");
player.sendMessage(ChatColor.GREEN + "/toggleopeninv - Toggle item openinv function");
player.sendMessage(ChatColor.GREEN + " (aliases: toi, toggleoi, toggleinv)");
player.sendMessage(ChatColor.GREEN + "/searchinv <Item> [MinAmount] - ");
player.sendMessage(ChatColor.GREEN + " Search and list players having a specific item.");
player.sendMessage(ChatColor.GREEN + " (aliases: si, search)");
player.sendMessage(ChatColor.GREEN + "/anychest - Toggle anychest function");
player.sendMessage(ChatColor.GREEN + " (aliases: ac)");
player.sendMessage(ChatColor.GREEN + "/silentchest - Toggle silent chest function");
player.sendMessage(ChatColor.GREEN + " (aliases: sc, silent)");
}
public static boolean hasPermission(Permissible player, String permission) {
String[] parts = permission.split("\\.");
String perm = "";
for (int i = 0; i < parts.length; i++) {
if (player.hasPermission(perm + "*")) {
return true;
}
perm += parts[i] + ".";
}
return player.hasPermission(permission);
}
}

View File

@@ -1,52 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
public class OpenInvEntityListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onEntityDamage(EntityDamageEvent event) {
if (event instanceof EntityDamageByEntityEvent) {
EntityDamageByEntityEvent evt = (EntityDamageByEntityEvent) event;
Entity attacker = evt.getDamager();
Entity defender = evt.getEntity();
if (!(attacker instanceof Player) || !(defender instanceof Player)) {
return;
}
Player player = (Player) attacker;
if (!(player.getItemInHand().getType().getId() == OpenInv.GetItemOpenInvItem()) || (!OpenInv.GetPlayerItemOpenInvStatus(player.getName())) || !OpenInv.hasPermission(player, "OpenInv.openinv")) {
return;
}
Player target = (Player) defender;
player.performCommand("openinv " + target.getName());
evt.setDamage(0);
evt.setCancelled(true);
}
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
public class OpenInvInventoryListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL)
public void onInventoryClick(InventoryClickEvent event) {
// If this is the top inventory
// if (event.getView().convertSlot(event.getRawSlot()) == event.getRawSlot())
// {
if (!OpenInv.inventoryAccess.check(event.getInventory(), event.getWhoClicked())) {
event.setCancelled(true);
}
// }
}
}

View File

@@ -1,133 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv;
import org.bukkit.ChatColor;
import org.bukkit.block.Chest;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
public class OpenInvPlayerListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(PlayerJoinEvent event) {
ISpecialPlayerInventory inventory = OpenInv.inventories.get(event.getPlayer().getName().toLowerCase());
if (inventory != null) {
inventory.PlayerGoOnline(event.getPlayer());
}
ISpecialEnderChest chest = OpenInv.enderChests.get(event.getPlayer().getName().toLowerCase());
if (chest != null) {
chest.PlayerGoOnline(event.getPlayer());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerQuit(PlayerQuitEvent event) {
ISpecialPlayerInventory inventory = OpenInv.inventories.get(event.getPlayer().getName().toLowerCase());
if (inventory != null) {
inventory.PlayerGoOffline();
}
ISpecialEnderChest chest = OpenInv.enderChests.get(event.getPlayer().getName().toLowerCase());
if (chest != null) {
chest.PlayerGoOffline();
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
if (event.getPlayer().isSneaking()) {
return;
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.useInteractedBlock() == Result.DENY) {
return;
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getType() == org.bukkit.Material.ENDER_CHEST) {
if (OpenInv.hasPermission(player, Permissions.PERM_SILENT) && OpenInv.GetPlayerSilentChestStatus(player.getName())) {
event.setCancelled(true);
player.openInventory(player.getEnderChest());
}
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getState() instanceof Chest) {
boolean silentchest = false;
boolean anychest = false;
int x = event.getClickedBlock().getX();
int y = event.getClickedBlock().getY();
int z = event.getClickedBlock().getZ();
if (OpenInv.hasPermission(player, Permissions.PERM_SILENT) && OpenInv.GetPlayerSilentChestStatus(player.getName())) {
silentchest = true;
}
if (OpenInv.hasPermission(player, Permissions.PERM_ANYCHEST) && OpenInv.GetPlayerAnyChestStatus(player.getName())) {
try {
anychest = OpenInv.anySilentChest.IsAnyChestNeeded(player, x, y, z);
}
catch (Exception e) {
player.sendMessage(ChatColor.RED + "Error while executing openinv. Unsupported CraftBukkit.");
e.printStackTrace();
}
}
// If the anychest or silentchest is active
if (anychest || silentchest) {
if (!OpenInv.anySilentChest.ActivateChest(player, anychest, silentchest, x, y, z)) {
event.setCancelled(true);
}
}
}
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getClickedBlock().getState() instanceof Sign) {
try {
Sign sign = ((Sign) event.getClickedBlock().getState());
if (OpenInv.hasPermission(player, Permissions.PERM_OPENINV) && sign.getLine(0).equalsIgnoreCase("[openinv]")) {
String text = sign.getLine(1).trim() + sign.getLine(2).trim() + sign.getLine(3).trim();
player.performCommand("openinv " + text);
}
}
catch (Exception ex) {
player.sendMessage("Internal Error.");
ex.printStackTrace();
}
}
if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) {
if (!(player.getItemInHand().getType().getId() == OpenInv.GetItemOpenInvItem()) || (!OpenInv.GetPlayerItemOpenInvStatus(player.getName())) || !OpenInv.hasPermission(player, Permissions.PERM_OPENINV)) {
return;
}
player.performCommand("openinv");
}
}
}

View File

@@ -1,16 +0,0 @@
package com.lishid.openinv;
public class Permissions {
public static final String PERM_OPENINV = "OpenInv.openinv";
public static final String PERM_OVERRIDE = "OpenInv.override";
public static final String PERM_EXEMPT = "OpenInv.exempt";
public static final String PERM_CROSSWORLD = "OpenInv.crossworld";
public static final String PERM_SILENT = "OpenInv.silent";
public static final String PERM_ANYCHEST = "OpenInv.anychest";
public static final String PERM_ENDERCHEST = "OpenInv.openender";
public static final String PERM_ENDERCHEST_ALL = "OpenInv.openenderall";
public static final String PERM_SEARCH = "OpenInv.search";
public static final String PERM_EDITINV = "OpenInv.editinv";
public static final String PERM_EDITENDER = "OpenInv.editender";
public static final String PERM_OPENSELF = "OpenInv.openself";
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
public class AnyChestPluginCommand implements CommandExecutor {
public AnyChestPluginCommand(OpenInv plugin) {
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
return true;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_ANYCHEST)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to use anychest.");
return true;
}
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerAnyChestStatus(sender.getName()))
sender.sendMessage("AnyChest is ON.");
else
sender.sendMessage("AnyChest is OFF.");
}
}
OpenInv.SetPlayerAnyChestStatus(sender.getName(), !OpenInv.GetPlayerAnyChestStatus(sender.getName()));
sender.sendMessage("AnyChest is now " + (OpenInv.GetPlayerAnyChestStatus(sender.getName()) ? "On" : "Off") + ".");
return true;
}
}

View File

@@ -1,118 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import java.util.HashMap;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.ISpecialEnderChest;
import com.lishid.openinv.internal.InternalAccessor;
public class OpenEnderPluginCommand implements CommandExecutor {
private final OpenInv plugin;
public static HashMap<Player, String> openEnderHistory = new HashMap<Player, String>();
public OpenEnderPluginCommand(OpenInv plugin) {
this.plugin = plugin;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
return true;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_ENDERCHEST)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player enderchest");
return true;
}
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
OpenInv.ShowHelp((Player) sender);
return true;
}
Player player = (Player) sender;
boolean offline = false;
// History management
String history = openEnderHistory.get(player);
if (history == null || history == "") {
history = player.getName();
openEnderHistory.put(player, history);
}
// Target selecting
Player target;
String name = "";
// Read from history if target is not named
if (args.length < 1) {
if (history != null && history != "") {
name = history;
}
else {
sender.sendMessage(ChatColor.RED + "OpenEnder history is empty!");
return true;
}
}
else {
name = args[0];
}
target = this.plugin.getServer().getPlayer(name);
if (target == null) {
// Try loading the player's data
target = OpenInv.playerLoader.loadPlayer(name);
if (target == null) {
sender.sendMessage(ChatColor.RED + "Player " + name + " not found!");
return true;
}
}
if (target != sender && !OpenInv.hasPermission(sender, Permissions.PERM_ENDERCHEST_ALL)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access other player's enderchest");
return true;
}
// Record the target
history = target.getName();
openEnderHistory.put(player, history);
// Create the inventory
ISpecialEnderChest chest = OpenInv.enderChests.get(target.getName().toLowerCase());
if (chest == null) {
chest = InternalAccessor.Instance.newSpecialEnderChest(target, !offline);
}
// Open the inventory
player.openInventory(chest.getBukkitInventory());
return true;
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import java.util.HashMap;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.ISpecialPlayerInventory;
import com.lishid.openinv.internal.InternalAccessor;
public class OpenInvPluginCommand implements CommandExecutor {
private final OpenInv plugin;
public static HashMap<Player, String> openInvHistory = new HashMap<Player, String>();
public OpenInvPluginCommand(OpenInv plugin) {
this.plugin = plugin;
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
return true;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_OPENINV)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
if (args.length > 0 && args[0].equalsIgnoreCase("?")) {
OpenInv.ShowHelp((Player) sender);
return true;
}
Player player = (Player) sender;
boolean offline = false;
// History management
String history = openInvHistory.get(player);
if (history == null || history == "") {
history = player.getName();
openInvHistory.put(player, history);
}
// Target selecting
Player target;
String name = "";
// Read from history if target is not named
if (args.length < 1) {
name = history;
}
else {
name = args[0];
}
target = this.plugin.getServer().getPlayer(name);
if (target == null) {
if (target == null) {
// Try loading the player's data
target = OpenInv.playerLoader.loadPlayer(name);
if (target == null) {
sender.sendMessage(ChatColor.RED + "Player " + name + " not found!");
return true;
}
}
}
// Permissions checks
if (!OpenInv.hasPermission(player, Permissions.PERM_OVERRIDE) && OpenInv.hasPermission(target, Permissions.PERM_EXEMPT)) {
sender.sendMessage(ChatColor.RED + target.getDisplayName() + "'s inventory is protected!");
return true;
}
// Crosswork check
if ((!OpenInv.hasPermission(player, Permissions.PERM_CROSSWORLD) && !OpenInv.hasPermission(player, Permissions.PERM_OVERRIDE)) && target.getWorld() != player.getWorld()) {
sender.sendMessage(ChatColor.RED + target.getDisplayName() + " is not in your world!");
return true;
}
// Self-open check
if (!OpenInv.hasPermission(player, Permissions.PERM_OPENSELF) && target.equals(player)) {
sender.sendMessage(ChatColor.RED + "You're not allowed to openinv yourself.");
return true;
}
// Record the target
history = target.getName();
openInvHistory.put(player, history);
// Create the inventory
ISpecialPlayerInventory inv = OpenInv.inventories.get(target.getName().toLowerCase());
if (inv == null) {
inv = InternalAccessor.Instance.newSpecialPlayerInventory(target, !offline);
}
// Open the inventory
player.openInventory(inv.getBukkitInventory());
return true;
}
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
public class SearchInvPluginCommand implements CommandExecutor {
public SearchInvPluginCommand() {
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (sender instanceof Player) {
if (!OpenInv.hasPermission(sender, Permissions.PERM_SEARCH)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
}
String PlayerList = "";
Material material = null;
int count = 1;
if (args.length >= 1) {
String[] gData = null;
gData = args[0].split(":");
material = Material.matchMaterial(gData[0]);
}
if (args.length >= 2) {
try {
count = Integer.parseInt(args[1]);
}
catch (NumberFormatException ex) {
sender.sendMessage(ChatColor.RED + "'" + args[1] + "' is not a number!");
return false;
}
}
if (material == null) {
sender.sendMessage(ChatColor.RED + "Unknown item");
return false;
}
for (Player templayer : Bukkit.getServer().getOnlinePlayers()) {
if (templayer.getInventory().contains(material, count)) {
PlayerList += templayer.getName() + " ";
}
}
sender.sendMessage("Players with the item " + material.toString() + ": " + PlayerList);
return true;
}
}

View File

@@ -1,57 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
public class SilentChestPluginCommand implements CommandExecutor {
public SilentChestPluginCommand(OpenInv plugin) {
}
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
return true;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_SILENT)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to use silent chest.");
return true;
}
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerSilentChestStatus(sender.getName()))
sender.sendMessage("SilentChest is ON.");
else
sender.sendMessage("SilentChest is OFF.");
}
}
OpenInv.SetPlayerSilentChestStatus(sender.getName(), !OpenInv.GetPlayerSilentChestStatus(sender.getName()));
sender.sendMessage("SilentChest is now " + (OpenInv.GetPlayerSilentChestStatus(sender.getName()) ? "On" : "Off") + ".");
return true;
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.commands;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
public class ToggleOpenInvPluginCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
sender.sendMessage(ChatColor.RED + "You can't use this from the console.");
return true;
}
if (!OpenInv.hasPermission(sender, Permissions.PERM_OPENINV)) {
sender.sendMessage(ChatColor.RED + "You do not have permission to access player inventories");
return true;
}
Player player = (Player) sender;
if (args.length > 0) {
if (args[0].equalsIgnoreCase("check")) {
if (OpenInv.GetPlayerItemOpenInvStatus(player.getName()))
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is ON.");
else
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is OFF.");
}
}
if (OpenInv.GetPlayerItemOpenInvStatus(player.getName())) {
OpenInv.SetPlayerItemOpenInvStatus(player.getName(), false);
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is OFF.");
}
else {
OpenInv.SetPlayerItemOpenInvStatus(player.getName(), true);
player.sendMessage("OpenInv with " + Material.getMaterial(OpenInv.GetItemOpenInvItem()).toString() + " is ON.");
}
return true;
}
}

View File

@@ -1,25 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
public interface IAnySilentChest {
public boolean IsAnyChestNeeded(Player p, int x, int y, int z);
public boolean ActivateChest(Player p, boolean anychest, boolean silentchest, int x, int y, int z);
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
public interface ISpecialEnderChest {
public Inventory getBukkitInventory();
public void InventoryRemovalCheck();
public void PlayerGoOnline(Player p);
public void PlayerGoOffline();
}

View File

@@ -1,30 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
public interface ISpecialPlayerInventory {
public Inventory getBukkitInventory();
public void InventoryRemovalCheck();
public void PlayerGoOnline(Player p);
public void PlayerGoOffline();
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
public class InternalAccessor {
public static InternalAccessor Instance;
private String version;
/*
* Returns false if version not supported
*/
public static boolean Initialize(Server server) {
Instance = new InternalAccessor();
String packageName = server.getClass().getPackage().getName();
Instance.version = packageName.substring(packageName.lastIndexOf('.') + 1);
try {
Class.forName("com.lishid.openinv.internal." + Instance.version + ".AnySilentChest");
return true;
}
catch (Exception e) {
return false;
}
}
public void PrintError() {
OpenInv.log("OpenInv encountered an error with the CraftBukkit version \"" + Instance.version + "\". Please look for an updated version of OpenInv.");
}
public IPlayerDataManager newPlayerDataManager() {
return (IPlayerDataManager) createObject(IPlayerDataManager.class, "PlayerDataManager");
}
public IInventoryAccess newInventoryAccess() {
return (IInventoryAccess) createObject(IInventoryAccess.class, "InventoryAccess");
}
public IAnySilentChest newAnySilentChest() {
return (IAnySilentChest) createObject(IAnySilentChest.class, "AnySilentChest");
}
public ISpecialPlayerInventory newSpecialPlayerInventory(Player player, boolean offline) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + ".SpecialPlayerInventory");
if (ISpecialPlayerInventory.class.isAssignableFrom(internalClass)) {
return (ISpecialPlayerInventory) internalClass.getConstructor(Player.class, Boolean.class).newInstance(player, offline);
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
public ISpecialEnderChest newSpecialEnderChest(Player player, boolean offline) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + ".SpecialEnderChest");
if (ISpecialEnderChest.class.isAssignableFrom(internalClass)) {
return (ISpecialEnderChest) internalClass.getConstructor(Player.class, Boolean.class).newInstance(player, offline);
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
private Object createObject(Class<? extends Object> assignableClass, String className) {
try {
Class<?> internalClass = Class.forName("com.lishid.openinv.internal." + version + "." + className);
if (assignableClass.isAssignableFrom(internalClass)) {
return internalClass.getConstructor().newInstance();
}
}
catch (Exception e) {
PrintError();
OpenInv.log(e);
}
return null;
}
}

View File

@@ -1,119 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IAnySilentChest;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
public class AnySilentChest implements IAnySilentChest {
public boolean IsAnyChestNeeded(Player p, int x, int y, int z) {
// FOR REFERENCE, LOOK AT net.minecraft.server.BlockChest
EntityPlayer player = ((CraftPlayer) p).getHandle();
World world = player.world;
// If block on top
if (world.getType(new BlockPosition(x, y + 1, z)).getBlock().c())
return true;
int id = Block.getId(world.getType(new BlockPosition(x, y, z)).getBlock());
// If block next to chest is chest and has a block on top
if ((Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x - 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x + 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z - 1)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z + 1)).getBlock().c()))
return true;
return false;
}
public boolean ActivateChest(Player p, boolean anychest, boolean silentchest, int x, int y, int z) {
EntityPlayer player = ((CraftPlayer) p).getHandle();
World world = player.world;
Object chest = (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z));
if (chest == null)
return true;
int id = Block.getId(world.getType(new BlockPosition(x, y, z)).getBlock());
if (!anychest) {
if (world.getType(new BlockPosition(x, y + 1, z)).getBlock().c())
return true;
if ((Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x - 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id) && (world.getType(new BlockPosition(x + 1, y + 1, z)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z - 1)).getBlock().c()))
return true;
if ((Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id) && (world.getType(new BlockPosition(x, y + 1, z + 1)).getBlock().c()))
return true;
}
if (Block.getId(world.getType(new BlockPosition(x - 1, y, z)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(new BlockPosition(x - 1, y, z)), (ITileInventory) chest);
if (Block.getId(world.getType(new BlockPosition(x + 1, y, z)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (ITileInventory) chest, (TileEntityChest) world.getTileEntity(new BlockPosition(x + 1, y, z)));
if (Block.getId(world.getType(new BlockPosition(x, y, z - 1)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z - 1)), (ITileInventory) chest);
if (Block.getId(world.getType(new BlockPosition(x, y, z + 1)).getBlock()) == id)
chest = new InventoryLargeChest("Large chest", (ITileInventory) chest, (TileEntityChest) world.getTileEntity(new BlockPosition(x, y, z + 1)));
boolean returnValue = true;
if (!silentchest) {
player.openContainer((IInventory) chest);
}
else {
try {
SilentContainerChest silentContainerChest = new SilentContainerChest(player.inventory, ((IInventory) chest), player);
int windowId = player.nextContainerCounter();
player.playerConnection.sendPacket(new PacketPlayOutOpenWindow(windowId, "minecraft:chest", ((IInventory) chest).getScoreboardDisplayName(), ((IInventory) chest).getSize()));
player.activeContainer = silentContainerChest;
player.activeContainer.windowId = windowId;
player.activeContainer.addSlotListener(player);
if (OpenInv.NotifySilentChest()) {
p.sendMessage("You are opening a chest silently.");
}
returnValue = false;
}
catch (Exception e) {
e.printStackTrace();
p.sendMessage(ChatColor.RED + "Error while sending silent chest.");
}
}
if (anychest && OpenInv.NotifyAnyChest()) {
p.sendMessage("You are opening a blocked chest.");
}
return returnValue;
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
import java.lang.reflect.Field;
import org.bukkit.entity.HumanEntity;
import org.bukkit.inventory.Inventory;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.Permissions;
import com.lishid.openinv.internal.IInventoryAccess;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
public class InventoryAccess implements IInventoryAccess {
public boolean check(Inventory inventory, HumanEntity player) {
IInventory inv = grabInventory(inventory);
if (inv instanceof SpecialPlayerInventory) {
if (!OpenInv.hasPermission(player, Permissions.PERM_EDITINV)) {
return false;
}
}
else if (inv instanceof SpecialEnderChest) {
if (!OpenInv.hasPermission(player, Permissions.PERM_EDITENDER)) {
return false;
}
}
return true;
}
private IInventory grabInventory(Inventory inventory) {
if(inventory instanceof CraftInventory) {
return ((CraftInventory) inventory).getInventory();
}
//Use reflection to find the iiventory
Class<? extends Inventory> clazz = inventory.getClass();
IInventory result = null;
for(Field f : clazz.getDeclaredFields()) {
f.setAccessible(true);
if(IInventory.class.isAssignableFrom(f.getDeclaringClass())) {
try {
result = (IInventory) f.get(inventory);
}
catch (Exception e) {
OpenInv.log(e);
}
}
}
return result;
}
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
import java.io.File;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.IPlayerDataManager;
import com.mojang.authlib.GameProfile;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.*;
public class PlayerDataManager implements IPlayerDataManager {
public Player loadPlayer(String name) {
try {
UUID uuid = matchUser(name);
if (uuid == null) {
return null;
}
// Default player folder
File playerfolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "playerdata");
if (!playerfolder.exists()) {
return null;
}
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid);
if (player == null) {
return null;
}
GameProfile profile = new GameProfile(uuid, player.getName());
MinecraftServer server = ((CraftServer) Bukkit.getServer()).getServer();
// Create an entity to load the player data
EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), profile, new PlayerInteractManager(server.getWorldServer(0)));
// Get the bukkit entity
Player target = (entity == null) ? null : entity.getBukkitEntity();
if (target != null) {
// Load data
target.loadData();
// Return the entity
return target;
}
}
catch (Exception e) {
OpenInv.log(e);
}
return null;
}
private static UUID matchUser(String search) {
UUID found = null;
String lowerSearch = search.toLowerCase();
int delta = 2147483647;
OfflinePlayer[] offlinePlayers = Bukkit.getOfflinePlayers();
for (OfflinePlayer player : offlinePlayers) {
String name = player.getName();
if (name == null){
continue;
}
if (name.equalsIgnoreCase(search)){
return player.getUniqueId();
}
if (name.toLowerCase().startsWith(lowerSearch)) {
int curDelta = name.length() - lowerSearch.length();
if (curDelta < delta) {
found = player.getUniqueId();
delta = curDelta;
}
if (curDelta == 0) {
break;
}
}
}
return found;
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
//Volatile
import net.minecraft.server.v1_8_R1.*;
public class SilentContainerChest extends ContainerChest {
public IInventory inv;
public SilentContainerChest(IInventory i1, IInventory i2, EntityHuman e1) {
super(i1, i2, e1);
inv = i2;
// close signal
inv.closeContainer(e1);
}
@Override
public void b(EntityHuman paramEntityHuman) {
// Don't send close signal twice, might screw up
}
}

View File

@@ -1,126 +0,0 @@
/*
* Copyright (C) 2011-2014 lishid. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.lishid.openinv.internal.v1_8_R1;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.internal.ISpecialEnderChest;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
//Volatile
import net.minecraft.server.v1_8_R1.*;
import org.bukkit.craftbukkit.v1_8_R1.entity.*;
import org.bukkit.craftbukkit.v1_8_R1.inventory.*;
public class SpecialEnderChest extends InventorySubcontainer implements IInventory, ISpecialEnderChest {
public List<HumanEntity> transaction = new ArrayList<HumanEntity>();
public boolean playerOnline = false;
private CraftPlayer owner;
private InventoryEnderChest enderChest;
private int maxStack = MAX_STACK;
private CraftInventory inventory = new CraftInventory(this);
public SpecialEnderChest(Player p, Boolean online) {
super(((CraftPlayer) p).getHandle().getEnderChest().getName(), ((CraftPlayer) p).getHandle().getEnderChest().hasCustomName(), ((CraftPlayer) p).getHandle().getEnderChest().getSize());
CraftPlayer player = (CraftPlayer) p;
this.enderChest = player.getHandle().getEnderChest();
this.owner = player;
this.items = enderChest.getContents();
OpenInv.enderChests.put(owner.getName().toLowerCase(), this);
}
public Inventory getBukkitInventory() {
return inventory;
}
public void InventoryRemovalCheck() {
owner.saveData();
if (transaction.isEmpty() && !playerOnline) {
OpenInv.enderChests.remove(owner.getName().toLowerCase());
}
}
public void PlayerGoOnline(Player p) {
if (!playerOnline) {
try {
InventoryEnderChest playerEnderChest = ((CraftPlayer) p).getHandle().getEnderChest();
Field field = playerEnderChest.getClass().getField("items");
field.setAccessible(true);
field.set(playerEnderChest, this.items);
}
catch (Exception e) {}
p.saveData();
playerOnline = true;
}
}
public void PlayerGoOffline() {
playerOnline = false;
}
public ItemStack[] getContents() {
return this.items;
}
public void onOpen(CraftHumanEntity who) {
transaction.add(who);
}
public void onClose(CraftHumanEntity who) {
transaction.remove(who);
this.InventoryRemovalCheck();
}
public List<HumanEntity> getViewers() {
return transaction;
}
public InventoryHolder getOwner() {
return this.owner;
}
public void setMaxStackSize(int size) {
maxStack = size;
}
public int getMaxStackSize() {
return maxStack;
}
public boolean a(EntityHuman entityhuman) {
return true;
}
public void startOpen() {
}
public void f() {
}
public void update() {
enderChest.update();
}
}

View File

@@ -1,510 +0,0 @@
package com.lishid.openinv.utils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
public final class ReflectionUtil {
private ReflectionUtil() {}
public static Class<?> getClass(String name, PackageType type) throws Exception {
return Class.forName(type + "." + name);
}
public static Class<?> getClass(String name, SubPackageType type) throws Exception {
return Class.forName(type + "." + name);
}
public static Constructor<?> getConstructor(Class<?> clazz, Class<?>... parameterTypes) {
Class<?>[] p = DataType.convertToPrimitive(parameterTypes);
for (Constructor<?> c : clazz.getConstructors())
if (DataType.equalsArray(DataType.convertToPrimitive(c.getParameterTypes()), p))
return c;
return null;
}
public static Constructor<?> getConstructor(String className, PackageType type, Class<?>... parameterTypes) throws Exception {
return getConstructor(getClass(className, type), parameterTypes);
}
public static Constructor<?> getConstructor(String className, SubPackageType type, Class<?>... parameterTypes) throws Exception {
return getConstructor(getClass(className, type), parameterTypes);
}
public static Object newInstance(Class<?> clazz, Object... args) throws Exception {
return getConstructor(clazz, DataType.convertToPrimitive(args)).newInstance(args);
}
public static Object newInstance(String className, PackageType type, Object... args) throws Exception {
return newInstance(getClass(className, type), args);
}
public static Object newInstance(String className, SubPackageType type, Object... args) throws Exception {
return newInstance(getClass(className, type), args);
}
public static Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) {
Class<?>[] p = DataType.convertToPrimitive(parameterTypes);
for (Method m : clazz.getMethods())
if (m.getName().equals(name) && DataType.equalsArray(DataType.convertToPrimitive(m.getParameterTypes()), p))
return m;
return null;
}
public static Method getMethod(String className, PackageType type, String name, Class<?>... parameterTypes) throws Exception {
return getMethod(getClass(className, type), name, parameterTypes);
}
public static Method getMethod(String className, SubPackageType type, String name, Class<?>... parameterTypes) throws Exception {
return getMethod(getClass(className, type), name, parameterTypes);
}
public static Object invokeMethod(String name, Object instance, Object... args) throws Exception {
return getMethod(instance.getClass(), name, DataType.convertToPrimitive(args)).invoke(instance, args);
}
public static Object invokeMethod(Class<?> clazz, String name, Object instance, Object... args) throws Exception {
return getMethod(clazz, name, DataType.convertToPrimitive(args)).invoke(instance, args);
}
public static Object invokeMethod(String className, PackageType type, String name, Object instance, Object... args) throws Exception {
return invokeMethod(getClass(className, type), name, instance, args);
}
public static Object invokeMethod(String className, SubPackageType type, String name, Object instance, Object... args) throws Exception {
return invokeMethod(getClass(className, type), name, instance, args);
}
public static Field getField(Class<?> clazz, String name) throws Exception {
Field f = clazz.getField(name);
f.setAccessible(true);
return f;
}
public static Field getField(String className, PackageType type, String name) throws Exception {
return getField(getClass(className, type), name);
}
public static Field getField(String className, SubPackageType type, String name) throws Exception {
return getField(getClass(className, type), name);
}
public static Field getDeclaredField(Class<?> clazz, String name) throws Exception {
Field f = clazz.getDeclaredField(name);
f.setAccessible(true);
return f;
}
public static Field getDeclaredField(String className, PackageType type, String name) throws Exception {
return getDeclaredField(getClass(className, type), name);
}
public static Field getDeclaredField(String className, SubPackageType type, String name) throws Exception {
return getDeclaredField(getClass(className, type), name);
}
public static Object getValue(Object instance, String fieldName) throws Exception {
return getField(instance.getClass(), fieldName).get(instance);
}
public static Object getValue(Class<?> clazz, Object instance, String fieldName) throws Exception {
return getField(clazz, fieldName).get(instance);
}
public static Object getValue(String className, PackageType type, Object instance, String fieldName) throws Exception {
return getValue(getClass(className, type), instance, fieldName);
}
public static Object getValue(String className, SubPackageType type, Object instance, String fieldName) throws Exception {
return getValue(getClass(className, type), instance, fieldName);
}
public static Object getDeclaredValue(Object instance, String fieldName) throws Exception {
return getDeclaredField(instance.getClass(), fieldName).get(instance);
}
public static Object getDeclaredValue(Class<?> clazz, Object instance, String fieldName) throws Exception {
return getDeclaredField(clazz, fieldName).get(instance);
}
public static Object getDeclaredValue(String className, PackageType type, Object instance, String fieldName) throws Exception {
return getDeclaredValue(getClass(className, type), instance, fieldName);
}
public static Object getDeclaredValue(String className, SubPackageType type, Object instance, String fieldName) throws Exception {
return getDeclaredValue(getClass(className, type), instance, fieldName);
}
public static void setValue(Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getField(instance.getClass(), fieldName);
f.set(instance, fieldValue);
}
public static void setValue(Object instance, FieldPair pair) throws Exception {
setValue(instance, pair.getName(), pair.getValue());
}
public static void setValue(Class<?> clazz, Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getField(clazz, fieldName);
f.set(instance, fieldValue);
}
public static void setValue(Class<?> clazz, Object instance, FieldPair pair) throws Exception {
setValue(clazz, instance, pair.getName(), pair.getValue());
}
public static void setValue(String className, PackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setValue(String className, PackageType type, Object instance, FieldPair pair) throws Exception {
setValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setValue(String className, SubPackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setValue(String className, SubPackageType type, Object instance, FieldPair pair) throws Exception {
setValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setValues(Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setValue(instance, pair);
}
public static void setValues(Class<?> clazz, Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setValue(clazz, instance, pair);
}
public static void setValues(String className, PackageType type, Object instance, FieldPair... pairs) throws Exception {
setValues(getClass(className, type), instance, pairs);
}
public static void setValues(String className, SubPackageType type, Object instance, FieldPair... pairs) throws Exception {
setValues(getClass(className, type), instance, pairs);
}
public static void setDeclaredValue(Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getDeclaredField(instance.getClass(), fieldName);
f.set(instance, fieldValue);
}
public static void setDeclaredValue(Object instance, FieldPair pair) throws Exception {
setDeclaredValue(instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(Class<?> clazz, Object instance, String fieldName, Object fieldValue) throws Exception {
Field f = getDeclaredField(clazz, fieldName);
f.set(instance, fieldValue);
}
public static void setDeclaredValue(Class<?> clazz, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(clazz, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(String className, PackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setDeclaredValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setDeclaredValue(String className, PackageType type, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValue(String className, SubPackageType type, Object instance, String fieldName, Object fieldValue) throws Exception {
setDeclaredValue(getClass(className, type), instance, fieldName, fieldValue);
}
public static void setDeclaredValue(String className, SubPackageType type, Object instance, FieldPair pair) throws Exception {
setDeclaredValue(className, type, instance, pair.getName(), pair.getValue());
}
public static void setDeclaredValues(Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setDeclaredValue(instance, pair);
}
public static void setDeclaredValues(Class<?> clazz, Object instance, FieldPair... pairs) throws Exception {
for (FieldPair pair : pairs)
setDeclaredValue(clazz, instance, pair);
}
public static void setDeclaredValues(String className, PackageType type, Object instance, FieldPair... pairs) throws Exception {
setDeclaredValues(getClass(className, type), instance, pairs);
}
public static void setDeclaredValues(String className, SubPackageType type, Object instance, FieldPair... pairs) throws Exception {
setDeclaredValues(getClass(className, type), instance, pairs);
}
public enum DataType {
BYTE(byte.class, Byte.class),
SHORT(short.class, Short.class),
INTEGER(int.class, Integer.class),
LONG(long.class, Long.class),
CHARACTER(char.class, Character.class),
FLOAT(float.class, Float.class),
DOUBLE(double.class, Double.class),
BOOLEAN(boolean.class, Boolean.class);
private static final Map<Class<?>, DataType> CLASS_MAP = new HashMap<Class<?>, DataType>();
private final Class<?> primitive;
private final Class<?> reference;
static {
for (DataType t : values()) {
CLASS_MAP.put(t.primitive, t);
CLASS_MAP.put(t.reference, t);
}
}
private DataType(Class<?> primitive, Class<?> reference) {
this.primitive = primitive;
this.reference = reference;
}
public Class<?> getPrimitive() {
return this.primitive;
}
public Class<?> getReference() {
return this.reference;
}
public static DataType fromClass(Class<?> c) {
return CLASS_MAP.get(c);
}
public static Class<?> getPrimitive(Class<?> c) {
DataType t = fromClass(c);
return t == null ? c : t.getPrimitive();
}
public static Class<?> getReference(Class<?> c) {
DataType t = fromClass(c);
return t == null ? c : t.getReference();
}
public static Class<?>[] convertToPrimitive(Class<?>[] classes) {
int length = classes == null ? 0 : classes.length;
Class<?>[] types = new Class<?>[length];
for (int i = 0; i < length; i++)
types[i] = getPrimitive(classes[i]);
return types;
}
public static Class<?>[] convertToPrimitive(Object[] objects) {
int length = objects == null ? 0 : objects.length;
Class<?>[] types = new Class<?>[length];
for (int i = 0; i < length; i++)
types[i] = getPrimitive(objects[i].getClass());
return types;
}
public static boolean equalsArray(Class<?>[] a1, Class<?>[] a2) {
if (a1 == null || a2 == null || a1.length != a2.length)
return false;
for (int i = 0; i < a1.length; i++)
if (!a1[i].equals(a2[i]) && !a1[i].isAssignableFrom(a2[i]))
return false;
return true;
}
}
public final class FieldPair {
private final String name;
private final Object value;
public FieldPair(String name, Object value) {
this.name = name;
this.value = value;
}
public String getName() {
return this.name;
}
public Object getValue() {
return this.value;
}
}
public enum PackageType {
MINECRAFT_SERVER("net.minecraft.server." + Bukkit.getServer().getClass().getPackage().getName().substring(23)),
CRAFTBUKKIT(Bukkit.getServer().getClass().getPackage().getName());
private final String name;
private PackageType(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return name;
}
}
public enum SubPackageType {
BLOCK,
CHUNKIO,
COMMAND,
CONVERSATIONS,
ENCHANTMENS,
ENTITY,
EVENT,
GENERATOR,
HELP,
INVENTORY,
MAP,
METADATA,
POTION,
PROJECTILES,
SCHEDULER,
SCOREBOARD,
UPDATER,
UTIL;
private final String name;
private SubPackageType() {
name = PackageType.CRAFTBUKKIT + "." + name().toLowerCase();
}
public String getName() {
return this.name;
}
@Override
public String toString() {
return name;
}
}
public enum PacketType {
HANDSHAKING_IN_SET_PROTOCOL("PacketHandshakingInSetProtocol"),
LOGIN_IN_ENCRYPTION_BEGIN("PacketLoginInEncryptionBegin"),
LOGIN_IN_START("PacketLoginInStart"),
LOGIN_OUT_DISCONNECT("PacketLoginOutDisconnect"),
LOGIN_OUT_ENCRYPTION_BEGIN("PacketLoginOutEncryptionBegin"),
LOGIN_OUT_SUCCESS("PacketLoginOutSuccess"),
PLAY_IN_ABILITIES("PacketPlayInAbilities"),
PLAY_IN_ARM_ANIMATION("PacketPlayInArmAnimation"),
PLAY_IN_BLOCK_DIG("PacketPlayInBlockDig"),
PLAY_IN_BLOCK_PLACE("PacketPlayInBlockPlace"),
PLAY_IN_CHAT("PacketPlayInChat"),
PLAY_IN_CLIENT_COMMAND("PacketPlayInClientCommand"),
PLAY_IN_CLOSE_WINDOW("PacketPlayInCloseWindow"),
PLAY_IN_CUSTOM_PAYLOAD("PacketPlayInCustomPayload"),
PLAY_IN_ENCHANT_ITEM("PacketPlayInEnchantItem"),
PLAY_IN_ENTITY_ACTION("PacketPlayInEntityAction"),
PLAY_IN_FLYING("PacketPlayInFlying"),
PLAY_IN_HELD_ITEM_SLOT("PacketPlayInHeldItemSlot"),
PLAY_IN_KEEP_ALIVE("PacketPlayInKeepAlive"),
PLAY_IN_LOOK("PacketPlayInLook"),
PLAY_IN_POSITION("PacketPlayInPosition"),
PLAY_IN_POSITION_LOOK("PacketPlayInPositionLook"),
PLAY_IN_SET_CREATIVE_SLOT("PacketPlayInSetCreativeSlot "),
PLAY_IN_SETTINGS("PacketPlayInSettings"),
PLAY_IN_STEER_VEHICLE("PacketPlayInSteerVehicle"),
PLAY_IN_TAB_COMPLETE("PacketPlayInTabComplete"),
PLAY_IN_TRANSACTION("PacketPlayInTransaction"),
PLAY_IN_UPDATE_SIGN("PacketPlayInUpdateSign"),
PLAY_IN_USE_ENTITY("PacketPlayInUseEntity"),
PLAY_IN_WINDOW_CLICK("PacketPlayInWindowClick"),
PLAY_OUT_ABILITIES("PacketPlayOutAbilities"),
PLAY_OUT_ANIMATION("PacketPlayOutAnimation"),
PLAY_OUT_ATTACH_ENTITY("PacketPlayOutAttachEntity"),
PLAY_OUT_BED("PacketPlayOutBed"),
PLAY_OUT_BLOCK_ACTION("PacketPlayOutBlockAction"),
PLAY_OUT_BLOCK_BREAK_ANIMATION("PacketPlayOutBlockBreakAnimation"),
PLAY_OUT_BLOCK_CHANGE("PacketPlayOutBlockChange"),
PLAY_OUT_CHAT("PacketPlayOutChat"),
PLAY_OUT_CLOSE_WINDOW("PacketPlayOutCloseWindow"),
PLAY_OUT_COLLECT("PacketPlayOutCollect"),
PLAY_OUT_CRAFT_PROGRESS_BAR("PacketPlayOutCraftProgressBar"),
PLAY_OUT_CUSTOM_PAYLOAD("PacketPlayOutCustomPayload"),
PLAY_OUT_ENTITY("PacketPlayOutEntity"),
PLAY_OUT_ENTITY_DESTROY("PacketPlayOutEntityDestroy"),
PLAY_OUT_ENTITY_EFFECT("PacketPlayOutEntityEffect"),
PLAY_OUT_ENTITY_EQUIPMENT("PacketPlayOutEntityEquipment"),
PLAY_OUT_ENTITY_HEAD_ROTATION("PacketPlayOutEntityHeadRotation"),
PLAY_OUT_ENTITY_LOOK("PacketPlayOutEntityLook"),
PLAY_OUT_ENTITY_METADATA("PacketPlayOutEntityMetadata"),
PLAY_OUT_ENTITY_STATUS("PacketPlayOutEntityStatus"),
PLAY_OUT_ENTITY_TELEPORT("PacketPlayOutEntityTeleport"),
PLAY_OUT_ENTITY_VELOCITY("PacketPlayOutEntityVelocity"),
PLAY_OUT_EXPERIENCE("PacketPlayOutExperience"),
PLAY_OUT_EXPLOSION("PacketPlayOutExplosion"),
PLAY_OUT_GAME_STATE_CHANGE("PacketPlayOutGameStateChange"),
PLAY_OUT_HELD_ITEM_SLOT("PacketPlayOutHeldItemSlot"),
PLAY_OUT_KEEP_ALIVE("PacketPlayOutKeepAlive"),
PLAY_OUT_KICK_DISCONNECT("PacketPlayOutKickDisconnect"),
PLAY_OUT_LOGIN("PacketPlayOutLogin"),
PLAY_OUT_MAP("PacketPlayOutMap"),
PLAY_OUT_MAP_CHUNK("PacketPlayOutMapChunk"),
PLAY_OUT_MAP_CHUNK_BULK("PacketPlayOutMapChunkBulk"),
PLAY_OUT_MULTI_BLOCK_CHANGE("PacketPlayOutMultiBlockChange"),
PLAY_OUT_NAMED_ENTITY_SPAWN("PacketPlayOutNamedEntitySpawn"),
PLAY_OUT_NAMED_SOUND_EFFECT("PacketPlayOutNamedSoundEffect"),
PLAY_OUT_OPEN_SIGN_EDITOR("PacketPlayOutOpenSignEditor"),
PLAY_OUT_OPEN_WINDOW("PacketPlayOutOpenWindow"),
PLAY_OUT_PLAYER_INFO("PacketPlayOutPlayerInfo"),
PLAY_OUT_POSITION("PacketPlayOutPosition"),
PLAY_OUT_REL_ENTITY_MOVE("PacketPlayOutRelEntityMove"),
PLAY_OUT_REL_ENTITY_MOVE_LOOK("PacketPlayOutRelEntityMoveLook"),
PLAY_OUT_REMOVE_ENTITY_EFFECT("PacketPlayOutRemoveEntityEffect"),
PLAY_OUT_RESPAWN("PacketPlayOutRespawn"),
PLAY_OUT_SCOREBOARD_DISPLAY_OBJECTIVE("PacketPlayOutScoreboardDisplayObjective"),
PLAY_OUT_SCOREBOARD_OBJECTIVE("PacketPlayOutScoreboardObjective"),
PLAY_OUT_SCOREBOARD_SCORE("PacketPlayOutScoreboardScore"),
PLAY_OUT_SCOREBOARD_TEAM("PacketPlayOutScoreboardTeam"),
PLAY_OUT_SET_SLOT("PacketPlayOutSetSlot"),
PLAY_OUT_SPAWN_ENTITY("PacketPlayOutSpawnEntity"),
PLAY_OUT_SPAWN_ENTITY_EXPERIENCE_ORB("PacketPlayOutSpawnEntityExperienceOrb"),
PLAY_OUT_SPAWN_ENTITY_LIVING("PacketPlayOutSpawnEntityLiving"),
PLAY_OUT_SPAWN_ENTITY_PAINTING("PacketPlayOutSpawnEntityPainting"),
PLAY_OUT_SPAWN_ENTITY_WEATHER("PacketPlayOutSpawnEntityWeather"),
PLAY_OUT_SPAWN_POSITION("PacketPlayOutSpawnPosition"),
PLAY_OUT_STATISTIC("PacketPlayOutStatistic"),
PLAY_OUT_TAB_COMPLETE("PacketPlayOutTabComplete"),
PLAY_OUT_TILE_ENTITY_DATA("PacketPlayOutTileEntityData"),
PLAY_OUT_TRANSACTION("PacketPlayOutTransaction"),
PLAY_OUT_UPDATE_ATTRIBUTES("PacketPlayOutUpdateAttributes"),
PLAY_OUT_UPDATE_HEALTH("PacketPlayOutUpdateHealth"),
PLAY_OUT_UPDATE_SIGN("PacketPlayOutUpdateSign"),
PLAY_OUT_UPDATE_TIME("PacketPlayOutUpdateTime"),
PLAY_OUT_WINDOW_ITEMS("PacketPlayOutWindowItems"),
PLAY_OUT_WORLD_EVENT("PacketPlayOutWorldEvent"),
PLAY_OUT_WORLD_PARTICLES("PacketPlayOutWorldParticles"),
STATUS_IN_PING("PacketStatusInPing"),
STATUS_IN_START("PacketStatusInStart"),
STATUS_OUT_PONG("PacketStatusOutPong"),
STATUS_OUT_SERVER_INFO("PacketStatusOutServerInfo");
private final String name;
private Class<?> packet;
private PacketType(String name) {
this.name = name;
}
public String getName() {
return this.getName();
}
public Class<?> getPacket() throws Exception {
return packet == null ? packet = ReflectionUtil.getClass(name, PackageType.MINECRAFT_SERVER) : packet;
}
}
}

View File

@@ -1,34 +0,0 @@
package com.lishid.openinv.utils;
import java.io.File;
import com.lishid.openinv.OpenInv;
import com.lishid.openinv.utils.Updater.UpdateResult;
public class UpdateManager {
public Updater updater;
public void Initialize(OpenInv plugin, File file) {
updater = new Updater(plugin, 31432, file);
// Create task to update
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
@Override
public void run() {
// Check for updates
if (OpenInv.GetCheckForUpdates()) {
UpdateResult result = updater.update();
if (result != UpdateResult.NO_UPDATE) {
if (result == UpdateResult.SUCCESS) {
OpenInv.log("Update found! Downloaded new version.");
OpenInv.log("This behaviour can be disabled in the config.yml");
}
else {
OpenInv.log("Update failed, reason: " + result.toString());
}
}
}
}
}, 0, 20 * 60 * 1000); // Update every once a while
}
}

View File

@@ -1,412 +0,0 @@
/*
* Updater for Bukkit.
*
* This class provides the means to safely and easily update a plugin, or check to see if it is updated using dev.bukkit.org
*/
package com.lishid.openinv.utils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.bukkit.plugin.Plugin;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/**
* Check dev.bukkit.org to find updates for a given plugin, and download the updates if needed.
* <p/>
* <b>VERY, VERY IMPORTANT</b>: Because there are no standards for adding auto-update toggles in your plugin's config, this system provides NO CHECK WITH YOUR CONFIG to make sure the user has allowed
* auto-updating. <br>
* It is a <b>BUKKIT POLICY</b> that you include a boolean value in your config that prevents the auto-updater from running <b>AT ALL</b>. <br>
* If you fail to include this option in your config, your plugin will be <b>REJECTED</b> when you attempt to submit it to dev.bukkit.org.
* <p/>
* An example of a good configuration option would be something similar to 'auto-update: true' - if this value is set to false you may NOT run the auto-updater. <br>
* If you are unsure about these rules, please read the plugin submission guidelines: http://goo.gl/8iU5l
*
* @author Gravity
* @version 2.0
*/
public class Updater {
private Plugin plugin;
private String versionName;
private String versionLink;
@SuppressWarnings("unused")
private String versionType;
@SuppressWarnings("unused")
private String versionGameVersion;
private boolean announce; // Whether to announce file downloads
private URL url; // Connecting to RSS
private File file; // The plugin's file
private int id = 31432; // Project's Curse ID
// SEE https://dev.bukkit.org/home/servermods-apikey/
private String apiKey = null; // BukkitDev ServerMods API key
private static final String TITLE_VALUE = "name"; // Gets remote file's title
private static final String LINK_VALUE = "downloadUrl"; // Gets remote file's download link
private static final String TYPE_VALUE = "releaseType"; // Gets remote file's release type
private static final String VERSION_VALUE = "gameVersion"; // Gets remote file's build version
private static final String QUERY = "/servermods/files?projectIds="; // Path to GET
private static final String HOST = "https://api.curseforge.com"; // Slugs will be appended to this to get to the project's RSS feed
private static final String[] NO_UPDATE_TAG = { "-DEV", "-PRE", "-SNAPSHOT" }; // If the version number contains one of these, don't update.
private static final int BYTE_SIZE = 1024; // Used for downloading files
private String updateFolder;// The folder that downloads will be placed in
private Updater.UpdateResult result = Updater.UpdateResult.SUCCESS; // Used for determining the outcome of the update process
/**
* Gives the dev the result of the update process. Can be obtained by called getResult().
*/
public enum UpdateResult {
/**
* The updater found an update, and has readied it to be loaded the next time the server restarts/reloads.
*/
SUCCESS,
/**
* The updater did not find an update, and nothing was downloaded.
*/
NO_UPDATE,
/**
* The server administrator has disabled the updating system
*/
DISABLED,
/**
* The updater found an update, but was unable to download it.
*/
FAIL_DOWNLOAD,
/**
* For some reason, the updater was unable to contact dev.bukkit.org to download the file.
*/
FAIL_DBO,
/**
* When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
*/
FAIL_NOVERSION,
/**
* The id provided by the plugin running the updater was invalid and doesn't exist on DBO.
*/
FAIL_BADID,
/**
* The server administrator has improperly configured their API key in the configuration
*/
FAIL_APIKEY,
/**
* The updater found an update, but because of the UpdateType being set to NO_DOWNLOAD, it wasn't downloaded.
*/
UPDATE_AVAILABLE
}
/**
* Initialize the updater
*
* @param plugin The plugin that is checking for an update.
* @param id The dev.bukkit.org id of the project
* @param file The file that the plugin is running from, get this by doing this.getFile() from within your main class.
*/
public Updater(Plugin plugin, int id, File file) {
this.plugin = plugin;
this.file = file;
this.id = id;
this.updateFolder = plugin.getServer().getUpdateFolder();
try {
this.url = new URL(Updater.HOST + Updater.QUERY + id);
}
catch (final MalformedURLException e) {
plugin.getLogger().severe("The project ID provided for updating, " + id + " is invalid.");
this.result = UpdateResult.FAIL_BADID;
e.printStackTrace();
}
}
/**
* Save an update from dev.bukkit.org into the server's update folder.
*/
private void saveFile(File folder, String file, String u) {
if (!folder.exists()) {
folder.mkdir();
}
BufferedInputStream in = null;
FileOutputStream fout = null;
try {
// Download the file
final URL url = new URL(u);
final int fileLength = url.openConnection().getContentLength();
in = new BufferedInputStream(url.openStream());
fout = new FileOutputStream(folder.getAbsolutePath() + "/" + file);
final byte[] data = new byte[Updater.BYTE_SIZE];
int count;
if (this.announce) {
this.plugin.getLogger().info("About to download a new update: " + this.versionName);
}
long downloaded = 0;
while ((count = in.read(data, 0, Updater.BYTE_SIZE)) != -1) {
downloaded += count;
fout.write(data, 0, count);
final int percent = (int) ((downloaded * 100) / fileLength);
if (this.announce && ((percent % 10) == 0)) {
this.plugin.getLogger().info("Downloading update: " + percent + "% of " + fileLength + " bytes.");
}
}
// Just a quick check to make sure we didn't leave any files from last time...
for (final File xFile : new File(this.plugin.getDataFolder().getParent(), this.updateFolder).listFiles()) {
if (xFile.getName().endsWith(".zip")) {
xFile.delete();
}
}
// Check to see if it's a zip file, if it is, unzip it.
final File dFile = new File(folder.getAbsolutePath() + "/" + file);
if (dFile.getName().endsWith(".zip")) {
// Unzip
this.unzip(dFile.getCanonicalPath());
}
if (this.announce) {
this.plugin.getLogger().info("Finished updating.");
}
}
catch (final Exception ex) {
this.plugin.getLogger().warning("The auto-updater tried to download a new update, but was unsuccessful.");
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
}
finally {
try {
if (in != null) {
in.close();
}
if (fout != null) {
fout.close();
}
}
catch (final Exception ex) {}
}
}
/**
* Part of Zip-File-Extractor, modified by Gravity for use with Bukkit
*/
private void unzip(String file) {
try {
final File fSourceZip = new File(file);
final String zipPath = file.substring(0, file.length() - 4);
ZipFile zipFile = new ZipFile(fSourceZip);
Enumeration<? extends ZipEntry> e = zipFile.entries();
while (e.hasMoreElements()) {
ZipEntry entry = e.nextElement();
File destinationFilePath = new File(zipPath, entry.getName());
destinationFilePath.getParentFile().mkdirs();
if (entry.isDirectory()) {
continue;
}
else {
final BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
int b;
final byte buffer[] = new byte[Updater.BYTE_SIZE];
final FileOutputStream fos = new FileOutputStream(destinationFilePath);
final BufferedOutputStream bos = new BufferedOutputStream(fos, Updater.BYTE_SIZE);
while ((b = bis.read(buffer, 0, Updater.BYTE_SIZE)) != -1) {
bos.write(buffer, 0, b);
}
bos.flush();
bos.close();
bis.close();
final String name = destinationFilePath.getName();
if (name.endsWith(".jar") && this.pluginFile(name)) {
destinationFilePath.renameTo(new File(this.plugin.getDataFolder().getParent(), this.updateFolder + "/" + name));
}
}
entry = null;
destinationFilePath = null;
}
e = null;
zipFile.close();
zipFile = null;
// Move any plugin data folders that were included to the right place, Bukkit won't do this for us.
for (final File dFile : new File(zipPath).listFiles()) {
if (dFile.isDirectory()) {
if (this.pluginFile(dFile.getName())) {
final File oFile = new File(this.plugin.getDataFolder().getParent(), dFile.getName()); // Get current dir
final File[] contents = oFile.listFiles(); // List of existing files in the current dir
for (final File cFile : dFile.listFiles()) // Loop through all the files in the new dir
{
boolean found = false;
for (final File xFile : contents) // Loop through contents to see if it exists
{
if (xFile.getName().equals(cFile.getName())) {
found = true;
break;
}
}
if (!found) {
// Move the new file into the current dir
cFile.renameTo(new File(oFile.getCanonicalFile() + "/" + cFile.getName()));
}
else {
// This file already exists, so we don't need it anymore.
cFile.delete();
}
}
}
}
dFile.delete();
}
new File(zipPath).delete();
fSourceZip.delete();
}
catch (final IOException ex) {
this.plugin.getLogger().warning("The auto-updater tried to unzip a new update file, but was unsuccessful.");
this.result = Updater.UpdateResult.FAIL_DOWNLOAD;
ex.printStackTrace();
}
new File(file).delete();
}
/**
* Check if the name of a jar is one of the plugins currently installed, used for extracting the correct files out of a zip.
*/
private boolean pluginFile(String name) {
for (final File file : new File("plugins").listFiles()) {
if (file.getName().equals(name)) {
return true;
}
}
return false;
}
/**
* Check to see if the program should continue by evaluation whether the plugin is already updated, or shouldn't be updated
*/
private boolean versionCheck(String title) {
final String version = this.plugin.getDescription().getVersion();
if (title.split(" ").length == 2) {
final String remoteVersion = title.split(" ")[1].split(" ")[0]; // Get the newest file's version number
if (this.hasTag(version) || version.equalsIgnoreCase(remoteVersion) || !isNewer(version, remoteVersion)) {
// We already have the latest version, or this build is tagged for no-update
this.result = Updater.UpdateResult.NO_UPDATE;
return false;
}
}
else {
this.plugin.getLogger().warning("File versions should follow the format 'PluginName VERSION'");
this.result = Updater.UpdateResult.FAIL_NOVERSION;
return false;
}
return true;
}
/**
* Evaluate whether the version number is marked showing that it should not be updated by this program
*/
private boolean hasTag(String version) {
for (final String string : Updater.NO_UPDATE_TAG) {
if (version.contains(string)) {
return true;
}
}
return false;
}
private boolean read() {
try {
final URLConnection conn = this.url.openConnection();
conn.setConnectTimeout(5000);
if (this.apiKey != null) {
conn.addRequestProperty("X-API-Key", this.apiKey);
}
conn.addRequestProperty("User-Agent", "Updater");
conn.setDoOutput(true);
final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
final String response = reader.readLine();
final JSONArray array = (JSONArray) JSONValue.parse(response);
if (array.size() == 0) {
this.plugin.getLogger().warning("The updater could not find any files for the project id " + this.id);
this.result = UpdateResult.FAIL_BADID;
return false;
}
this.versionName = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TITLE_VALUE);
this.versionLink = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.LINK_VALUE);
this.versionType = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.TYPE_VALUE);
this.versionGameVersion = (String) ((JSONObject) array.get(array.size() - 1)).get(Updater.VERSION_VALUE);
return true;
}
catch (final IOException e) {
if (e.getMessage().contains("HTTP response code: 403")) {
this.plugin.getLogger().warning("dev.bukkit.org rejected the API key provided in plugins/Updater/config.yml");
this.plugin.getLogger().warning("Please double-check your configuration to ensure it is correct.");
this.result = UpdateResult.FAIL_APIKEY;
}
else {
this.plugin.getLogger().warning("The updater could not contact curse for updating.");
this.result = UpdateResult.FAIL_DBO;
}
e.printStackTrace();
return false;
}
}
private static boolean isNewer(String oldVers, String newVers) {
String s1 = normalisedVersion(oldVers);
String s2 = normalisedVersion(newVers);
int cmp = s1.compareTo(s2);
return cmp < 0;
}
public static String normalisedVersion(String version) {
return normalisedVersion(version, ".", 3);
}
public static String normalisedVersion(String version, String sep, int maxWidth) {
String[] split = Pattern.compile(sep, Pattern.LITERAL).split(version);
StringBuilder sb = new StringBuilder();
for (String s : split) {
sb.append(String.format("%" + maxWidth + 's', s));
}
return sb.toString();
}
public UpdateResult update() {
if (Updater.this.url != null) {
// Obtain the results of the project's file feed
if (Updater.this.read()) {
if (Updater.this.versionCheck(Updater.this.versionName)) {
if (Updater.this.versionLink != null) {
String name = Updater.this.file.getName();
// If it's a zip file, it shouldn't be downloaded as the plugin's name
if (Updater.this.versionLink.endsWith(".zip")) {
final String[] split = Updater.this.versionLink.split("/");
name = split[split.length - 1];
}
Updater.this.saveFile(new File(Updater.this.plugin.getDataFolder().getParent(), Updater.this.updateFolder), name, Updater.this.versionLink);
}
}
}
}
return this.result;
}
}

View File

@@ -1,38 +0,0 @@
name: OpenInv
main: com.lishid.openinv.OpenInv
version: 2.2.5
author: lishid
description: >
This plugin allows you to open a player's inventory as a chest and interact with it in real time.
commands:
openinv:
aliases: [oi, inv, open]
description: Open a player's inventory
usage: |
/<command> - Open last person's inventory
/<command> <Player> - Open a player's inventory
openender:
aliases: [oe]
description: Opens the enderchest of a player
usage: |
/<command> <Player> - Opens a player's enderchest
searchinv:
aliases: [si]
description: Search and list players having a specific item
usage: |
/<command> <Item> [MinAmount] - Item can be the Item ID or the CraftBukkit Item Name, MinAmount is the minimum amount to be considered.
toggleopeninv:
aliases: [toi, toggleoi, toggleinv]
description: Toggle item openinv function
usage: |
/<command> [Check] - Checks whether item openinv is enabled
silentchest:
aliases: [sc, silent]
description: Toggle silent chest function, which hides the animation of a chest when opened or closed, and suppresses the sound.
usage: |
/<command> [Check] - Checks whether silent chest is enabled
anychest:
aliases: [ac]
description: Toggle anychest function, which allows opening of blocked chests.
usage: |
/<command> [Check] - Checks whether anychest is enabled