Compare commits

...

378 Commits

Author SHA1 Message Date
Johan af02eb3f14
#579 Updated for the stock counter to be updated on any hopper input (#591) 2024-05-14 13:07:05 +01:00
Max Lee 7dbc1ad5d3
Merge pull request #592 from Johanmans10/bypass-price-limit 2024-04-30 01:20:52 +01:00
Phoenix616 9f4037c312
Disable Paper's NMS remapping introduced in 1.20.5. 2024-04-30 01:09:55 +01:00
Phoenix616 b6b7a6bf43
Improve EnumParser to better take in the closeness of names into account
This is necessary to support new 1.20.5 items as well as better support some older ones.

Also use the properly formatted name when trying to valueOf get the Enum directly.
2024-04-30 01:06:49 +01:00
Phoenix616 60b10a83b6
Adjust to use "new" potion API and improve custom effects
This makes it compile with 1.20.5
2024-04-30 00:24:14 +01:00
Phoenix616 b54824052d
Build against 1.20.5 2024-04-30 00:23:10 +01:00
Johanmans10 8a26b15ea9 #539 Implemented permissions for bypassing min/max sell/buy prices 2024-04-30 01:00:13 +02:00
Phoenix616 09170f6d51
Add official RedProtect repo and update dependency (Fixes #589) 2024-04-26 17:22:34 +01:00
Phoenix616 488cd2e9d5
Use doubles for tax amount in config instead of int (Fixes #578)
For some reason this already was a float internally but not exposed in the config? Wat.
2024-04-26 16:51:42 +01:00
Phoenix616 46d4f583ba
Revert adventure-text-serializer-gson to 4.14.0 (Fixes #590)
Newer versions don't seem to be compatible with current platform release :S
2024-04-23 00:32:27 +01:00
Phoenix616 7a73d66939
Update adventure (Fixes #582) 2024-04-19 16:56:09 +01:00
Phoenix616 6f5b56d20a
Fix Folia compatibility by using own ExecutorService (Fixes #587) 2024-03-25 22:40:03 +01:00
Phoenix616 b386cacb5e
Improve the Updater (Fixes #541, #586)
- Use Modrinth for version checks and downloads
- Fix a possible deadlock in the version check
- Actually compare the version numbers in the check
- Add verification of the sha1 hash sum of the downloaded file
2024-03-04 19:28:02 +01:00
Phoenix616 df17fe7483
[CI-SKIP] Fix wrong author and add some more docs 2024-02-07 17:20:29 +01:00
Phoenix616 d3f8abb80e
Add release update notification (#541)
Also add that config option to our metrics
2024-02-07 16:10:08 +01:00
Phoenix616 2388da62a5
Log tax messages to the shop logger too
This makes it possible to turn these messages off or reroute them to the shop log file
2023-10-08 20:55:38 +01:00
Phoenix616 53aa75d9b4
Fix that Double Chests weren't supported in some cases (Fixes #568) 2023-09-28 17:26:37 +01:00
Phoenix616 a413e86ccf
Fix transaction message getting sent twice when server economy account is set
This was due to the ServerAccountCorrector calling the economy events another time with the new account.
 Directly setting the new account is the far better approach and has been adjusted for all currency events.
2023-09-26 20:22:15 +01:00
Phoenix616 0fcbcbbb11
Fix ChestShop not starting if an error occurred hooking into a dependency 2023-09-25 12:55:42 +01:00
Phoenix616 a9cb8096e6
Use InventoryHolder couldBeShopContainer check (Might help with #566) 2023-09-22 01:45:45 +01:00
Phoenix616 17bd2a1fcb
Fix issues with spaces in shortened item names 2023-08-27 16:29:06 +01:00
Phoenix616 d426492561
Fix issues with new 1.20 back side of signs
This basically blocks any text on the backside of a sign if the front is a valid shop
 and with that also fixes an issue where valid shop sign syntax was detected on back
 side sign changes.
2023-08-27 15:55:53 +01:00
Joo200 f3cf4cd96e
catch ClassCastException in the ItemDatabase update method (#563) 2023-08-14 20:01:49 +01:00
Phoenix616 9bbd6028d0
Add log message that informs about the server being rate limited. 2023-08-13 19:48:18 +01:00
Phoenix616 2b36b7314f
Work around potential issues with Mojang API ratelimites on startup (#560) 2023-08-13 19:46:44 +01:00
Phoenix616 0b8fe80443
Further item name shortening fixes 2023-07-08 16:13:29 +01:00
Phoenix616 3ef02d05cd
Restore spaces in string that might be already be shortened (Fixes #558) 2023-06-21 18:32:47 +01:00
Phoenix616 3a1885e2f3
Improve logger handling
- Log transactions with custom logger `ChestShop Shops`
- Fix that non-shop log messages show in log file (Fixes #551)
- Fix shop removal logging being broken
- Allow reloading `LOG_TO_FILE` and `LOG_TO_CONSOLE` config options
- Fix the wording in the comments on `LOG_TO_CONSOLE` and `LOG_ALL_SHOP_REMOVALS`
2023-06-18 22:12:08 +01:00
Phoenix616 54cc1ce842
Fix wrong capitalization of item names (Fixes #549, #557)
Regression caused from change in ed642ccf
2023-06-17 14:53:47 +01:00
Phoenix616 dc6aa21a91
Fix compatibility with older versions (Fixes #556) 2023-06-15 16:24:00 +01:00
Phoenix616 6ba8e092b8
Add some more detailed server type statistics 2023-06-15 15:47:35 +01:00
Phoenix616 97ffc31d2b
Separate economy adapter from other dependencies in metrics 2023-06-15 15:27:03 +01:00
Phoenix616 ed642ccf3c
Compile against 1.20.1 and fix some short name issues
Partially helps with #516, #521, #549
2023-06-15 00:28:47 +01:00
Phoenix616 999f596125
Add metrics for used economy and hooked dependencies 2023-06-14 15:50:43 +01:00
Phoenix616 30ff61d14f
Improve INCLUDE_SETTINGS_IN_METRICS option comment 2023-06-14 15:46:44 +01:00
Phoenix616 978f1270f5
Send ShopDestroyedEvent for sign edits that break a shop 2023-06-13 00:22:30 +01:00
Phoenix616 7203ec17cd
Add compatibility for new sign-editing in 1.20
This allows editing of signs if you hold a sign in hand but block any other interaction.
2023-06-13 00:11:48 +01:00
Max Lee 6602be68e3
Merge pull request #553 from Krakenied/strip-price-colors
Add option to strip price colors
2023-05-20 14:46:08 +01:00
Krakenied 2fb82a2529
Strip price colors 2023-05-20 14:56:06 +02:00
Phoenix616 05ebe2d054
Properly shutdown ExecutorService 2023-03-29 16:54:10 +01:00
Phoenix616 013a21159f
Ensure NameManager caches stay consistent with multiple threads accessing them 2023-03-24 00:13:41 +01:00
Phoenix616 d52c329618
Make compatible with Folia by using own ExecutorService
Our async tasks were already pretty world-independent (update checker as
 well as some logging so this should work pretty well)

For the rest the ORMLite library should already be able to handle access from
 different threads and whether economy plugins are compatible with Folia is
 up to them, not us...
2023-03-23 23:50:43 +01:00
Phoenix616 b425dfb69f
[CI-SKIP] Add Qodana project to readme 2023-03-01 18:44:56 +01:00
Phoenix616 0a16ec1d08
Add qodana baseline of accepted warnings 2023-03-01 18:42:40 +01:00
Phoenix616 362dd856d3
Cleanup: Properly log errors 2023-03-01 18:36:46 +01:00
Phoenix616 385672ecd4
Cleanup: Fix potential NPE in Updater 2023-03-01 18:34:21 +01:00
Phoenix616 92a013dd10
Cleanup: Properly log errors 2023-03-01 18:27:04 +01:00
Phoenix616 7a09c53bde
Cleanup: Better specify exception catch and remove unused import 2023-03-01 18:16:14 +01:00
Phoenix616 93f14a330d
Cleanup: Use String#join instead of Stream#collect 2023-03-01 18:15:27 +01:00
Phoenix616 199573df59
Fix potential NullPointerExceptions 2023-03-01 18:14:18 +01:00
Phoenix616 a38f309453
Cleanup: Properly log errors 2023-03-01 18:11:35 +01:00
Phoenix616 02ef09c586
Use non-deprecated setScale, remove duplicate division calls, NPE-Fix 2023-03-01 18:10:25 +01:00
Phoenix616 aa7bfbb7ee
DoubleChest left/right side holder might be null/not BlockStates. Check that. 2023-03-01 18:06:35 +01:00
Phoenix616 7d92cb820d
Don't check block type twice
This also prevents it from getting it multiple types which would result in a chunk data query too...
2023-03-01 18:03:34 +01:00
Phoenix616 4de1f1e8c5
Fix potential out of bounds and NPE 2023-03-01 18:01:33 +01:00
Phoenix616 7ee8d07242
Cleanup: Use chained StringBuilder#append 2023-03-01 17:59:02 +01:00
Phoenix616 37c7b97fe8
Improve protection type mapping
This will return if a non-compatible type is used instead of using private.
2023-03-01 17:57:31 +01:00
Phoenix616 0d8d37eb85
Cleanup: Use 0-sized arrays and fix division 2023-03-01 17:54:31 +01:00
Phoenix616 e6a2d2f1c5
Cleanup: Properly log errors and remove unnecessary early return 2023-03-01 17:51:56 +01:00
Phoenix616 ada8cf3ae8
Cleanup: 0-sized arrays, NPE, Math.min usage 2023-03-01 17:48:07 +01:00
Phoenix616 fbfe789bfa
Fix potential NPE and properly log exceptions 2023-03-01 17:44:39 +01:00
Phoenix616 f547995164
Cleanup: Mark EconomyAPI as Nullable and fix potential NPE 2023-03-01 17:37:22 +01:00
Phoenix616 d6673c8afd
Cleanup: Use try-with and small fixes like NPE
Also make class final so that no class can extend it and start the thread
2023-03-01 17:33:34 +01:00
Phoenix616 f0661656a0
Cleanup: Use try-with and other small improvements 2023-03-01 17:24:47 +01:00
Phoenix616 967a315ff8
Add Qodana code quality checker 2023-03-01 16:26:51 +01:00
Phoenix616 59e82c7078
Add metrics for settings as separate charts
bStats has unfortunately not implemented advanced bar charts
 in years and not accepted my PR for it either :( We do it the
 hart way then...
2023-02-05 22:02:10 +01:00
Phoenix616 236c4b1b37
Add links to IssueHunt.
Also remove Patreon and deprecate dev-Bukkit bug tracker in the readme
2023-02-04 15:23:50 +01:00
Phoenix616 32879ad3cd
Update maven repos
- WorldGuard is now on enginehub.org domain
- RedProtect is in central and doesn't need repo anymore
2023-01-22 21:35:39 +01:00
Phoenix616 c15afb4d0b
Update adventure and MineDown 2023-01-22 21:29:09 +01:00
Phoenix616 852f20a50e
Add ItemBridge support (Resolves #361)
This also slightly changes how the max width is applied for generating sign item IDs
2022-12-29 15:25:26 +01:00
Phoenix616 e3ab44ae32
Don't fail account UUID checks on players that are online
This should fix some issues with Floodgate which kept popping up
2022-11-23 22:15:21 +01:00
Phoenix616 9233064273
Adjust comment regarding floodgate to their current default prefix 2022-11-17 23:28:09 +01:00
Phoenix616 93e2895645
Remove deprecated reflection API usage 2022-10-13 14:14:20 +01:00
Phoenix616 e99ae1eb1e
Fix spigot build profile by using same version as paper dependency 2022-10-12 16:53:00 +01:00
Felipe Foschiera 9842f47f37
Trigger transaction message from CurrencyTransferEvent and consider tax (#534)
Triggers TransactionMessageSender from CurrencyTransferEvent, wrapping TransactionEvent inside of it.
Add logic to showamount after taxes on the message.
2022-10-12 16:37:18 +01:00
Max Lee 34df7e368a
Merge pull request #527 from JLLeitschuh/fix/JLL/use_https_to_resolve_dependencies_maven
[SECURITY] Use HTTPS to resolve dependencies in Maven Build
2022-09-12 23:26:02 +01:00
Jonathan Leitschuh 5066a21a5f
vuln-fix: Use HTTPS instead of HTTP to resolve deps CVE-2021-26291
This fixes a security vulnerability in this project where the `pom.xml`
files were configuring Maven to resolve dependencies over HTTP instead of
HTTPS.

Weakness: CWE-829: Inclusion of Functionality from Untrusted Control Sphere
Severity: High
CVSSS: 8.1
Detection: CodeQL & OpenRewrite (https://public.moderne.io/recipes/org.openrewrite.maven.security.UseHttpsForRepositories)

Reported-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>
Signed-off-by: Jonathan Leitschuh <Jonathan.Leitschuh@gmail.com>

Bug-tracker: https://github.com/JLLeitschuh/security-research/issues/8

Co-authored-by: Moderne <team@moderne.io>
2022-07-14 22:41:37 +00:00
Phoenix616 da3928b723
Add meta information for more item types 2022-07-06 22:08:55 +01:00
Phoenix616 d0af977212
Fix issue when creating sign with own name without existing account
This was only an issue if the autofill functionality for the own name wasn't used.
Now if there is no account with a certain name then the player can use it if it matches their user name
2022-07-06 20:05:21 +01:00
Phoenix616 e3cd50cf75
Add some debug logging for shop sign creation 2022-07-06 00:54:44 +01:00
Phoenix616 a78b925181
Update dependencies (mainly ORMLite and bStats)
Also fixed the outdated Reserve maven repository. That massively slowed down builds...
2022-06-28 16:24:17 +01:00
Phoenix616 5b712f9ed5
Back to development builds 2022-06-28 16:12:44 +01:00
Phoenix616 c54ed6b2f5
Prepare 3.12.2 release 2022-06-28 14:54:20 +01:00
Max Lee f57d1b04a2
New Crowdin updates (#518)
* New translations lang.en.yml (Chinese Simplified)

* New translations lang.en.yml (Turkish)

* New translations lang.en.yml (Russian)

* New translations lang.en.yml (Ukrainian)

* New translations lang.en.yml (Ukrainian)

* New translations lang.en.yml (Spanish)

* New translations lang.en.yml (French)

* New translations lang.en.yml (Italian)

* New translations lang.en.yml (Ukrainian)

* New translations lang.en.yml (Ukrainian)

* New translations lang.en.yml (Italian)

* New translations lang.en.yml (Ukrainian)
2022-06-28 12:44:19 +01:00
Max Lee 6b372883ac
Merge pull request #522 from Krakenied/master
Fix zero partial price exploit
2022-06-28 12:21:31 +01:00
Krakenied fd969cd996 Fix zero partial price exploit 2022-06-28 11:09:29 +02:00
Phoenix616 fd0035a99c
Build against 1.19 and replace apache-commons-lang 2022-06-13 22:52:26 +01:00
Phoenix616 4285358910
Back to snapshots for development 2022-06-04 14:51:40 +01:00
Phoenix616 dc731919e3
Prepare 3.12.1 release 2022-06-04 14:51:18 +01:00
Phoenix616 a112c50d55
Update language files with latest Crowdin translations. 2022-06-04 14:49:48 +01:00
Phoenix616 4c7b018f8c
Ignore formatting when parsing sign lines (#503) 2022-02-17 21:29:43 +01:00
Krakenied 2ebc2f285b
Fix NFE and some other possible issues (#502)
Fixes #499
2022-02-17 16:34:25 +01:00
Phoenix616 e40b5567b8
Require count digit on quantity line if module is enabled
Also use the same pattern for validating the line in QuantityUtil and ChestShoSign.
2022-02-04 16:28:44 +01:00
Phoenix616 419527efaa
Fix item id not being valid with numbers and underscore (Fixes #498) 2022-01-29 22:23:51 +01:00
Phoenix616 99a609b423
Unify sign line parsing and strip owner line whitespace 2022-01-26 20:35:42 +01:00
jeffry1829 5ec2ab61e5
Make Unicode character valid in ChestShop Sign (#497) 2022-01-26 14:26:03 +01:00
Phoenix616 d1bbcfce47
Sync log4j version with updated server version
The log4j bugs weren't an issue as log4j wasn't included in the plugin directly
 but simply depended on the version the server provided. This just syncs it with
 the commonly used server version again. (And shuts up dependabot)
2022-01-10 17:07:59 +01:00
Phoenix616 fa924f0212
Full message support on 1.18 2021-12-03 19:12:18 +01:00
Phoenix616 f9a0bf90a3
Don't search unknown players by default
This shouldn't be needed in any of the internal usage of the event
 and could lead to unexpected network lookups.
2021-11-17 14:03:51 +01:00
Phoenix616 02dd167024
Add better message for when the shop's sell price is above the buy price 2021-10-15 15:50:04 +01:00
Phoenix616 8655870077
Add clarifying comment to buy tax bypass 2021-10-15 15:39:01 +01:00
Phoenix616 01b7cddaa3
Use correct tax amount 🙈 2021-10-15 15:26:44 +01:00
Phoenix616 6962ae2590
Added log messages to tax and discount module (#478) 2021-10-15 15:17:09 +01:00
Phoenix616 03317c2c78
Fix prefix not getting parsed with MineDown 2021-10-15 14:22:14 +01:00
Max Lee a84cd7a2aa Improve agreements 2021-10-15 01:15:52 +01:00
TreyRuffy bb7bd3415c
ChestShop Sign Dying (#479)
Signed-off-by: TreyRuffy <TreyRuffy@users.noreply.github.com>
2021-10-12 23:30:49 +01:00
Phoenix616 4f8e7938e2
Update Adventure (Fixes #474) 2021-09-10 22:22:16 +01:00
Phoenix616 986a22fb73
Make sure to not load chunks to check for shops (Fixes #473) 2021-09-01 16:30:14 +01:00
Phoenix616 f7aa80e566
[CI-SKIP] Convert to issue form system 2021-08-05 22:19:10 +01:00
Phoenix616 d193812392
Fix shop not buying/selling errors being the wrong way around 2021-07-25 22:31:11 +01:00
Phoenix616 13b727c779
Fix possible NPE when loading material sets from config 2021-07-09 11:26:30 +01:00
Phoenix616 129663650f
Build and test against 1.17
Also update maven-compiler-plugin
2021-07-04 14:20:43 +01:00
Phoenix616 d3cb06acc0
Use the correct messages 🙈 (Fixes #462) 2021-06-26 23:06:21 +01:00
Phoenix616 965d931978
Include prices in price restriction messages (Resolves #461) 2021-06-24 19:40:58 +01:00
Phoenix616 1b85bda656
Fix some block place and break access message issues
Messages were missing on block break and placement sent multiple.
2021-06-21 16:16:19 +01:00
Phoenix616 42a7cc4789
Check if sign actually applies to container (Fixes #460) 2021-06-21 16:07:42 +01:00
Phoenix616 a549b4ef51
[CI-SKIP] Update issue templates to include link to dev builds 2021-06-19 19:25:00 +01:00
Phoenix616 988420992c
Sync version of adventure gson serializer dependency with api 2021-06-18 22:52:05 +01:00
Phoenix616 df2e4394c4
Use more exact shop detection with /shopinfo 2021-06-13 15:48:04 +01:00
Phoenix616 63f68d5444
Fix test profiles activation 2021-06-13 15:47:41 +01:00
Phoenix616 6f4be10065
Prepare next version and update Minedown 2021-06-09 00:47:42 +01:00
Phoenix616 8cb78165a3
3.12 release 2021-06-06 22:02:53 +01:00
Max Lee 3cc219e86b
New Crowdin updates (#399) 2021-06-06 22:01:55 +01:00
Phoenix616 052560393c
Add additional plugin, server and java version stats 2021-06-04 00:35:27 +01:00
Phoenix616 2545b95d54
Fix ShowItem not working in messages 2021-05-16 19:53:30 +01:00
Phoenix616 f825995a85
Don't automatically add prefix to certain messages 2021-05-16 19:39:53 +01:00
Phoenix616 2be9207faa
Differentiate between shop accessing and administrating
This fixes LWC donation and display protections not working correctly
2021-05-16 00:31:33 +01:00
Phoenix616 5b214e09f5
Fix turning off default protection if protected externally (Fixes #445) 2021-05-16 00:17:25 +01:00
Phoenix616 d3ed650b74
Make LWC donation and display protections work with shops 2021-05-16 00:15:59 +01:00
Phoenix616 c9b8e136d1
Fix that shop info doesn't show when protected externally 2021-05-16 00:06:01 +01:00
Phoenix616 ca25dca6b0
Fix writing of config values with backslashes (Fixes #444)
The VALID_PLAYERNAME_REGEXP setting needs to be changed to "^\\w+$" to work properly.
2021-05-15 14:22:55 +01:00
Phoenix616 b09a1255a7
Allow building against latest Paper 2021-05-15 14:13:13 +01:00
Phoenix616 b8899c8333
Turn off internal protection for more protection plugins (Resolves #442)
This expands the TURN_OFF_DEFAULT_PROTECTION_WHEN_PROTECTED_EXTERNALLY
 config option so that it applies to more protection plugins rather than
 just LWC. Currently supported: BlockLocker, Deadbolt, Lockette (1.x),
 LockettePro (2.x) and SimpleChestLock
2021-05-04 16:36:32 +01:00
Phoenix616 07a22fa534
Try to create an account when creating shop for other players 2021-04-18 18:24:22 +01:00
Phoenix616 4172d8d484
Cancel sign change and interact if previous events are cancelled
This fixes that blocks get placed/items used when trying to auto-fill
 a shop sign and the sign is getting destroyed for some reason
2021-04-16 21:03:37 +01:00
Phoenix616 02cd9d4ec0
Work around getItemMeta returning null in some cases (Fixes #432) 2021-04-12 16:08:03 +01:00
Phoenix616 0c51af6b45
Further improve item name shortening for alias system (#425)
This will correctly shorten dashes and ignore the case when comparing
 name starts
2021-03-27 21:07:18 +01:00
Phoenix616 76e3f5b3c8
Add check if item has meta as that could've returned null (Fixes #427) 2021-03-27 18:44:04 +01:00
Phoenix616 66201a9062
Directly use Regex group matching to get playername (Fixes #428) 2021-03-27 18:34:03 +01:00
Phoenix616 ce7cade0a5
Fix potential case issues on item alias part matching 2021-03-26 15:57:38 +01:00
Phoenix616 b93361cd07
Fix shortened item aliases not working (Fixes #425) 2021-03-26 15:55:29 +01:00
Phoenix616 a3c5ec618a
Allow multiline comments and improve comment about Floodgate 2021-03-18 14:41:34 +01:00
Daniel B f68a3203ac
Allow specifying of valid player names (#410)
This allows plugins which allow differently named players to work e.g. Floodgate. (Fixes #383)

Co-authored-by: Phoenix616 <max@themoep.de>
2021-03-17 22:46:05 +01:00
Rutger Kok e6b05ff982 Add support for BlockLocker
It's no longer possible to open shops on chests that were protected by someone else.
2021-03-17 22:11:54 +01:00
Phoenix616 aadfaff397
Update MineDown to 1.7.0 and adventure to 4.7.0 2021-03-17 22:09:13 +01:00
Phoenix616 227e3e77f0
Update double chests too after transactions (Fixes #412) 2021-03-03 18:16:14 +01:00
Phoenix616 7b2fe6c1f3
Rework how accounts are queried from (short form) user names
The AccountQueryEvent will now check both, short names as well as full
 names and the getAccountFromShortName method will no longer check full
 names or offline player names. (Names in a short format wouldn't match
 them anyways)
This should not change the overal behaviour as the plugin uses the
 AccountQueryEvent internally as that is now checking for full name/
 offline player accounts. (And short names are matched first)
2021-02-24 15:18:15 +01:00
Phoenix616 68c9df2078
Re-add ability to quickly create buy shops by only specifying price
The change to the more advanced patterns required it to always specify B
2021-02-24 15:01:10 +01:00
Phoenix616 3ec68dcd50
Just use latest instead of specific version 2021-02-19 13:38:35 +01:00
Phoenix616 ebdebb81df
Implement /shopinfo command/chest click for shop info (Resolves #203)
The shop information can include the owner, available stock, the buy/
 sell prices and detailed item information from the ItemInfoEvent.
This also includes an item component in both /iteminfo and /shopinfo if
 ShowItem is installed.
2021-02-15 17:28:33 +01:00
Phoenix616 2f0e7b81a8
Add separate error message for invalid names 2021-02-05 15:26:39 +01:00
Phoenix616 320d9ad9fb
Fix adapter backwards compatiblity (and getHolder loading 🙈) 2021-01-28 17:33:40 +01:00
Phoenix616 65df4c40c6
Improve performance by using Paper's non-snapshot states if available 2021-01-28 17:18:42 +01:00
Phoenix616 22d3f262f5
Improve NameManager OfflinePlayer and UUID version handling (#383)
This also removes the deprecated methods which might interact with the
 changed methods in unintended ways from the NameManager and the
 PreTransactionEvent.
2021-01-28 15:26:23 +01:00
Phoenix616 ba47b82ba1
Make Admin Shops use containers if available (Resolves #402)
Admin Shops can now be stocked the same way as player shops if a valid
 shop container is nearby. If no container is nearby it will just work
 as an unlimited shop.

This behaviour can be disabled with the FORCE_UNLIMITED_ADMIN_SHOP
 config option so that an admin shop is unlimited even though a valid
 container is next to it.
2021-01-24 23:33:36 +01:00
Phoenix616 9843e92525
Make price line pattern matcher more strict (Fixes #401) 2021-01-14 19:01:04 +01:00
Phoenix616 71e8ef732f
Fix error in short item code generation logic (Fixes #395) 2021-01-06 20:08:38 +01:00
Phoenix616 66eaab7c4d
Include log level (if not INFO) and error stack trace in log file 2021-01-04 22:42:17 +01:00
Phoenix616 0d6f588e45
[CI-SKIP] Improve display of custom funding links and add Ko-fi 2021-01-03 20:53:21 +01:00
Phoenix616 f12e6ed3b6
Use full item name instead of shortened for aliases
This fixes an issue where aliases would not show up in transaction
 messages when the aliases item's string was shortened.

Also remove an unnecessary string length calculation from the
 MaterialUtil getName method if there was no max width set.
2021-01-03 20:47:07 +01:00
Phoenix616 819f028774
Don't use contains check when searching alias without meta (Fixes #394) 2021-01-03 20:13:26 +01:00
Phoenix616 b774258051
Remove OddItem support and implement own alias system (Resolves #360)
Reasoning why this is part of the core and not a separate addon:
The impact of this function is extremely lightweight when (almost) no
 aliases are defined so someone who isn't using this should not have a
 worse experience.
Additionally including it in the core (similarly to the discounts,
 taxes and restrictions) promotes it's availability while not massively
 increasing the maintainability.

As for the OddItem removal:
That plugin hasn't been developed for over 7 years and such unique
 plugin support should really be added via separate addon using the
 events system which, with the new ItemStringQueryEvent, now actually
 supports doing that from an external plugin.
If someone really managed to run their own private fork of OddItem for
 all those years then they should be able to also provide such a simple
 addon.
2021-01-03 01:55:01 +01:00
Phoenix616 9dce41aa30
Work around internal issue with ender chests
At the moment they can't be shops anyways so this shouldn't have any impact.
2020-12-20 14:50:57 +01:00
Max Lee 4c44e49c54
New Crowdin updates (#375)
* New translations lang.en.yml (German)

* New translations lang.en.yml (Hungarian)

* New translations lang.en.yml (Czech)

* New translations lang.en.yml (Romanian)

* New translations lang.en.yml (Norwegian)

* New translations lang.en.yml (Chinese Traditional)

* New translations lang.en.yml (Portuguese, Brazilian)

* New translations lang.en.yml (Vietnamese)

* New translations lang.en.yml (Chinese Simplified)

* New translations lang.en.yml (Ukrainian)

* New translations lang.en.yml (Turkish)

* New translations lang.en.yml (Swedish)

* New translations lang.en.yml (Serbian (Cyrillic))

* New translations lang.en.yml (Russian)

* New translations lang.en.yml (Polish)

* New translations lang.en.yml (Dutch)

* New translations lang.en.yml (French)

* New translations lang.en.yml (Korean)

* New translations lang.en.yml (Japanese)

* New translations lang.en.yml (Italian)

* New translations lang.en.yml (Hebrew)

* New translations lang.en.yml (Finnish)

* New translations lang.en.yml (Greek)

* New translations lang.en.yml (Danish)

* New translations lang.en.yml (Catalan)

* New translations lang.en.yml (Arabic)

* New translations lang.en.yml (Afrikaans)

* New translations lang.en.yml (Spanish)

* New translations lang.en.yml (Spanish, Mexico)

* Include languages_mapping in crowdin config file
2020-12-07 18:13:04 +01:00
Phoenix616 395f0513a6
Improve ignore config setting comments to make it more clear 2020-12-04 15:01:26 +01:00
Phoenix616 4e40823d23
Fix ShowItem incompatibility as well as update adventure 2020-12-04 14:53:27 +01:00
Phoenix616 af32b041ff
Implement missing Inventory#isEmpty method in AdminInventory 2020-12-01 20:05:11 +01:00
Phoenix616 51e9d5f267
Use adventure text api in order to fix hover issues
This also has the side-effect of restoring CraftBukkit compatibility

Also move all libraries into a dedicated Libs package
2020-12-01 19:48:26 +01:00
Phoenix616 8aff3da550
Add ENSURE_CORRECT_PLAYERID to properties metrics chart
Also fix missing new line in metrics chart code.
2020-11-18 15:31:44 +01:00
Phoenix616 fd81bc143e
Ignore signs and transactions made by players with invalid names 2020-11-18 15:10:58 +01:00
Phoenix616 9bf6e1abc1
Check permission for shop name again after querying it
This protects people that use valid player names as admin shop names
 from people with the same username as well as other cases where the API
 is used to manage access to shops with certain special names.
2020-11-17 19:54:08 +01:00
Phoenix616 00fa83c56a
Improve UUID version handling (Fixes #382, #337)
This adds a new config option ENSURE_CORRECT_PLAYERID which lets one
 disable the check whether or not a player's UUID version matches the
 server's online/offline mode UUID version. Due to potential issues with
 offline players which might occur when this isn't checked disabling
 this can't really be supported and any issues should first be tested if
 they still occur with this option enabled.

This also forces the UUID to version 4 if the server runs in online mode
 as it should not be necessary in any normal setup to detect the UUID
 version dynamically in that case. (Dynamic detection is only necessary
 for offline mode which might potentially run behind a proxy)
2020-11-16 14:09:07 +01:00
Phoenix616 cbb605df81
Set default locale to English if it is set to an invalid one in config 2020-11-12 23:16:40 +01:00
Phoenix616 4bd443c69f
Fix NPE in StockCounter for admin shops/missing containers 2020-11-02 01:08:15 +01:00
Phoenix616 7a1c091c74
Fix NPE in StockCounter on autofill/no item found (Fixes #376) 2020-11-02 01:06:48 +01:00
Phoenix616 b43454531e
Add crowdin setup 2020-10-31 21:56:35 +01:00
Phoenix616 9a08e8897b
Don't allow opening of shops via sign by default
This has caused lots of confusion in the past regarding the first time
setup and might be a feature that people don't even want to have.
(e.g. to allow coloring of the signs with dyes)
2020-10-31 20:59:15 +01:00
Phoenix616 f0bf9a08ec
Add message to access and creative mode ignoring 2020-10-31 20:59:07 +01:00
Phoenix616 717dcc8d9d
Add German language file 2020-10-30 23:25:13 +01:00
Daniel Dušek c7b07e04d2
Add - Czech localization file (#362) 2020-10-30 22:52:53 +01:00
Brice Frisco 8d98958c2d
Stock Counter (Resolves #365) (#368) 2020-10-30 22:45:16 +01:00
dependabot[bot] 15b9382b09
Bump junit from 4.12 to 4.13.1 (#367)
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-14 18:15:32 +01:00
Phoenix616 a14aaa7235
Fix issues with LWC Single/Double Chest protections (Fixes #359) 2020-09-24 16:54:36 +01:00
Phoenix616 9dd56385eb
Update lang to 1.3. Fixes legacy local.yml loading issue. 2020-09-24 15:27:39 +01:00
Phoenix616 de14a1affb
Don't parse player name as count in /csgive (Fixes #355) 2020-09-14 14:36:10 +01:00
Corey Shupe 282c39c975
Implement LockettePro support and improve soft dependency handling (#354) 2020-09-10 14:32:34 +01:00
Phoenix616 741a10cb95
Update server economy account warning to include shop creation and uuid 2020-09-08 12:20:19 +01:00
Phoenix616 ea5318dcf1
[CI-SKIP] Improve bug report template 2020-09-02 21:46:31 +01:00
Phoenix616 b02cc9f909
[CI-SKIP] Update version 2020-07-28 00:30:56 +01:00
Phoenix616 5f40273756
[CI-SKIP] Improve issue templates.
This will auto-hide the instructions and format the config/log better :)
2020-07-28 00:29:01 +01:00
Phoenix616 89ba3d0559
Fix default profile activation 2020-07-08 21:43:34 +01:00
Phoenix616 5bc23214e0
Fix issue with items that have a single character in name (Fixes #339) 2020-07-08 21:40:06 +01:00
Phoenix616 6a34054bc7
Add latest test profile 2020-07-08 21:38:54 +01:00
Phoenix616 d1f6b1616b
Fix sign sticker on 1.14+ 2020-07-08 21:01:33 +01:00
Phoenix616 8c1c80e138
Don't log SERVER_ECONOMY_ACCOUNT warning if it's not set 2020-07-04 18:18:59 +01:00
Phoenix616 efe9ff7024
Fix TURN_OFF_SIGN_PROTECTION not working (Fixes #233) 2020-07-03 18:56:29 +01:00
Phoenix616 742f838f09
Add support for component bungee messages
Requires at least BungeeCord commit a64c34d
2020-07-03 18:24:14 +01:00
Phoenix616 98985efd63
Fix legacy %material placeholder not working 2020-07-03 18:13:40 +01:00
Phoenix616 9cbf33f68f
Cancel before sending message so that access is denied in case of error 2020-07-03 17:26:13 +01:00
Phoenix616 493784c2dd
Fix that the legacy locale is not automatically used 2020-07-03 17:15:36 +01:00
Phoenix616 18c3f8f5ef
Prepare next version 2020-07-03 17:15:08 +01:00
Phoenix616 908c128a10
3.11 release 2020-06-29 15:51:06 +01:00
Phoenix616 3c51f8ed87
Move more info to hover in shop messages to clean up chat
Also fix placeholders not working in not enough stock message
2020-06-28 23:00:45 +01:00
Phoenix616 a0630db6f8
Fix typo in iteminfo book generation message key 2020-06-28 18:09:00 +01:00
Phoenix616 6ee7c4759a
Add multi-language and component message support (Requires Spigot)
Message configs are now selected based on the client's language (can be
 toggled in the config) and will use MineDown formatting to allow
 display of component messages as well as usage of RGB colors in 1.16.
If found the legacy local.yml will be used instead of the per-language
 files. Move your local.yml to the correct lang config to if you want to
 use the per-client language option.
Version was also changed to 3.11 due to the many internal changes that
 have accumulated over time
2020-06-28 18:00:09 +01:00
Phoenix616 cbd427f561 Cancel left click event on shop sign
This should improve compatibility with other plugins that handle left
 click interactions too and shouldn't apply to shop signs you don't have
 access to.
Also includes a small improvement to not run the same permission check
 twice in certain cases.
2020-06-24 16:00:16 +01:00
Phoenix616 4a04a623a7 Improve metrics additions 2020-06-21 19:09:25 +01:00
Phoenix616 4885092435 Include license and readme in jar file 2020-06-19 21:47:50 +01:00
Phoenix616 183c724583 Improve Metrics and add /csmetrics command
This adds some more logging to the metrics for some interesting plugin
 settings as well as player account and transaction counts. This data
 about account count and average transaction and item counts is also
 exposed ingame via the /csmetrics command.

This also removes the outdated mcstats metrics as that site is long dead
 now, the last data is from two years ago...
2020-06-19 21:47:28 +01:00
Phoenix616 1f8fd09dd2 Only update player name on join, don't create new accounts 2020-06-19 20:57:22 +01:00
Phoenix616 0b39fd1a5e Make auto-fill check both buy and sell permission separately (#325)
Also slightly changes how the auto fill message is sent to not send two
 messages (autofill and no-permission)
2020-06-19 18:58:00 +01:00
Phoenix616 7161c0bfdf Add Jenkins test build notifier
This will only send a notification into the log once for each new build
 and can be disabled in the config or the new jenkinsBuildNotifier.yml
2020-06-18 21:19:35 +01:00
Phoenix616 d3f717deef Do not enabled automatic updates by default without user interaction 2020-06-18 18:14:53 +01:00
Phoenix616 3e061dda8d Add config option to limit trading with shops via protections 2020-06-18 17:01:52 +01:00
Phoenix616 e520544d3a Add ability to reload discount and restriction module (Resolves #252) 2020-06-10 14:03:22 +01:00
Phoenix616 4acd3efda1 Allow metadata ID to be used in shop transaction permission 2020-06-01 23:05:59 +01:00
Phoenix616 080597c515 Allow metadata ID to be used in shop creation permission 2020-06-01 22:58:34 +01:00
Phoenix616 9f84ca8551 Add option to sell/buy all with shift (#320) 2020-05-31 14:50:53 +01:00
Max Lee f0641abf2e Automatically add enhancement label to issues 2020-05-30 13:51:37 +01:00
Phoenix616 9dfbca3f81 Add more links to issue template config 2020-05-27 21:38:28 +01:00
Phoenix616 24d89dbc44 Add ability to mark all economic events as handled
This resolves issues when multiple listeners try to handle the same
 events e.g. for group support which would've passed it onto the normal
 listeners leading to errors
2020-05-21 01:08:00 +01:00
Phoenix616 d868fe8af6 Put the correct sign type into the chest on empty remove (Fixes #313) 2020-05-20 00:43:20 +01:00
Phoenix616 e22a50e1cf Only close our own inventories on teleport (Fixes #312) 2020-05-16 22:33:52 +01:00
Phoenix616 7f32a7a722 Fix InventoryOpenEvent listener not applying to non chest containers 2020-05-16 22:30:14 +01:00
Phoenix616 009febb5c2 Call add and subtract event to give listeners a chance to handle them 2020-05-13 23:56:47 +01:00
Phoenix616 7b978ae022 Fix shop sign getting broken with autofill marker (Fixes #310) 2020-05-13 23:13:56 +01:00
Phoenix616 f044938b80 0 is a legal price now, reflect that in the tests 2020-05-06 22:00:15 +01:00
Phoenix616 a427c8c5a0 Fix shop removal with partial transactions (Fixes #307) 2020-05-06 20:53:59 +01:00
Phoenix616 7fbf50cefe Allow 0 for a free shop (Resolves #297) 2020-05-06 20:38:41 +01:00
Phoenix616 8e6bc01d97 Fix automatic setting of item lines (Fixes #308) 2020-05-06 20:17:50 +01:00
Phoenix616 3bf54529db Add support for LWC LimitsV2 module (#293)
This adds a new OTHER_BREAK PreShopCreationEvent outcome to indicate
that the sign should get broken. To keep it backwards compatible with
other plugins setCancelled still sets OTHER and will not lead to a sign
break. The breaking logic is also moved out of the ErrorMessageSender
as it didn't make sense to have there. (It also wasted a tiny bit of CPU
by getting the block of the state again)

Do do this it now fully depend on LWCX, it's the only active version anways.

Also fixed a possible out of index error with the sign lines.
2020-05-04 22:33:46 +01:00
Phoenix616 bb4eece790 Fix pre-events not being uncancellable 2020-05-04 22:16:09 +01:00
Phoenix616 0c1b82c9d8 Fix /iteminfo coloring 2020-05-04 21:40:39 +01:00
Phoenix616 432c505806 Add more information to /iteminfo command and make messages configurable 2020-05-03 15:05:24 +01:00
Phoenix616 0d08880ed2 Some cleanup of code and deprecated stuff
Also deprecate amount and account setters on the CurrencyCheckEvent,
these values will be final in a future version. Changing them here does
not make sense in the internal structure.
2020-05-03 02:37:06 +01:00
Phoenix616 9ae6725c5d Listen on low so that other plugins could hook into before
This is necessary if you want to do something like automatic completion
of the sign for group/bank accounts.
2020-05-03 02:34:57 +01:00
Phoenix616 4673a3f00c Use AccountQueryEvent in NameManager#canUseName utility method 2020-05-02 19:25:44 +01:00
Phoenix616 8c83be59ba Option to block shop creation when LWC limit is reached (Resolves #293) 2020-05-02 15:57:42 +01:00
Phoenix616 3bed1261aa Move account access check to an event
This should be the final addition that allows other plugins to implement
multi-owner/group shops like requested in #119 and #133 by listening to
all the transaction events to handle money flow and shop/account access
events to enable access to the actual shop.
2020-05-02 15:32:48 +01:00
Phoenix616 c6f6672188 Fix uuid version check 2020-04-27 21:39:46 +01:00
Phoenix616 744b02a447 Add ability to configure server economy account UUID
This adds better support for fake server economy accounts by not
requiring that a player with the name already has an account but allow
automatic creation of the account with the fake UUID provided in the
config.

Old configs that only define the name will still work though.
2020-04-27 21:00:46 +01:00
Phoenix616 e57bf94923 Don't continue checking if account can hold currency if already done 2020-04-24 14:54:48 +01:00
Phoenix616 781e017ae9 Add debug logging for configuration loading 2020-04-17 15:10:27 +01:00
Phoenix616 2426aef969 Add space at start of comments when writing config 2020-04-17 15:10:09 +01:00
Phoenix616 e8034e20b0 Change hard limit to one million 2020-04-17 14:51:37 +01:00
Phoenix616 bb17cc35a0 Add ability to set max amount and improve error messages 2020-03-24 00:31:08 +01:00
Phoenix616 059abc569c 3.10.2 snapshot development 2020-03-24 00:30:46 +01:00
Josh Roy 809546ecab
Update bStats to 1.7 (#295)
Also updates bStats maven repo to new one
2020-03-08 18:26:52 +01:00
Phoenix616 86ea093994 3.10.1 release 2020-02-22 17:43:47 +01:00
Phoenix616 7bd00f23be Block change block event for shop containers/signs 2020-02-22 17:43:17 +01:00
Phoenix616 9d6c2f8c1b Improve LWC shop protection messages 2020-02-15 18:20:55 +01:00
Phoenix616 3fe35cd0a5 Clean cache after protection registration and fire post event 2020-02-15 18:10:18 +01:00
Phoenix616 23ceaff2ed Remove old LWC artifact in local repo 2020-02-15 18:05:40 +01:00
Phoenix616 2a37304113 Specify locale when changing case (Fixes #291) 2020-01-29 16:35:04 +01:00
Phoenix616 d4bb775086 Check WorldGuard's chest protection too 2020-01-17 17:41:22 +01:00
Max Lee 584a2bd51c
Create FUNDING.yml 2020-01-16 00:20:55 +01:00
Phoenix616 d0919e78d7 Fix BuildPermissionEvent logic
This makes it possible for other plugins to force allow placing of shops
and avoid potential issues in the check if multiple plugins are changing
the allow settings.

ALso while the allow counters were a nice idea they might not have even
worked correctly because in the case of only one plugin disallowing it
they would've still returned true at the end. Only if one listener
allowed it and another disallowed it the end result would be disallow.

This also makes the BuildPermissionEvent implement Cancellable (which is
an inverted allowed) in order to easily ignore events that are already
disallowed by another listener.
2020-01-16 00:04:28 +01:00
Phoenix616 bd8f2dfc19 Add RedProtect support 2020-01-15 23:44:34 +01:00
Phoenix616 3d83bfdddd Add GriefPrevention support (Resolves #227)
Also slightly cleanup dependency logic loading to not have per-plugin
code in Dependencies class
2020-01-15 23:40:42 +01:00
Phoenix616 37b7669acb Fix players being allowed to build outside regions (Fixes #278)
Basically players were allowed to build shops outside regions in the
case where no region manager was available.
2020-01-15 23:13:18 +01:00
Phoenix616 4067427650 Add ability to define type of LWC protection in config 2020-01-15 22:55:40 +01:00
Phoenix616 d5198bd155 [CI-SKIP] Prepare next version 2019-12-09 22:59:41 +01:00
Phoenix616 e15633a4a8 3.10 release 2019-12-09 22:54:29 +01:00
Phoenix616 2075693e12 Add ability to send shop owner notifications via BungeeCord 2019-11-19 21:50:17 +01:00
Phoenix616 38f93c1d1d Fix default maven profile activation 2019-11-04 17:42:33 +01:00
Phoenix616 40467e3522 Add support for Paper's BlockDestroyEvent for sign breaks (Fixes #214)
Instead of using different modules we use a bukkit profile to test backwards compatibility with pure-Bukkit servers and use the paper-api in the default profile. This should really be done with modules in the future. The actual selection which listener to use is handled by checking if the event class exists on event registration.
2019-11-04 17:26:43 +01:00
Phoenix616 35dd13f917 Slightly change access_denied message to indicate container access error
This is necessary as the no_permission error was worded exactly the same making debugging more difficulty. Also this way someone who thinks he needs to access the chest doesn't think that he doesn't have access to the shop at all.
2019-11-04 17:26:22 +01:00
g--o 88a23a38db Added toggle access command (#271)
Thanks to @g--o and @JRoy
2019-09-16 12:56:30 +01:00
Phoenix616 c028015d5e Improve /cstoggle by using UUIDs and HashSet instead of ArrayList 2019-09-14 23:49:40 +01:00
Phoenix616 d51a512ab8 Fix Vault CurrencyAmountEvent listener returning wrong balance 2019-09-06 13:31:19 +01:00
Phoenix616 bc680afcaf Slightly rework item comparison to fix legacy spawn eggs (Fixes #264) 2019-09-06 13:23:49 +01:00
Phoenix616 58cf96dca8 Fix issues with double chests not begin detected as shops in some cases 2019-08-31 11:20:02 +01:00
Phoenix616 b718fcc429 Use shop owner for LWC protections and not the creator
Also fix a bug where it was possible for shops to be created with shop owners that did not have a valid Account information when other plugins manipulated the name line after the NameChecker checked it. Now it gets checked twice, once at the start (to abort early and to populate the ownerAccount field) and once at the end to check again when the name line changed.
2019-08-22 14:03:27 +01:00
Phoenix616 f90d23cfc3 Update LWC protection and support more LWC forks (LWCX and Modern-LWC) 2019-08-22 13:09:54 +01:00
Phoenix616 285b6b9089 Fix othername permissions 2019-08-20 22:41:17 +01:00
Phoenix616 5eb467a1d3 Set UUID version if players are online when manager loads (Fixes #265) 2019-08-20 13:15:41 +01:00
Phoenix616 930b2cc07b Use prepared queries to avoid special character issues (Fixes #258) 2019-07-29 18:50:26 +01:00
Phoenix616 e972a7d657 Add further workarounds for comparing meta data (Fixes #250)
This is especially obvious for books where all the text will have color codes after dumping and loading them from the database. In order to be able to still compare to them we will load the item the item and then dump it again if it doesn't match the original one.
2019-07-08 18:05:50 +01:00
Phoenix616 6c5e5ece81 Block editing of shop signs that user can't destroy (BUKKIT-1293) 2019-07-08 17:21:21 +01:00
Phoenix616 481e2bdf9e Fix issue with sign sticker 2019-06-09 15:04:14 +01:00
Phoenix616 b0523b2199 Add cooldown for empty/full shop owner notifications 2019-05-27 17:11:09 +01:00
Max Lee cef571223e
Create SECURITY.md 2019-05-26 17:16:10 +01:00
Phoenix616 26daa4531d Always register WorldGuard shop flag (Fixes #230)
The configuration isn't loaded yet when the plugin loaded and the flag initialised so we can't check that there. The config option is checked at the point where the flag is handled though so there is no difference from the intended functionality when registering the flag anyways. The only downside is that this could be a tiny bit confusing to the user in the end but I think that is a small price to pay when it wouldn't be working at all otherwise.
2019-05-13 00:16:57 +01:00
Phoenix616 17e7ab781b Fix shops creation message getting send on auto fill
The NameChecker now catches any exception that might be thrown when querying the account in order to properly cancel in the case of an unknown account or an error that occurred in the event to not let players create shops that they don't have access to if an exception occurs.
2019-05-08 00:22:16 +01:00
Phoenix616 2907706399 Actually send error message and break sign on unknown player error 2019-05-08 00:11:47 +01:00
Phoenix616 a8c57bc0ab Set name line empty if name isn't usable and always use account name 2019-05-07 23:57:56 +01:00
Phoenix616 ca94031ddf Fix wrong UUID version comparison logic 2019-05-07 23:48:04 +01:00
Phoenix616 a95f7316d6 Fix ProtectionCheckEvent not being used when accessing shop 2019-05-07 16:13:56 +01:00
Phoenix616 b300798a62 Make querying of accounts by (short) name an event too 2019-05-06 23:49:16 +01:00
Phoenix616 dcd1dd9057 Add different issue templates 2019-05-06 20:08:12 +01:00
Phoenix616 619ac17cd5 Also use registry to get the allow-shop flag 2019-05-06 18:19:33 +01:00
Phoenix616 2e0c5a3cac Fix issues with user account storage on shop creation
This fixes a bug where shop owner accounts would not be created in certain cases e.g. when the owner had certain admin permissions or created the account for another player while having certain admin permissions.
2019-05-05 19:23:04 +01:00
Phoenix616 4769ae2cf7 Allow loading of different numbers to BigDecimal (Fixes #225)
Also catch wrong types when loading the config better without completely disabling the plugin and revert "Make sure special parsers are setup" and register the parsers static again.
2019-05-03 21:08:56 +01:00
Phoenix616 da1dd94705 Make sure special parsers are setup (Might prevent #225)
There might have been a case where when trying to pair the yml file with the Properties class the parsers weren't actually registered yet as nothing was called on the Properties file yet.
2019-05-02 18:35:45 +01:00
Phoenix616 816fe77f27 Use registry to register WorldGuard flags 2019-05-01 17:21:46 +01:00
Phoenix616 04f91aa55b Add warnings about unknown OfflinePlayer to VaultListener
Also improve the ReserveListener by removing the query for the OfflinePlayer object as that's not necessary to use the Reserve economy API as it directly provides methods for UUID usage (and account existence has usually been checked before already)
Also use canAddHoldings to check if economy user can hold money instead of adding and removing.
2019-05-01 17:13:47 +01:00
Phoenix616 0b57382cd0 Don't use sign Material as that's going to change in 1.14 2019-05-01 17:01:43 +01:00
Phoenix616 c538f4a3dc Remove all usage of old MaterialData and other outdated code
This should make it more future-proof and possibly even work with 1.14 already.
2019-04-30 21:31:24 +01:00
Phoenix616 6773f89221 Improve internal precision of calculations (Fixes #221) 2019-04-30 16:02:01 +01:00
Phoenix616 8cf9934a32 Properly set scale and rounding mode for divisions (Fixes #220) 2019-04-28 15:41:20 +01:00
Phoenix616 f0bc277566 Move to BigDecimal in all events and use the CurrencyTransferEvent
Also add tax bypass permissions (#204, ChestShop.notax.buy/sell). When buying with that permission the buyer does not have to pay the tax (the seller will still get the reduced amount), when selling the seller will get the full amount instead of one lowered by the tax.
The permissions of the shop owner play no role due to there not being a good way of checking offline player permissions and it being a bit non-obvious for the buying/selling player if a shop would be with or without tax. This way all that gets changed is the amount the player pays/receives and not the shop owner (and the player should know their permissions/rank already)

The transfer event was necessary in order to correctly resolve who triggered the transaction. Economy adapters are now required to implement a listener for each of the events. It's also recommended that economy adapters that support transfers directly use that instead of passing it through subtract and add with the processTransfer method.
2019-04-25 18:44:22 +01:00
Phoenix616 6e78fea880 Update the container on item transfers (Fixes #217)
This was removed when the item transfer code changed which stopped comparators from working. This shouldn't be a true block update that observers can detect but only a notification for surrounding blocks that the content might have changed.
2019-04-23 18:00:21 +01:00
Phoenix616 eb7abda01f Add missing item permissions and remove duplicate ones (Fixes #215) 2019-04-08 15:25:56 +01:00
Phoenix616 28fb5cbeff Fix message amount display error when buying from admin shops
Also fix that the stock items were modified when buying from the admin shop
2019-04-07 13:48:16 +01:00
Phoenix616 53ae07b779 Fix error that prevented selling to admin shops 2019-04-07 13:47:18 +01:00
Phoenix616 8a09e91e62 Better catch for potential exceptions when de-serializing items
For some reason items might corrupt in the encoding and decoding process leading to invalid YAML strings. There's also another issue of the server not being able to load certain old itemstacks. (seems to be especially an issue with old entity types)
2019-04-06 18:39:49 +01:00
Phoenix616 52492f8469 Support registering of economy providers after the plugin loaded 2019-04-06 16:24:20 +01:00
Phoenix616 5a99180aab Fix Admin Shops by adding more implementation to AdminInventory
This is required as we now move items between inventories instead of individually removing and adding. This basically adds the stock represented by the admin shop sign to the AdminInventory so all transactions should always be possible.
2019-04-05 15:17:15 +01:00
Phoenix616 1952e5cb46 Directly transfer items (Fixes #169) 2019-04-05 14:49:27 +01:00
Phoenix616 c1509dc03a Let's not kid ourselves, this update is too big to be a patch version
This also uses the Bukkit API again instead of spigot (it doesn't use anything from spigot) and also makes sure to exclude any other Bukkit version from the dependency tree
2019-04-02 19:23:52 +01:00
Phoenix616 2007c4a7fb Fix ShopDestroyEvent not getting called in certain cases
This could be triggered by updating a column of physics blocks with a shop attached
2019-04-01 13:55:21 +01:00
Phoenix616 be5f47cb1f Add config option to toggle if all shop removals should be logged
Removals that happened due to no player being present will always be logged
2019-04-01 13:49:48 +01:00
Phoenix616 03589b3fef Catch Vault issues with null usernames (#158, #194, #205, #209) 2019-03-30 16:09:48 +01:00
Phoenix616 b37bf12e7f Convert old item metadata to newest version (Fixes #202) 2019-03-30 15:40:31 +01:00
Phoenix616 8cd89bf8ee Work around issue with serialising explorer maps
This is due to a bug in CraftBukkit not properly supporting localised/translatable display names when serialising item meta.
This also adds/improves the message when no valid item ID could be generated.
2019-03-19 19:08:53 +01:00
Phoenix616 355fa58ac4 Properly detect if reserve economy is provided (Fixes #210) 2019-03-18 15:10:18 +01:00
Phoenix616 b931393a62 Fix more issues with the shop access permissions (#207) 2019-03-08 22:52:08 +01:00
Phoenix616 de5b505ec5 Fix issues with shop sign opening and access permissions (Fixes #207) 2019-03-07 17:33:47 +01:00
Phoenix616 6d99210dd7 Really fix price restriction 2019-01-19 00:40:50 +01:00
Phoenix616 20228dd645 Fix amount in price restriction not working correctly
Also fix copy and paste error in SELL_PRICE_BELOW_MIN error message.
2019-01-18 22:27:40 +01:00
Phoenix616 91a372bd05 Make Item and Material parsing events 2019-01-17 19:50:47 +01:00
Phoenix616 c8d0590614 Add different error messages when price limits are exceeded (#193) 2019-01-17 18:08:54 +01:00
Phoenix616 6fe6f80538 Use Bukkit's inbuilt command permissions system (Fixes #186)
Also fix plugin not disabling when dependencies aren't loaded correctly
2018-11-25 00:59:13 +01:00
Phoenix616 bc14736a37 Ignore cancelled sign change events 2018-11-21 00:45:39 +01:00
Phoenix616 b853afbd52 Fix issues with the permissions for using different player names
This adds separate permissions for accessing, creating and destroying of shops.
It also adds a config entry to allow using of a shop even if someone have access to it due to their permissions.
2018-11-19 21:10:48 +01:00
Phoenix616 b17a471c21 Check if dependency is enabled (Fixes #181) 2018-11-12 15:03:13 +01:00
Phoenix616 727f49fc28 Update to new Vector API in WorldEdit/WorldGuard 7 2018-11-06 15:35:28 +01:00
Phoenix616 7adf02e532 Improve hopper protection
The InventoryMoveItemEvent is now only used for Hopper Minecarts, other blocks that could move items (Hoppers and Droppers) are checked on place. That way players that have access to a shop can just use the blocks normally but other players can't place them.
2018-10-23 19:27:39 +01:00
Phoenix616 fe85dafec5 Allow configuring what containers can be used for shops (Fixes #175) 2018-10-23 19:14:21 +01:00
Phoenix616 b5f77d67b5 Fix color of ShowItem insert bleeding into message 2018-10-23 19:02:09 +01:00
Phoenix616 470f9d8d99 Allow admins to create shops for other users (Fixes #176) 2018-10-23 16:06:27 +01:00
Phoenix616 2243c2fbc6 Update to new AuthMeReloaded API (Fixes #174) 2018-10-15 20:27:38 +01:00
Phoenix616 4ef24c3828 Update to official WG/WE repository 2018-10-15 20:22:42 +01:00
Phoenix616 26d800132c Merge branch '1.12' into 1.13 2018-09-26 17:02:21 +01:00
Phoenix616 b30688b635 Fix issues with admin shops, ops and creative mode (Fixes #168) 2018-09-26 17:02:13 +01:00
Phoenix616 39a522716f Merge branch '1.12' into 1.13 2018-09-18 00:09:07 +01:00
Phoenix616 96b5a675d6 Allow admins to use the admin shop in all cases
This fixes issues with the auto complete right click functionality not working with admin shops
2018-09-18 00:08:54 +01:00
Phoenix616 0c6aaaa6f7 Fix damage not getting set properly 2018-09-10 01:11:42 +01:00
Phoenix616 adf5340932 Remove spaces from shortened strings for better readability
This is necessary due to the extended length of some material names in 1.13 as they directly include the variation into the material where previously only the data value was used. (E.g. BLACK_STAINED_GLASS_PANE) By removing the spaces and using uppercase characters to separate parts the string stays both readable to the user and parseable by the plugin. It should also be backwards compatible with the old format. (At least with 1.13 material names)
2018-09-10 00:33:27 +01:00
Phoenix616 e1998bcd7c Merge branch '1.12' into 1.13
# Conflicts:
#	src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java
2018-09-10 00:31:26 +01:00
Phoenix616 12d2107ce1 Fix passing Material enum strings directly to getShortenedName 2018-09-10 00:25:25 +01:00
Phoenix616 173991ab4a Use spaces and proper casing for calculating width (Fixes #166)
This also slightly changes which name part gets shortened first. It will first shorten the longest name and then the rest.
2018-09-09 22:43:49 +01:00
Phoenix616 54b3b6966c Merge branch '1.12' into 1.13
# Conflicts:
#	src/main/java/com/Acrobot/Breeze/Utils/MaterialUtil.java
#	src/test/java/com/Acrobot/Breeze/Tests/MaterialTest.java
2018-09-09 14:47:32 +01:00
Phoenix616 516eefc5b6 Allow longer item names on signs by calculating the width
This uses hardcoded widths for the main characters used in Minecraft and might not be precise for some characters but better than the previous 15 char hard limit.
2018-09-09 14:37:09 +01:00
Phoenix616 2459a406fe Auto-correct item code on sign.
This also makes it possible to input IDs on the sign that are longer than the max sign letters that the plugin uses as that just gets auto corrected back down to the correct amount.
2018-09-09 13:26:08 +01:00
Phoenix616 afab90d021 Fix wording in of incorrect item id message 2018-09-09 13:24:26 +01:00
Phoenix616 f1ce97dbce Merge branch '1.12' into 1.13 2018-09-05 23:07:49 +01:00
Phoenix616 df76347697 Improve sign price corrections
This includes a new price_precision config entry to set the amount of decimal places to allow on a shop sign (set to 2 by default). Thanks to @andrewkm for this idea.
It also fixes an issue where a zero inside the decimal places was removed instead of it's end and also removes the point and zeros from prices that only have trailing zeros to avoid confusion (e.g. in languages that use the decimal point for thousands)
2018-09-05 23:07:35 +01:00
Phoenix616 01ebedcc84 Merge branch '1.12' into 1.13 2018-08-31 13:55:54 +01:00
Phoenix616 06e4e1bf17 Don't search offline players when checking for existing (Fixes #162) 2018-08-31 13:55:36 +01:00
Phoenix616 5f0bbfff0c Merge branch '1.12' into 1.13 2018-08-29 21:29:52 +01:00
Phoenix616 dd4177e7cb Query server's player cache for missing players
This uses a cache for players that we haven't found before so to not check the cache too often and also checks if the online mode of the queried OfflinePlayer matches the server's mode (gotten from the first logged-in player in order to be compatible with BungeeCord requiring the server to be in offline-mode.
2018-08-29 21:29:27 +01:00
Phoenix616 640364e7e7 Actually return the block that the sign is attached to 2018-08-29 21:02:06 +01:00
Brokkonaut f5b3aa6b75 1.13 allows single chests besides each other. Handle that correctly. 2018-08-29 20:57:48 +01:00
Brokkonaut b972e79132 Update to WorldGuard 7.0.0 (for 1.13) 2018-08-29 20:32:27 +01:00
Phoenix616 cf9e5bafde Merge branch '1.12' into 1.13 2018-08-23 22:12:58 +01:00
Phoenix616 836ef56864 Add more message placeholders (Fixes #159) 2018-08-23 22:10:24 +01:00
Phoenix616 e155b98c96 Merge branch '1.12' into 1.13 2018-08-22 00:29:12 +01:00
Phoenix616 fc62ab0823 Use different economy responses and make some events cancellable
Also moved the creation fee subtraction to before the shop was created to be able to cancel the creation
2018-08-22 00:21:34 +01:00
Phoenix616 3ae71cf13a Indicate server version in project version 2018-08-21 23:51:19 +01:00
Phoenix616 89dcde83de Differentiate between wall and standing signs (Fixes #147) 2018-07-29 15:05:45 +01:00
Phoenix616 c38af9d5b3 Fix plugin.yml (Fixes #146) 2018-07-28 19:41:02 +01:00
Phoenix616 d4e39a3751 Make it compile with 1.13. No guarantees that it works without issues! 2018-07-26 00:13:06 +01:00
Phoenix616 5680b22345 Prepare next version 2018-07-25 22:54:51 +01:00
Phoenix616 6c15890c7e Update version for 3.9.2 release 2018-07-15 21:45:18 +01:00
Phoenix616 d24cbe515c Remove mcstats maven repo as it is offline
Don't give up on the site yet and add it to the local repo though
2018-07-11 18:17:05 +01:00
Daniel V 8ae72e5803 Feature: Reserve Support (#143)
This introduces a new AccountCheckEvent to check if a user actually has an account with the used economy plugin. Also fix CurrencyTransferEvent logic (even though it's not used anywhere currently)
2018-07-11 18:09:07 +01:00
Phoenix616 7311907559 Add more per material shop creation permissions 2018-06-24 00:17:35 +01:00
Phoenix616 1cb481097c Don't refund for non-finished signs (Fixes #141) 2018-06-23 15:01:41 +01:00
Phoenix616 247b2d239e Fix issues with alternative block materials forms
This also removes the number in the name from /iteminfo and the autocompletion
2018-06-18 20:04:17 +01:00
Phoenix616 5b925171e3 Add support for stored enchantments to /iteminfo and format it better
Partly fixes #135
2018-06-03 19:23:40 +01:00
Phoenix616 c39720b449 Don't try to get the MaterialData from every damage (Fixes #131)
We will have to add a new way of handling that for 1.13 anyways when data isn't using damage values anymore.
2018-05-15 23:58:34 +01:00
Phoenix616 036d309965 [CI-SKIP] Fix some formatting inconsistencies
Remove spaces on empty lines and changed doc comment formatting
Also includes some minor comments and import fixes
2018-05-07 15:32:51 +01:00
Phoenix616 b3e62f199f Add option to set the worlds where shops should be removed (Fixes #129)
This also adds the ability to set string lists in the config.
2018-05-05 16:23:30 +01:00
Phoenix616 34b6ab2cf2 Update spigot-api and ormlite-jdbc
This might fix the Xerial driver warning from #9/BUKKIT-1073
2018-04-18 14:58:15 +01:00
Phoenix616 88bffc6fde Move comments in above config fields.
This should help avoid confusions as it matches the class' annotation layout and almost every other plugin does it like this.
2018-04-02 18:20:52 +01:00
Phoenix616 ae8d1c79e9 Properly check if item is empty (Fixes #123) 2018-04-02 17:41:45 +01:00
Phoenix616 e40865ac26 Prepare next version 2018-03-22 18:59:12 +01:00
213 changed files with 61240 additions and 2957 deletions

8
.crowdin.yml Normal file
View File

@ -0,0 +1,8 @@
files:
- source: /src/main/resources/languages/lang.en.yml
translation: /src/main/resources/languages/lang.%two_letters_code%.yml
languages_mapping:
two_letters_code:
zh-TW: zh_TW
pt-BR: pt_BR
es-MX: es_MX

6
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,6 @@
# These are supported funding model platforms
github: Phoenix616
custom: ['https://acrobot-paypal.phoenix616.dev', 'https://tip.phoenix616.dev']
issuehunt: ChestShop-authors/ChestShop-3
ko_fi: Phoenix616

View File

@ -1,25 +0,0 @@
[Please search for tickets with the same issues as yours first before reporting!]
#### Plugin Version
[The full plugin version that you are running from /version ChestShop]
 
#### Plugin Config
[The full config.yml file of ChestShop]
 
#### Server Version
[The full server version that you are running from /version]
 
#### Server Log
[The full latest.log file]
#### What other plugins are you running?
[List of your plugins, ideally with the version] 
#### What is happening?
[Explain what happens and what steps should be done to reproduce the issue. Ideally with pictures and the full error log!] 
#### What did you expect to happen?
[Explain what you expected to happen after performing the previously described steps]
Please provide any additional info below
[And delete the lines in square brackets]

89
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@ -0,0 +1,89 @@
name: Bug report
description: Report an error that happens in the plugin.
labels: []
body:
- type: markdown
attributes:
value: >
Please check if there are already similar issues open, also please test if your issue
isn't already fixed by trying the latest development build: https://ci.minebench.de/job/ChestShop-3/
- type: textarea
attributes:
label: What is happening?
description: Explain what happens and what steps should be done to reproduce the issue. Ideally with pictures and the full error log!
validations:
required: true
- type: textarea
attributes:
label: What did you expect to happen?
description: Explain what you expected to happen after performing the previously described steps
validations:
required: true
- type: input
attributes:
label: Plugin Version
description: The full plugin version that you are running from `/version ChestShop`
validations:
required: true
- type: textarea
attributes:
label: Plugin Config
description: The full `config.yml` file of ChestShop.
value: |
<details>
<summary>Config</summary>
```yaml
[Put the config here]
```
</details>
validations:
required: true
- type: input
attributes:
label: Server Version
description: The full server version that you are running from `/version`.
validations:
required: true
- type: textarea
attributes:
label: Server Log
description: The full `latest.log` file, especially important if you have a stack trace
value: |
<details>
<summary>Log</summary>
```
[Your log here]
```
</details>
validations:
required: true
- type: textarea
attributes:
label: What other plugins are you running?
description: List of your plugins, ideally with the version
validations:
required: true
- type: checkboxes
attributes:
label: Agreements
description: Please agree to the following.
options:
- label: I have checked if there are already similar issues open.
- label: I am running the latest development version available from https://ci.minebench.de/job/ChestShop-3/.
- label: The version [has support](https://github.com/ChestShop-authors/ChestShop-3/security/policy#supported-versions) for this type of issue.
- label: I have provided all requested information and agree to the terms of the [project's license](https://github.com/ChestShop-authors/ChestShop-3/blob/master/LICENSE)
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: "#chestshop IRC channel on irc.spi.gt"
url: "https://kiwiirc.com/client/irc.spi.gt/#chestshop"
about: Live chat with the devs and other members of the community
- name: SpigotMC discussion thread
url: https://www.spigotmc.org/threads/chestshop.295890/
about: Ask and answer questions or discuss the plugin in general

43
.github/ISSUE_TEMPLATE/enhancement.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: Enhancement
description: Request a feature or suggest an idea for this project
labels: [enhancement]
body:
- type: markdown
attributes:
value: >
Please check if there are already similar issues open, also please test if your request
isn't already implemented by trying the latest development build: https://ci.minebench.de/job/ChestShop-3/
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when...
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
- type: checkboxes
attributes:
label: Agreements
description: Please agree to the following.
options:
- label: I have checked if there are already similar issues open.
required: true
- label: I have checked that the latest build doesn't already implement the feature.
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

21
.github/workflows/code_quality.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
jobs:
qodana:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2022.3.4
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }}
with:
args: --baseline,.github/workflows/qodana/qodana.sarif.json

50233
.github/workflows/qodana/qodana.sarif.json vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -34,4 +34,7 @@ Links
* [Forum Thread](http://forums.bukkit.org/threads/4150/)
* [BukkitDev site](http://dev.bukkit.org/projects/chestshop/)
* [SpigotMC site](https://www.spigotmc.org/resources/chestshop.51856/)
* [Bug Tracker](http://dev.bukkit.org/server-mods/chestshop/tickets/?status=+)
* [Bounties via IssueHunt](https://issuehunt.io/r/ChestShop-authors/ChestShop-3?tab=idle)
* [Localization](https://crowdin.com/project/chestshop-3)
* [Qodana code quality](https://qodana.cloud/projects/zxDG5/)
* [Old bug Tracker](http://dev.bukkit.org/server-mods/chestshop/tickets/?status=+) (please use GitHub issues!)

27
SECURITY.md Normal file
View File

@ -0,0 +1,27 @@
# Security Policy
## Supported Versions
Overview over versions of ChestShop that are currently being actively developed
and which ones are supported with security updates if necessary.
| Version | Active development | Security Fixes |
| ------------- | ------------------ | ------------------ |
| latest | :heavy_check_mark: | :heavy_check_mark: |
| < latest | :x: | :x: |
| 3.9.3-1.12 | :x: | :heavy_check_mark: |
| 3.9.2-1.8.8 | :x: | :heavy_check_mark: |
Updates for the different versions can be found on the [build server](https://ci.minebench.de/job/ChestShop-3/),
only the latest version will get releases on the main project pages.
## Reporting a Vulnerability
If you want to report an issue which might pose a security risk then
you can do this in private at the following locations:
- [Start a conversation](https://www.spigotmc.org/conversations/add?to=Phoenix616&title=ChestShop%20Vulnerability) with Phoenix616 on SpigotMC.org
- Message phoenix616 on the [SpigotMC IRC network](https://irc.spi.gt/) (irc.spi.gt)
- Message phoenix616 on the [Esper.net IRC network](https://esper.net)
For issues that do not have security implications please [open an issue](https://github.com/ChestShop-authors/ChestShop-3/issues/new/choose).

967
pom.xml
View File

@ -1,373 +1,594 @@
<?xml version="1.0" encoding="UTF-8"?>
<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.acrobot.chestshop</groupId>
<artifactId>chestshop</artifactId>
<version>3.9.1</version>
<description>Chest-and-sign shop plugin for Bukkit</description>
<name>ChestShop</name>
<scm>
<connection>scm:git:git://github.com/ChestShop-authors/ChestShop-3</connection>
<developerConnection>scm:git:ssh://git@github.com/ChestShop-authors/ChestShop-3.git</developerConnection>
<url>https://github.com/ChestShop-authors/ChestShop-3</url>
</scm>
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public</url>
</repository>
<repository>
<id>sk89q-repo</id>
<url>http://maven.sk89q.com/artifactory/repo/</url>
</repository>
<repository>
<id>mcstats-releases</id>
<url>http://repo.mcstats.org/content/repositories/releases/</url>
</repository>
<repository>
<id>bstats-repo</id>
<url>http://repo.bstats.org/content/repositories/releases/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>http://nexus.hc.to/content/repositories/pub_releases/</url>
</repository>
<repository>
<id>authme-repo</id>
<url>http://ci.xephi.fr/plugin/repository/everything/</url>
</repository>
<repository>
<id>minebench-repo</id>
<url>https://repo.minebench.de/</url>
</repository>
<repository>
<id>local_repo</id>
<url>file://${project.basedir}/repo/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.j256.ormlite</groupId>
<artifactId>ormlite-jdbc</artifactId>
<version>4.48</version>
<scope>compile</scope>
</dependency>
<!-- Should be kept in sync with Mojang -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.12-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mcstats.bukkit</groupId>
<artifactId>metrics</artifactId>
<version>R8-SNAPSHOT</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit-lite</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>Vault</artifactId>
<version>1.6.6</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>info.somethingodd</groupId>
<artifactId>odditem</artifactId>
<version>0.9.5</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.herocraftonline.heroes</groupId>
<artifactId>Heroes</artifactId>
<version>1.5.5</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.2-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.griefcraft.lwc</groupId>
<artifactId>lwc</artifactId>
<version>4.3.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.daemitus.deadbolt</groupId>
<artifactId>deadbolt</artifactId>
<version>2.2</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sk89q</groupId>
<artifactId>worldguard</artifactId>
<version>6.1.1-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.webkonsept.bukkit.simplechestlock</groupId>
<artifactId>simplechestlock</artifactId>
<version>1.2.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.yi.acru.bukkit.lockette</groupId>
<artifactId>lockette</artifactId>
<version>1.8.14</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.bekvon.bukkit</groupId>
<artifactId>residence</artifactId>
<version>4.6.1.4</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.themoep.showitem</groupId>
<artifactId>api</artifactId>
<version>1.2.24</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>org.mcstats.bukkit</include>
<include>org.bstats:*</include>
<include>net.gravitydevelopment.updater</include>
<include>com.j256.ormlite</include>
<include>org.apache.logging.log4j</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>org.mcstats</pattern>
<shadedPattern>com.Acrobot.ChestShop.Metrics.MCStats</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>com.Acrobot.ChestShop.Metrics.BStats</shadedPattern>
</relocation>
<relocation>
<pattern>net.gravitydevelopment.updater</pattern>
<shadedPattern>com.Acrobot.ChestShop.Updater</shadedPattern>
</relocation>
<relocation>
<pattern>com.j256.ormlite</pattern>
<shadedPattern>com.Acrobot.ChestShop.ORMlite</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>${buildNumber}</build.number>
<bukkit.plugin.version>${project.version} ${buildDescription}</bukkit.plugin.version>
</properties>
<profiles>
<profile>
<id>static_build_number</id>
<activation>
<property>
<name>!env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>0</buildNumber>
<buildDescription>(compiled at ${maven.build.timestamp})</buildDescription>
</properties>
</profile>
<profile>
<id>dynamic_build_number</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildNumber>${env.BUILD_NUMBER}</buildNumber>
<buildDescription>(build ${env.BUILD_NUMBER})</buildDescription>
</properties>
</profile>
</profiles>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<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.acrobot.chestshop</groupId>
<artifactId>chestshop</artifactId>
<version>3.12.3-SNAPSHOT</version>
<description>Chest-and-sign shop plugin for Bukkit</description>
<name>ChestShop</name>
<scm>
<connection>scm:git:git://github.com/ChestShop-authors/ChestShop-3</connection>
<developerConnection>scm:git:ssh://git@github.com/ChestShop-authors/ChestShop-3.git</developerConnection>
<url>https://github.com/ChestShop-authors/ChestShop-3</url>
</scm>
<repositories>
<repository>
<id>enginehub-repo</id>
<url>https://maven.enginehub.org/repo/</url>
</repository>
<repository>
<id>codemc-repo</id>
<url>https://repo.codemc.org/repository/maven-public/</url>
</repository>
<repository>
<id>lwcx-repo</id>
<url>https://ci.ender.zone/plugin/repository/everything/</url>
</repository>
<repository>
<id>vault-repo</id>
<url>https://nexus.hc.to/content/repositories/pub_releases/</url>
</repository>
<repository>
<id>minebench-repo</id>
<url>https://repo.minebench.de/</url>
</repository>
<repository>
<id>local_repo</id>
<url>file://${project.basedir}/repo/</url>
</repository>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
<repository>
<id>NyaaCat</id>
<url>https://ci.nyaacat.com/maven/</url>
</repository>
<repository>
<id>redprotect-repo</id>
<url>https://raw.githubusercontent.com/FabioZumbi12/RedProtect/mvn-repo/</url>
</repository>
<repository>
<id>adventure-repo</id>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.j256.ormlite</groupId>
<artifactId>ormlite-jdbc</artifactId>
<version>6.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.themoep.utils</groupId>
<artifactId>lang-bukkit</artifactId>
<version>1.3-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>de.themoep</groupId>
<artifactId>minedown-adventure</artifactId>
<version>1.7.2-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
<version>4.3.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-text-serializer-gson</artifactId>
<version>4.14.0</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Should be kept in sync with Mojang -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.17.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bstats</groupId>
<artifactId>bstats-bukkit</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>net.milkbowl.vault</groupId>
<artifactId>Vault</artifactId>
<version>1.6.6</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.herocraftonline.heroes</groupId>
<artifactId>Heroes</artifactId>
<version>1.5.5</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>fr.xephi</groupId>
<artifactId>authme</artifactId>
<version>5.6.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.griefcraft.lwc</groupId>
<artifactId>LWCX</artifactId>
<version>2.2.5</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.daemitus.deadbolt</groupId>
<artifactId>deadbolt</artifactId>
<version>2.2</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.sk89q.worldedit</groupId>
<artifactId>worldedit-core</artifactId>
<version>7.0.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.sk89q.worldguard</groupId>
<artifactId>worldguard-legacy</artifactId>
<version>7.0.0-SNAPSHOT</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.TechFortress</groupId>
<artifactId>GriefPrevention</artifactId>
<version>16.12.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.github.jojodmo</groupId>
<artifactId>ItemBridge</artifactId>
<version>b0054538c1</version>
</dependency>
<dependency>
<groupId>br.net.fabiozumbi12.RedProtect</groupId>
<artifactId>RedProtect-Spigot</artifactId>
<version>7.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>br.net.fabiozumbi12.RedProtect</groupId>
<artifactId>RedProtect-Core</artifactId>
<version>7.7.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.webkonsept.bukkit.simplechestlock</groupId>
<artifactId>simplechestlock</artifactId>
<version>1.2.1</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.yi.acru.bukkit.lockette</groupId>
<artifactId>lockette</artifactId>
<version>1.8.14</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>nl.rutgerkok</groupId>
<artifactId>blocklocker</artifactId>
<version>1.9</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.bekvon.bukkit</groupId>
<artifactId>residence</artifactId>
<version>4.6.1.4</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>bukkit</artifactId>
</exclusion>
<exclusion>
<groupId>org.bukkit</groupId>
<artifactId>craftbukkit</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>de.themoep.showitem</groupId>
<artifactId>api</artifactId>
<version>1.6.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>net.tnemc</groupId>
<artifactId>Reserve</artifactId>
<version>0.1.5.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.crafter.mc</groupId>
<artifactId>lockettepro</artifactId>
<version>2.10-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<forceCreation>true</forceCreation>
<archive>
<manifest>
<addDefaultEntries>true</addDefaultEntries>
</manifest>
<manifestEntries>
<Distribution-Type>${buildType}</Distribution-Type>
<Built-At>${maven.build.timestamp}</Built-At>
<Build-Jdk>${java.runtime.version}</Build-Jdk>
<paperweight-mappings-namespace>mojang</paperweight-mappings-namespace>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>de.themoep:*</include>
<include>de.themoep.utils:*</include>
<include>net.kyori:*</include>
<include>org.bstats:*</include>
<include>net.gravitydevelopment.updater</include>
<include>com.j256.ormlite</include>
<include>javax.persistence</include>
</includes>
</artifactSet>
<relocations>
<relocation>
<pattern>de.themoep.utils.lang</pattern>
<shadedPattern>com.Acrobot.ChestShop.Libs.Lang</shadedPattern>
</relocation>
<relocation>
<pattern>de.themoep.minedown.adventure</pattern>
<shadedPattern>com.Acrobot.ChestShop.Libs.MineDown</shadedPattern>
</relocation>
<relocation>
<pattern>net.kyori</pattern>
<shadedPattern>com.Acrobot.ChestShop.Libs.Kyori</shadedPattern>
</relocation>
<relocation>
<pattern>org.bstats</pattern>
<shadedPattern>com.Acrobot.ChestShop.Metrics.BStats</shadedPattern>
</relocation>
<relocation>
<pattern>net.gravitydevelopment.updater</pattern>
<shadedPattern>com.Acrobot.ChestShop.Updater</shadedPattern>
</relocation>
<relocation>
<pattern>com.j256.ormlite</pattern>
<shadedPattern>com.Acrobot.ChestShop.Libs.ORMlite</shadedPattern>
</relocation>
<relocation>
<pattern>javax.persistence</pattern>
<shadedPattern>com.Acrobot.ChestShop.Libs.javax.persistence</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>.</directory>
<includes>
<include>LICENSE</include>
<include>README.md</include>
</includes>
</resource>
</resources>
</build>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<build.number>${buildNumber}</build.number>
<user.name>${buildType}</user.name>
<bukkit.plugin.version>${project.version} ${buildDescription}</bukkit.plugin.version>
<bukkit.dependency.version>1.20.5-R0.1-SNAPSHOT</bukkit.dependency.version>
</properties>
<profiles>
<profile>
<id>default</id>
<activation>
<property>
<name>!testprofile</name>
</property>
</activation>
<repositories>
<repository>
<id>paper-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>${bukkit.dependency.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
</profile>
<profile>
<id>spigot</id>
<activation>
<property>
<name>testprofile</name>
<value>spigot</value>
</property>
</activation>
<repositories>
<repository>
<id>spigotmc-repo</id>
<url>https://hub.spigotmc.org/nexus/content/groups/public</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>${bukkit.dependency.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}-Spigot</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/Paper*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>backwards</id>
<activation>
<property>
<name>testprofile</name>
<value>backwards</value>
</property>
</activation>
<repositories>
<repository>
<id>paper-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.destroystokyo.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.13.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.name}-1.13.2</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/PaperLatest*.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>static_build_number</id>
<activation>
<property>
<name>!env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildType>manual</buildType>
<buildNumber>0</buildNumber>
<buildDescription>(compiled at ${maven.build.timestamp})</buildDescription>
</properties>
</profile>
<profile>
<id>dynamic_build_number</id>
<activation>
<property>
<name>env.BUILD_NUMBER</name>
</property>
</activation>
<properties>
<buildType>jenkins</buildType>
<buildNumber>${env.BUILD_NUMBER}</buildNumber>
<buildDescription>(build ${env.BUILD_NUMBER})</buildDescription>
</properties>
</profile>
</profiles>
</project>

View File

@ -1 +0,0 @@
fe29bd77061f1ced861554d6d6d28cf7

View File

@ -1 +0,0 @@
d9c12329efa1b76057c6ec6eb9371823369e27f5

View File

@ -1 +0,0 @@
45ebcc66da30cab5c5d7b2c2e29651df

View File

@ -1 +0,0 @@
46707be3cc027277671520396501466157b6dcce

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>com.griefcraft.lwc</groupId>
<artifactId>lwc</artifactId>
<versioning>
<release>4.3.1</release>
<versions>
<version>4.3.1</version>
</versions>
<lastUpdated>20130308183115</lastUpdated>
</versioning>
</metadata>

View File

@ -1 +0,0 @@
eff48fcb809bfa5cef0a0660d8449ee3

View File

@ -1 +0,0 @@
e9c597ab4ce925154cad4e0d10aff5161d051a06

View File

@ -1 +0,0 @@
c7b3b9b7cd7cd27a50c079ce4349f628

View File

@ -1 +0,0 @@
697ff692f06dc9064badb2610fd52399651b8274

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>info.somethingodd</groupId>
<artifactId>odditem</artifactId>
<version>0.9.5</version>
<description>POM was created from install:install-file</description>
</project>

View File

@ -1 +0,0 @@
9fa8325983cb90581156734d0500aacb

View File

@ -1 +0,0 @@
288ed3c67d66d532eac2128c120a6d74986823d6

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>info.somethingodd</groupId>
<artifactId>odditem</artifactId>
<versioning>
<release>0.9.5</release>
<versions>
<version>0.9.5</version>
</versions>
<lastUpdated>20130826223956</lastUpdated>
</versioning>
</metadata>

View File

@ -1 +0,0 @@
7240080156e6cd8df6f550049950a99c

View File

@ -1 +0,0 @@
23208c6e99b6df3664c26f771fa941a37387f428

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata modelVersion="1.1.0">
<groupId>org.mcstats.bukkit</groupId>
<artifactId>metrics</artifactId>
<version>R8-SNAPSHOT</version>
<versioning>
<snapshot>
<localCopy>true</localCopy>
</snapshot>
<lastUpdated>20180711163419</lastUpdated>
<snapshotVersions>
<snapshotVersion>
<extension>jar</extension>
<value>R8-SNAPSHOT</value>
<updated>20180711163419</updated>
</snapshotVersion>
<snapshotVersion>
<extension>pom</extension>
<value>R8-SNAPSHOT</value>
<updated>20180711163419</updated>
</snapshotVersion>
</snapshotVersions>
</versioning>
</metadata>

View File

@ -0,0 +1 @@
46632b59c9d3d47442b77c0554325496

View File

@ -0,0 +1 @@
20541fa3752c0169c1ac8af2232281ccb1e01969

View File

@ -0,0 +1 @@
d0916f0631371e2cd1ba27b7a24e9528

View File

@ -0,0 +1 @@
c473200c17a6157d7f59f5935df2169088a78d8e

View File

@ -2,8 +2,8 @@
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.griefcraft.lwc</groupId>
<artifactId>lwc</artifactId>
<version>4.3.1</version>
<groupId>org.mcstats.bukkit</groupId>
<artifactId>metrics</artifactId>
<version>R8-SNAPSHOT</version>
<description>POM was created from install:install-file</description>
</project>

View File

@ -0,0 +1 @@
209c9099f021a6a85d1d337de6c2e74a

View File

@ -0,0 +1 @@
38207d9da32bb25d60da9676d1d34966567d62e5

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
<groupId>org.mcstats.bukkit</groupId>
<artifactId>metrics</artifactId>
<versioning>
<versions>
<version>R8-SNAPSHOT</version>
</versions>
<lastUpdated>20180711163419</lastUpdated>
</versioning>
</metadata>

View File

@ -0,0 +1 @@
52f4c1a0347427b995125e438ea9b81d

View File

@ -0,0 +1 @@
ce375488dc6366cc09efdc249006b25ff0dec3cf

View File

@ -1,20 +1,21 @@
package com.Acrobot.Breeze.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
public class SimpleCache<K, V> {
private final LinkedHashMap<K, V> map;
private final Map<K, V> map;
public SimpleCache(int cacheSize) {
map = new LinkedHashMap<K, V>(cacheSize * 10/9, 0.7f, true) {
map = Collections.synchronizedMap(new LinkedHashMap<K, V>(cacheSize * 10/9, 0.7f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheSize;
}
};
});
}
public V put(K key, V value) {
@ -40,7 +41,7 @@ public class SimpleCache<K, V> {
}
}
private boolean contains(K key) {
public boolean contains(K key) {
return map.containsKey(key);
}
}

View File

@ -0,0 +1,22 @@
package com.Acrobot.Breeze.Configuration.Annotations;
import com.Acrobot.Breeze.Configuration.ValueParser;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation for a parser
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Parser {
/**
* This option's comment
*
* @return Comment
*/
public String value();
}

View File

@ -7,8 +7,15 @@ import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.Acrobot.Breeze.Configuration.Annotations.Parser;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
@ -21,6 +28,18 @@ import com.Acrobot.Breeze.Configuration.Annotations.PrecededBySpace;
* @author Acrobot
*/
public class Configuration {
private static Map<String, ValueParser> parsers = new HashMap<>();
private static final ValueParser DEFAULT_PARSER = new ValueParser();
private static final ValueParser ENUM_PARSER = new ValueParser() {
@Override
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof String && type.isEnum()) {
return Enum.valueOf((Class<? extends Enum>) type, ((String) object).toUpperCase(Locale.ROOT));
}
return object;
}
};
/**
* Loads a YAML-formatted file into a class and modifies the file if some of class's fields are missing
*
@ -28,8 +47,24 @@ public class Configuration {
* @param clazz Class to modify
*/
public static void pairFileAndClass(File file, Class<?> clazz) {
pairFileAndClass(file, clazz, Bukkit.getLogger());
}
/**
* Loads a YAML-formatted file into a class and modifies the file if some of class's fields are missing
*
* @param file File to load
* @param clazz Class to modify
* @param logger The logger to use to log some information about the pairing
*/
public static void pairFileAndClass(File file, Class<?> clazz, Logger logger) {
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
boolean debug = config.getBoolean("DEBUG", false);
if (debug) {
logger.log(Level.INFO, "Loading configuration " + file.getName());
}
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(file, true));
@ -39,6 +74,9 @@ public class Configuration {
for (Field field : clazz.getDeclaredFields()) {
if (!Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) || !Modifier.isPublic(field.getModifiers())) {
if (debug) {
logger.log(Level.WARNING, "Field " + field.getName() + " is private, transient or not static!");
}
continue;
}
@ -46,9 +84,9 @@ public class Configuration {
try {
if (config.isSet(path)) {
field.set(null, ValueParser.parseToJava(config.get(path)));
} else if (config.isSet(path.toLowerCase())) {
field.set(null, ValueParser.parseToJava(config.get(path.toLowerCase())));
field.set(null, getParser(field).parseToJava(field.getType(), config.get(path)));
} else if (config.isSet(path.toLowerCase(Locale.ROOT))) {
field.set(null, getParser(field).parseToJava(field.getType(), config.get(path.toLowerCase(Locale.ROOT))));
} else {
if (field.isAnnotationPresent(PrecededBySpace.class)) {
writer.newLine();
@ -57,17 +95,18 @@ public class Configuration {
writer.write(FieldParser.parse(field));
writer.newLine();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
if (debug) {
logger.log(Level.INFO, field.getName() + ": " + Configuration.getParser(field).parseToYAML(field.get(null)));
}
} catch (IllegalArgumentException | IllegalAccessException | IOException e) {
logger.log(Level.SEVERE, "Error while loading field " + field.getName() + " in configuration " + file.getName(), e);
}
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
logger.log(Level.SEVERE, "Error while loading configuration " + file.getName(), e);
}
}
@ -87,7 +126,7 @@ public class Configuration {
return lastLine.isEmpty();
} catch (FileNotFoundException e) {
e.printStackTrace();
Logger.getLogger("Configuration").log(Level.SEVERE, "Error while checking if file " + file.getName() + " ends with space", e);
return false;
}
}
@ -99,7 +138,7 @@ public class Configuration {
* @return Parsed output
*/
public static String parseToConfig(Object value) {
return ValueParser.parseToYAML(value);
return DEFAULT_PARSER.parseToYAML(value);
}
/**
@ -111,4 +150,44 @@ public class Configuration {
public static String getColoured(String string) {
return ChatColor.translateAlternateColorCodes('&', string);
}
/**
* Register a parser
* @param name The name of the parser
* @param valueParser The parser itself
*/
public static void registerParser(String name, ValueParser valueParser) {
parsers.put(name.toLowerCase(Locale.ROOT), valueParser);
}
/**
* Get a registered parser
* @param name The name of the parser
* @return The parser or null if it doesn't exist
*/
public static ValueParser getParser(String name) {
return parsers.get(name.toLowerCase(Locale.ROOT));
}
/**
* Get the parser that should be used for a field
* @param field The field
* @return The registered parser if the field has a Parser annotation or the default one
*/
public static ValueParser getParser(Field field) {
ValueParser parser = null;
if (field.isAnnotationPresent(Parser.class)) {
parser = Configuration.getParser(field.getAnnotation(Parser.class).value());
}
if (parser == null) {
parser = Configuration.getParser(field.getType().getSimpleName());
}
if (parser == null && field.getType().isEnum()) {
parser = Configuration.ENUM_PARSER;
}
if (parser == null) {
parser = Configuration.DEFAULT_PARSER;
}
return parser;
}
}

View File

@ -3,6 +3,8 @@ package com.Acrobot.Breeze.Configuration;
import com.Acrobot.Breeze.Configuration.Annotations.ConfigurationComment;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Acrobot
@ -17,15 +19,19 @@ public class FieldParser {
public static String parse(Field field) {
StringBuilder builder = new StringBuilder(50);
try {
builder.append(field.getName()).append(": ").append(ValueParser.parseToYAML(field.get(null)));
} catch (IllegalAccessException e) {
e.printStackTrace();
return "";
if (field.isAnnotationPresent(ConfigurationComment.class)) {
for (String commentLine : field.getAnnotation(ConfigurationComment.class).value().split("\n")) {
builder.append("# ").append(commentLine).append('\n');
}
}
if (field.isAnnotationPresent(ConfigurationComment.class)) {
builder.append('\n').append('#').append(field.getAnnotation(ConfigurationComment.class).value());
ValueParser parser = Configuration.getParser(field);
try {
builder.append(field.getName()).append(": ").append(parser.parseToYAML(field.get(null)));
} catch (IllegalAccessException e) {
Logger.getLogger("FieldParser").log(Level.SEVERE, "Error while parsing field", e);
return "";
}
return builder.toString();

View File

@ -2,9 +2,12 @@ package com.Acrobot.Breeze.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Acrobot
@ -16,9 +19,22 @@ public class ValueParser {
* @param object Object to parse
* @return YAML string
*/
public static String parseToYAML(Object object) {
public String parseToYAML(Object object) {
if (object instanceof Number || object instanceof Boolean) {
return String.valueOf(object);
} else if (object instanceof Collection) {
StringBuilder sb = new StringBuilder();
for (Object o : (Collection) object) {
sb.append("\n- ").append(parseToYAML(o));
}
return sb.toString();
} else if (object instanceof String) {
String[] lines = ((String) object).split("\\R");
if (lines.length == 1) {
return '\"' + String.valueOf(object).replace("\\", "\\\\") + '\"';
} else {
return "|-\n" + Arrays.stream(lines).map(s -> " " + s.replace("\\", "\\\\")).collect(Collectors.joining("\n"));
}
} else {
return '\"' + String.valueOf(object) + '\"';
}
@ -27,12 +43,13 @@ public class ValueParser {
/**
* Parses a YAML "object" to Java-compatible object
*
* @param type The type of the returned object
* @param object Object to parse
* @return Java-compatible object
*/
public static Object parseToJava(Object object) {
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof ConfigurationSection) {
Map<String, List<String>> map = new HashMap<String, List<String>>();
Map<String, List<String>> map = new HashMap<>();
for (String message : ((ConfigurationSection) object).getKeys(false)) {
map.put(message, ((ConfigurationSection) object).getStringList(message));

View File

@ -1,8 +1,12 @@
package com.Acrobot.Breeze.Database;
import com.Acrobot.ChestShop.ChestShop;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.Entity;
@ -60,7 +64,7 @@ public class Database {
try {
table.create(fields);
} catch (SQLException e) {
e.printStackTrace();
Logger.getLogger("Database").log(Level.SEVERE, "Error while creating database from " + clazz.getName() + " (" + fields + ")", e);
return false;
}

View File

@ -36,7 +36,7 @@ public class EntityParser {
fields.add(convertToSQL(field));
}
return fields.stream().collect(Collectors.joining(","));
return String.join(",", fields);
}
/**

View File

@ -1,6 +1,8 @@
package com.Acrobot.Breeze.Database;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A class representing a Row in SQL query
@ -85,21 +87,16 @@ public class Row {
try {
object = clazz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException | IllegalAccessException e) {
Logger.getLogger("Row").log(Level.SEVERE, "Error while creating new instance of class " + clazz.getName() + " for row", e);
return null;
}
for (Map.Entry<String, String> value : values.entrySet()) {
try {
clazz.getDeclaredField(value.getKey()).set(object, value.getValue());
} catch (NoSuchFieldException ex) {
ex.printStackTrace();
} catch (IllegalAccessException ex) {
ex.printStackTrace();
} catch (NoSuchFieldException | IllegalAccessException ex) {
Logger.getLogger("Row").log(Level.SEVERE, "Error while setting field " + value.getKey() + " to " + value.getValue() + " of class " + clazz.getName(), ex);
}
}

View File

@ -132,7 +132,7 @@ public class Table {
String statement;
if (condition == null || condition.isEmpty()) {
String format = '\'' + row.getValues().stream().collect(Collectors.joining("', ")) + '\'';
String format = '\'' + String.join("', ", row.getValues()) + '\'';
statement = String.format(INSERT_VALUES, format);
} else {
String format = row.getKeysAndValues().entrySet().stream()

View File

@ -1,14 +1,15 @@
package com.Acrobot.Breeze.Utils;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Chest;
import org.bukkit.block.BlockFace;
import org.bukkit.block.DoubleChest;
import org.bukkit.block.Sign;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.material.Attachable;
/**
* @author Acrobot
@ -21,8 +22,12 @@ public class BlockUtil {
* @return Is this block a sign?
*/
public static boolean isSign(Block block) {
return block.getType() == Material.SIGN_POST
|| block.getType() == Material.WALL_SIGN;
if (!isLoaded(block)) {
return false;
}
BlockData data = block.getBlockData();
return data instanceof Sign || data instanceof WallSign;
}
/**
@ -32,7 +37,7 @@ public class BlockUtil {
* @return Is this block a chest?
*/
public static boolean isChest(Block block) {
return block.getState() instanceof Chest;
return BlockUtil.isLoaded(block) && block.getBlockData() instanceof org.bukkit.block.data.type.Chest;
}
/**
@ -42,7 +47,7 @@ public class BlockUtil {
* @return Is this holder a chest?
*/
public static boolean isChest(InventoryHolder holder) {
return holder instanceof Chest || holder instanceof DoubleChest;
return holder instanceof org.bukkit.block.Chest || holder instanceof DoubleChest;
}
/**
@ -51,8 +56,46 @@ public class BlockUtil {
* @param sign Sign which is attached
* @return Block to which the sign is attached
*/
public static Block getAttachedBlock(Sign sign) {
return sign.getBlock().getRelative(((Attachable) sign.getData()).getAttachedFace());
public static Block getAttachedBlock(org.bukkit.block.Sign sign) {
BlockFace direction;
BlockData blockData = sign.getBlockData();
if (blockData instanceof WallSign) {
direction = ((Directional) blockData).getFacing().getOppositeFace();
} else if (blockData instanceof Sign) {
direction = BlockFace.DOWN;
} else {
throw new IllegalArgumentException("Cannot get direction of " + blockData.getClass().getSimpleName());
}
return sign.getBlock().getRelative(direction);
}
/**
* Convert a blockface to a major direction
*
* @param face The face to get the major direction from
* @return The major direction. For middle directions it will return the next clockwise direction
*/
public static BlockFace getMajorDirection(BlockFace face) {
switch (face) {
case NORTH_WEST:
case NORTH_NORTH_WEST:
case NORTH_NORTH_EAST:
return BlockFace.NORTH;
case NORTH_EAST:
case EAST_NORTH_EAST:
case EAST_SOUTH_EAST:
return BlockFace.EAST;
case SOUTH_EAST:
case SOUTH_SOUTH_EAST:
case SOUTH_SOUTH_WEST:
return BlockFace.SOUTH;
case SOUTH_WEST:
case WEST_NORTH_WEST:
case WEST_SOUTH_WEST:
return BlockFace.WEST;
default:
return face;
}
}
/**
@ -68,4 +111,14 @@ public class BlockUtil {
return true;
}
/**
* Check if the chunk a block is in is loaded
*
* @param block The block to check
* @return Whether or not the chunk is loaded
*/
public static boolean isLoaded(Block block) {
return block.getWorld().isChunkLoaded(block.getX() >> 4, block.getZ() >> 4);
}
}

View File

@ -1,5 +1,9 @@
package com.Acrobot.Breeze.Utils.Encoding;
import java.nio.charset.StandardCharsets;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* <p>Encodes and decodes to and from Base64 notation.</p>
* <p>Homepage: <a href="http://iharder.net/base64">http://iharder.net/base64</a>.</p>
@ -34,7 +38,7 @@ package com.Acrobot.Breeze.Utils.Encoding;
* @version 2.3.7
*/
public class Base64 {
/* ******** P U B L I C F I E L D S ******** */
@ -87,8 +91,8 @@ public class Base64 {
* <a href="http://www.faqs.org/qa/rfcc-1940.html">http://www.faqs.org/qa/rfcc-1940.html</a>.
*/
public final static int ORDERED = 32;
/* ******** P R I V A T E F I E L D S ******** */
@ -119,7 +123,7 @@ public class Base64 {
private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding
/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */
/**
@ -178,7 +182,7 @@ public class Base64 {
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
};
/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */
/**
@ -305,7 +309,7 @@ public class Base64 {
-9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255
};
/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */
@ -349,10 +353,10 @@ public class Base64 {
* Defeats instantiation.
*/
private Base64() {}
/* ******** E N C O D I N G M E T H O D S ******** */
@ -559,59 +563,31 @@ public class Base64 {
throw new NullPointerException("Cannot serialize a null object.");
} // end if: null
// Streams
java.io.ByteArrayOutputStream baos = null;
java.io.OutputStream b64os = null;
java.util.zip.GZIPOutputStream gzos = null;
java.io.ObjectOutputStream oos = null;
try {
// ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
baos = new java.io.ByteArrayOutputStream();
b64os = new Base64.OutputStream(baos, ENCODE | options);
// ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream
try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options)) {
if ((options & GZIP) != 0) {
// Gzip
gzos = new java.util.zip.GZIPOutputStream(b64os);
oos = new java.io.ObjectOutputStream(gzos);
try (java.util.zip.GZIPOutputStream gzos = new java.util.zip.GZIPOutputStream(b64os);
java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(gzos)){
oos.writeObject(serializableObject);
}
} else {
// Not gzipped
oos = new java.io.ObjectOutputStream(b64os);
try (java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(b64os)) {
oos.writeObject(serializableObject);
}
}
oos.writeObject(serializableObject);
// Return value according to relevant encoding.
try {
return baos.toString(PREFERRED_ENCODING);
} // end try
catch (java.io.UnsupportedEncodingException uue) {
// Fall back to some Java default
return baos.toString();
} // end catch
} // end try
catch (java.io.IOException e) {
// Catch it and then throw it immediately so that
// the finally{} block is called for cleanup.
throw e;
} // end catch
finally {
try {
oos.close();
} catch (Exception e) {
}
try {
gzos.close();
} catch (Exception e) {
}
try {
b64os.close();
} catch (Exception e) {
}
try {
baos.close();
} catch (Exception e) {
}
} // end finally
// Return value according to relevant encoding.
try {
return new String(baos.toByteArray(), PREFERRED_ENCODING);
} // end try
catch (java.io.UnsupportedEncodingException uue) {
// Fall back to some Java default
return new String(baos.toByteArray());
} // end catch
} // end encode
@ -635,7 +611,6 @@ public class Base64 {
} catch (java.io.IOException ex) {
assert false : ex.getMessage();
} // end catch
assert encoded != null;
return encoded;
} // end encodeBytes
@ -700,7 +675,6 @@ public class Base64 {
} catch (java.io.IOException ex) {
assert false : ex.getMessage();
} // end catch
assert encoded != null;
return encoded;
} // end encodeBytes
@ -810,40 +784,13 @@ public class Base64 {
// Compress?
if ((options & GZIP) != 0) {
java.io.ByteArrayOutputStream baos = null;
java.util.zip.GZIPOutputStream gzos = null;
Base64.OutputStream b64os = null;
try {
// GZip -> Base64 -> ByteArray
baos = new java.io.ByteArrayOutputStream();
b64os = new Base64.OutputStream(baos, ENCODE | options);
gzos = new java.util.zip.GZIPOutputStream(b64os);
try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
Base64.OutputStream b64os = new Base64.OutputStream(baos, ENCODE | options);
java.util.zip.GZIPOutputStream gzos = new java.util.zip.GZIPOutputStream(b64os)) {
gzos.write(source, off, len);
gzos.close();
return baos.toByteArray();
} // end try
catch (java.io.IOException e) {
// Catch it and then throw it immediately so that
// the finally{} block is called for cleanup.
throw e;
} // end catch
finally {
try {
gzos.close();
} catch (Exception e) {
}
try {
b64os.close();
} catch (Exception e) {
}
try {
baos.close();
} catch (Exception e) {
}
} // end finally
return baos.toByteArray();
} // end if: compress
// Else, don't compress. Better not to use streams at all then.
@ -903,11 +850,11 @@ public class Base64 {
} // end else: don't compress
} // end encodeBytesToBytes
/* ******** D E C O D I N G M E T H O D S ******** */
@ -1162,20 +1109,16 @@ public class Base64 {
// Check to see if it's gzip-compressed
// GZIP Magic Two-Byte Number: 0x8b1f (35615)
boolean dontGunzip = (options & DONT_GUNZIP) != 0;
if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) {
if (bytes.length >= 4 && !dontGunzip) {
int head = (bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00);
if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) {
java.io.ByteArrayInputStream bais = null;
java.util.zip.GZIPInputStream gzis = null;
java.io.ByteArrayOutputStream baos = null;
byte[] buffer = new byte[2048];
int length = 0;
try {
baos = new java.io.ByteArrayOutputStream();
bais = new java.io.ByteArrayInputStream(bytes);
gzis = new java.util.zip.GZIPInputStream(bais);
try (java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(bytes);
java.util.zip.GZIPInputStream gzis = new java.util.zip.GZIPInputStream(bais)) {
while ((length = gzis.read(buffer)) >= 0) {
baos.write(buffer, 0, length);
@ -1184,25 +1127,10 @@ public class Base64 {
// No error? Get new bytes.
bytes = baos.toByteArray();
} // end try
catch (java.io.IOException e) {
e.printStackTrace();
} catch (java.io.IOException e) {
Logger.getLogger("Base64").log(Level.SEVERE, "Unable to decode", e);
// Just return originally-decoded bytes
} // end catch
finally {
try {
baos.close();
} catch (Exception e) {
}
try {
gzis.close();
} catch (Exception e) {
}
try {
bais.close();
} catch (Exception e) {
}
} // end finally
}
} // end if: gzipped
} // end if: bytes.length >= 2
@ -1252,55 +1180,30 @@ public class Base64 {
// Decode and gunzip if necessary
byte[] objBytes = decode(encodedObject, options);
java.io.ByteArrayInputStream bais = null;
java.io.ObjectInputStream ois = null;
Object obj = null;
try {
bais = new java.io.ByteArrayInputStream(objBytes);
try (java.io.ByteArrayInputStream bais = new java.io.ByteArrayInputStream(objBytes)) {
// If no custom class loader is provided, use Java's builtin OIS.
if (loader == null) {
ois = new java.io.ObjectInputStream(bais);
try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(bais)) {
return ois.readObject();
} // end try
} // end if: no loader provided
// Else make a customized object input stream that uses
// the provided class loader.
else {
ois = new java.io.ObjectInputStream(bais) {
try (java.io.ObjectInputStream ois = new java.io.ObjectInputStream(bais) {
@Override
public Class<?> resolveClass(java.io.ObjectStreamClass streamClass)
throws java.io.IOException, ClassNotFoundException {
Class<?> c = Class.forName(streamClass.getName(), false, loader);
if (c == null) {
return super.resolveClass(streamClass);
} else {
return c; // Class loader knows of this class.
} // end else: not null
return Class.forName(streamClass.getName(), false, loader);
} // end resolveClass
}; // end ois
}) { // end ois
return ois.readObject();
} // end try
} // end else: no custom class loader
obj = ois.readObject();
} // end try
catch (java.io.IOException e) {
throw e; // Catch and throw in order to execute finally{}
} // end catch
catch (java.lang.ClassNotFoundException e) {
throw e; // Catch and throw in order to execute finally{}
} // end catch
finally {
try {
bais.close();
} catch (Exception e) {
}
try {
ois.close();
} catch (Exception e) {
}
} // end finally
return obj;
} // end decodeObject
@ -1325,17 +1228,10 @@ public class Base64 {
throw new NullPointerException("Data to encode was null.");
} // end iff
Base64.OutputStream bos = null;
try {
bos = new Base64.OutputStream(
new java.io.FileOutputStream(filename), Base64.ENCODE);
try (OutputStream bos = new OutputStream(
new java.io.FileOutputStream(filename), Base64.ENCODE)) {
bos.write(dataToEncode);
} finally {
try {
bos.close();
} catch (Exception e) {
}
} // end finally
}
} // end encodeToFile
@ -1356,21 +1252,10 @@ public class Base64 {
public static void decodeToFile(String dataToDecode, String filename)
throws java.io.IOException {
Base64.OutputStream bos = null;
try {
bos = new Base64.OutputStream(
new java.io.FileOutputStream(filename), Base64.DECODE);
try (OutputStream bos = new OutputStream(
new java.io.FileOutputStream(filename), Base64.DECODE)) {
bos.write(dataToDecode.getBytes(PREFERRED_ENCODING));
} // end try
catch (java.io.IOException e) {
throw e; // Catch and throw to execute finally{} block
} // end catch: java.io.IOException
finally {
try {
bos.close();
} catch (Exception e) {
}
} // end finally
}
} // end decodeToFile
@ -1393,24 +1278,22 @@ public class Base64 {
throws java.io.IOException {
byte[] decodedData = null;
Base64.InputStream bis = null;
try {
// Set up some useful variables
java.io.File file = new java.io.File(filename);
byte[] buffer = null;
int length = 0;
int numBytes = 0;
// Set up some useful variables
java.io.File file = new java.io.File(filename);
byte[] buffer = null;
int length = 0;
int numBytes = 0;
// Check for size of file
if (file.length() > Integer.MAX_VALUE) {
throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes).");
} // end if: file too big for int index
buffer = new byte[(int) file.length()];
// Check for size of file
if (file.length() > Integer.MAX_VALUE) {
throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes).");
} // end if: file too big for int index
buffer = new byte[(int) file.length()];
// Open a stream
bis = new Base64.InputStream(
new java.io.BufferedInputStream(
new java.io.FileInputStream(file)), Base64.DECODE);
// Open a stream
try (Base64.InputStream bis = new Base64.InputStream(
new java.io.BufferedInputStream(
new java.io.FileInputStream(file)), Base64.DECODE)) {
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
@ -1420,17 +1303,7 @@ public class Base64 {
// Save in a variable to return
decodedData = new byte[length];
System.arraycopy(buffer, 0, decodedData, 0, length);
} // end try
catch (java.io.IOException e) {
throw e; // Catch and release to execute finally{}
} // end catch: java.io.IOException
finally {
try {
bis.close();
} catch (Exception e) {
}
} // end finally
} // end try
return decodedData;
} // end decodeFromFile
@ -1454,18 +1327,16 @@ public class Base64 {
throws java.io.IOException {
String encodedData = null;
Base64.InputStream bis = null;
try {
// Set up some useful variables
java.io.File file = new java.io.File(filename);
byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5)
int length = 0;
int numBytes = 0;
// Set up some useful variables
java.io.File file = new java.io.File(filename);
byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5)
int length = 0;
int numBytes = 0;
// Open a stream
bis = new Base64.InputStream(
// Open a stream
try (Base64.InputStream bis = new Base64.InputStream(
new java.io.BufferedInputStream(
new java.io.FileInputStream(file)), Base64.ENCODE);
new java.io.FileInputStream(file)), Base64.ENCODE)) {
// Read until done
while ((numBytes = bis.read(buffer, length, 4096)) >= 0) {
@ -1476,15 +1347,6 @@ public class Base64 {
encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING);
} // end try
catch (java.io.IOException e) {
throw e; // Catch and release to execute finally{}
} // end catch: java.io.IOException
finally {
try {
bis.close();
} catch (Exception e) {
}
} // end finally
return encodedData;
} // end encodeFromFile
@ -1501,21 +1363,10 @@ public class Base64 {
throws java.io.IOException {
String encoded = Base64.encodeFromFile(infile);
java.io.OutputStream out = null;
try {
out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outfile));
out.write(encoded.getBytes("US-ASCII")); // Strict, 7-bit output.
} // end try
catch (java.io.IOException e) {
throw e; // Catch and release to execute finally{}
} // end catch
finally {
try {
out.close();
} catch (Exception ex) {
}
} // end finally
try (java.io.OutputStream out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outfile))) {
out.write(encoded.getBytes(StandardCharsets.US_ASCII)); // Strict, 7-bit output.
}
} // end encodeFileToFile
@ -1531,24 +1382,13 @@ public class Base64 {
throws java.io.IOException {
byte[] decoded = Base64.decodeFromFile(infile);
java.io.OutputStream out = null;
try {
out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outfile));
try (java.io.OutputStream out = new java.io.BufferedOutputStream(
new java.io.FileOutputStream(outfile))) {
out.write(decoded);
} // end try
catch (java.io.IOException e) {
throw e; // Catch and release to execute finally{}
} // end catch
finally {
try {
out.close();
} catch (Exception ex) {
}
} // end finally
}
} // end decodeFileToFile
/* ******** I N N E R C L A S S I N P U T S T R E A M ******** */
@ -1754,12 +1594,12 @@ public class Base64 {
} // end read
} // end inner class InputStream
/* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */

View File

@ -0,0 +1,78 @@
package com.Acrobot.Breeze.Utils;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.DoubleChest;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import java.util.function.BiFunction;
public class ImplementationAdapter {
private static BiFunction<Inventory, Boolean, InventoryHolder> HOLDER_PROVIDER;
private static BiFunction<DoubleChest, Boolean, InventoryHolder> LEFT_HOLDER_PROVIDER;
private static BiFunction<DoubleChest, Boolean, InventoryHolder> RIGHT_HOLDER_PROVIDER;
private static BiFunction<Block, Boolean, BlockState> STATE_PROVIDER;
static {
try {
Inventory.class.getMethod("getHolder", boolean.class);
Class c = Class.forName("com.Acrobot.Breeze.Utils.ImplementationFeatures.PaperLatestHolder");
HOLDER_PROVIDER = (BiFunction<Inventory, Boolean, InventoryHolder>) c.getDeclaredField("PROVIDER").get(null);
LEFT_HOLDER_PROVIDER = (BiFunction<DoubleChest, Boolean, InventoryHolder>) c.getDeclaredField("LEFT_PROVIDER").get(null);
RIGHT_HOLDER_PROVIDER = (BiFunction<DoubleChest, Boolean, InventoryHolder>) c.getDeclaredField("RIGHT_PROVIDER").get(null);
} catch (NoSuchMethodException | ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
HOLDER_PROVIDER = (inventory, useSnapshot) -> inventory.getHolder();
LEFT_HOLDER_PROVIDER = (doubleChest, useSnapshot) -> doubleChest.getLeftSide();
RIGHT_HOLDER_PROVIDER = (doubleChest, useSnapshot) -> doubleChest.getRightSide();
}
try {
Block.class.getMethod("getState", boolean.class);
Class c = Class.forName("com.Acrobot.Breeze.Utils.ImplementationFeatures.PaperLatestState");
STATE_PROVIDER = (BiFunction<Block, Boolean, BlockState>) c.getDeclaredField("PROVIDER").get(null);
} catch (NoSuchMethodException | ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
STATE_PROVIDER = (block, useSnapshot) -> block.getState();
}
}
/**
* Get the inventory's holder.
* @param inventory The inventory
* @param useSnapshot Whether or not the holder should be a snapshot (if possible)
* @return The inventory's holder
*/
public static InventoryHolder getHolder(Inventory inventory, boolean useSnapshot) {
return HOLDER_PROVIDER.apply(inventory, useSnapshot);
}
/**
* Get the a DoubleChest's left side
* @param doubleChest The DoubleChest
* @param useSnapshot Whether or not the holder should be a snapshot (if possible)
* @return The left side's holder
*/
public static InventoryHolder getLeftSide(DoubleChest doubleChest, boolean useSnapshot) {
return LEFT_HOLDER_PROVIDER.apply(doubleChest, useSnapshot);
}
/**
* Get the a DoubleChest's right side
* @param doubleChest The DoubleChest
* @param useSnapshot Whether or not the holder should be a snapshot (if possible)
* @return The left right's holder
*/
public static InventoryHolder getRightSide(DoubleChest doubleChest, boolean useSnapshot) {
return RIGHT_HOLDER_PROVIDER.apply(doubleChest, useSnapshot);
}
/**
* Get a block state
* @param block The block to get the state from
* @param useSnapshot Whether or not the state should be a snapshot (if possible)
* @return The block's state
*/
public static BlockState getState(Block block, boolean useSnapshot) {
return STATE_PROVIDER.apply(block, useSnapshot);
}
}

View File

@ -0,0 +1,17 @@
package com.Acrobot.Breeze.Utils.ImplementationFeatures;
import org.bukkit.block.DoubleChest;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import java.util.function.BiFunction;
public class PaperLatestHolder {
public static final BiFunction<Inventory, Boolean, InventoryHolder> PROVIDER = Inventory::getHolder;
public static final BiFunction<DoubleChest, Boolean, InventoryHolder> LEFT_PROVIDER = DoubleChest::getLeftSide;
public static final BiFunction<DoubleChest, Boolean, InventoryHolder> RIGHT_PROVIDER = DoubleChest::getRightSide;
}

View File

@ -0,0 +1,12 @@
package com.Acrobot.Breeze.Utils.ImplementationFeatures;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import java.util.function.BiFunction;
public class PaperLatestState {
public static final BiFunction<Block, Boolean, BlockState> PROVIDER = Block::getState;
}

View File

@ -15,7 +15,7 @@ import org.bukkit.inventory.ItemStack;
*/
public class InventoryUtil {
private static Boolean legacyContents = null;
private static ItemStack[] getStorageContents(Inventory inventory) {
if (legacyContents == null) {
try {
@ -74,7 +74,7 @@ public class InventoryUtil {
return true;
}
/**
* Count amount of empty slots in an inventory
*
@ -88,7 +88,7 @@ public class InventoryUtil {
emptyAmount++;
}
}
return emptyAmount;
}
@ -109,7 +109,7 @@ public class InventoryUtil {
return true;
}
/**
* Checks if items fit in the inventory
*
@ -124,7 +124,7 @@ public class InventoryUtil {
return false;
}
}
return true;
}
@ -138,7 +138,7 @@ public class InventoryUtil {
public static boolean fits(ItemStack item, Inventory inventory) {
int left = item.getAmount();
if (inventory.getMaxStackSize() == Integer.MAX_VALUE) {
if (inventory.getSize() == Integer.MAX_VALUE) {
return true;
}
@ -162,6 +162,62 @@ public class InventoryUtil {
return left <= 0;
}
/**
* Transfers an item from one inventory to another one
*
* @param item Item to transfer
* @param sourceInventory Inventory to transfer the item from
* @param targetInventory Inventory to transfer the item to
* @return Number of leftover items
*/
public static int transfer(ItemStack item, Inventory sourceInventory, Inventory targetInventory) {
return transfer(item, sourceInventory, targetInventory, item.getMaxStackSize());
}
/**
* Transfers an item from one inventory to another one
*
* @param item Item to transfer
* @param sourceInventory Inventory to transfer the item from
* @param targetInventory Inventory to transfer the item to
* @param maxStackSize Maximum item's stack size
* @return Number of leftover items
*/
public static int transfer(ItemStack item, Inventory sourceInventory, Inventory targetInventory, int maxStackSize) {
if (item.getAmount() < 1) {
return 0;
}
int amount = item.getAmount();
for (ItemStack currentItem : sourceInventory) {
if (MaterialUtil.equals(currentItem, item)) {
ItemStack clone = currentItem.clone();
if (currentItem.getAmount() >= amount) {
clone.setAmount(amount);
amount = 0;
} else {
clone.setAmount(currentItem.getAmount());
amount -= clone.getAmount();
}
int leftOver = add(clone, targetInventory, maxStackSize);
if (leftOver > 0) {
currentItem.setAmount(currentItem.getAmount() - clone.getAmount() + leftOver);
if (amount > 0) {
amount += leftOver;
} else {
return leftOver;
}
} else {
currentItem.setAmount(currentItem.getAmount() - clone.getAmount());
}
}
if (amount <= 0) {
break;
}
}
return amount;
}
/**
* Adds an item to the inventory with given maximum stack size
*
@ -206,8 +262,7 @@ public class InventoryUtil {
}
// Don't use the armor slots or extra slots
private static int effectiveSize(Inventory inventory)
{
private static int effectiveSize(Inventory inventory) {
return getStorageContents(inventory).length;
}
@ -296,7 +351,7 @@ public class InventoryUtil {
itemList.add(item.clone());
}
return itemList.toArray(new ItemStack[itemList.size()]);
return itemList.toArray(new ItemStack[0]);
}
/**
@ -333,15 +388,17 @@ public class InventoryUtil {
/**
* Get the max size an item stack is allowed to stack to while respecting the STACK_TO_64 config property
*
* @param item The item to get the max stacksize of
* @return The max stacksize of the item stack's type or 64 if STACK_TO_64 is enabled
*/
public static int getMaxStackSize(ItemStack item) {
return Properties.STACK_TO_64 ? 64 : item.getMaxStackSize();
}
/**
* Get an array of different item stacks that are properly stacked to their max stack size
*
* @param items The items to stack
* @return An array of item stacks which's amount is a maximum of the allowed stack size
*/
@ -356,7 +413,7 @@ public class InventoryUtil {
stackedItems.add(item.clone());
continue;
}
for (int i = 0; i < Math.floor(item.getAmount() / maxStackSize); i++) {
for (int i = 0; i < Math.floor((double) item.getAmount() / maxStackSize); i++) {
ItemStack itemClone = item.clone();
itemClone.setAmount(maxStackSize);
stackedItems.add(itemClone);
@ -367,6 +424,6 @@ public class InventoryUtil {
stackedItems.add(rest);
}
}
return stackedItems.toArray(new ItemStack[stackedItems.size()]);
return stackedItems.toArray(new ItemStack[0]);
}
}

View File

@ -2,34 +2,39 @@ package com.Acrobot.Breeze.Utils;
import com.Acrobot.Breeze.Collection.SimpleCache;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.google.common.collect.ImmutableMap;
import com.Acrobot.ChestShop.Events.MaterialParseEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import de.themoep.ShowItem.api.ShowItem;
import info.somethingodd.OddItem.OddItem;
import org.bukkit.CoalType;
import org.bukkit.DyeColor;
import de.themoep.minedown.adventure.Replacer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.SandstoneType;
import org.bukkit.TreeSpecies;
import org.bukkit.entity.EntityType;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BookMeta;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.material.*;
import org.bukkit.plugin.Plugin;
import org.json.simple.JSONObject;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.nodes.Tag;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftCharWidth;
import static com.Acrobot.Breeze.Utils.StringUtil.getMinecraftStringWidth;
/**
* @author Acrobot
@ -40,10 +45,41 @@ public class MaterialUtil {
public static final boolean LONG_NAME = true;
public static final boolean SHORT_NAME = false;
/**
* @deprecated Use {@link MaterialUtil#MAXIMUM_SIGN_WIDTH}
*/
@Deprecated
public static final short MAXIMUM_SIGN_LETTERS = 15;
// 15 dashes fit on one sign line with the default resource pack:
public static final int MAXIMUM_SIGN_WIDTH = (short) getMinecraftStringWidth("---------------");
private static final Map<String, String> ABBREVIATIONS = StringUtil.map(
"Egg", "Eg",
"Spawn", "Spaw",
"Pottery", "Pot",
"Heartbreak", "Heartbr",
"Sherd", "Sher"
);
private static final Map<String, String> UNIDIRECTIONAL_ABBREVIATIONS = StringUtil.map(
"Endermite", "Endmite",
"Endmite", "Endmit",
"Wayfinder", "Wayfndr",
"Wayfndr", "Wf",
"Heartbr", "Hrtbr",
"Hrtbr", "Hrtb"
);
private static final SimpleCache<String, Material> MATERIAL_CACHE = new SimpleCache<>(Properties.CACHE_SIZE);
private static final Yaml YAML = new Yaml(new YamlBukkitConstructor(), new YamlRepresenter(), new DumperOptions());
private static class YamlBukkitConstructor extends YamlConstructor {
public YamlBukkitConstructor() {
this.yamlConstructors.put(new Tag(Tag.PREFIX + "org.bukkit.inventory.ItemStack"), yamlConstructors.get(Tag.MAP));
}
}
/**
* Checks if the itemStack is empty or null
*
@ -68,25 +104,55 @@ public class MaterialUtil {
if (one.isSimilar(two)) {
return true;
}
// Special check for banners as they might include the deprecated base color
if (one.getType() == two.getType()
&& one.getType() == Material.BANNER
&& one.getDurability() == two.getDurability()) {
Map<String, Object> m1 = new HashMap<>(one.getItemMeta().serialize());
Map<String, Object> m2 = new HashMap<>(two.getItemMeta().serialize());
Object c1 = m1.remove("base-color");
Object c2 = m2.remove("base-color");
return (one.getData().equals(two.getData()) || c1.equals(c2)) && m1.equals(m2);
// Additional checks as serialisation and de-serialisation might lead to different item meta
// This would only be done if the items share the same item meta type so it shouldn't be too inefficient
// Special check for books as their pages might change when serialising (See SPIGOT-3206 and ChestShop#250)
// Special check for explorer maps/every item with a localised name (See SPIGOT-4672)
// Special check for legacy spawn eggs (See ChestShop#264)
if (one.getType() != two.getType()
|| one.getDurability() != two.getDurability()
|| (one.hasItemMeta() && two.hasItemMeta() && one.getItemMeta().getClass() != two.getItemMeta().getClass())) {
return false;
}
// Special check for books as their pages might change when serialising (See SPIGOT-3206)
return one.getType() == two.getType()
&& one.getDurability() == two.getDurability()
&& one.getData().equals(two.getData())
&& one.hasItemMeta() && two.hasItemMeta()
&& one.getItemMeta() instanceof BookMeta && two.getItemMeta() instanceof BookMeta
&& one.getItemMeta().serialize().equals(two.getItemMeta().serialize());
if (!one.hasItemMeta() && !two.hasItemMeta()) {
return true;
}
ItemMeta oneMeta = one.getItemMeta();
ItemMeta twoMeta = two.getItemMeta();
// return true if both are null or same, false if only one is null
if (oneMeta == twoMeta || oneMeta == null || twoMeta == null) {
return oneMeta == twoMeta;
}
Map<String, Object> oneSerMeta = oneMeta.serialize();
Map<String, Object> twoSerMeta = twoMeta.serialize();
if (oneSerMeta.equals(twoSerMeta)) {
return true;
}
// Try to use same parsing as the YAML dumper in the ItemDatabase when generating the code as the last resort
ItemStack oneDumped = YAML.loadAs(YAML.dump(one), ItemStack.class);
if (oneDumped.isSimilar(two)) {
return true;
}
ItemMeta oneDumpedMeta = oneDumped.getItemMeta();
if (oneDumpedMeta != null && oneDumpedMeta.serialize().equals(twoSerMeta)) {
return true;
}
ItemStack twoDumped = YAML.loadAs(YAML.dump(two), ItemStack.class);
if (oneDumped.isSimilar(twoDumped)) {
return true;
}
ItemMeta twoDumpedMeta = twoDumped.getItemMeta();
if (oneDumpedMeta != null && twoDumpedMeta != null && oneDumpedMeta.serialize().equals(twoDumpedMeta.serialize())) {
return true;
}
// return true if both are null or same, false otherwise
return oneDumpedMeta == twoDumpedMeta;
}
/**
@ -96,7 +162,15 @@ public class MaterialUtil {
* @return Material found
*/
public static Material getMaterial(String name) {
String formatted = name.toUpperCase();
String replacedName = name;
// revert unidirectional abbreviations
List<Map.Entry<String, String>> abbreviations = new ArrayList<>(UNIDIRECTIONAL_ABBREVIATIONS.entrySet());
for (int i = abbreviations.size() - 1; i >= 0; i--) {
Map.Entry<String, String> entry = abbreviations.get(i);
replacedName = replacedName.replaceAll(entry.getValue() + "(_|$)?", entry.getKey() + "$1");
}
String formatted = name.replaceAll("(?<!^)(?>\\s?)([A-Z1-9])", "_$1").replace(' ', '_').toUpperCase(Locale.ROOT);
Material material = MATERIAL_CACHE.get(formatted);
if (material != null) {
@ -110,7 +184,7 @@ public class MaterialUtil {
return material;
}
material = new EnumParser<Material>().parse(name, Material.values());
material = new EnumParser<Material>().parse(replacedName, Material.values());
if (material != null) {
MATERIAL_CACHE.put(formatted, material);
}
@ -118,8 +192,29 @@ public class MaterialUtil {
return material;
}
/**
* Get a list with item information
*
* @param items The items to get the information from
* @return The list, including the amount and names of the items
* @deprecated Use {@link ItemUtil#getItemList(ItemStack[])} instead!
*/
@Deprecated
public static String getItemList(ItemStack[] items) {
ItemStack[] mergedItems = InventoryUtil.mergeSimilarStacks(items);
List<String> itemText = new ArrayList<>();
for (ItemStack item : mergedItems) {
itemText.add(item.getAmount() + " " + getName(item));
}
return String.join(", ", itemText);
}
/**
* Returns item's name
* Use {@link ItemUtil#getName(ItemStack, int)} if you want to get name aliases too!
*
* @param itemStack ItemStack to name
* @return ItemStack's name
@ -138,114 +233,147 @@ public class MaterialUtil {
*/
@Deprecated
public static String getName(ItemStack itemStack, boolean showDataValue) {
String dataName = DataValue.name(itemStack);
if (dataName != null && showDataValue) {
return StringUtil.capitalizeFirstLetter(dataName + '_' + itemStack.getType(), '_');
} else {
return StringUtil.capitalizeFirstLetter(itemStack.getType().toString(), '_');
}
return getName(itemStack, 0);
}
/**
* Returns item's name, just like on the sign
* Use {@link ItemUtil#getSignName(ItemStack)} if you want to get name aliases too!
*
* @param itemStack ItemStack to name
* @return ItemStack's name
*/
public static String getSignName(ItemStack itemStack) {
return getName(itemStack, MAXIMUM_SIGN_LETTERS);
return getName(itemStack, MAXIMUM_SIGN_WIDTH);
}
/**
* Returns item's name, with a maximum length
* Returns item's name, with a maximum width.
* Use {@link ItemUtil#getName(ItemStack, int)} if you want to get name aliases too!
*
* @param itemStack ItemStack to name
* @param maxLength The max length that the name should have; 0 or below if it should be unlimited
* @param maxWidth The max width that the name should have; 0 or below if it should be unlimited
* @return ItemStack's name
*/
public static String getName(ItemStack itemStack, int maxLength) {
String alias = Odd.getAlias(itemStack);
String itemName = alias != null ? alias : itemStack.getType().toString();
String data = DataValue.name(itemStack);
public static String getName(ItemStack itemStack, int maxWidth) {
String itemName = itemStack.getType().toString();
String durability = "";
if (data == null) {
if (itemStack.getDurability() != 0) {
durability = ":" + itemStack.getDurability();
}
if (itemStack.getDurability() != 0) {
durability = ":" + itemStack.getDurability();
}
data = data != null ? data + "_" : "";
String metaData = "";
if (itemStack.hasItemMeta()) {
metaData = "#" + Metadata.getItemCode(itemStack);
}
int codeLength = (data + itemName + durability + metaData).length();
String code = data + itemName;
if (maxLength > 0 && codeLength > maxLength) {
int exceeding = codeLength - maxLength;
code = getShortenedName(code, code.length() - exceeding);
}
code = StringUtil.capitalizeFirstLetter(code, '_') + durability + metaData;
ItemStack codeItem = getItem(code);
if (!equals(itemStack, codeItem)) {
throw new IllegalArgumentException("Cannot generate code for item " + itemStack + " with maximum length of " + maxLength
+ " (code " + code + " results in item " + codeItem + ")");
String code = StringUtil.capitalizeFirstLetter(itemName, '_');
if (maxWidth > 0) {
int codeWidth = getMinecraftStringWidth(code + durability + metaData);
if (codeWidth > maxWidth) {
int exceeding = codeWidth - maxWidth;
code = getShortenedName(code, getMinecraftStringWidth(code) - exceeding);
}
}
return code;
return code + durability + metaData;
}
/**
* Get an item name shortened to a max length that is still reversable by {@link #getMaterial(String)}
*
* @param itemName The name of the item
* @param maxLength The max length
* @param maxWidth The max width
* @return The name shortened to the max length
*/
public static String getShortenedName(String itemName, int maxLength) {
if (itemName.length() <= maxLength) {
return itemName;
public static String getShortenedName(String itemName, int maxWidth) {
// Restore spaces in string that might be already be shortened
String name = itemName.replaceAll("([a-z])([A-Z1-9])", "$1 $2");
name = StringUtil.capitalizeFirstLetter(name.replace('_', ' '), ' ');
int width = getMinecraftStringWidth(name);
if (width <= maxWidth) {
return name;
}
int exceeding = itemName.length() - maxLength;
String[] itemParts = itemName.split("_");
String[] itemParts = name.split("[ \\-]");
String noSpaceName = String.join("", itemParts);
width = getMinecraftStringWidth(noSpaceName);
if (width <= maxWidth) {
return noSpaceName;
}
// Abbreviate some terms manually
for (Map.Entry<String, String> entry : ABBREVIATIONS.entrySet()) {
name = name.replaceAll(entry.getKey() + "( |$)", entry.getValue() + "$1");
itemParts = name.split("[ \\-]");
noSpaceName = String.join("", itemParts);
width = getMinecraftStringWidth(noSpaceName);
if (width <= maxWidth) {
return noSpaceName;
}
}
// Apply unidirectional abbreviations if it still doesn't work
for (Map.Entry<String, String> entry : UNIDIRECTIONAL_ABBREVIATIONS.entrySet()) {
name = name.replaceAll(entry.getKey() + "( |$)", entry.getValue() + "$1");
itemParts = name.split("[ \\-]");
noSpaceName = String.join("", itemParts);
width = getMinecraftStringWidth(noSpaceName);
if (width <= maxWidth) {
return noSpaceName;
}
}
int exceeding = width - maxWidth;
int shortestIndex = 0;
int longestIndex = 0;
for (int i = 0; i < itemParts.length; i++) {
if (itemParts[longestIndex].length() < itemParts[i].length()) {
if (getMinecraftStringWidth(itemParts[longestIndex]) < getMinecraftStringWidth(itemParts[i])) {
longestIndex = i;
}
if (itemParts[shortestIndex].length() > itemParts[i].length()) {
if (getMinecraftStringWidth(itemParts[shortestIndex]) > getMinecraftStringWidth(itemParts[i])) {
shortestIndex = i;
}
}
if (itemParts[longestIndex].length() - itemParts[shortestIndex].length() > exceeding) {
itemParts[longestIndex] = itemParts[longestIndex].substring(0, itemParts[longestIndex].length() - exceeding);
} else {
for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) {
int remove = 0;
if (itemParts[i].length() > itemParts[shortestIndex].length()) {
remove = itemParts[i].length() - itemParts[shortestIndex].length();
}
if (remove > exceeding) {
remove = exceeding;
}
itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - remove);
exceeding -= remove;
int shortestWidth = getMinecraftStringWidth(itemParts[shortestIndex]);
int longestWidth = getMinecraftStringWidth(itemParts[longestIndex]);
int remove = longestWidth - shortestWidth;
while (remove > 0 && exceeding > 0) {
int endWidth = getMinecraftCharWidth(itemParts[longestIndex].charAt(itemParts[longestIndex].length() - 1));
itemParts[longestIndex] = itemParts[longestIndex].substring(0, itemParts[longestIndex].length() - 1);
remove -= endWidth;
exceeding -= endWidth;
}
for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) {
int partWidth = getMinecraftStringWidth(itemParts[i]);
if (partWidth > shortestWidth) {
remove = partWidth - shortestWidth;
}
while (exceeding > 0) {
for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) {
itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - 1);
exceeding--;
}
if (remove > exceeding) {
remove = exceeding;
}
while (remove > 0) {
int endWidth = getMinecraftCharWidth(itemParts[i].charAt(itemParts[i].length() - 1));
itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - 1);
remove -= endWidth;
exceeding -= endWidth;
}
}
return String.join("_", itemParts);
while (exceeding > 0) {
for (int i = itemParts.length - 1; i >= 0 && exceeding > 0; i--) {
int endWidth = getMinecraftCharWidth(itemParts[i].charAt(itemParts[i].length() - 1));
itemParts[i] = itemParts[i].substring(0, itemParts[i].length() - 1);
exceeding -= endWidth;
}
}
return String.join("", itemParts);
}
/**
* Gives you an ItemStack from a String
*
@ -253,54 +381,27 @@ public class MaterialUtil {
* @return ItemStack
*/
public static ItemStack getItem(String itemName) {
ItemStack itemStack = Odd.getFromString(itemName);
if (itemStack != null) {
return itemStack;
}
String[] split = itemName.split("[:\\-#]");
for (int i = 0; i < split.length; i++) {
split[i] = split[i].trim();
}
Material material = getMaterial(split[0]);
short durability = getDurability(itemName);
MaterialData data = null;
MaterialParseEvent parseEvent = new MaterialParseEvent(split[0], durability);
Bukkit.getPluginManager().callEvent(parseEvent);
Material material = parseEvent.getMaterial();
if (material == null) {
if (!split[0].contains(" ")) {
return null;
}
for (int index = split[0].indexOf(' '); index >= 0 && index + 1 < split[0].length(); index = split[0].indexOf(' ', index + 1)) {
material = getMaterial(split[0].substring(index + 1));
if (material != null) {
data = DataValue.getData(split[0].substring(0, index), material);
break;
}
}
if (material == null) {
return null;
}
return null;
}
itemStack = new ItemStack(material);
if (data == null && durability > 0 && material.getMaxDurability() == 0) {
data = material.getNewData((byte) durability);
}
if (data != null) {
itemStack.setData(data);
durability = data.getData();
}
itemStack.setDurability(durability);
ItemStack itemStack = new ItemStack(material);
ItemMeta meta = getMetadata(itemName);
if (meta != null) {
if (meta instanceof Damageable) {
((Damageable) meta).setDamage(durability);
}
itemStack.setItemMeta(meta);
}
@ -348,128 +449,21 @@ public class MaterialUtil {
return Metadata.getFromCode(group);
}
//1.13 TODO: Get rid of numeric data values with the API that replaces MaterialData
public static class DataValue {
/**
* Gets the data value from a string
*
* @param type Data Value string
* @param material Material
* @return data value
* @deprecated Use {@link #getData}
*/
@Deprecated
public static byte get(String type, Material material) {
if (material == null || material.getData() == null) {
return 0;
}
MaterialData data = getData(type, material);
return data != null ? data.getData() : 0;
}
/**
* Gets the dat from a string
*
* @param type Data Value string
* @param material Material
* @return data
*/
public static MaterialData getData(String type, Material material) {
type = type.toUpperCase().replace(" ", "_");
MaterialData materialData = new ItemStack(material).getData();
if (materialData instanceof TexturedMaterial) {
TexturedMaterial texturedMaterial = (TexturedMaterial) materialData;
Material texture = new EnumParser<Material>().parse(type, texturedMaterial.getTextures().toArray(new Material[0]));
if (texture != null) {
((TexturedMaterial) materialData).setMaterial(texture);
}
} else if (materialData instanceof Colorable) {
DyeColor color = new EnumParser<DyeColor>().parse(type, DyeColor.values());
if (color != null) {
((Colorable) materialData).setColor(color);
}
} else if (materialData instanceof Wood) {
TreeSpecies species = new EnumParser<TreeSpecies>().parse(type, TreeSpecies.values());
if (species != null) {
((Wood) materialData).setSpecies(species);
}
} else if (materialData instanceof SpawnEgg) {
EntityType entityType = new EnumParser<EntityType>().parse(type, EntityType.values());
if (entityType != null) {
((SpawnEgg) materialData).setSpawnedType(entityType);
}
} else if (materialData instanceof Coal) {
CoalType coalType = new EnumParser<CoalType>().parse(type, CoalType.values());
if (coalType != null) {
((Coal) materialData).setType(coalType);
}
} else if (materialData instanceof Sandstone) {
SandstoneType sandstoneType = new EnumParser<SandstoneType>().parse(type, SandstoneType.values());
if (sandstoneType != null) {
((Sandstone) materialData).setType(sandstoneType);
}
}
return materialData;
}
/**
* Returns a string with the DataValue
*
* @param itemStack ItemStack to describe
* @return Data value string
*/
public static String name(ItemStack itemStack) {
MaterialData data = itemStack.getData();
if (data == null) {
return null;
}
if (data instanceof TexturedMaterial) {
return ((TexturedMaterial) data).getMaterial().name();
} else if (data instanceof Colorable) {
DyeColor color = ((Colorable) data).getColor();
return (color != null ? color.name() : null);
} else if (data instanceof Wood) {
//TreeSpecies specie = TreeSpecies.getByData((byte) (data.getData() & 3)); //This works, but not as intended
TreeSpecies specie = ((Wood) data).getSpecies();
return (specie != null && specie != TreeSpecies.GENERIC ? specie.name() : null);
} else if (data instanceof SpawnEgg) {
EntityType type = ((SpawnEgg) data).getSpawnedType();
return (type != null ? type.name() : null);
} else if (data instanceof Coal) {
CoalType coal = ((Coal) data).getType();
return (coal != null && coal != CoalType.COAL ? coal.name() : null);
} else if (data instanceof Sandstone) {
SandstoneType type = ((Sandstone) data).getType();
return (type != null && type != SandstoneType.CRACKED ? type.name() : null);
} else {
return null;
}
}
}
private static class EnumParser<E extends Enum<E>> {
private E parse(String name, E[] values) {
name = name.toUpperCase();
String formatted = name.replaceAll("(?<!^)(?>\\s?)([A-Z1-9])", "_$1").toUpperCase(Locale.ROOT).replace(' ', '_');
try {
return E.valueOf(values[0].getDeclaringClass(), name);
return E.valueOf(values[0].getDeclaringClass(), formatted);
} catch (IllegalArgumentException exception) {
E currentEnum = null;
String[] typeParts = name.split("[ _]");
List<E> possibleEnums = new ArrayList<>();
String[] typeParts = formatted.split("_");
int length = Short.MAX_VALUE;
for (E e : values) {
String enumName = e.name();
if (enumName.length() < length && enumName.startsWith(name)) {
length = (short) enumName.length();
currentEnum = e;
} else if (typeParts.length > 1) {
if (enumName.length() < length && enumName.startsWith(formatted)) {
length = enumName.length();
possibleEnums.add(e);
} else if (typeParts.length > 1) {
String[] nameParts = enumName.split("_");
if (typeParts.length == nameParts.length) {
boolean matched = true;
@ -480,13 +474,28 @@ public class MaterialUtil {
}
}
if (matched) {
currentEnum = e;
break;
possibleEnums.add(e);
}
}
}
}
return currentEnum;
if (possibleEnums.size() == 1) {
return possibleEnums.get(0);
} else if (possibleEnums.size() > 1) {
int formattedLength = formatted.length();
int closestDeviation = Short.MAX_VALUE;
E closestEnum = null;
for (E possibleEnum : possibleEnums) {
int deviation = possibleEnum.name().length() - formattedLength;
if (deviation < closestDeviation) {
closestDeviation = deviation;
closestEnum = possibleEnum;
}
}
return closestEnum;
}
return null;
}
}
}
@ -519,54 +528,9 @@ public class MaterialUtil {
}
}
public static class Odd {
private static boolean isInitialized = false;
/**
* Returns the item stack from OddItem plugin
*
* @param itemName Item name to parse
* @return itemStack that was parsed
*/
public static ItemStack getFromString(String itemName) {
if (!isInitialized) {
return null;
}
String name = itemName.replace(':', ';');
try {
return OddItem.getItemStack(name);
} catch (Exception ex) {
return null;
}
}
public static String getAlias(ItemStack itemStack) {
if (!isInitialized) {
return null;
}
try {
Collection<String> aliases = OddItem.getAliases(itemStack);
if (!aliases.isEmpty()) {
return aliases.iterator().next();
}
} catch (Exception ignored) {}
return null;
}
/**
* Lets the class know that it's safe to use the OddItem methods now
*/
public static void initialize() {
isInitialized = true;
}
}
public static class Show {
private static ShowItem showItem = null;
/**
* Lets the class know that it's safe to use the ShowItem methods now
*
@ -575,7 +539,7 @@ public class MaterialUtil {
public static void initialize(Plugin plugin) {
showItem = (ShowItem) plugin;
}
/**
* Send a message with hover info and icons
*
@ -583,35 +547,61 @@ public class MaterialUtil {
* @param message The raw message
* @param stock The items in stock
*/
public static boolean sendMessage(Player player, String message, ItemStack[] stock) {
public static boolean sendMessage(Player player, Messages.Message message, ItemStack[] stock, Map<String, String> replacementMap, String... replacements) {
return sendMessage(player, player.getName(), message, stock, replacementMap, replacements);
}
/**
* Send a message with hover info and icons
*
* @param player The player to send the message to
* @param playerName The name of the player in case he is offline and bungee messages are enabled
* @param message The raw message
* @param stock The items in stock
*/
public static boolean sendMessage(Player player, String playerName, Messages.Message message, ItemStack[] stock, Map<String, String> replacementMap, String... replacements) {
return sendMessage(player, playerName, message, true, stock, replacementMap, replacements);
}
/**
* Send a message with hover info and icons
*
* @param player The player to send the message to
* @param playerName The name of the player in case he is offline and bungee messages are enabled
* @param message The raw message
* @param showPrefix If the prefix should show
* @param stock The items in stock
*/
public static boolean sendMessage(Player player, String playerName, Messages.Message message, boolean showPrefix, ItemStack[] stock, Map<String, String> replacementMap, String... replacements) {
if (showItem == null) {
return false;
}
List<String> itemJson = new ArrayList<>();
TextComponent.Builder itemComponent = Component.text();
for (ItemStack item : InventoryUtil.mergeSimilarStacks(stock)) {
try {
itemJson.add(showItem.getItemConverter().createComponent(item, Level.OFF).toJsonString(player));
itemComponent.append(GsonComponentSerializer.gson().deserialize(showItem.getItemConverter().createComponent(item, Level.FINE).toJsonString(player)));
} catch (Exception e) {
ChestShop.getPlugin().getLogger().log(Level.WARNING, "Error while trying to send message '" + message + "' to player " + player.getName() + ": " + e.getMessage());
return false;
}
}
String joinedItemJson = itemJson.stream().collect(Collectors.joining("," + new JSONObject(ImmutableMap.of("text", " ")).toJSONString() + ", "));
String messageJsonString = Arrays.stream(message.split("%item"))
.map(s -> new JSONObject(ImmutableMap.of("text", s)).toJSONString())
.collect(Collectors.joining("," + joinedItemJson + ","));
while (messageJsonString.startsWith(",")) {
messageJsonString = messageJsonString.substring(1);
Map<String, String> newMap = new LinkedHashMap<>(replacementMap);
newMap.put("material", "item");
newMap.remove("item");
Component component = new Replacer()
.placeholderSuffix("")
.replace("item",itemComponent.build())
.replaceIn(message.getComponent(player, showPrefix, newMap, replacements));
if (player != null) {
ChestShop.getAudiences().player(player).sendMessage(component);
return true;
} else if (playerName != null) {
ChestShop.sendBungeeMessage(playerName, component);
return true;
}
while (messageJsonString.endsWith(",")) {
messageJsonString = messageJsonString.substring(0, messageJsonString.length() - 1);
}
showItem.tellRaw(player, messageJsonString);
return true;
}
}

View File

@ -154,4 +154,14 @@ public class NumberUtil {
return Integer.toString(number);
}
}
/**
* Convert a long to an integer while not overflowing but returning Integer.MAX_VALUE
*
* @param number The long to convert
* @return The integer value or Integer.MAX_VALUE on overflow
*/
public static int toInt(long number) {
return number > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) number;
}
}

View File

@ -1,11 +1,15 @@
package com.Acrobot.Breeze.Utils;
import java.math.BigDecimal;
import java.util.Locale;
/**
* @author Acrobot
*/
public class PriceUtil {
public static final double NO_PRICE = -1;
public static final double FREE = 0;
public static final BigDecimal NO_PRICE = BigDecimal.valueOf(-1);
public static final BigDecimal FREE = BigDecimal.valueOf(0);
public static final BigDecimal MAX = BigDecimal.valueOf(Double.MAX_VALUE);
public static final String FREE_TEXT = "free";
@ -13,15 +17,15 @@ public class PriceUtil {
public static final char SELL_INDICATOR = 's';
/**
* Gets the price from the text
* Gets the exact price from the text
*
* @param text Text to check
* @param indicator Price indicator (for example, B for buy)
* @return price
* @return exact price
*/
public static double get(String text, char indicator) {
String[] split = text.replace(" ", "").toLowerCase().split(":");
String character = String.valueOf(indicator).toLowerCase();
public static BigDecimal getExact(String text, char indicator) {
String[] split = text.replace(" ", "").toLowerCase(Locale.ROOT).split(":");
String character = String.valueOf(indicator).toLowerCase(Locale.ROOT);
for (String part : split) {
if (!part.startsWith(character) && !part.endsWith(character)) {
@ -34,28 +38,63 @@ public class PriceUtil {
return FREE;
}
if (NumberUtil.isDouble(part)) {
double price = Double.valueOf(part);
try {
BigDecimal price = new BigDecimal(part);
if (Double.isInfinite(price) || price <= 0) {
if (price.compareTo(MAX) > 0 || price.compareTo(BigDecimal.ZERO) < 0) {
return NO_PRICE;
} else {
return price;
}
}
} catch (NumberFormatException ignored) {}
}
return NO_PRICE;
}
/**
* Gets the price from the text
*
* @param text Text to check
* @param indicator Price indicator (for example, B for buy)
* @return price
* @deprecated Use {@link #getExact(String, char)}
*/
@Deprecated
public static double get(String text, char indicator) {
return getExact(text, indicator).doubleValue();
}
/**
* Gets the exact buy price from the text
*
* @param text Text to check
* @return Exact buy price
*/
public static BigDecimal getExactBuyPrice(String text) {
return getExact(text, BUY_INDICATOR);
}
/**
* Gets the exact sell price from the text
*
* @param text Text to check
* @return Exact sell price
*/
public static BigDecimal getExactSellPrice(String text) {
return getExact(text, SELL_INDICATOR);
}
/**
* Gets the buy price from the text
*
* @param text Text to check
* @return Buy price
* @deprecated Use {@link #getExactBuyPrice(String)}
*/
@Deprecated
public static double getBuyPrice(String text) {
return get(text, BUY_INDICATOR);
return getExactBuyPrice(text).doubleValue();
}
/**
@ -63,9 +102,11 @@ public class PriceUtil {
*
* @param text Text to check
* @return Sell price
* @deprecated Use {@link #getExactSellPrice(String)}
*/
@Deprecated
public static double getSellPrice(String text) {
return get(text, SELL_INDICATOR);
return getExactSellPrice(text).doubleValue();
}
/**
@ -96,7 +137,7 @@ public class PriceUtil {
* @return If the text contains indicated price
*/
public static boolean hasPrice(String text, char indicator) {
return get(text, indicator) != NO_PRICE;
return getExact(text, indicator).compareTo(NO_PRICE) != 0;
}
/**

View File

@ -0,0 +1,22 @@
package com.Acrobot.Breeze.Utils;
import java.util.regex.Pattern;
/**
* @author bricefrisco
*/
public class QuantityUtil {
public static final Pattern QUANTITY_LINE_WITH_COUNTER_PATTERN = Pattern.compile("^Q [1-9][0-9]{0,4} : C [0-9]{1,5}$");
public static int parseQuantity(String quantityLine) throws IllegalArgumentException {
if (quantityLineContainsCounter(quantityLine)) {
return Integer.parseInt(quantityLine.split(" : ")[0].replace("Q ", ""));
}
return Integer.parseInt(quantityLine);
}
public static boolean quantityLineContainsCounter(String quantityLine) {
return QUANTITY_LINE_WITH_COUNTER_PATTERN.matcher(quantityLine).matches();
}
}

View File

@ -1,9 +1,13 @@
package com.Acrobot.Breeze.Utils;
import org.apache.commons.lang.WordUtils;
import org.bukkit.ChatColor;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author Acrobot
@ -18,9 +22,16 @@ public class StringUtil {
* @return Reformatted string
*/
public static String capitalizeFirstLetter(String string, char separator) {
char[] separators = new char[]{separator};
if (string == null || string.isEmpty()) {
return string;
}
return WordUtils.capitalizeFully(string, separators).replace(String.valueOf(separator), " ");
// Split into words
String[] words = string.toLowerCase(Locale.ROOT).split(String.valueOf(separator));
// Capitalize every word and return joined string
return Arrays.stream(words)
.map(word -> word.substring(0, 1).toUpperCase(Locale.ROOT) + word.substring(1))
.collect(Collectors.joining(" "));
}
/**
@ -28,7 +39,7 @@ public class StringUtil {
*
* @param string String to reformat
* @return Reformatted string
* @see com.Acrobot.Breeze.Utils.StringUtil#capitalizeFirstLetter(String, char)
* @see StringUtil#capitalizeFirstLetter(String, char)
*/
public static String capitalizeFirstLetter(String string) {
return capitalizeFirstLetter(string, ' ');
@ -77,4 +88,87 @@ public class StringUtil {
return output;
}
private static String characters = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_'abcdefghijklmnopqrstuvwxyz{|}~¦ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜø£Ø׃áíóúñѪº¿®¬½¼¡«»";
private static int[] extraWidth = {4,2,5,6,6,6,6,3,5,5,5,6,2,6,2,6,6,6,6,6,6,6,6,6,6,6,2,2,5,6,5,6,7,6,6,6,6,6,6,6,6,4,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,4,6,6,3,6,6,6,6,6,5,6,6,2,6,5,3,6,6,6,6,6,6,6,4,6,6,6,6,6,6,5,2,5,7,6,6,6,6,6,6,6,6,6,6,6,6,4,6,3,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,4,6,6,3,6,6,6,6,6,6,6,7,6,6,6,2,6,6,8,9,9,6,6,6,8,8,6,8,8,8,8,8,6,6,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,6,9,9,9,5,9,9,8,7,7,8,7,8,8,8,7,8,8,7,9,9,6,7,7,7,7,7,9,6,7,8,7,6,6,9,7,6,7,1};
/**
* Get the width that a character is displayed with in the default resource pack.
* This relies on a hardcoded character to width mapping and might not be precise in places.
* @param c The character to get the width of
* @return The width of the character (will return 10 for characters that we don't know the width of)
*/
public static int getMinecraftCharWidth(char c) {
if (c != ChatColor.COLOR_CHAR) {
int index = characters.indexOf(c);
if (index > -1) {
return extraWidth[index];
} else {
return 10;
}
}
return 0;
}
/**
* Get the width that a string is displayed with in the default resource pack.
* This relies on a hardcoded character to width mapping and might not be precise in places.
* @param string The string to get the width of
* @return The width of the string
*/
public static int getMinecraftStringWidth(String string) {
int width = 0;
for (char c : string.toCharArray()) {
width += getMinecraftCharWidth(c);
}
return width;
}
/**
* Strip whitespace from the front and back
* @param string The string to strip
* @return The string with all whitespace from front and back stripped; returns null if input is null
*/
public static String strip(String string) {
if (string == null)
return null;
// The result stripped string
StringBuilder stripped = new StringBuilder();
// The current white space which only gets added once we find a non-whitespace character
StringBuilder cachedWhitespace = new StringBuilder();
// Check each code point (not characters to support UTF16 properly)
for (int codePoint : string.codePoints().toArray()) {
// Check if it's a whitespace, so we know if we should add it
if (!Character.isWhitespace(codePoint)) {
// Check if we have cached whitespace, if so append it first and reset the cache
if (cachedWhitespace.length() > 0) {
stripped.append(cachedWhitespace);
cachedWhitespace = new StringBuilder();
}
// Append current code point
stripped.appendCodePoint(codePoint);
} else if (stripped.length() > 0) {
// If we already have some non-whitespace content in the final stripped
// then cache the current whitespace as it wasn't at the start
cachedWhitespace.appendCodePoint(codePoint);
} // Otherwise, this was the start, and we don't need the cached whitespace
}
// Return the stripped string, without any whitespace from the end left
return stripped.toString();
}
/**
* Create a map from strings
* @param values The values to add
* @return The map
*/
public static Map<String, String> map(String... values) {
Map<String, String> map = new LinkedHashMap<>();
for (int i = 0; i + 1 < values.length; i+=2) {
map.put(values[i], values[i + 1]);
}
return map;
}
}

View File

@ -3,8 +3,10 @@ package com.Acrobot.ChestShop;
import com.Acrobot.Breeze.Configuration.Configuration;
import com.Acrobot.ChestShop.Commands.Give;
import com.Acrobot.ChestShop.Commands.ItemInfo;
import com.Acrobot.ChestShop.Commands.ShopInfo;
import com.Acrobot.ChestShop.Commands.Toggle;
import com.Acrobot.ChestShop.Commands.Version;
import com.Acrobot.ChestShop.Commands.AccessToggle;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Database.Migrations;
@ -17,11 +19,17 @@ import com.Acrobot.ChestShop.Listeners.Economy.TaxModule;
import com.Acrobot.ChestShop.Listeners.AuthMeChestShopListener;
import com.Acrobot.ChestShop.Listeners.GarbageTextListener;
import com.Acrobot.ChestShop.Listeners.Item.ItemMoveListener;
import com.Acrobot.ChestShop.Listeners.Item.ItemStringListener;
import com.Acrobot.ChestShop.Listeners.ItemInfoListener;
import com.Acrobot.ChestShop.Listeners.Modules.ItemAliasModule;
import com.Acrobot.ChestShop.Listeners.Modules.MetricsModule;
import com.Acrobot.ChestShop.Listeners.Modules.StockCounterModule;
import com.Acrobot.ChestShop.Listeners.ShopInfoListener;
import com.Acrobot.ChestShop.Listeners.SignParseListener;
import com.Acrobot.ChestShop.Listeners.Modules.DiscountModule;
import com.Acrobot.ChestShop.Listeners.Modules.PriceRestrictionModule;
import com.Acrobot.ChestShop.Listeners.Player.*;
import com.Acrobot.ChestShop.Listeners.PostShopCreation.CreationFeeGetter;
import com.Acrobot.ChestShop.Listeners.PreShopCreation.CreationFeeGetter;
import com.Acrobot.ChestShop.Listeners.PostShopCreation.MessageSender;
import com.Acrobot.ChestShop.Listeners.PostShopCreation.ShopCreationLogger;
import com.Acrobot.ChestShop.Listeners.PostShopCreation.SignSticker;
@ -36,8 +44,18 @@ import com.Acrobot.ChestShop.Logging.FileFormatter;
import com.Acrobot.ChestShop.Metadata.ItemDatabase;
import com.Acrobot.ChestShop.Signs.RestrictedSign;
import com.Acrobot.ChestShop.UUIDs.NameManager;
import com.Acrobot.ChestShop.Updater.JenkinsBuildsNotifier;
import com.Acrobot.ChestShop.Updater.Updater;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Marker;
@ -47,8 +65,18 @@ import org.apache.logging.log4j.core.config.LoggerConfig;
import org.apache.logging.log4j.core.filter.AbstractFilter;
import org.apache.logging.log4j.message.Message;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.AdvancedBarChart;
import org.bstats.charts.DrilldownPie;
import org.bstats.charts.MultiLineChart;
import org.bstats.charts.SimpleBarChart;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Server;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.Event;
import org.bukkit.event.Listener;
@ -58,9 +86,19 @@ import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarFile;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Main file of the plugin
@ -71,61 +109,106 @@ public class ChestShop extends JavaPlugin {
private static ChestShop plugin;
private static Server server;
private static PluginDescriptionFile description;
private static final ExecutorService executorService = Executors.newCachedThreadPool();
private static Metrics bStats;
private static BukkitAudiences audiences;
private static File dataFolder;
private static ItemDatabase itemDatabase;
private static Logger logger;
private static Logger shopLogger;
private FileHandler handler;
private List<PluginCommand> commands = new ArrayList<>();
public ChestShop() {
dataFolder = getDataFolder();
logger = getLogger();
shopLogger = Logger.getLogger("ChestShop Shops");
shopLogger.setParent(logger);
description = getDescription();
server = getServer();
plugin = this;
}
public void onEnable() {
Configuration.pairFileAndClass(loadFile("config.yml"), Properties.class);
Configuration.pairFileAndClass(loadFile("local.yml"), Messages.class);
@Override
public void onLoad() {
Dependencies.initializePlugins();
}
@Override
public void onEnable() {
bStats = new Metrics(this, 1109);
audiences = BukkitAudiences.create(this);
turnOffDatabaseLogging();
if (!handleMigrations()) {
return;
}
registerCommand("iteminfo", new ItemInfo(), Permission.ITEMINFO);
registerCommand("shopinfo", new ShopInfo(), Permission.SHOPINFO);
registerCommand("csVersion", new Version(), Permission.ADMIN);
registerCommand("csMetrics", new com.Acrobot.ChestShop.Commands.Metrics(), Permission.ADMIN);
registerCommand("csGive", new Give(), Permission.ADMIN);
registerCommand("cstoggle", new Toggle(), Permission.NOTIFY_TOGGLE);
registerCommand("csaccess", new AccessToggle(), Permission.ACCESS_TOGGLE);
loadConfig();
itemDatabase = new ItemDatabase();
NameManager.load();
Dependencies.loadPlugins();
if (!Dependencies.loadPlugins()) {
getServer().getPluginManager().disablePlugin(this);
return;
}
registerEvents();
if (Properties.LOG_TO_FILE) {
File log = loadFile("ChestShop.log");
FileHandler handler = loadHandler(log.getAbsolutePath());
handler.setFormatter(new FileFormatter());
this.handler = handler;
logger.addHandler(handler);
}
if (!Properties.LOG_TO_CONSOLE) {
logger.setUseParentHandlers(false);
}
getCommand("iteminfo").setExecutor(new ItemInfo());
getCommand("csVersion").setExecutor(new Version());
getCommand("csGive").setExecutor(new Give());
getCommand("cstoggle").setExecutor(new Toggle());
registerPluginMessagingChannels();
startStatistics();
startBuildNotificatier();
startUpdater();
}
private void registerCommand(String name, CommandExecutor executor, Permission permission) {
PluginCommand command = getCommand(name);
if (command != null) {
command.setExecutor(executor);
command.setPermission(permission.toString());
commands.add(command);
}
}
public void loadConfig() {
Configuration.pairFileAndClass(loadFile("config.yml"), Properties.class, getBukkitLogger());
Messages.load();
NameManager.load();
commands.forEach(c -> c.setPermissionMessage(Messages.ACCESS_DENIED.getTextWithPrefix(null)));
if (handler != null) {
shopLogger.removeHandler(handler);
}
if (Properties.LOG_TO_FILE) {
if (handler == null) {
File log = loadFile("ChestShop.log");
handler = loadHandler(log.getAbsolutePath());
handler.setFormatter(new FileFormatter());
}
shopLogger.addHandler(handler);
}
shopLogger.setUseParentHandlers(Properties.LOG_TO_CONSOLE);
}
private void turnOffDatabaseLogging() {
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
org.apache.logging.log4j.core.config.Configuration config = ctx.getConfiguration();
@ -176,7 +259,7 @@ public class ChestShop extends JavaPlugin {
try {
previousVersion.save(versionFile);
} catch (IOException e) {
e.printStackTrace();
getLogger().log(java.util.logging.Level.SEVERE, "Unable to save new database version " + Migrations.CURRENT_DATABASE_VERSION, e);
}
}
@ -193,7 +276,7 @@ public class ChestShop extends JavaPlugin {
try {
previousVersion.save(versionFile);
} catch (IOException e) {
e.printStackTrace();
getLogger().log(java.util.logging.Level.SEVERE, "Unable to save new database version " + newVersion, e);
}
}
return true;
@ -214,7 +297,7 @@ public class ChestShop extends JavaPlugin {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
getBukkitLogger().log(java.util.logging.Level.SEVERE, "Unable to load file " + file.getName(), e);
}
}
@ -227,14 +310,17 @@ public class ChestShop extends JavaPlugin {
try {
handler = new FileHandler(path, true);
} catch (IOException ex) {
ex.printStackTrace();
getBukkitLogger().log(java.util.logging.Level.SEVERE, "Unable to load handler " + path, ex);
}
return handler;
}
public void onDisable() {
getServer().getScheduler().cancelTasks(this);
executorService.shutdown();
try {
executorService.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException ignored) {}
Toggle.clearToggledPlayers();
@ -248,6 +334,10 @@ public class ChestShop extends JavaPlugin {
private void registerEvents() {
registerEvent(new com.Acrobot.ChestShop.Plugins.ChestShop()); //Chest protection
registerEvent(new Dependencies());
registerEvent(new NameManager());
registerPreShopCreationEvents();
registerPreTransactionEvents();
registerPostShopCreationEvents();
@ -267,9 +357,12 @@ public class ChestShop extends JavaPlugin {
registerEvent(new PlayerLeave());
registerEvent(new PlayerTeleport());
registerEvent(new SignParseListener());
registerEvent(new ItemStringListener());
registerEvent(new ItemInfoListener());
registerEvent(new ShopInfoListener());
registerEvent(new GarbageTextListener());
Plugin authMe = getServer().getPluginManager().getPlugin("AuthMe");
if (authMe != null && authMe.isEnabled()) {
registerEvent(new AuthMeChestShopListener());
@ -317,6 +410,7 @@ public class ChestShop extends JavaPlugin {
registerEvent(new AmountAndPriceChecker());
}
registerEvent(new InvalidNameIgnorer());
registerEvent(new CreativeModeIgnorer());
registerEvent(new ErrorMessageSender());
registerEvent(new PermissionChecker());
@ -335,8 +429,11 @@ public class ChestShop extends JavaPlugin {
}
private void registerModules() {
registerEvent(new ItemAliasModule());
registerEvent(new DiscountModule());
registerEvent(new MetricsModule());
registerEvent(new PriceRestrictionModule());
registerEvent(new StockCounterModule());
registerEconomicalModules();
}
@ -346,27 +443,130 @@ public class ChestShop extends JavaPlugin {
registerEvent(new TaxModule());
}
private void registerPluginMessagingChannels() {
if (Properties.BUNGEECORD_MESSAGES) {
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
}
}
public void registerEvent(Listener listener) {
getServer().getPluginManager().registerEvents(listener, this);
}
private void startStatistics() {
try {
new org.mcstats.Metrics(this).start();
} catch (IOException ex) {
ChestShop.getBukkitLogger().severe("There was an error while submitting MCStats statistics.");
}
new org.bstats.MetricsLite(this);
try (JarFile jarFile = new JarFile(this.getFile())) {
String dist = jarFile.getManifest().getMainAttributes().getValue("Distribution-Type");
bStats.addCustomChart(new SimplePie("distributionType", () -> dist));
} catch (IOException ignored) {}
String serverVersion = getServer().getBukkitVersion().split("-")[0];
bStats.addCustomChart(createStaticDrilldownStat("versionMcSelf", serverVersion, getDescription().getVersion()));
bStats.addCustomChart(createStaticDrilldownStat("versionSelfMc", getDescription().getVersion(), serverVersion));
bStats.addCustomChart(createStaticDrilldownStat("serverTypeVersionSelf", getServer().getName(), getDescription().getVersion()));
bStats.addCustomChart(createStaticDrilldownStat("versionSelfServerType", getDescription().getVersion(), getServer().getName()));
bStats.addCustomChart(createStaticDrilldownStat("versionMcServerType", serverVersion, getServer().getName()));
bStats.addCustomChart(createStaticDrilldownStat("serverTypeVersionMc", getServer().getName(), serverVersion));
String javaVersion = System.getProperty("java.version");
bStats.addCustomChart(createStaticDrilldownStat("versionJavaSelf", javaVersion, getDescription().getVersion()));
bStats.addCustomChart(createStaticDrilldownStat("versionSelfJava", getDescription().getVersion(), javaVersion));
bStats.addCustomChart(createStaticDrilldownStat("versionJavaMc", javaVersion, serverVersion));
bStats.addCustomChart(createStaticDrilldownStat("versionMcJava", serverVersion, javaVersion));
bStats.addCustomChart(new SingleLineChart("shopAccounts", NameManager::getAccountCount));
bStats.addCustomChart(new MultiLineChart("transactionCount", () -> ImmutableMap.of(
"total", MetricsModule.getTotalTransactions(),
"buy", MetricsModule.getBuyTransactions(),
"sell", MetricsModule.getSellTransactions()
)));
bStats.addCustomChart(new MultiLineChart("itemCount", () -> ImmutableMap.of(
"total", MetricsModule.getTotalItemsCount(),
"buy", MetricsModule.getSoldItemsCount(),
"sell", MetricsModule.getBoughtItemsCount()
)));
bStats.addCustomChart(new SimplePie("includeSettingsInMetrics", () -> Properties.INCLUDE_SETTINGS_IN_METRICS ? "enabled" : "disabled"));
if (!Properties.INCLUDE_SETTINGS_IN_METRICS) return;
bStats.addCustomChart(new SimplePie("ensure-correct-playerid", () -> Properties.ENSURE_CORRECT_PLAYERID ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("allow-sign-chest-open", () -> Properties.ALLOW_SIGN_CHEST_OPEN ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("uses-server-economy-account", () -> !Properties.SERVER_ECONOMY_ACCOUNT.isEmpty() ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("uses-server-economy-account-uuid", () -> !Properties.SERVER_ECONOMY_ACCOUNT_UUID.equals(new UUID(0, 0)) ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("allow-partial-transactions", () -> Properties.ALLOW_PARTIAL_TRANSACTIONS ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("bungeecord-messages", () -> Properties.BUNGEECORD_MESSAGES ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("allow-multiple-shops-at-one-block", () -> Properties.ALLOW_MULTIPLE_SHOPS_AT_ONE_BLOCK ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("allow-partial-transactions", () -> Properties.ALLOW_PARTIAL_TRANSACTIONS ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("log-to-console", () -> Properties.LOG_TO_CONSOLE ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("log-to-file", () -> Properties.LOG_TO_FILE ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("auto-update", () -> !Properties.TURN_OFF_UPDATES ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("release-notifications", () -> !Properties.TURN_OFF_UPDATE_NOTIFIER ? "enabled" : "disabled"));
bStats.addCustomChart(new SimplePie("dev-build-notifications", () -> !Properties.TURN_OFF_DEV_UPDATE_NOTIFIER ? "enabled" : "disabled"));
bStats.addCustomChart(new AdvancedBarChart("pluginProperties", () -> {
Map<String, int[]> map = new LinkedHashMap<>();
map.put("ensure-correct-playerid", getChartArray(Properties.ENSURE_CORRECT_PLAYERID));
map.put("reverse-buttons", getChartArray(Properties.REVERSE_BUTTONS));
map.put("shift-sells-in-stacks", getChartArray(Properties.SHIFT_SELLS_IN_STACKS));
map.put("shift-sells-everything", getChartArray(Properties.SHIFT_SELLS_EVERYTHING));
map.put("allow-sign-chest-open", getChartArray(!Properties.ALLOW_SIGN_CHEST_OPEN));
map.put("sign-dying", getChartArray(!Properties.SIGN_DYING));
map.put("remove-empty-shops", getChartArray(!Properties.REMOVE_EMPTY_SHOPS));
map.put("remove-empty-chests", getChartArray(!Properties.REMOVE_EMPTY_CHESTS));
map.put("uses-server-economy-account", getChartArray(!Properties.SERVER_ECONOMY_ACCOUNT.isEmpty()));
map.put("uses-server-economy-account-uuid", getChartArray(!Properties.SERVER_ECONOMY_ACCOUNT_UUID.equals(new UUID(0, 0))));
map.put("allow-multiple-shops-at-one-block", getChartArray(Properties.ALLOW_MULTIPLE_SHOPS_AT_ONE_BLOCK));
map.put("allow-partial-transactions", getChartArray(Properties.ALLOW_PARTIAL_TRANSACTIONS));
map.put("bungeecord-messages", getChartArray(Properties.BUNGEECORD_MESSAGES));
map.put("log-to-console", getChartArray(Properties.LOG_TO_CONSOLE));
map.put("log-to-file", getChartArray(Properties.LOG_TO_FILE));
map.put("auto-update", getChartArray(!Properties.TURN_OFF_UPDATES));
map.put("release-notifications", getChartArray(!Properties.TURN_OFF_UPDATE_NOTIFIER));
map.put("dev-build-notifications", getChartArray(!Properties.TURN_OFF_DEV_UPDATE_NOTIFIER));
return map;
}));
bStats.addCustomChart(new SimpleBarChart("shopContainers",
() -> Properties.SHOP_CONTAINERS.stream().map(Material::name).collect(Collectors.toMap(k -> k, k -> 1))));
}
public static DrilldownPie createStaticDrilldownStat(String statId, String value1, String value2) {
final Map<String, Map<String, Integer>> map = ImmutableMap.of(value1, ImmutableMap.of(value2, 1));
return new DrilldownPie(statId, () -> map);
}
private int[] getChartArray(boolean value) {
return new int[]{!value ? 1 : 0, value ? 0 : 1};
}
private static final int PROJECT_BUKKITDEV_ID = 31263;
private void startUpdater() {
if (Properties.TURN_OFF_UPDATES) {
getLogger().info("Auto-updater is disabled. If you want the plugin to automatically download new releases then set 'TURN_OFF_UPDATES' to 'false' in your config.yml!");
if (!Properties.TURN_OFF_UPDATE_NOTIFIER) {
final Updater updater = new Updater(this, getPluginName().toLowerCase(Locale.ROOT), this.getFile(), Updater.UpdateType.NO_DOWNLOAD, true);
runInAsyncThread(() -> {
if (updater.getResult() == Updater.UpdateResult.UPDATE_AVAILABLE) {
getLogger().info("There is a new version available: " + updater.getLatestName() + ". You can download it from https://modrinth.com/plugin/" + getPluginName().toLowerCase(Locale.ROOT));
}
});
}
return;
}
new Updater(this, PROJECT_BUKKITDEV_ID, this.getFile(), Updater.UpdateType.DEFAULT, true);
new Updater(this, getPluginName().toLowerCase(Locale.ROOT), this.getFile(), Updater.UpdateType.DEFAULT, true);
}
private static final String PROJECT_JENKINS_JOB_URL = "https://ci.minebench.de/job/ChestShop-3/";
private void startBuildNotificatier() {
if (Properties.TURN_OFF_DEV_UPDATE_NOTIFIER) {
return;
}
new JenkinsBuildsNotifier(this, PROJECT_JENKINS_JOB_URL);
}
///////////////////////////////////////////////////////////////////////////////
@ -379,10 +579,20 @@ public class ChestShop extends JavaPlugin {
return dataFolder;
}
public static Logger getShopLogger() {
return shopLogger;
}
public static Logger getBukkitLogger() {
return logger;
}
public static void logDebug(String message) {
if (Properties.DEBUG) {
getBukkitLogger().info("[DEBUG] " + message);
}
}
public static Server getBukkitServer() {
return server;
}
@ -403,11 +613,51 @@ public class ChestShop extends JavaPlugin {
return plugin;
}
public static Metrics getMetrics() {
return bStats;
}
public static BukkitAudiences getAudiences() {
return audiences;
}
public static void registerListener(Listener listener) {
plugin.registerEvent(listener);
}
public static void callEvent(Event event) {
public static <E extends Event> E callEvent(E event) {
Bukkit.getPluginManager().callEvent(event);
return event;
}
public static void sendBungeeMessage(String playerName, Messages.Message message, Map<String, String> replacementMap, String... replacements) {
sendBungeeMessage(playerName, message.getComponent(null, true, replacementMap, replacements));
}
public static void sendBungeeMessage(String playerName, String message) {
sendBungeeMessage(playerName, "Message", message);
}
public static void sendBungeeMessage(String playerName, BaseComponent[] message) {
sendBungeeMessage(playerName, "MessageRaw", ComponentSerializer.toString(message));
}
public static void sendBungeeMessage(String playerName, Component message) {
sendBungeeMessage(playerName, "MessageRaw", GsonComponentSerializer.gson().serialize(message));
}
private static void sendBungeeMessage(String playerName, String channel, String message) {
if (Properties.BUNGEECORD_MESSAGES && !Bukkit.getOnlinePlayers().isEmpty()) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF(channel);
out.writeUTF(playerName);
out.writeUTF(message);
Bukkit.getOnlinePlayers().iterator().next().sendPluginMessage(plugin, "BungeeCord", out.toByteArray());
}
}
public static void runInAsyncThread(Runnable runnable) {
executorService.submit(runnable);
}
}

View File

@ -0,0 +1,56 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.google.common.base.Preconditions;
import org.bukkit.OfflinePlayer;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author g--o
*/
public class AccessToggle implements CommandExecutor {
private static final Set<UUID> toggledPlayers = new HashSet<>();
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!(sender instanceof Player)) {
return false;
}
Player player = (Player) sender;
if (setIgnoring(player, !isIgnoring(player))) {
Messages.TOGGLE_ACCESS_OFF.sendWithPrefix(player);
} else {
Messages.TOGGLE_ACCESS_ON.sendWithPrefix(player);
}
return true;
}
public static boolean isIgnoring(OfflinePlayer player) {
return player != null && isIgnoring(player.getUniqueId());
}
private static boolean isIgnoring(UUID playerId) {
return toggledPlayers.contains(playerId);
}
public static boolean setIgnoring(Player player, boolean ignoring) {
Preconditions.checkNotNull(player); // Make sure the player instance is not null, in case there are any errors in the code
if (ignoring) {
toggledPlayers.add(player.getUniqueId());
} else {
toggledPlayers.remove(player.getUniqueId());
}
return ignoring;
}
}

View File

@ -4,7 +4,8 @@ import com.Acrobot.Breeze.Utils.InventoryUtil;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.NumberUtil;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -12,8 +13,9 @@ import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author Acrobot
@ -22,11 +24,6 @@ public class Give implements CommandExecutor {
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!Permission.has(sender, Permission.ADMIN)) {
sender.sendMessage(Messages.prefix(Messages.ACCESS_DENIED));
return true;
}
if (args.length < 1) {
return false;
}
@ -34,7 +31,7 @@ public class Give implements CommandExecutor {
Player receiver = (sender instanceof Player ? (Player) sender : null);
int quantity = 1;
List<Integer> disregardedIndexes = new ArrayList<Integer>();
Set<Integer> disregardedIndexes = new HashSet<>();
if (args.length > 1) {
for (int index = args.length - 1; index >= 0; --index) {
@ -50,7 +47,7 @@ public class Give implements CommandExecutor {
}
for (int index = args.length - 1; index >= 0; --index) {
if (!NumberUtil.isInteger(args[index]) || Integer.parseInt(args[index]) < 0) {
if (disregardedIndexes.contains(index) || !NumberUtil.isInteger(args[index]) || Integer.parseInt(args[index]) < 0) {
continue;
}
@ -62,28 +59,26 @@ public class Give implements CommandExecutor {
}
if (receiver == null) {
sender.sendMessage(Messages.prefix(Messages.PLAYER_NOT_FOUND));
Messages.PLAYER_NOT_FOUND.sendWithPrefix(sender);
return true;
}
ItemStack item = getItem(args, disregardedIndexes);
if (MaterialUtil.isEmpty(item)) {
sender.sendMessage(Messages.prefix(Messages.INCORRECT_ITEM_ID));
Messages.INCORRECT_ITEM_ID.sendWithPrefix(sender);
return true;
}
item.setAmount(quantity);
InventoryUtil.add(item, receiver.getInventory());
sender.sendMessage(Messages.prefix(Messages.ITEM_GIVEN
.replace("%item", MaterialUtil.getName(item))
.replace("%player", receiver.getName())));
Messages.ITEM_GIVEN.send(sender, "item", ItemUtil.getName(item), "player", receiver.getName());
return true;
}
private static ItemStack getItem(String[] arguments, List<Integer> disregardedElements) {
private static ItemStack getItem(String[] arguments, Set<Integer> disregardedElements) {
StringBuilder builder = new StringBuilder(arguments.length * 5);
for (int index = 0; index < arguments.length; ++index) {
@ -94,6 +89,8 @@ public class Give implements CommandExecutor {
builder.append(arguments[index]).append(' ');
}
return MaterialUtil.getItem(builder.toString());
ItemParseEvent parseEvent = new ItemParseEvent(builder.toString());
Bukkit.getPluginManager().callEvent(parseEvent);
return parseEvent.getItem();
}
}

View File

@ -4,15 +4,25 @@ import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.Breeze.Utils.StringUtil;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Events.ItemInfoEvent;
import com.Acrobot.ChestShop.Events.ItemParseEvent;
import com.Acrobot.ChestShop.Utils.ItemUtil;
import com.google.common.collect.ImmutableMap;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
import java.util.logging.Level;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo;
import static com.Acrobot.ChestShop.Configuration.Messages.iteminfo_shopname;
/**
* @author Acrobot
@ -28,24 +38,24 @@ public class ItemInfo implements CommandExecutor {
item = ((HumanEntity) sender).getItemInHand();
} else {
item = MaterialUtil.getItem(StringUtil.joinArray(args));
ItemParseEvent parseEvent = new ItemParseEvent(StringUtil.joinArray(args));
Bukkit.getPluginManager().callEvent(parseEvent);
item = parseEvent.getItem();
}
if (MaterialUtil.isEmpty(item)) {
return false;
}
sender.sendMessage(Messages.prefix(iteminfo));
iteminfo.send(sender);
if (!sendItemName(sender, item, Messages.iteminfo_fullname)) return true;
try {
sender.sendMessage(ChatColor.WHITE + "Full Name: " + ChatColor.GRAY + MaterialUtil.getName(item));
iteminfo_shopname.send(sender, "item", ItemUtil.getSignName(item));
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.RED + "Full Name Error: " + e.getMessage());
}
try {
sender.sendMessage(ChatColor.WHITE + "Shop Sign: " + ChatColor.GRAY + MaterialUtil.getSignName(item));
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.RED + "Shop Sign Error: " + e.getMessage());
sender.sendMessage(ChatColor.RED + "Error while generating shop sign name. Please contact an admin or take a look at the console/log!");
ChestShop.getPlugin().getLogger().log(Level.SEVERE, "Error while generating shop sign item name", e);
return true;
}
ItemInfoEvent event = new ItemInfoEvent(sender, item);
@ -54,23 +64,18 @@ public class ItemInfo implements CommandExecutor {
return true;
}
private static String getNameAndID(ItemStack item) {
return MaterialUtil.getName(item);
}
private static String getDurability(ItemStack item) {
if (item.getDurability() != 0) {
return ChatColor.DARK_GREEN + ":" + Integer.toString(item.getDurability());
} else {
return "";
public static boolean sendItemName(CommandSender sender, ItemStack item, Messages.Message message) {
try {
Map<String, String> replacementMap = ImmutableMap.of("item", ItemUtil.getName(item));
if (!Properties.SHOWITEM_MESSAGE || !(sender instanceof Player)
|| !MaterialUtil.Show.sendMessage((Player) sender, sender.getName(), message, false, new ItemStack[]{item}, replacementMap)) {
message.send(sender, replacementMap);
}
} catch (IllegalArgumentException e) {
sender.sendMessage(ChatColor.RED + "Error while generating full name. Please contact an admin or take a look at the console/log!");
ChestShop.getPlugin().getLogger().log(Level.SEVERE, "Error while generating full item name", e);
return false;
}
}
private static String getMetadata(ItemStack item) {
if (!item.hasItemMeta()) {
return "";
}
return ChatColor.GOLD + "#" + MaterialUtil.Metadata.getItemCode(item);
return true;
}
}

View File

@ -0,0 +1,26 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Listeners.Modules.MetricsModule;
import com.Acrobot.ChestShop.UUIDs.NameManager;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
/**
* @author Acrobot
*/
public class Metrics implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
Messages.METRICS.send(sender,
"accounts", String.valueOf(NameManager.getAccountCount()),
"totalTransactions", String.valueOf(MetricsModule.getTotalTransactions()),
"buyTransactions", String.valueOf(MetricsModule.getBuyTransactions()),
"sellTransactions", String.valueOf(MetricsModule.getSellTransactions()),
"totalItems", String.valueOf(MetricsModule.getTotalItemsCount()),
"boughtItems", String.valueOf(MetricsModule.getBoughtItemsCount()),
"soldItems", String.valueOf(MetricsModule.getSoldItemsCount())
);
return true;
}
}

View File

@ -0,0 +1,43 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Events.ShopInfoEvent;
import com.Acrobot.ChestShop.Signs.ChestShopSign;
import com.Acrobot.ChestShop.Utils.uBlock;
import org.bukkit.ChatColor;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
/**
* @author Phoenix616
*/
public class ShopInfo implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (sender instanceof Player) {
Block target = ((Player) sender).getTargetBlockExact(5);
if (target != null) {
Sign sign = null;
if (ChestShopSign.isValid(target)) {
sign = (Sign) target.getState();
} else if (uBlock.couldBeShopContainer(target)) {
sign = uBlock.getConnectedSign(target);
}
if (sign != null) {
ShopInfoEvent event = new ShopInfoEvent((Player) sender, sign);
ChestShop.callEvent(event);
} else {
Messages.NO_SHOP_FOUND.sendWithPrefix(sender);
}
}
} else {
sender.sendMessage(ChatColor.RED + "Command must be run by a player!");
}
return true;
}
}

View File

@ -1,22 +1,23 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Permission;
import org.apache.commons.lang.Validate;
import com.google.common.base.Preconditions;
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.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* @author KingFaris10
*/
public class Toggle implements CommandExecutor {
private static final List<String> toggledPlayers = new ArrayList<String>();
private static final Set<UUID> toggledPlayers = new HashSet<>();
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
@ -24,21 +25,16 @@ public class Toggle implements CommandExecutor {
return false;
}
if (!Permission.has(sender, Permission.NOTIFY_TOGGLE)) {
sender.sendMessage(Messages.ACCESS_DENIED);
return true;
}
Player player = (Player) sender;
if (args.length != 0) {
return false;
}
if (setIgnoring(player, !toggledPlayers.contains(player.getName()))) {
player.sendMessage(Messages.prefix(Messages.TOGGLE_MESSAGES_OFF));
if (setIgnoring(player, !isIgnoring(player))) {
Messages.TOGGLE_MESSAGES_OFF.sendWithPrefix(player);
} else {
player.sendMessage(Messages.prefix(Messages.TOGGLE_MESSAGES_ON));
Messages.TOGGLE_MESSAGES_ON.sendWithPrefix(player);
}
return true;
@ -49,24 +45,28 @@ public class Toggle implements CommandExecutor {
}
public static boolean isIgnoring(OfflinePlayer player) {
return player != null && isIgnoring(player.getName());
return player != null && isIgnoring(player.getUniqueId());
}
public static boolean isIgnoring(UUID playerId) {
return toggledPlayers.contains(playerId);
}
/**
* @deprecated Use {@link #isIgnoring(UUID)}
*/
@Deprecated
public static boolean isIgnoring(String playerName) {
return toggledPlayers.contains(playerName);
return isIgnoring(Bukkit.getOfflinePlayer(playerName));
}
public static boolean setIgnoring(Player player, boolean ignoring) {
Validate.notNull(player); // Make sure the player instance is not null, in case there are any errors in the code
Preconditions.checkNotNull(player); // Make sure the player instance is not null, in case there are any errors in the code
if (ignoring) {
if (!toggledPlayers.contains(player.getName())) {
toggledPlayers.add(player.getName());
}
toggledPlayers.add(player.getUniqueId());
} else {
if (toggledPlayers.contains(player.getName())) {
toggledPlayers.remove(player.getName());
}
toggledPlayers.remove(player.getUniqueId());
}
return ignoring;

View File

@ -1,11 +1,7 @@
package com.Acrobot.ChestShop.Commands;
import com.Acrobot.Breeze.Configuration.Configuration;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Configuration.Messages;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Permission;
import com.Acrobot.ChestShop.UUIDs.NameManager;
import com.Acrobot.ChestShop.Events.ChestShopReloadEvent;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
@ -16,15 +12,8 @@ import org.bukkit.command.CommandSender;
*/
public class Version implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (!Permission.has(sender, Permission.ADMIN)) {
sender.sendMessage(Messages.prefix(Messages.ACCESS_DENIED));
return true;
}
if (args.length > 0 && args[0].equals("reload")) {
Configuration.pairFileAndClass(ChestShop.loadFile("config.yml"), Properties.class);
Configuration.pairFileAndClass(ChestShop.loadFile("local.yml"), Messages.class);
NameManager.load();
ChestShop.callEvent(new ChestShopReloadEvent(sender));
sender.sendMessage(ChatColor.DARK_GREEN + "The config was reloaded.");
return true;

View File

@ -1,83 +1,234 @@
package com.Acrobot.ChestShop.Configuration;
import com.Acrobot.Breeze.Configuration.Annotations.PrecededBySpace;
import com.Acrobot.Breeze.Configuration.Configuration;
import com.Acrobot.ChestShop.ChestShop;
import de.themoep.minedown.adventure.MineDown;
import de.themoep.utils.lang.bukkit.BukkitLanguageConfig;
import de.themoep.utils.lang.bukkit.LanguageManager;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.md_5.bungee.api.chat.BaseComponent;
import org.bukkit.command.CommandSender;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
/**
* @author Acrobot
*/
public class Messages {
public static String prefix = "&a[Shop] &r";
public static String iteminfo = "&aItem Information: &r";
public static Message prefix;
@PrecededBySpace
public static String ACCESS_DENIED = "You don't have permission to do that!";
public static Message shopinfo;
public static Message shopinfo_buy;
public static Message shopinfo_sell;
@PrecededBySpace
public static String NOT_ENOUGH_MONEY = "You don't have enough money!";
public static String NOT_ENOUGH_MONEY_SHOP = "Shop owner doesn't have enough money!";
public static Message iteminfo;
public static Message iteminfo_fullname;
public static Message iteminfo_shopname;
public static Message iteminfo_repaircost;
public static Message iteminfo_book;
public static Message iteminfo_book_generation;
public static Message iteminfo_leather_color;
public static Message iteminfo_bundle_items;
public static Message iteminfo_axolotl_variant;
public static Message iteminfo_recipes;
public static Message iteminfo_map_view;
public static Message iteminfo_map_location;
public static Message iteminfo_tropical_fish;
public static Message iteminfo_crossbow_projectiles;
public static Message iteminfo_crossbow_projectile;
public static Message iteminfo_lore;
@PrecededBySpace
public static String CLIENT_DEPOSIT_FAILED = "Money deposit to your account failed!";
public static String SHOP_DEPOSIT_FAILED = "Money deposit to shop owner failed!";
public static String NO_ECONOMY_ACCOUNT = "Economy account from shop owner doesn't exist!";
public static Message METRICS;
@PrecededBySpace
public static String NO_BUYING_HERE = "You can't buy here!";
public static String NO_SELLING_HERE = "You can't sell here!";
public static Message ACCESS_DENIED;
public static Message TRADE_DENIED;
public static Message TRADE_DENIED_ACCESS_PERMS;
public static Message TRADE_DENIED_CREATIVE_MODE;
@PrecededBySpace
public static String NOT_ENOUGH_SPACE_IN_INVENTORY = "You haven't got enough space in inventory!";
public static String NOT_ENOUGH_SPACE_IN_CHEST = "There isn't enough space in chest!";
public static String NOT_ENOUGH_ITEMS_TO_SELL = "You don't have enough items to sell!";
public static String NOT_ENOUGH_SPACE_IN_YOUR_SHOP = "%material&7 shop at &r%world/%x/%y/%z&7 is full! (%seller tried to sell)";
public static Message NOT_ENOUGH_MONEY;
public static Message NOT_ENOUGH_MONEY_SHOP;
@PrecededBySpace
public static String NOT_ENOUGH_STOCK = "This shop is out of stock.";
public static String NOT_ENOUGH_STOCK_IN_YOUR_SHOP = "%material&7 shop at &r%world/%x/%y/%z&7 is out of stock! (%buyer tried to buy)";
public static Message CLIENT_DEPOSIT_FAILED;
public static Message SHOP_DEPOSIT_FAILED;
public static Message NO_ECONOMY_ACCOUNT;
@PrecededBySpace
public static String YOU_BOUGHT_FROM_SHOP = "You bought %item from %owner for %price.";
public static String SOMEBODY_BOUGHT_FROM_YOUR_SHOP = "%buyer bought %item for %price from you.";
public static Message NO_BUYING_HERE;
public static Message NO_SELLING_HERE;
@PrecededBySpace
public static String YOU_SOLD_TO_SHOP = "You sold %item to %buyer for %price.";
public static String SOMEBODY_SOLD_TO_YOUR_SHOP = "%seller sold %item for %price to you.";
public static Message NOT_ENOUGH_SPACE_IN_INVENTORY;
public static Message NOT_ENOUGH_SPACE_IN_CHEST;
public static Message NOT_ENOUGH_ITEMS_TO_SELL;
public static Message NOT_ENOUGH_SPACE_IN_YOUR_SHOP;
@PrecededBySpace
public static String YOU_CANNOT_CREATE_SHOP = "You can't create this type of shop!";
public static String NO_CHEST_DETECTED = "Couldn't find a chest!";
public static String INVALID_SHOP_DETECTED = "The shop cannot be used!";
public static String CANNOT_ACCESS_THE_CHEST = "You don't have permissions to access this chest!";
@PrecededBySpace
public static String CLICK_TO_AUTOFILL_ITEM = "Click the sign with the item that this shop is for!";
public static String NO_ITEM_IN_HAND = "You don't have an item in your hand to autofill!";
@PrecededBySpace
public static String PROTECTED_SHOP = "Successfully protected the shop with LWC!";
public static String SHOP_CREATED = "Shop successfully created!";
public static String SHOP_FEE_PAID = "You have been charged %amount";
public static String SHOP_REFUNDED = "You have been refunded %amount.";
public static String ITEM_GIVEN = "Given %item to %player.";
public static Message NOT_ENOUGH_STOCK;
public static Message NOT_ENOUGH_STOCK_IN_YOUR_SHOP;
@PrecededBySpace
public static String RESTRICTED_SIGN_CREATED = "Sign successfully created!";
public static Message YOU_BOUGHT_FROM_SHOP;
public static Message SOMEBODY_BOUGHT_FROM_YOUR_SHOP;
@PrecededBySpace
public static String PLAYER_NOT_FOUND = "Player not found!";
public static String NO_PERMISSION = "You don't have permissions to do that!";
public static String INCORRECT_ITEM_ID = "You have specified invalid item id!";
public static String NOT_ENOUGH_PROTECTIONS = "Could not create a protection!";
public static Message YOU_SOLD_TO_SHOP;
public static Message SOMEBODY_SOLD_TO_YOUR_SHOP;
@PrecededBySpace
public static String CANNOT_CREATE_SHOP_HERE = "You can't create shop here!";
public static Message YOU_CANNOT_CREATE_SHOP;
public static Message NO_CHEST_DETECTED;
public static Message INVALID_SHOP_DETECTED;
public static Message INVALID_SHOP_PRICE;
public static Message INVALID_SHOP_QUANTITY;
public static Message CANNOT_ACCESS_THE_CHEST;
public static Message CANNOT_CHANGE_SIGN_BACKSIDE;
@PrecededBySpace
public static String TOGGLE_MESSAGES_OFF = "You will no longer receive messages from your shop(s).";
public static String TOGGLE_MESSAGES_ON = "You will now receive messages from your shop(s).";
public static Message SELL_PRICE_HIGHER_THAN_BUY_PRICE;
public static Message SELL_PRICE_ABOVE_MAX;
public static Message SELL_PRICE_BELOW_MIN;
public static Message BUY_PRICE_ABOVE_MAX;
public static Message BUY_PRICE_BELOW_MIN;
public static Message CLICK_TO_AUTOFILL_ITEM;
public static Message NO_ITEM_IN_HAND;
public static Message PROTECTED_SHOP;
public static Message PROTECTED_SHOP_SIGN;
public static Message SHOP_CREATED;
public static Message SHOP_FEE_PAID;
public static Message SHOP_REFUNDED;
public static Message ITEM_GIVEN;
public static Message RESTRICTED_SIGN_CREATED;
public static Message PLAYER_NOT_FOUND;
public static Message NO_PERMISSION;
public static Message INCORRECT_ITEM_ID;
public static Message INVALID_CLIENT_NAME;
public static Message NOT_ENOUGH_PROTECTIONS;
public static Message NO_SHOP_FOUND;
public static Message CANNOT_CREATE_SHOP_HERE;
public static Message TOGGLE_MESSAGES_OFF;
public static Message TOGGLE_MESSAGES_ON;
public static Message TOGGLE_ACCESS_ON;
public static Message TOGGLE_ACCESS_OFF;
@Deprecated
public static String prefix(String message) {
return Configuration.getColoured( prefix + message);
return Configuration.getColoured(prefix.getLang(null) + message);
}
@Deprecated
public static String replace(String message, String... replacements) {
for (int i = 0; i + 1 < replacements.length; i+=2) {
message = message.replace("%" + replacements[i], replacements[i+1]);
}
return Configuration.getColoured(message);
}
private static LanguageManager manager;
public static void load() {
for (Field field : Messages.class.getFields()) {
if (!Modifier.isStatic(field.getModifiers())) {
continue;
}
try {
field.set(null, new Message(field.getName()));
} catch (IllegalAccessException e) {
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while setting Message " + field.getName() + "!", e);
}
}
manager = new LanguageManager(ChestShop.getPlugin(), Properties.DEFAULT_LANGUAGE);
if (manager.getDefaultConfig() == null) {
manager.setDefaultLocale("en");
ChestShop.getBukkitLogger().log(Level.WARNING, "There is no language file for your DEFAULT_LANGUAGE config setting of '" + Properties.DEFAULT_LANGUAGE + "' in your languages folder! Using default English as default until you have created one or changed the config option to another, existing language file.");
}
// Legacy locale.yml file
File legacyFile = new File(ChestShop.getPlugin().getDataFolder(), "local.yml");
if (legacyFile.exists()) {
ChestShop.getBukkitLogger().log(Level.INFO, "Found legacy local.yml. Loading it as 'legacy' language and using that for all messages.");
ChestShop.getBukkitLogger().log(Level.INFO, "As long as the legacy file is used automatic language switching based on the client settings will not be supported!");
ChestShop.getBukkitLogger().log(Level.INFO, "Import it into the corresponding language file and remove/rename the file if you don't want it anymore!");
manager.addConfig(new BukkitLanguageConfig(ChestShop.getPlugin(), "", legacyFile, "legacy", false));
manager.setDefaultLocale("legacy");
Properties.USE_CLIENT_LOCALE = false;
}
if (!Properties.USE_CLIENT_LOCALE) {
manager.setProvider(sender -> null);
}
}
public static class Message {
private String key;
public Message(String key) {
this.key = key;
}
public void sendWithPrefix(CommandSender sender, Map<String, String> replacementMap, String... replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, true, replacementMap, replacements));
}
public void sendWithPrefix(CommandSender sender, Map<String, String> replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, true, replacements));
}
public void sendWithPrefix(CommandSender sender, String... replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, true, Collections.emptyMap(), replacements));
}
public void send(CommandSender sender, String... replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, false, Collections.emptyMap(), replacements));
}
public void send(CommandSender sender, Map<String, String> replacements) {
ChestShop.getAudiences().sender(sender).sendMessage(getComponent(sender, false, replacements));
}
@Deprecated
public BaseComponent[] getComponents(CommandSender sender, boolean prefixSuffix, Map<String, String> replacementMap, String... replacements) {
return BungeeComponentSerializer.get().serialize(getComponent(sender, prefixSuffix, replacementMap, replacements));
}
public Component getComponent(CommandSender sender, boolean prefixSuffix, Map<String, String> replacementMap, String... replacements) {
MineDown mineDown = new MineDown("%prefix" + getLang(sender));
mineDown.placeholderSuffix("");
if (prefixSuffix) {
mineDown.replace("prefix", MineDown.parse(prefix.getLang(sender)));
} else {
mineDown.replace("prefix", "");
}
mineDown.replace(replacementMap);
mineDown.replace(replacements);
return mineDown.toComponent();
}
private String getLang(CommandSender sender) {
return manager.getConfig(sender).get(key);
}
public String getTextWithPrefix(CommandSender sender, Map<String, String> replacementMap, String... replacements) {
return LegacyComponentSerializer.legacySection().serialize(getComponent(sender, true, replacementMap, replacements));
}
public String getTextWithPrefix(CommandSender sender, String... replacements) {
return getTextWithPrefix(sender, Collections.emptyMap(), replacements);
}
public String getTextWithPrefix(CommandSender sender, Map<String, String> replacements) {
return getTextWithPrefix(sender, replacements, new String[0]);
}
public String getKey() {
return key;
}
}
}

View File

@ -1,37 +1,166 @@
package com.Acrobot.ChestShop.Configuration;
import com.Acrobot.Breeze.Configuration.Annotations.ConfigurationComment;
import com.Acrobot.Breeze.Configuration.Annotations.Parser;
import com.Acrobot.Breeze.Configuration.Annotations.PrecededBySpace;
import com.Acrobot.Breeze.Configuration.Configuration;
import com.Acrobot.Breeze.Configuration.ValueParser;
import com.Acrobot.ChestShop.ChestShop;
import com.Acrobot.ChestShop.Security;
import org.bukkit.Material;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* @author Acrobot
*/
public class Properties {
static {
Configuration.registerParser("StringSet", new ValueParser() {
@Override
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof Collection) {
return new LinkedHashSet<>((Collection<String>) object);
}
return object;
}
});
Configuration.registerParser("MaterialSet", new ValueParser() {
@Override
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof Collection) {
EnumSet<Material> set = EnumSet.noneOf(Material.class);
for (Object o : (Collection) object) {
if (o instanceof Material) {
set.add((Material) o);
} else if (o instanceof String) {
Material m = Material.getMaterial(((String) o).toUpperCase(Locale.ROOT));
if (m != null) {
set.add(m);
} else {
ChestShop.getBukkitLogger().log(Level.WARNING, o + " is not a valid Material name in the config!");
}
}
}
return set;
}
return object;
}
});
Configuration.registerParser("BigDecimal", new ValueParser() {
@Override
public String parseToYAML(Object object) {
if (object instanceof BigDecimal) {
return object.toString();
}
return super.parseToYAML(object);
}
@Override
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof Double) {
return BigDecimal.valueOf((Double) object);
} else if (object instanceof Long) {
return BigDecimal.valueOf((Long) object);
} else if (object instanceof Integer) {
return BigDecimal.valueOf((Integer) object);
}
return object;
}
});
Configuration.registerParser("UUID", new ValueParser() {
@Override
public String parseToYAML(Object object) {
if (object instanceof UUID) {
return object.toString();
}
return super.parseToYAML(object);
}
@Override
public <T> Object parseToJava(Class<T> type, Object object) {
if (object instanceof String) {
return UUID.fromString((String) object);
}
return object;
}
});
}
@ConfigurationComment("Should the plugin log some messages that are useful for debugging?")
public static boolean DEBUG = false;
@PrecededBySpace
@ConfigurationComment("Do you want to turn off the automatic updates of ChestShop?")
public static boolean TURN_OFF_UPDATES = false;
public static boolean TURN_OFF_UPDATES = true;
@ConfigurationComment("Do you want to turn off the automatic notifications for releases?")
public static boolean TURN_OFF_UPDATE_NOTIFIER = false;
@ConfigurationComment("Do you want to turn off the automatic notifications for new development builds?")
public static boolean TURN_OFF_DEV_UPDATE_NOTIFIER = false;
@ConfigurationComment("Do you want to include some values of this config in the metrics? (This will not submit any sensitive data and helps in the development process)")
public static boolean INCLUDE_SETTINGS_IN_METRICS = true;
@PrecededBySpace
@ConfigurationComment("How large should the internal caches be?")
public static int CACHE_SIZE = 1000;
@PrecededBySpace
@ConfigurationComment("The default language when the client's language can't be found.")
public static String DEFAULT_LANGUAGE = "en";
@ConfigurationComment("Should the plugin try to use a language file that matches the client's locale setting?")
public static boolean USE_CLIENT_LOCALE = true;
@ConfigurationComment("Should the plugin strip the colors from formatted price?")
public static boolean STRIP_PRICE_COLORS = false;
@PrecededBySpace
@ConfigurationComment("What containers are allowed to hold a shop? (Only blocks with inventories work!)")
@Parser("MaterialSet")
public static Set<Material> SHOP_CONTAINERS = EnumSet.of(
Material.CHEST,
Material.TRAPPED_CHEST
);
@PrecededBySpace
@ConfigurationComment("(In 1/1000th of a second) How often can a player use the shop sign?")
public static int SHOP_INTERACTION_INTERVAL = 250;
@ConfigurationComment("Do you want to allow using shops to people in creative mode?")
@ConfigurationComment("Do you want to block people in creative mode from using shops?")
public static boolean IGNORE_CREATIVE_MODE = true;
@ConfigurationComment("Do you want to block people who have access to a shop due to their permissions from using it? (owners are always ignored)")
public static boolean IGNORE_ACCESS_PERMS = true;
@ConfigurationComment("If true, people will buy with left-click and sell with right-click.")
public static boolean REVERSE_BUTTONS = false;
@ConfigurationComment("If true, people will be able to buy/sell in 64 stacks while holding the crouch button.")
public static boolean SHIFT_SELLS_IN_STACKS = false;
@ConfigurationComment("If true, people will be able to sell/buy everything available of the same type.")
public static boolean SHIFT_SELLS_EVERYTHING = false;
@ConfigurationComment("What can you do by clicking shift with SHIFT_SELLS_IN_STACKS turned on? (ALL/BUY/SELL)")
public static String SHIFT_ALLOWS = "ALL";
@ConfigurationComment("Can shop's chest be opened by owner with right-clicking a shop's sign?")
public static boolean ALLOW_SIGN_CHEST_OPEN = true;
public static boolean ALLOW_SIGN_CHEST_OPEN = false;
@ConfigurationComment("If true and in 1.14+, the owner of a chest shop can click with a dye / ink sac to dye the sign.")
public static boolean SIGN_DYING = true;
@ConfigurationComment("If true, when you left-click your own shop sign you won't open chest's inventory, but instead you will start destroying the sign.")
public static boolean ALLOW_LEFT_CLICK_DESTROYING = true;
@ -43,29 +172,54 @@ public class Properties {
@ConfigurationComment("If true, if the REMOVE_EMPTY_SHOPS option is turned on, the chest is also destroyed.")
public static boolean REMOVE_EMPTY_CHESTS = false;
@ConfigurationComment("A list of worlds in which to remove empty shops with the previous config. Case sensitive. An empty list means all worlds.")
@Parser("StringSet")
public static Set<String> REMOVE_EMPTY_WORLDS = new LinkedHashSet<>(Arrays.asList("world1", "world2"));
@PrecededBySpace
@ConfigurationComment("First line of your Admin Shop's sign should look like this:")
public static String ADMIN_SHOP_NAME = "Admin Shop";
@ConfigurationComment("The economy account which Admin Shops should use and to which all taxes will go")
@ConfigurationComment("Make all admin shops be unlimited even if they have a shop container at the sign")
public static boolean FORCE_UNLIMITED_ADMIN_SHOP = false;
@ConfigurationComment("The name of the economy account which Admin Shops should use and to which all taxes will go")
public static String SERVER_ECONOMY_ACCOUNT = "";
@ConfigurationComment("The uuid of the economy account for the Admin Shop. Useful for fake accounts as normally only accounts of players work")
public static UUID SERVER_ECONOMY_ACCOUNT_UUID = new UUID(0, 0);
@ConfigurationComment("Percent of the price that should go to the server's account. (100 = 100 percent)")
public static int TAX_AMOUNT = 0;
public static double TAX_AMOUNT = 0;
@ConfigurationComment("Percent of the price that should go to the server's account when buying from an Admin Shop.")
public static int SERVER_TAX_AMOUNT = 0;
public static double SERVER_TAX_AMOUNT = 0;
@ConfigurationComment("Amount of money player must pay to create a shop")
public static double SHOP_CREATION_PRICE = 0;
public static BigDecimal SHOP_CREATION_PRICE = BigDecimal.valueOf(0);
@ConfigurationComment("How much money do you get back when destroying a sign?")
public static double SHOP_REFUND_PRICE = 0;
public static BigDecimal SHOP_REFUND_PRICE = BigDecimal.valueOf(0);
@ConfigurationComment("How many decimal places are allowed at a maximum for prices?")
public static int PRICE_PRECISION = 2;
@ConfigurationComment("This makes sure that the UUIDs of player shop accounts match the server's online-mode setting. Disabling this might lead to issues with offline players and is therefore unsupported!")
public static boolean ENSURE_CORRECT_PLAYERID = true;
@ConfigurationComment("This regexp validates the name of the player. If the name doesn't match, the player will neither be able to create a valid shop sign, nor buy/sell from a shop.\n" +
"Note for Bedrock support: If you have Floodgate on your server, you should set this regexp to ^\\\\.?\\\\w+$ and ENSURE_CORRECT_PLAYERID to false\n" +
"If your Floodgate prefix is not a dot, then change the first . in the regexp (the one before the question mark) to whatever your prefix is.")
public static String VALID_PLAYERNAME_REGEXP = "^\\w+$";
@PrecededBySpace
@ConfigurationComment("Should we block shops that sell things for more than they buy? (This prevents newbies from creating shops that would be exploited)")
public static boolean BLOCK_SHOPS_WITH_SELL_PRICE_HIGHER_THAN_BUY_PRICE = true;
@PrecededBySpace
@ConfigurationComment("Maximum amount of items that can be bought/sold at a shop. Default 3456 is a double chest of 64 stacks.")
public static int MAX_SHOP_AMOUNT = 3456;
@PrecededBySpace
@ConfigurationComment("Do you want to allow other players to build a shop on a block where there's one already?")
public static boolean ALLOW_MULTIPLE_SHOPS_AT_ONE_BLOCK = false;
@ -76,11 +230,17 @@ public class Properties {
@ConfigurationComment("Can '?' be put in place of item name in order for the sign to be auto-filled?")
public static boolean ALLOW_AUTO_ITEM_FILL = true;
@PrecededBySpace
@ConfigurationComment("Enable this if you use BungeeCord and want players to receive shop notifications on other servers")
public static boolean BUNGEECORD_MESSAGES = false;
@PrecededBySpace
@ConfigurationComment("Do you want to show \"Out of stock\" messages?")
public static boolean SHOW_MESSAGE_OUT_OF_STOCK = true;
@ConfigurationComment("Do you want to show \"Full shop\" messages?")
public static boolean SHOW_MESSAGE_FULL_SHOP = true;
@ConfigurationComment("How many seconds do you want to wait before showing notifications for the same shop to the owner again?")
public static long NOTIFICATION_MESSAGE_COOLDOWN = 10;
@PrecededBySpace
@ConfigurationComment("Can players hide the \"Out of stock\" messages with /cstoggle?")
@ -98,9 +258,12 @@ public class Properties {
@ConfigurationComment("If true, plugin will log transactions in its own file")
public static boolean LOG_TO_FILE = false;
@ConfigurationComment("Do you want ChestShop's messages to show up in console?")
@ConfigurationComment("Do you want ChestShop's transaction messages to show up in console?")
public static boolean LOG_TO_CONSOLE = true;
@ConfigurationComment("Should all shop removals be logged?")
public static boolean LOG_ALL_SHOP_REMOVALS = true;
@PrecededBySpace
@ConfigurationComment("Do you want to stack all items up to 64 item stacks?")
public static boolean STACK_TO_64 = false;
@ -117,20 +280,32 @@ public class Properties {
@ConfigurationComment("Do you want to turn off the default sign protection? Warning! Other players will be able to destroy other people's shops!")
public static boolean TURN_OFF_SIGN_PROTECTION = false;
@ConfigurationComment("Do you want to disable the hopper protection, which prevents the hoppers from taking items out of chests?")
@ConfigurationComment("Do you want to disable the hopper protection, which prevents Hopper-Minecarts from taking items out of shops?")
public static boolean TURN_OFF_HOPPER_PROTECTION = false;
@ConfigurationComment("Only allow users to buy/sell that have access to the sign's protection? (E.g. LWC protection)")
public static boolean CHECK_ACCESS_FOR_SHOP_USE = false;
@ConfigurationComment("Do you want to protect shop chests with LWC?")
public static boolean PROTECT_CHEST_WITH_LWC = false;
@ConfigurationComment("Of which type should the container protection be? Possible type: public, private, donate and on some LWC versions display")
public static Security.Type LWC_CHEST_PROTECTION_TYPE = Security.Type.PRIVATE;
@ConfigurationComment("Do you want to protect shop signs with LWC?")
public static boolean PROTECT_SIGN_WITH_LWC = false;
@ConfigurationComment("Of which type should the sign protection be? Possible type: public, private, donate and on some LWC versions display")
public static Security.Type LWC_SIGN_PROTECTION_TYPE = Security.Type.PRIVATE;
@ConfigurationComment("Should the chest's LWC protection be removed once the shop sign is destroyed? ")
public static boolean REMOVE_LWC_PROTECTION_AUTOMATICALLY = true;
@ConfigurationComment("Should LWC limits block shop creations?")
public static boolean LWC_LIMITS_BLOCK_CREATION = true;
@PrecededBySpace
@ConfigurationComment("Do you want to only let people build inside regions?")
@ConfigurationComment("Do you want to only let people build inside WorldGuard regions?")
public static boolean WORLDGUARD_INTEGRATION = false;
@ConfigurationComment("Do you want to only let people build inside region flagged by doing /region regionName flag allow-shop allow?")
@ -139,6 +314,14 @@ public class Properties {
@ConfigurationComment("Do you want ChestShop to respect WorldGuard's chest protection?")
public static boolean WORLDGUARD_USE_PROTECTION = false;
@PrecededBySpace
@ConfigurationComment("Do you want to only let people build inside GriefPrevention claims?")
public static boolean GRIEFPREVENTION_INTEGRATION = false;
@PrecededBySpace
@ConfigurationComment("Do you want to only let people build inside RedProtect regions?")
public static boolean REDPROTECT_INTEGRATION = false;
@PrecededBySpace
@ConfigurationComment("Do you want to deny shop access to unlogged users?")
public static boolean AUTHME_HOOK = true;
@ -149,8 +332,12 @@ public class Properties {
@PrecededBySpace
@ConfigurationComment("How much Heroes exp should people get for creating a ChestShop?")
public static double HEROES_EXP = 100;
@PrecededBySpace
@ConfigurationComment("Add icons and make item names hoverable in transaction messages when ShowItem is installed?")
public static boolean SHOWITEM_MESSAGE = true;
@PrecededBySpace
@ConfigurationComment("Add stock counter to quantity line?")
public static boolean USE_STOCK_COUNTER = false;
}

View File

@ -1,10 +1,12 @@
package com.Acrobot.ChestShop.Containers;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
@ -17,6 +19,14 @@ import org.bukkit.inventory.ItemStack;
* @author Acrobot
*/
public class AdminInventory implements Inventory {
private ItemStack[] content;
private int maxStackSize = 64;
public AdminInventory(ItemStack[] content) {
this.content = content;
}
@Override
public int getSize() {
return Integer.MAX_VALUE;
@ -24,106 +34,112 @@ public class AdminInventory implements Inventory {
@Override
public int getMaxStackSize() {
return Integer.MAX_VALUE;
return maxStackSize;
}
@Override
public void setMaxStackSize(int i) {
maxStackSize = i;
}
@Override
public String getName() {
return "Admin inventory";
}
@Override
public ItemStack getItem(int i) {
if (content.length > i) {
return content[i];
}
return null;
}
@Override
public void setItem(int i, ItemStack itemStack) {
if (i > getSize()) {
throw new IllegalArgumentException("Slot is outside inventory. Max size is " + getSize());
}
if (i >= content.length) {
content = Arrays.copyOfRange(content, 0, i);
}
content[i] = itemStack;
}
@Override
public HashMap<Integer, ItemStack> addItem(ItemStack... itemStacks) {
return new HashMap<Integer, ItemStack>();
return new HashMap<>();
}
@Override
public HashMap<Integer, ItemStack> removeItem(ItemStack... itemStacks) {
return new HashMap<Integer, ItemStack>();
return new HashMap<>();
}
public HashMap<Integer, ItemStack> removeItemAnySlot(ItemStack... items) throws IllegalArgumentException {
return new HashMap<>();
}
@Override
public ItemStack[] getContents() {
return new ItemStack[]{
new ItemStack(Material.CHEST, 1),
new ItemStack(Material.AIR, Integer.MAX_VALUE)
};
return content;
}
@Override
public void setContents(ItemStack[] itemStacks) {
content = itemStacks;
}
@Override
public ItemStack[] getStorageContents() {
return new ItemStack[0];
return content;
}
@Override
public void setStorageContents(ItemStack[] itemStacks) throws IllegalArgumentException {
}
@Override
public boolean contains(int i) {
return true;
content = itemStacks;
}
@Override
public boolean contains(Material material) {
return true;
return first(material) > -1;
}
@Override
public boolean contains(ItemStack itemStack) {
return true;
}
@Override
public boolean contains(int i, int i1) {
return true;
return first(itemStack) > -1;
}
@Override
public boolean contains(Material material, int i) {
return true;
int amount = 0;
for (ItemStack item : content) {
if (item != null && item.getType() == material) {
amount += item.getAmount();
}
}
return amount >= i;
}
@Override
public boolean contains(ItemStack itemStack, int i) {
return true;
int amount = 0;
for (ItemStack item : content) {
if (MaterialUtil.equals(item, itemStack)) {
amount += itemStack.getAmount();
}
}
return amount >= i;
}
@Override
public boolean containsAtLeast(ItemStack itemStack, int i) {
return true;
}
@Override
public HashMap<Integer, ? extends ItemStack> all(int i) {
HashMap<Integer, ItemStack> items = new HashMap<Integer, ItemStack>();
items.put(1, new ItemStack(i, Integer.MAX_VALUE));
return items;
return contains(itemStack, i);
}
@Override
public HashMap<Integer, ? extends ItemStack> all(Material material) {
HashMap<Integer, ItemStack> items = new HashMap<>();
if (material.getMaxDurability() != 0) {
HashMap<Integer, ItemStack> items = new HashMap<Integer, ItemStack>();
for (short currentDurability = 0; currentDurability < material.getMaxDurability(); currentDurability++) {
items.put((int) currentDurability, new ItemStack(material, Integer.MAX_VALUE, currentDurability));
@ -132,34 +148,42 @@ public class AdminInventory implements Inventory {
return items;
}
return all(material.getId());
items.put(1, new ItemStack(material, Integer.MAX_VALUE));
return items;
}
@Override
public HashMap<Integer, ? extends ItemStack> all(ItemStack itemStack) {
HashMap<Integer, ItemStack> items = new HashMap<Integer, ItemStack>();
ItemStack clone = itemStack.clone();
clone.setAmount(Integer.MAX_VALUE);
if (itemStack != null) {
ItemStack clone = itemStack.clone();
clone.setAmount(Integer.MAX_VALUE);
items.put(1, clone);
items.put(1, clone);
}
return items;
}
@Override
public int first(int i) {
return 0;
}
@Override
public int first(Material material) {
return 0;
for (int i = 0; i < content.length; i++) {
if (content[i] != null && content[i].getType() == material) {
return i;
}
}
return -1;
}
@Override
public int first(ItemStack itemStack) {
return 0;
for (int i = 0; i < content.length; i++) {
if (MaterialUtil.equals(content[i], itemStack)) {
return i;
}
}
return -1;
}
@Override
@ -167,8 +191,8 @@ public class AdminInventory implements Inventory {
return 0;
}
@Override
public void remove(int i) {
public boolean isEmpty() {
return false;
}
@Override
@ -187,12 +211,15 @@ public class AdminInventory implements Inventory {
public void clear() {
}
@Override
public List<HumanEntity> getViewers() {
return new ArrayList<HumanEntity>();
public int close() {
return 0;
}
@Override
public List<HumanEntity> getViewers() {
return new ArrayList<>();
}
public String getTitle() {
return "Admin inventory";
}
@ -207,14 +234,18 @@ public class AdminInventory implements Inventory {
return null;
}
@Override
public ListIterator<ItemStack> iterator() {
public InventoryHolder getHolder(boolean useSnapshot) {
return null;
}
@Override
public ListIterator<ItemStack> iterator() {
return Arrays.asList(content).listIterator();
}
@Override
public ListIterator<ItemStack> iterator(int i) {
return null;
return Arrays.asList(content).listIterator(i);
}
@Override

View File

@ -33,8 +33,12 @@ public class Account {
}
public Account(String name, UUID uuid) {
this(name, NameUtil.stripUsername(name), uuid);
}
public Account(String name, String shortName, UUID uuid) {
this.name = name;
this.shortName = NameUtil.stripUsername(name);
this.shortName = shortName;
this.uuid = uuid;
}

View File

@ -4,8 +4,8 @@ import com.Acrobot.ChestShop.ChestShop;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.LruObjectCache;
import com.j256.ormlite.db.SqliteDatabaseType;
import com.j256.ormlite.jdbc.JdbcConnectionSource;
import com.j256.ormlite.jdbc.db.SqliteDatabaseType;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.table.TableUtils;

View File

@ -14,7 +14,7 @@ public class Item {
@DatabaseField(canBeNull = false, generatedId = true)
private int id;
@DatabaseField(columnName = "code", canBeNull = false, unique = true)
@DatabaseField(columnName = "code", canBeNull = false, index = true)
private String base64ItemCode;
public Item() {

View File

@ -5,6 +5,7 @@ import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.GenericRawResults;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Date;
import java.util.UUID;
@ -16,7 +17,7 @@ import java.util.logging.Level;
* @author Andrzej Pomirski
*/
public class Migrations {
public static final int CURRENT_DATABASE_VERSION = 3;
public static final int CURRENT_DATABASE_VERSION = 4;
/**
* Migrates a database from the given version
@ -27,8 +28,6 @@ public class Migrations {
public static int migrate(int currentVersion) {
if (currentVersion != CURRENT_DATABASE_VERSION) {
ChestShop.getBukkitLogger().info("Updating database...");
} else {
return CURRENT_DATABASE_VERSION;
}
switch (currentVersion) {
@ -45,6 +44,12 @@ public class Migrations {
return -1;
}
case 3:
if (migrateTo4()) {
currentVersion++;
} else {
return -1;
}
case 4:
default:
break;
//do nothing
@ -60,7 +65,7 @@ public class Migrations {
accounts.executeRaw("ALTER TABLE `accounts` ADD COLUMN lastSeenName VARCHAR");
return true;
} catch (SQLException e) {
e.printStackTrace();
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v2", e);
return false;
}
}
@ -104,14 +109,41 @@ public class Migrations {
lastInfo = System.currentTimeMillis();
}
}
results.close();
try {
results.close();
} catch (Exception e1) {
ChestShop.getBukkitLogger().log(Level.WARNING, "Error while closing results! " + e.getMessage());
}
ChestShop.getBukkitLogger().log(Level.INFO, success + " accounts successfully migrated. " + error + " accounts failed to migrate!");
}
ChestShop.getBukkitLogger().log(Level.INFO, "Migration of accounts table finished in " + (System.currentTimeMillis() - start) / 1000.0 + "s!");
return true;
} catch (SQLException e) {
e.printStackTrace();
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v3", e);
return false;
}
}
private static boolean migrateTo4() {
try {
Dao<Item, Integer> itemsOld = DaoCreator.getDao(Item.class);
itemsOld.executeRawNoArgs("ALTER TABLE `items` RENAME TO `items-old`");
Dao<Item, Integer> items = DaoCreator.getDaoAndCreateTable(Item.class);
long start = System.currentTimeMillis();
try {
items.executeRawNoArgs("INSERT INTO `items` (id, code) SELECT id, code uuid FROM `items-old`");
} catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while inserting items into new database while migrating to v4", e);
}
ChestShop.getBukkitLogger().log(Level.INFO, "Migration of items table finished in " + (System.currentTimeMillis() - start) / 1000.0 + "s!");
return true;
} catch (SQLException e) {
ChestShop.getBukkitLogger().log(Level.SEVERE, "Error while migrating database to v4", e);
return false;
}
}

View File

@ -2,20 +2,35 @@ package com.Acrobot.ChestShop;
import com.Acrobot.Breeze.Utils.MaterialUtil;
import com.Acrobot.ChestShop.Configuration.Properties;
import com.Acrobot.ChestShop.Listeners.Economy.EconomyAdapter;
import com.Acrobot.ChestShop.Listeners.Economy.Plugins.ReserveListener;
import com.Acrobot.ChestShop.Listeners.Economy.Plugins.VaultListener;
import com.Acrobot.ChestShop.Plugins.*;
import com.sk89q.worldguard.bukkit.WorldGuardPlugin;
import com.google.common.collect.ImmutableMap;
import org.bstats.charts.DrilldownPie;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.server.PluginEnableEvent;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.stream.Collectors;
/**
* @author Acrobot
*/
public class Dependencies {
public static void loadPlugins() {
public class Dependencies implements Listener {
private static final Map<String, String> versions = new HashMap<>();
public static void initializePlugins() {
PluginManager pluginManager = Bukkit.getPluginManager();
for (String dependency : ChestShop.getDependencies()) {
@ -25,20 +40,6 @@ public class Dependencies {
initializePlugin(dependency, plugin);
}
}
loadEconomy();
}
private static void loadEconomy() {
String plugin = "Vault";
Listener economy = VaultListener.initializeVault();
if (economy == null) {
return;
}
ChestShop.registerListener(economy);
ChestShop.getBukkitLogger().info(plugin + " loaded! Found an economy plugin!");
}
private static void initializePlugin(String name, Plugin plugin) { //Really messy, right? But it's short and fast :)
@ -50,6 +51,82 @@ public class Dependencies {
return;
}
switch (dependency) {
//Terrain protection plugins
case WorldGuard:
WorldGuardFlags.ENABLE_SHOP.getName(); // force the static code to run
break;
}
PluginDescriptionFile description = plugin.getDescription();
ChestShop.getBukkitLogger().info(description.getName() + " version " + description.getVersion() + " loaded.");
}
public static boolean loadPlugins() {
PluginManager pluginManager = Bukkit.getPluginManager();
for (String dependency : ChestShop.getDependencies()) {
Plugin plugin = pluginManager.getPlugin(dependency);
if (plugin != null && plugin.isEnabled()) {
try {
loadPlugin(dependency, plugin);
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Unable to hook into " + plugin.getName() + " " + plugin.getDescription().getVersion(), e);
}
}
}
if (loadEconomy()) {
Map<String, Map<String, Integer>> map = versions.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<String, Map<String, Integer>>(e.getKey(), ImmutableMap.of(e.getValue(), 1)))
.collect(Collectors.toMap(
AbstractMap.SimpleEntry::getKey,
AbstractMap.SimpleEntry::getValue
));
ChestShop.getMetrics().addCustomChart(new DrilldownPie("dependencies", () -> map));
return true;
}
return false;
}
private static boolean loadEconomy() {
String plugin = "none";
EconomyAdapter economy = null;
if(Bukkit.getPluginManager().getPlugin("Reserve") != null) {
plugin = "Reserve";
economy = ReserveListener.prepareListener();
}
if(Bukkit.getPluginManager().getPlugin("Vault") != null) {
plugin = "Vault";
economy = VaultListener.initializeVault();
}
if (economy == null) {
ChestShop.getBukkitLogger().severe("No Economy adapter found! You need to install either Vault or Reserve!");
return false;
}
ChestShop.getMetrics().addCustomChart(ChestShop.createStaticDrilldownStat("economyAdapter", plugin, Bukkit.getPluginManager().getPlugin(plugin).getDescription().getVersion()));
ChestShop.getMetrics().addCustomChart(ChestShop.createStaticDrilldownStat("economyPlugin", economy.getProviderInfo().getName(), economy.getProviderInfo().getVersion()));
ChestShop.registerListener(economy);
ChestShop.getBukkitLogger().info(plugin + " loaded!");
return true;
}
private static void loadPlugin(String name, Plugin plugin) { //Really messy, right? But it's short and fast :)
Dependency dependency;
try {
dependency = Dependency.valueOf(name);
} catch (IllegalArgumentException exception) {
return;
}
Listener listener = null;
switch (dependency) {
@ -60,12 +137,18 @@ public class Dependencies {
case Lockette:
listener = new Lockette();
break;
case LockettePro:
listener = new LockettePro();
break;
case Deadbolt:
listener = new Deadbolt();
break;
case SimpleChestLock:
listener = SimpleChestLock.getSimpleChestLock(plugin);
break;
case BlockLocker:
listener = new BlockLocker();
break;
case Residence:
if (plugin.getDescription().getVersion().startsWith("2")) {
ChestShop.getBukkitLogger().severe("You are using an old version of Residence! " +
@ -79,7 +162,6 @@ public class Dependencies {
//Terrain protection plugins
case WorldGuard:
WorldGuardPlugin worldGuard = (WorldGuardPlugin) plugin;
boolean inUse = Properties.WORLDGUARD_USE_PROTECTION || Properties.WORLDGUARD_INTEGRATION;
if (!inUse) {
@ -87,15 +169,29 @@ public class Dependencies {
}
if (Properties.WORLDGUARD_USE_PROTECTION) {
ChestShop.registerListener(new WorldGuardProtection(worldGuard));
ChestShop.registerListener(new WorldGuardProtection(plugin));
}
if (Properties.WORLDGUARD_INTEGRATION) {
listener = new WorldGuardBuilding(worldGuard);
listener = new WorldGuardBuilding(plugin);
}
break;
case GriefPrevention:
if (!Properties.GRIEFPREVENTION_INTEGRATION) {
return;
}
listener = new GriefPrevenentionBuilding(plugin);
break;
case RedProtect:
if (!Properties.REDPROTECT_INTEGRATION) {
return;
}
listener = new RedProtectBuilding(plugin);
break;
//Other plugins
case Heroes:
Heroes heroes = Heroes.getHeroes(plugin);
@ -106,8 +202,8 @@ public class Dependencies {
listener = heroes;
break;
case OddItem:
MaterialUtil.Odd.initialize();
case ItemBridge:
listener = new ItemBridge();
break;
case ShowItem:
MaterialUtil.Show.initialize(plugin);
@ -119,22 +215,35 @@ public class Dependencies {
}
PluginDescriptionFile description = plugin.getDescription();
versions.put(description.getName(), description.getVersion());
ChestShop.getBukkitLogger().info(description.getName() + " version " + description.getVersion() + " loaded.");
}
private static enum Dependency {
LWC,
Lockette,
LockettePro,
Deadbolt,
SimpleChestLock,
BlockLocker,
Residence,
OddItem,
WorldGuard,
GriefPrevention,
RedProtect,
Heroes,
ItemBridge,
ShowItem
}
@EventHandler(priority = EventPriority.MONITOR)
public void onEnable(PluginEnableEvent event) {
Plugin plugin = event.getPlugin();
if (ChestShop.getDependencies().contains(plugin.getName())) {
loadPlugin(plugin.getName(), plugin);
}
}
}

View File

@ -33,20 +33,32 @@ public class Economy {
return !ChestShopSign.isAdminShop(inventory) || NameManager.getServerEconomyAccount() != null;
}
/**
* @deprecated Directly call the {@link CurrencyAddEvent}
*/
@Deprecated
public static boolean add(UUID name, World world, double amount) {
CurrencyAddEvent event = new CurrencyAddEvent(BigDecimal.valueOf(amount), name, world);
ChestShop.callEvent(event);
return true;
return event.wasHandled();
}
/**
* @deprecated Directly call the {@link CurrencySubtractEvent}
*/
@Deprecated
public static boolean subtract(UUID name, World world, double amount) {
CurrencySubtractEvent event = new CurrencySubtractEvent(BigDecimal.valueOf(amount), name, world);
ChestShop.callEvent(event);
return true;
return event.wasHandled();
}
/**
* @deprecated Directly call the {@link CurrencyCheckEvent}
*/
@Deprecated
public static boolean hasEnough(UUID name, World world, double amount) {
CurrencyCheckEvent event = new CurrencyCheckEvent(BigDecimal.valueOf(amount), name, world);
ChestShop.callEvent(event);
@ -54,10 +66,18 @@ public class Economy {
return event.hasEnough();
}
public static String formatBalance(double amount) {
CurrencyFormatEvent event = new CurrencyFormatEvent(BigDecimal.valueOf(amount));
public static String formatBalance(BigDecimal amount) {
CurrencyFormatEvent event = new CurrencyFormatEvent(amount);
ChestShop.callEvent(event);
return event.getFormattedAmount();
}
/**
* @deprecated Use {@link #formatBalance(BigDecimal)}
*/
@Deprecated
public static String formatBalance(double amount) {
return formatBalance(BigDecimal.valueOf(amount));
}
}

View File

@ -0,0 +1,57 @@
package com.Acrobot.ChestShop.Events;
import com.Acrobot.ChestShop.Database.Account;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
/**
* Represents an access request for a specific account.
*/
public class AccountAccessEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
private final Account account;
private boolean canAccess = false;
public AccountAccessEvent(Player player, Account account) {
super(player);
this.account = account;
}
/**
* The account to check the access for
*
* @return The account
*/
public Account getAccount() {
return account;
}
/**
* Whether or not the player can access the account.
*
* @return Whether or not the player can access the account
*/
public boolean canAccess() {
return canAccess;
}
/**
* Set whether or not the player can access the account.
*
* @param canAccess Whether or not the player can access the account
*/
public void setAccess(boolean canAccess) {
this.canAccess = canAccess;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,57 @@
package com.Acrobot.ChestShop.Events;
import com.Acrobot.ChestShop.Database.Account;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Represents a query for an account by using the name (e.g. from the shop sign)
*/
public class AccountQueryEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final String name;
private Account account = null;
private boolean searchOfflinePlayers = false;
public AccountQueryEvent(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
/**
* Get whether or not offline player data should be searched (too)
* @return Whether or not offline player data should be searched (too)
*/
public boolean searchOfflinePlayers() {
return searchOfflinePlayers;
}
/**
* Set whether or not offline player data should be searched (too).
* This could lead to network lookups if the player by the name never joined the server!
* @param searchOfflinePlayers Whether or not offline player data should be searched (too)
*/
public void searchOfflinePlayers(boolean searchOfflinePlayers) {
this.searchOfflinePlayers = searchOfflinePlayers;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -0,0 +1,35 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Represents a plugin reload call
*
* @author Acrobot
*/
public class ChestShopReloadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private CommandSender sender;
public ChestShopReloadEvent(CommandSender sender) {
this.sender = sender;
}
/**
* @return CommandSender who initiated the call
*/
public CommandSender getSender() {
return sender;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View File

@ -1,7 +1,6 @@
package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
@ -11,10 +10,10 @@ import java.util.UUID;
*
* @author Acrobot
*/
public class AccountCheckEvent extends Event {
public class AccountCheckEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
boolean outcome;
private boolean outcome = false;
private UUID account;
private World world;

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -11,13 +10,13 @@ import java.util.UUID;
/**
* Represents an addition of goods to entity
*
* Use {@link CurrencyTransferEvent} if you want to transfer money from one account to another one!
*
* @author Acrobot
*/
public class CurrencyAddEvent extends Event {
public class CurrencyAddEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
boolean added;
private BigDecimal amount;
private UUID target;
private World world;
@ -68,18 +67,22 @@ public class CurrencyAddEvent extends Event {
/**
* @return Was the money already added to the account?
* @deprecated Use {@link #wasHandled()}
*/
@Deprecated
public boolean isAdded() {
return added;
return wasHandled();
}
/**
* Set if the money was added to the account
*
* @param added Was the money added?
* @deprecated Use {@link #setHandled(boolean)}
*/
@Deprecated
public void setAdded(boolean added) {
this.added = added;
setHandled(added);
}
/**
@ -96,6 +99,13 @@ public class CurrencyAddEvent extends Event {
return target;
}
/**
* @param target Account from which the currency is subtracted
*/
public void setTarget(UUID target) {
this.target = target;
}
public HandlerList getHandlers() {
return handlers;
}

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -13,7 +12,7 @@ import java.util.UUID;
*
* @author Acrobot
*/
public class CurrencyAmountEvent extends Event {
public class CurrencyAmountEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
private BigDecimal amount = BigDecimal.ZERO;
@ -77,6 +76,13 @@ public class CurrencyAmountEvent extends Event {
return account;
}
/**
* @param account Account that is checked
*/
public void setAccount(UUID account) {
this.account = account;
}
public HandlerList getHandlers() {
return handlers;
}

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -13,10 +12,10 @@ import java.util.UUID;
*
* @author Acrobot
*/
public class CurrencyCheckEvent extends Event {
public class CurrencyCheckEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
boolean outcome;
private boolean outcome = false;
private BigDecimal amount;
private UUID account;
@ -59,6 +58,7 @@ public class CurrencyCheckEvent extends Event {
* @return Amount of currency, as a double
* @deprecated Use {@link #getAmount()} if possible
*/
@Deprecated
public double getDoubleAmount() {
return amount.doubleValue();
}
@ -67,7 +67,9 @@ public class CurrencyCheckEvent extends Event {
* Sets the amount of currency transferred
*
* @param amount Amount to transfer
* @deprecated The amount should not be changed!
*/
@Deprecated
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
@ -76,8 +78,9 @@ public class CurrencyCheckEvent extends Event {
* Sets the amount of currency transferred
*
* @param amount Amount to transfer
* @deprecated Use {@link #setAmount(java.math.BigDecimal)} if possible
* @deprecated The amount should not be changed! Use {@link #setAmount(java.math.BigDecimal)} if possible
*/
@Deprecated
public void setAmount(double amount) {
this.amount = BigDecimal.valueOf(amount);
}

View File

@ -1,6 +1,5 @@
package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -10,7 +9,7 @@ import java.math.BigDecimal;
*
* @author Acrobot
*/
public class CurrencyFormatEvent extends Event {
public class CurrencyFormatEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
private final BigDecimal amount;

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -13,10 +12,10 @@ import java.util.UUID;
*
* @author Acrobot
*/
public class CurrencyHoldEvent extends Event {
public class CurrencyHoldEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
boolean canHold = true;
private boolean canHold = true;
private BigDecimal amount;
private UUID account;

View File

@ -2,7 +2,6 @@ package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -11,13 +10,13 @@ import java.util.UUID;
/**
* Represents a subtraction of goods from entity
*
* Use {@link CurrencyTransferEvent} if you want to transfer money from one account to another one!
*
* @author Acrobot
*/
public class CurrencySubtractEvent extends Event {
public class CurrencySubtractEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
boolean subtracted;
private BigDecimal amount;
private UUID target;
private World world;
@ -68,18 +67,22 @@ public class CurrencySubtractEvent extends Event {
/**
* @return Was the money already subtracted?
* @deprecated Use {@link #wasHandled()}
*/
@Deprecated
public boolean isSubtracted() {
return subtracted;
return wasHandled();
}
/**
* Set if the money was subtracted from the account
*
* @param subtracted Was the money subtracted?
* @deprecated Use {@link #setHandled(boolean)}
*/
@Deprecated
public void setSubtracted(boolean subtracted) {
this.subtracted = subtracted;
setHandled(subtracted);
}
/**
@ -96,6 +99,13 @@ public class CurrencySubtractEvent extends Event {
return target;
}
/**
* @param target Account from which the currency is subtracted
*/
public void setTarget(UUID target) {
this.target = target;
}
public HandlerList getHandlers() {
return handlers;
}

View File

@ -1,7 +1,8 @@
package com.Acrobot.ChestShop.Events.Economy;
import com.Acrobot.ChestShop.Events.TransactionEvent;
import org.bukkit.World;
import org.bukkit.event.Event;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import java.math.BigDecimal;
@ -12,49 +13,73 @@ import java.util.UUID;
*
* @author Acrobot
*/
public class CurrencyTransferEvent extends Event {
public class CurrencyTransferEvent extends EconomicEvent {
private static final HandlerList handlers = new HandlerList();
private BigDecimal amount;
private World world;
private UUID sender;
private UUID receiver;
private boolean success;
private BigDecimal amountSent;
private BigDecimal amountReceived;
public CurrencyTransferEvent(BigDecimal amount, UUID sender, UUID receiver, World world) {
this.amount = amount;
this.world = world;
private final Player initiator;
this.sender = sender;
this.receiver = receiver;
private UUID partner;
private Direction direction;
private final TransactionEvent transactionEvent;
public CurrencyTransferEvent(BigDecimal amount, Player initiator, UUID partner, Direction direction) {
this(amount, amount, initiator, partner, direction);
}
public CurrencyTransferEvent(double amount, UUID sender, UUID receiver, World world) {
this(BigDecimal.valueOf(amount), sender, receiver, world);
public CurrencyTransferEvent(BigDecimal amountSent, BigDecimal amountReceived, Player initiator, UUID partner, Direction direction) {
this(amountSent, amountReceived, initiator, partner, direction, null);
}
public CurrencyTransferEvent(BigDecimal amount, Player initiator, UUID partner, Direction direction, TransactionEvent transactionEvent) {
this(amount, amount, initiator, partner, direction, transactionEvent);
}
public CurrencyTransferEvent(BigDecimal amountSent, BigDecimal amountReceived, Player initiator, UUID partner, Direction direction, TransactionEvent transactionEvent) {
this.amountSent = amountSent;
this.amountReceived = amountReceived;
this.initiator = initiator;
this.partner = partner;
this.direction = direction;
this.transactionEvent = transactionEvent;
}
/**
* @return Amount of currency
* @return Amount of currency sent
* @deprecated Use {@link #getAmountSent()} and {@link #getAmountReceived()}
*/
@Deprecated
public BigDecimal getAmount() {
return amount;
return amountSent;
}
/**
* @return Amount of currency, as a double
* @deprecated Use {@link #getAmount()} if possible
*/
@Deprecated
public double getDoubleAmount() {
return amount.doubleValue();
return getAmount().doubleValue();
}
/**
* Sets the amount of currency transferred
*
* @param amount Amount to transfer
* @deprecated Use {@link #setAmountSent(BigDecimal)} and {@link #setAmountReceived(BigDecimal)}
*/
@Deprecated
public void setAmount(BigDecimal amount) {
this.amount = amount;
this.amountSent = amount;
this.amountReceived = amount;
}
/**
@ -63,45 +88,127 @@ public class CurrencyTransferEvent extends Event {
* @param amount Amount to transfer
* @deprecated Use {@link #setAmount(java.math.BigDecimal)} if possible
*/
@Deprecated
public void setAmount(double amount) {
this.amount = BigDecimal.valueOf(amount);
setAmount(BigDecimal.valueOf(amount));
}
/**
* Get the amount sent (subtracted from the sender account)
*
* @return The amount that got sent
*/
public BigDecimal getAmountSent() {
return amountSent;
}
/**
* Set the amount sent (subtracted from the sender account)
*
* @param amountSent The amount that got sent
*/
public void setAmountSent(BigDecimal amountSent) {
this.amountSent = amountSent;
}
/**
* Get the amount received (added from the receiver account)
*
* @return The amount that gets received
*/
public BigDecimal getAmountReceived() {
return amountReceived;
}
/**
* Set the amount received (added from the receiver account)
*
* @param amountReceived The amount that gets received
*/
public void setAmountReceived(BigDecimal amountReceived) {
this.amountReceived = amountReceived;
}
/**
* @return If the currency has been successfully transferred
* @deprecated Use {@link #wasHandled()}
*/
@Deprecated
public boolean hasBeenTransferred() {
return success;
return wasHandled();
}
/**
* Sets the transaction's outcome
*
* @param success If the currency has been successfully transferred
* @deprecated Use {@link #setHandled(boolean)}
*/
@Deprecated
public void setTransferred(boolean success) {
this.success = success;
setHandled(success);
}
/**
* @return the direction that the money is transacted
*/
public Direction getDirection() {
return direction;
}
/**
* Gets the {@link TransactionEvent} associated with this currency transfer event.
*
* @return the transaction event.
*/
public TransactionEvent getTransactionEvent() {
return transactionEvent;
}
/**
* Get the player who initiated this transaction
*
* @return The player who initiated this transaction
*/
public Player getInitiator() {
return initiator;
}
/**
* @return the partner of this transaction
*/
public UUID getPartner() {
return partner;
}
/**
* Set the partner of the transaction
*
* @param partner the new partner of this transaction
*/
public void setPartner(UUID partner) {
this.partner = partner;
}
/**
* @return The world in which the transaction occurs
*/
public World getWorld() {
return world;
return initiator.getWorld();
}
/**
* @return Sender of the money
*/
public UUID getSender() {
return sender;
return direction == Direction.PARTNER ? initiator.getUniqueId() : partner;
}
/**
* @return Receiver of the money
*/
public UUID getReceiver() {
return receiver;
return direction == Direction.PARTNER ? partner : initiator.getUniqueId();
}
public HandlerList getHandlers() {
@ -111,4 +218,9 @@ public class CurrencyTransferEvent extends Event {
public static HandlerList getHandlerList() {
return handlers;
}
public enum Direction {
PARTNER,
INITIATOR;
}
}

View File

@ -0,0 +1,26 @@
package com.Acrobot.ChestShop.Events.Economy;
import org.bukkit.event.Event;
public abstract class EconomicEvent extends Event {
private boolean handled = false;
/**
* Get whether or not this event was successfully handled by a listener
*
* @return Whether or not the amount was successfully handled
*/
public boolean wasHandled() {
return handled;
}
/**
* Set whether or not this event was successfully handled by a listener
*
* @param handled Whether or not the amount was successfully handled
*/
public void setHandled(boolean handled) {
this.handled = handled;
}
}

View File

@ -0,0 +1,56 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
public class ItemParseEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final String itemString;
private ItemStack item = null;
public ItemParseEvent(String itemString) {
this.itemString = itemString;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Get the item string that should be parsed
* @return The item string to parse
*/
public String getItemString() {
return itemString;
}
/**
* Set the item that the string represents
* @param item The item for the string
*/
public void setItem(ItemStack item) {
this.item = item;
}
/**
* The item that was parsed
* @return The parsed item or null if none was found
*/
public ItemStack getItem() {
return item;
}
/**
* Whether or not the item string of this event has a parsed item
* @return True if an item was successfully parsed; false if not
*/
public boolean hasItem() {
return item != null;
}
}

View File

@ -0,0 +1,72 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.ItemStack;
public class ItemStringQueryEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private String itemString = null;
private final ItemStack item;
private final int maxWidth;
/**
* Query the item string representation of a certain item with a certain length
* @param item The item to query the string for
*/
public ItemStringQueryEvent(ItemStack item) {
this(item, 0);
}
/**
* Query the item string representation of a certain item with a certain length
* @param item The item to query the string for
* @param maxWidth The max width of the item string
*/
public ItemStringQueryEvent(ItemStack item, int maxWidth) {
this.item = item;
this.maxWidth = maxWidth;
}
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
/**
* The item for which the string is queried
* @return The item
*/
public ItemStack getItem() {
return item;
}
/**
* Get the item string that represents the item
* @return The item string that represents the item
*/
public String getItemString() {
return itemString;
}
/**
* Set the item string that represents the item
* @param itemString The item string that represents the item
*/
public void setItemString(String itemString) {
this.itemString = itemString;
}
/**
* Get the max width that the result item string should have
* @return The max width of the result item string
*/
public int getMaxWidth() {
return maxWidth;
}
}

View File

@ -0,0 +1,67 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.Material;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class MaterialParseEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final String materialString;
private final short data;
private Material material = null;
public MaterialParseEvent(String materialString, short data) {
this.materialString = materialString;
this.data = data;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Get the material string that should be parsed
* @return The material string to parse
*/
public String getMaterialString() {
return materialString;
}
/**
* Get the data of legacy materials that might result in different flattening materials
* @return The data
*/
public short getData() {
return data;
}
/**
* Set the material that the string represents
* @param material The material for the string
*/
public void setMaterial(Material material) {
this.material = material;
}
/**
* The material that was parsed
* @return The parsed material or null if none was found
*/
public Material getMaterial() {
return material;
}
/**
* Whether or not the material string of this event has a parsed material
* @return True if an material was successfully parsed; false if not
*/
public boolean hasMaterial() {
return material != null;
}
}

View File

@ -1,19 +1,24 @@
package com.Acrobot.ChestShop.Events;
import com.Acrobot.ChestShop.Database.Account;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import javax.annotation.Nullable;
/**
* Represents a state before shop is created
*
* @author Acrobot
*/
public class PreShopCreationEvent extends Event {
public class PreShopCreationEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player creator;
@Nullable private Account ownerAccount = null;
private CreationOutcome outcome = CreationOutcome.SHOP_CREATED_SUCCESSFULLY;
private Sign sign;
@ -30,10 +35,25 @@ public class PreShopCreationEvent extends Event {
*
* @return Is event cancelled?
*/
@Override
public boolean isCancelled() {
return outcome != CreationOutcome.SHOP_CREATED_SUCCESSFULLY;
}
/**
* Set if event is cancelled. This sets a generic {@link CreationOutcome#OTHER};
*
* @param cancel Cancel the event?
*/
@Override
public void setCancelled(boolean cancel) {
if (cancel) {
outcome = CreationOutcome.OTHER;
} else {
outcome = CreationOutcome.SHOP_CREATED_SUCCESSFULLY;
}
}
/**
* Returns the outcome of the event
*
@ -126,6 +146,25 @@ public class PreShopCreationEvent extends Event {
return signLines;
}
/**
* Get the account of the shop owner
*
* @return the Account of the shop owner; null if not found
*/
@Nullable
public Account getOwnerAccount() {
return ownerAccount;
}
/**
* Set the account of the shop owner
*
* @param ownerAccount the Account of the shop owner
*/
public void setOwnerAccount(@Nullable Account ownerAccount) {
this.ownerAccount = ownerAccount;
}
public HandlerList getHandlers() {
return handlers;
}
@ -141,12 +180,16 @@ public class PreShopCreationEvent extends Event {
INVALID_ITEM,
INVALID_PRICE,
INVALID_QUANTITY,
ITEM_AUTOFILL,
ITEM_AUTOFILL(false),
UNKNOWN_PLAYER,
SELL_PRICE_HIGHER_THAN_BUY_PRICE,
SELL_PRICE_ABOVE_MAX,
SELL_PRICE_BELOW_MIN,
BUY_PRICE_ABOVE_MAX,
BUY_PRICE_BELOW_MIN,
NO_CHEST,
@ -159,8 +202,31 @@ public class PreShopCreationEvent extends Event {
/**
* For plugin use
*/
OTHER,
OTHER(false),
/**
* Break the sign
*/
OTHER_BREAK,
SHOP_CREATED_SUCCESSFULLY
SHOP_CREATED_SUCCESSFULLY(false);
private final boolean breakSign;
CreationOutcome() {
this.breakSign = true;
}
CreationOutcome(boolean breakSign) {
this.breakSign = breakSign;
}
/**
* Get whether or not this outcome should result in the shop sign getting broken
*
* @return Whether or not the shop sign gets broken
*/
public boolean shouldBreakSign() {
return breakSign;
}
}
}

View File

@ -6,11 +6,15 @@ import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.math.BigDecimal;
import static com.Acrobot.ChestShop.Events.PreTransactionEvent.TransactionOutcome.OTHER;
import static com.Acrobot.ChestShop.Events.PreTransactionEvent.TransactionOutcome.TRANSACTION_SUCCESFUL;
import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType;
@ -19,7 +23,7 @@ import static com.Acrobot.ChestShop.Events.TransactionEvent.TransactionType;
*
* @author Acrobot
*/
public class PreTransactionEvent extends Event {
public class PreTransactionEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Player client;
@ -32,16 +36,17 @@ public class PreTransactionEvent extends Event {
private Inventory clientInventory;
private ItemStack[] items;
private double price;
private BigDecimal exactPrice;
private TransactionOutcome transactionOutcome = TRANSACTION_SUCCESFUL;
public PreTransactionEvent(Inventory ownerInventory, Inventory clientInventory, ItemStack[] items, double price, Player client, Account ownerAccount, Sign sign, TransactionType type) {
public PreTransactionEvent(Inventory ownerInventory, Inventory clientInventory, ItemStack[] items, BigDecimal exactPrice, Player client, Account ownerAccount, Sign sign, TransactionType type) {
this.ownerInventory = ownerInventory;
this.clientInventory = (clientInventory == null ? client.getInventory() : clientInventory);
this.items = items;
this.price = price;
this.exactPrice = exactPrice;
this.client = client;
this.ownerAccount = ownerAccount;
@ -50,6 +55,14 @@ public class PreTransactionEvent extends Event {
this.transactionType = type;
}
/**
* @deprecated Use {@link #PreTransactionEvent(Inventory, Inventory, ItemStack[], BigDecimal, Player, Account, Sign, TransactionType)}
*/
@Deprecated
public PreTransactionEvent(Inventory ownerInventory, Inventory clientInventory, ItemStack[] items, double price, Player client, Account ownerAccount, Sign sign, TransactionType type) {
this(ownerInventory, clientInventory, items, BigDecimal.valueOf(price), client, ownerAccount, sign, type);
}
/**
* @return Shop's sign
*/
@ -58,19 +71,43 @@ public class PreTransactionEvent extends Event {
}
/**
* @return Total price of the items
* Get the exact total price
*
* @return Exact total price of the items
*/
public BigDecimal getExactPrice() {
return exactPrice;
}
/**
* Sets the exact price of the items
*
* @param exactPrice Price of the items
*/
public void setExactPrice(BigDecimal exactPrice) {
this.exactPrice = exactPrice;
}
/**
* Get the total price
*
* @return Total price of the items
* @deprecated Use {@link #getExactPrice()}
*/
@Deprecated
public double getPrice() {
return price;
return exactPrice.doubleValue();
}
/**
* Sets the price of the items
*
* @param price Price of the items
* @deprecated Use {@link #setExactPrice(BigDecimal)}
*/
@Deprecated
public void setPrice(double price) {
this.price = price;
this.exactPrice = BigDecimal.valueOf(price);
}
/**
@ -112,26 +149,6 @@ public class PreTransactionEvent extends Event {
this.ownerAccount = ownerAccount;
}
/**
* @return Shop's owner
* @deprecated Use {@link #getOwnerAccount}
*/
@Deprecated
public OfflinePlayer getOwner() {
return Bukkit.getOfflinePlayer(ownerAccount.getUuid());
}
/**
* Sets the shop's owner
*
* @param owner Shop owner
* @deprecated Use {@link #setOwnerAccount(Account)}
*/
@Deprecated
public void setOwner(OfflinePlayer owner) {
this.ownerAccount = NameManager.getAccount(owner.getUniqueId());
}
/**
* @return Owner's inventory
*/
@ -178,6 +195,15 @@ public class PreTransactionEvent extends Event {
return transactionOutcome != TRANSACTION_SUCCESFUL;
}
@Override
public void setCancelled(boolean cancel) {
if (cancel) {
transactionOutcome = OTHER;
} else {
transactionOutcome = TRANSACTION_SUCCESFUL;
}
}
/**
* @return Transaction's outcome
*/
@ -221,6 +247,7 @@ public class PreTransactionEvent extends Event {
NOT_ENOUGH_STOCK_IN_INVENTORY,
INVALID_SHOP,
INVALID_CLIENT_NAME,
SPAM_CLICKING_PROTECTION,
CREATIVE_MODE_PROTECTION,

View File

@ -2,22 +2,24 @@ package com.Acrobot.ChestShop.Events.Protection;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import javax.annotation.Nullable;
/**
* @author Acrobot
*/
public class BuildPermissionEvent extends Event {
public class BuildPermissionEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player player;
private Location chest, sign;
private int disallowed = 0;
private int received = 0;
private boolean allowed = true;
public BuildPermissionEvent(Player player, Location chest, Location sign) {
public BuildPermissionEvent(Player player, @Nullable Location chest, Location sign) {
this.player = player;
this.chest = chest;
this.sign = sign;
@ -27,7 +29,7 @@ public class BuildPermissionEvent extends Event {
return player;
}
public Location getChest() {
public @Nullable Location getChest() {
return chest;
}
@ -36,24 +38,19 @@ public class BuildPermissionEvent extends Event {
}
public void allow() {
received++;
allowed = true;
}
public boolean isAllowed() {
return disallowed != received || received == 0;
return allowed;
}
public void allow(boolean yesOrNot) {
if (yesOrNot) {
allow();
} else {
disallow();
}
allowed = yesOrNot;
}
public void disallow() {
received++;
disallowed++;
allowed = false;
}
public HandlerList getHandlers() {
@ -63,4 +60,14 @@ public class BuildPermissionEvent extends Event {
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return !isAllowed();
}
@Override
public void setCancelled(boolean cancel) {
allow(!cancel);
}
}

View File

@ -1,10 +1,13 @@
package com.Acrobot.ChestShop.Events.Protection;
import com.Acrobot.ChestShop.Security;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import java.util.UUID;
/**
* @author Acrobot
@ -12,14 +15,26 @@ import org.bukkit.event.HandlerList;
public class ProtectBlockEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private Player player;
private Block block;
private final Player player;
private final UUID protectionOwner;
private final Block block;
private final Security.Type type;
boolean isProtected = false;
public ProtectBlockEvent(Block block, Player player) {
this(block, player, player.getUniqueId());
}
public ProtectBlockEvent(Block block, Player player, UUID protectionOwner) {
this(block, player, protectionOwner, Security.Type.PRIVATE);
}
public ProtectBlockEvent(Block block, Player player, UUID protectionOwner, Security.Type type) {
this.block = block;
this.player = player;
this.protectionOwner = protectionOwner;
this.type = type;
}
public boolean isProtected() {
@ -38,6 +53,14 @@ public class ProtectBlockEvent extends Event {
return player;
}
public UUID getProtectionOwner() {
return protectionOwner;
}
public Security.Type getType() {
return type;
}
public HandlerList getHandlers() {
return handlers;
}

View File

@ -13,6 +13,7 @@ public class ProtectionCheckEvent extends Event {
private Result result = Result.DEFAULT;
private boolean ignoreBuiltInProtection = false;
private boolean checkManagement = true;
private Block block;
private Player player;
@ -27,10 +28,21 @@ public class ProtectionCheckEvent extends Event {
this.ignoreBuiltInProtection = ignoreBuiltInProtection;
}
public ProtectionCheckEvent(Block block, Player player, boolean ignoreBuiltInProtection, boolean checkManagement) {
this.block = block;
this.player = player;
this.ignoreBuiltInProtection = ignoreBuiltInProtection;
this.checkManagement = checkManagement;
}
public boolean isBuiltInProtectionIgnored() {
return ignoreBuiltInProtection;
}
public boolean checkCanManage() {
return checkManagement;
}
public Result getResult() {
return result;
}

View File

@ -1,6 +1,8 @@
package com.Acrobot.ChestShop.Events;
import com.Acrobot.ChestShop.Database.Account;
import org.bukkit.block.Chest;
import org.bukkit.block.Container;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
@ -20,13 +22,25 @@ public class ShopCreatedEvent extends Event {
private final Sign sign;
private final String[] signLines;
@Nullable private final Chest chest;
@Nullable private final Account ownerAccount;
@Nullable private final Container container;
@Deprecated
public ShopCreatedEvent(Player creator, Sign sign, @Nullable Chest chest, String[] signLines) {
this(creator, sign, (Container) chest, signLines);
}
@Deprecated
public ShopCreatedEvent(Player creator, Sign sign, @Nullable Container container, String[] signLines) {
this(creator, sign, container, signLines, null);
}
public ShopCreatedEvent(Player creator, Sign sign, @Nullable Container container, String[] signLines, @Nullable Account ownerAccount) {
this.creator = creator;
this.sign = sign;
this.chest = chest;
this.container = container;
this.signLines = signLines.clone();
this.ownerAccount = ownerAccount;
}
/**
@ -67,12 +81,39 @@ public class ShopCreatedEvent extends Event {
}
/**
* Returns the shop's chest (if applicable)
* Returns the shop's container (if applicable)
*
* @return Shop's chest
* @return Shop's container
*/
@Nullable public Container getContainer() {
return container;
}
/**
* @deprecated Use {@link #getContainer()}
*/
@Deprecated
@Nullable public Chest getChest() {
return chest;
return container instanceof Chest ? (Chest) container : null;
}
/**
* Get the account of the shop's owner
*
* @return The account of the shop's owner; null if no Account could be found
*/
@Nullable
public Account getOwnerAccount() {
return ownerAccount;
}
/**
* Check whether or not the created shop is owned by the creator
*
* @return <tt>true</tt> if the owner account is the creators one (or null); <tt>false</tt> if it's not
*/
public boolean createdByOwner() {
return ownerAccount == null || ownerAccount.getUuid().equals(creator.getUniqueId());
}
public HandlerList getHandlers() {

View File

@ -1,6 +1,7 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.block.Chest;
import org.bukkit.block.Container;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
@ -19,12 +20,17 @@ public class ShopDestroyedEvent extends Event {
private final Player destroyer;
private final Sign sign;
private final Chest chest;
private final Container container;
@Deprecated
public ShopDestroyedEvent(@Nullable Player destroyer, Sign sign, @Nullable Chest chest) {
this(destroyer, sign, (Container) chest);
}
public ShopDestroyedEvent(@Nullable Player destroyer, Sign sign, @Nullable Container container) {
this.destroyer = destroyer;
this.sign = sign;
this.chest = chest;
this.container = container;
}
/**
@ -37,8 +43,16 @@ public class ShopDestroyedEvent extends Event {
/**
* @return Shop's chest
*/
@Nullable public Container getContainer() {
return container;
}
/**
* @deprecated Use {@link #getContainer()}
*/
@Deprecated
@Nullable public Chest getChest() {
return chest;
return container instanceof Chest ? (Chest) container : null;
}
/**

View File

@ -0,0 +1,58 @@
package com.Acrobot.ChestShop.Events;
import org.bukkit.block.Sign;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Represents a /shopinfo call or middle click on a sign
*
* @author Phoenix616
*/
public class ShopInfoEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Player sender;
private Sign sign;
private boolean cancelled = false;
public ShopInfoEvent(Player sender, Sign sign) {
this.sender = sender;
this.sign = sign;
}
/**
* @return The Player who initiated the call
*/
public Player getSender() {
return sender;
}
/**
* @return The shop sign
*/
public Sign getSign() {
return sign;
}
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancel) {
cancelled = cancel;
}
}

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